Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle ref assemblies in up-to-date check #2414

Merged
merged 7 commits into from
Jun 21, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<Compile Update="ProjectSystem\Rules\ResolvedAnalyzerReference.xaml.cs">
<DependentUpon>ResolvedAnalyzerReference.xaml</DependentUpon>
</Compile>
<Compile Update="ProjectSystem\Rules\ResolvedCompilationReference.cs">
<DependentUpon>ResolvedCompilationReference.xaml</DependentUpon>
</Compile>
<Compile Update="ProjectSystem\Rules\ResolvedPackageReference.cs">
<DependentUpon>ResolvedPackageReference.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -91,6 +94,9 @@
<Compile Update="ProjectSystem\Rules\UpToDateCheckOutput.cs">
<DependentUpon>UpToDateCheckOutput.xaml</DependentUpon>
</Compile>
<Compile Update="ProjectSystem\Rules\CopyUpToDateMarker.cs">
<DependentUpon>CopyUpToDateMarker.xaml</DependentUpon>
</Compile>
<Compile Update="ProjectSystem\Rules\Folder.cs">
<DependentUpon>Folder.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -168,6 +174,10 @@
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
</XamlPropertyRule>
<XamlPropertyRule Include="ProjectSystem\Rules\CopyUpToDateMarker.xaml">
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
</XamlPropertyRule>
<XamlPropertyRule Include="ProjectSystem\Rules\Folder.xaml">
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
Expand Down Expand Up @@ -280,6 +290,10 @@
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
</XamlPropertyRule>
<XamlPropertyRule Include="ProjectSystem\Rules\ResolvedCompilationReference.xaml">
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
</XamlPropertyRule>
<XamlPropertyRule Include="ProjectSystem\Rules\ProjectDebugger.xaml">
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
<SubType>Designer</SubType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ private void Log(LogLevel level, string message, params object[] values)

private static ImmutableHashSet<string> ReferenceSchemas => ImmutableHashSet<string>.Empty
.Add(ResolvedAnalyzerReference.SchemaName)
.Add(ResolvedAssemblyReference.SchemaName)
.Add(ResolvedCOMReference.SchemaName)
.Add(ResolvedProjectReference.SchemaName);
.Add(ResolvedCompilationReference.SchemaName);

private static ImmutableHashSet<string> UpToDateSchemas => ImmutableHashSet<string>.Empty
.Add(CopyUpToDateMarker.SchemaName)
.Add(UpToDateCheckInput.SchemaName)
.Add(UpToDateCheckOutput.SchemaName);

private static ImmutableHashSet<string> ProjectPropertiesSchemas => ImmutableHashSet<string>.Empty
.Add(ConfigurationGeneral.SchemaName)
.Union(ReferenceSchemas);
.Union(ReferenceSchemas)
.Union(UpToDateSchemas);

private readonly IProjectSystemOptions _projectSystemOptions;
private readonly ConfiguredProject _configuredProject;
Expand All @@ -79,11 +83,15 @@ private void Log(LogLevel level, string message, params object[] values)
private bool _isDisabled = true;
private bool _itemsChangedSinceLastCheck = true;
private string _msBuildProjectFullPath;
private string _markerFile;
private HashSet<string> _imports = new HashSet<string>();
private HashSet<string> _itemTypes = new HashSet<string>();
private Dictionary<string, HashSet<string>> _items = new Dictionary<string, HashSet<string>>();
private Dictionary<string, HashSet<string>> _references = new Dictionary<string, HashSet<string>>();
private HashSet<string> _customInputs = new HashSet<string>();
private HashSet<string> _customOutputs = new HashSet<string>();
private HashSet<string> _analyzerReferences = new HashSet<string>();
private HashSet<string> _compilationReferences = new HashSet<string>();
private HashSet<string> _referenceMarkerFiles = new HashSet<string>();
private Dictionary<string, HashSet<string>> _outputGroups = new Dictionary<string, HashSet<string>>();

[ImportingConstructor]
Expand Down Expand Up @@ -117,14 +125,51 @@ private void OnProjectChanged(IProjectSubscriptionUpdate e)
_isDisabled = disableFastUpToDateCheckString != null && string.Equals(disableFastUpToDateCheckString, TrueValue, StringComparison.OrdinalIgnoreCase);

_msBuildProjectFullPath = e.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildProjectFullPathProperty, _msBuildProjectFullPath);
foreach (var referenceSchema in ReferenceSchemas)

if (e.ProjectChanges.TryGetValue(ResolvedAnalyzerReference.SchemaName, out var changes) &&
changes.Difference.AnyChanges)
{
if (e.ProjectChanges.TryGetValue(referenceSchema, out var changes) &&
changes.Difference.AnyChanges)
_analyzerReferences = new HashSet<string>(changes.After.Items.Select(item => item.Value[ResolvedPath]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which sort of string comparison do we want to use for this HashSet?

}

if (e.ProjectChanges.TryGetValue(ResolvedCompilationReference.SchemaName, out changes) &&
changes.Difference.AnyChanges)
{
_compilationReferences.Clear();
_referenceMarkerFiles.Clear();

foreach (var item in changes.After.Items)
{
_references[referenceSchema] = new HashSet<string>(changes.After.Items.Select(item => item.Value[ResolvedPath]));
_compilationReferences.Add(item.Value[ResolvedPath]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also

                    _compilationReferences.Add(item.Value[OriginalPath]);

if it exists?

if (!string.IsNullOrWhiteSpace(item.Value[CopyUpToDateMarker.SchemaName]))
{
_referenceMarkerFiles.Add(item.Value[CopyUpToDateMarker.SchemaName]);
}
}
}

if (e.ProjectChanges.TryGetValue(UpToDateCheckInput.SchemaName, out var inputs) &&
inputs.Difference.AnyChanges)
{
_customInputs = new HashSet<string>(inputs.After.Items.Select(item => item.Value[FullPath]));
}

if (e.ProjectChanges.TryGetValue(UpToDateCheckOutput.SchemaName, out var outputs) &&
outputs.Difference.AnyChanges)
{
_customOutputs = new HashSet<string>(outputs.After.Items.Select(item => item.Value[FullPath]));
}

if (e.ProjectChanges.TryGetValue(CopyUpToDateMarker.SchemaName, out var upToDateMarkers) &&
upToDateMarkers.Difference.AnyChanges &&
upToDateMarkers.After.Items.Count == 1)
{
_markerFile = upToDateMarkers.After.Items.Single().Value[FullPath];
}
else
{
_markerFile = null;
}
}

private void OnProjectImportsChanged(IProjectImportTreeSnapshot e)
Expand Down Expand Up @@ -161,12 +206,6 @@ private void OnSourceItemChanged(IProjectSubscriptionUpdate e, IProjectItemSchem
_items[itemType.Key] = new HashSet<string>(items);
_itemsChangedSinceLastCheck = true;
}

if (e.ProjectChanges.TryGetValue(UpToDateCheckOutput.SchemaName, out var outputs) &&
outputs.Difference.AnyChanges)
{
_customOutputs = new HashSet<string>(outputs.After.Items.Select(item => item.Value[FullPath]));
}
}

private void OnOutputGroupChanged(IImmutableDictionary<string, IOutputGroup> e)
Expand Down Expand Up @@ -289,10 +328,9 @@ private HashSet<string> CollectInputs(Logger logger)
AddInputs(logger, inputs, pair.Value, pair.Key);
}

foreach (var pair in _references)
{
AddInputs(logger, inputs, pair.Value, pair.Key);
}
AddInputs(logger, inputs, _analyzerReferences, ResolvedAnalyzerReference.SchemaName);
AddInputs(logger, inputs, _compilationReferences, ResolvedCompilationReference.SchemaName);
AddInputs(logger, inputs, _customInputs, UpToDateCheckInput.SchemaName);

return inputs;
}
Expand All @@ -306,20 +344,20 @@ private HashSet<string> CollectOutputs(Logger logger)
AddOutputs(logger, outputs, pair.Value, pair.Key);
}

AddOutputs(logger, outputs, _customOutputs, "UpToDateCheckOutput");
AddOutputs(logger, outputs, _customOutputs, UpToDateCheckOutput.SchemaName);

return outputs;
}

private (DateTime? time, string path) GetLatestInput(HashSet<string> inputs, IDictionary<string, DateTime> timestampCache)
private (DateTime? time, string path) GetLatestInput(HashSet<string> inputs, IDictionary<string, DateTime> timestampCache, bool ignoreMissing = false)
{
DateTime? latest = DateTime.MinValue;
string latestPath = null;

foreach (var input in inputs)
{
var time = GetTimestamp(input, timestampCache);
if (latest != null && (time == null || time > latest))
if (latest != null && (time == null && !ignoreMissing || time > latest))
{
latest = time;
latestPath = input;
Expand Down Expand Up @@ -347,6 +385,50 @@ private HashSet<string> CollectOutputs(Logger logger)
return (earliest, earliestPath);
}

// Reference assembly copy markers are strange. The property is always going to be present on
// references to SDK-based projects, regardless of whether or not those referenced projects
// will actually produce a marker. And an item always will be present in an SDK-based project,
// regardless of whether or not the project produces a marker. So, basically, we only check
// here if the project actually produced a marker and we only check it against references that
// actually produced a marker.
private bool CheckMarkers(Logger logger, IDictionary<string, DateTime> timestampCache)
{
if (string.IsNullOrWhiteSpace(_markerFile) || !_referenceMarkerFiles.Any())
{
return true;
}

foreach (var referenceMarkerFile in _referenceMarkerFiles)
{
logger.Verbose("Found possible input marker '{0}'.", referenceMarkerFile);
}

logger.Verbose("Found possible output marker '{0}'.", _markerFile);

var latestInputMarker = GetLatestInput(_referenceMarkerFiles, timestampCache, true);
var outputMarkerTime = GetTimestamp(_markerFile, timestampCache);

if (latestInputMarker.time != null)
{
logger.Info("Lastest write timestamp on input marker is {0} on '{1}'.", latestInputMarker.time.Value, latestInputMarker.path);
}
else
{
logger.Info("No input markers exist, skipping marker check.");
}

if (outputMarkerTime != null)
{
logger.Info("Write timestamp on output marker is {0} on '{1}'.", outputMarkerTime, _markerFile);
}
else
{
logger.Info("Output marker '{0}' does not exist, skipping marker check.", _markerFile);
}

return latestInputMarker.time == null || outputMarkerTime == null || outputMarkerTime > latestInputMarker.time;
}

public async Task<bool> IsUpToDateAsync(BuildAction buildAction, TextWriter logWriter, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down Expand Up @@ -385,7 +467,13 @@ private HashSet<string> CollectOutputs(Logger logger)
}

// We are up to date if the earliest output write happened after the latest input write
var isUpToDate = latestInput.time != null && earliestOutput.time != null && earliestOutput.time > latestInput.time;
var markersUpToDate = CheckMarkers(logger, timestampCache);
var isUpToDate =
latestInput.time != null
&& earliestOutput.time != null
&& earliestOutput.time > latestInput.time
&& markersUpToDate;

logger.Info("Project is{0} up to date.", (!isUpToDate ? " not" : ""));

return isUpToDate;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;

namespace Microsoft.VisualStudio.ProjectSystem
{
[ExcludeFromCodeCoverage]
[SuppressMessage("Style", "IDE0016:Use 'throw' expression")]
partial class CopyUpToDateMarker
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.-->
<Rule
Name="CopyUpToDateMarker"
DisplayName="Up-to-date check marker for reference assemblies"
PageTemplate="generic"
Description="File Properties"
xmlns="http://schemas.microsoft.com/build/2009/properties">
<Rule.DataSource>
<DataSource Persistence="ProjectFile" HasConfigurationCondition="False" ItemType="CopyUpToDateMarker" SourceOfDefaultValue="AfterContext" />
</Rule.DataSource>
<Rule.Categories>
<Category Name="Advanced" DisplayName="Advanced" />
<Category Name="Misc" DisplayName="Misc" />
</Rule.Categories>

<StringProperty
Name="FullPath"
DisplayName="Full Path"
ReadOnly="true"
Category="Misc"
Description="Location of the file.">
<StringProperty.DataSource>
<DataSource Persistence="Intrinsic" ItemType="CopyUpToDateMarker" PersistedName="FullPath" SourceOfDefaultValue="AfterContext" />
</StringProperty.DataSource>
</StringProperty>

<BoolProperty Name="Visible" Default="false" Visible="false" />
</Rule>
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,9 @@
<NameValuePair Name="DefaultMetadata_Generator" Value="ResXFileCodeGenerator" />
</ContentType>

<ContentType
Name="UpToDateCheckInput"
DisplayName="Up-to-date check input"
ItemType="UpToDateCheckInput">
</ContentType>

<ContentType
Name="UpToDateCheckOutput"
DisplayName="Up-to-date check output"
ItemType="UpToDateCheckOutput">
</ContentType>

<ItemType Name="None" DisplayName="None"/>
<ItemType Name="Content" DisplayName="Content" />
<ItemType Name="EmbeddedResource" DisplayName="Embedded resource"/>
<ItemType Name="UpToDateCheckInput" DisplayName="Up-to-date check input"/>
<ItemType Name="UpToDateCheckOutput" DisplayName="Up-to-date check output" UpToDateCheckInput="False"/>

<FileExtension Name=".asax" ContentType="Asax" />
<FileExtension Name=".asmx" ContentType="HTML" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;

namespace Microsoft.VisualStudio.ProjectSystem
{
[ExcludeFromCodeCoverage]
[SuppressMessage("Style", "IDE0016:Use 'throw' expression")]
partial class ResolvedCompilationReference
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -->
<Rule
Name="ResolvedCompilationReference"
DisplayName="Resolved Compilation Reference"
PageTemplate="generic"
Description="Resolved Compilation Reference Properties"
xmlns="http://schemas.microsoft.com/build/2009/properties">
<Rule.DataSource>
<DataSource Persistence="ResolvedReference" ItemType="ReferencePathWithRefAssemblies" HasConfigurationCondition="False" SourceOfDefaultValue="AfterContext"
SourceType="TargetResults" MSBuildTarget="CollectResolvedCompilationReferencesDesignTime"/>
</Rule.DataSource>

<StringProperty Name="ResolvedPath" ReadOnly="True" DisplayName="Path" Description="Location of the referenced assembly.">
<StringProperty.DataSource>
<DataSource PersistedName="Identity" SourceOfDefaultValue="AfterContext" />
</StringProperty.DataSource>
</StringProperty>

<StringProperty Name="CopyUpToDateMarker" ReadOnly="True" Visible="False"/>
</Rule>
11 changes: 11 additions & 0 deletions src/Targets/Microsoft.Managed.DesignTime.targets
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@
<Context>File</Context>
</PropertyPageSchema>

<PropertyPageSchema Include="$(ManagedXamlResourcesDirectory)CopyUpToDateMarker.xaml">
<Context>File</Context>
</PropertyPageSchema>

<PropertyPageSchema Include="$(ManagedXamlResourcesDirectory)SpecialFolder.xaml">
<Context>File;ProjectSubscriptionService</Context>
</PropertyPageSchema>
Expand Down Expand Up @@ -240,6 +244,10 @@
<PropertyPageSchema Include="$(ManagedXamlResourcesDirectory)ResolvedAnalyzerReference.xaml">
<Context>ProjectSubscriptionService;BrowseObject</Context>
</PropertyPageSchema>

<PropertyPageSchema Include="$(ManagedXamlResourcesDirectory)ResolvedCompilationReference.xaml">
<Context>ProjectSubscriptionService</Context>
</PropertyPageSchema>
</ItemGroup>

<!-- Targets -->
Expand All @@ -266,5 +274,8 @@

<!-- This target collects all Analyzers in the project. -->
<Target Name="CollectAnalyzersDesignTime" DependsOnTargets="CompileDesignTime" Returns="@(Analyzer)" />

<!-- This target collects all the resolved references that are used to actually compile. -->
<Target Name="CollectResolvedCompilationReferencesDesignTime" DependsOnTargets="CompileDesignTime" Returns="@(ReferencePathWithRefAssemblies)" />

</Project>