diff --git a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml
index 57e38860ffe..16153962a0d 100644
--- a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml
+++ b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml
@@ -6,7 +6,7 @@
-
+
diff --git a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml.vb b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml.vb
index a004504a7c5..031f3576eda 100644
--- a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml.vb
+++ b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageControl.xaml.vb
@@ -18,7 +18,7 @@ Namespace Microsoft.VisualStudio.Editors.OptionPages
Dim binding = New Binding() With {
.Source = _generalOptions,
- .Path = New Windows.PropertyPath(NameOf(GeneralOptions.FastUpToDateCheckDisabled)),
+ .Path = New Windows.PropertyPath(NameOf(GeneralOptions.FastUpToDateCheckEnabled)),
.UpdateSourceTrigger = UpdateSourceTrigger.Explicit
}
@@ -27,11 +27,11 @@ Namespace Microsoft.VisualStudio.Editors.OptionPages
binding = New Binding() With {
.Source = _generalOptions,
- .Path = New Windows.PropertyPath(NameOf(GeneralOptions.OutputPaneEnabled)),
+ .Path = New Windows.PropertyPath(NameOf(GeneralOptions.VerboseFastUpToDateLogging)),
.UpdateSourceTrigger = UpdateSourceTrigger.Explicit
}
- bindingExpression = OutputPane.SetBinding(CheckBox.IsCheckedProperty, binding)
+ bindingExpression = VerboseFastUpToDateLogging.SetBinding(CheckBox.IsCheckedProperty, binding)
AddBinding(bindingExpression)
End Sub
End Class
diff --git a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.Designer.vb b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.Designer.vb
index 6f0854cb439..92120bb8c67 100644
--- a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.Designer.vb
+++ b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.Designer.vb
@@ -49,23 +49,23 @@ Namespace My.Resources
Return resourceMan
End Get
End Property
-
+
'''
''' Overrides the current thread's CurrentUICulture property for all
''' resource lookups using this strongly typed resource class.
'''
- _
+
Public Shared Property Culture() As Global.System.Globalization.CultureInfo
Get
Return resourceCulture
End Get
Set
- resourceCulture = value
+ resourceCulture = Value
End Set
End Property
-
+
'''
- ''' Looks up a localized string similar to Always call MSBuild, even if a project appears to be up to date..
+ ''' Looks up a localized string similar to Don't call MSBuild if a project appears to be up to date..
'''
Public Shared ReadOnly Property General_FastUpToDateCheck() As String
Get
@@ -74,11 +74,11 @@ Namespace My.Resources
End Property
'''
- ''' Looks up a localized string similar to Log project information to the Output window..
+ ''' Looks up a localized string similar to Log diagnostic information for project up to date checks to the output window..
'''
- Public Shared ReadOnly Property General_OutputPane() As String
+ Public Shared ReadOnly Property General_VerboseFastUpToDateLogging() As String
Get
- Return ResourceManager.GetString("General_OutputPane", resourceCulture)
+ Return ResourceManager.GetString("General_VerboseFastUpToDateLogging", resourceCulture)
End Get
End Property
End Class
diff --git a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.resx b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.resx
index ebb5f0f6ff2..bdba7df8263 100644
--- a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.resx
+++ b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptionPageResources.resx
@@ -118,9 +118,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- Always call MSBuild, even if a project appears to be up to date.
+ Don't call MSBuild if a project appears to be up to date.
-
- Log project information to the Output window.
+
+ Log diagnostic information for project up to date checks to the output window.
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptions.vb b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptions.vb
index 798a3270e52..bda823eed99 100644
--- a/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptions.vb
+++ b/src/Microsoft.VisualStudio.Editors/OptionPages/GeneralOptions.vb
@@ -9,26 +9,26 @@ Namespace Microsoft.VisualStudio.Editors.OptionPages
Private Class SVsSettingsPersistenceManager
End Class
- Private Const FastUpToDateSettingKey As String = "ManagedProjectSystem\FastUpToDateCheckDisabled"
- Private Const OutputPaneSettingKey As String = "ManagedProjectSystem\OutputPaneEnabled"
+ Private Const FastUpToDateEnabledSettingKey As String = "ManagedProjectSystem\FastUpToDateCheckEnabled"
+ Private Const VerboseFastUpToDateLoggingSettingKey As String = "ManagedProjectSystem\VerboseFastUpToDateLogging"
Private ReadOnly _settingsManager As ISettingsManager
- Public Property FastUpToDateCheckDisabled As Boolean
+ Public Property FastUpToDateCheckEnabled As Boolean
Get
- Return If(_settingsManager?.GetValueOrDefault(FastUpToDateSettingKey, False), False)
+ Return If(_settingsManager?.GetValueOrDefault(FastUpToDateEnabledSettingKey, True), True)
End Get
Set
- _settingsManager.SetValueAsync(FastUpToDateSettingKey, Value, isMachineLocal:=False)
+ _settingsManager.SetValueAsync(FastUpToDateEnabledSettingKey, Value, isMachineLocal:=False)
End Set
End Property
- Public Property OutputPaneEnabled As Boolean
+ Public Property VerboseFastUpToDateLogging As Boolean
Get
- Return If(_settingsManager?.GetValueOrDefault(OutputPaneSettingKey, False), False)
+ Return If(_settingsManager?.GetValueOrDefault(VerboseFastUpToDateLoggingSettingKey, False), False)
End Get
Set
- _settingsManager.SetValueAsync(OutputPaneSettingKey, Value, isMachineLocal:=False)
+ _settingsManager.SetValueAsync(VerboseFastUpToDateLoggingSettingKey, Value, isMachineLocal:=False)
End Set
End Property
diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/EnvironmentVariableProjectSystemOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/EnvironmentVariableProjectSystemOptions.cs
deleted file mode 100644
index 9c68be6d90c..00000000000
--- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/EnvironmentVariableProjectSystemOptions.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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;
-using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.ProjectSystem.Utilities;
-
-namespace Microsoft.VisualStudio.ProjectSystem.VS
-{
- [Export(typeof(IProjectSystemOptions))]
- internal class EnvironmentVariableProjectSystemOptions : IProjectSystemOptions
- {
- private readonly IEnvironmentHelper _environment;
-
- [ImportingConstructor]
- public EnvironmentVariableProjectSystemOptions(IEnvironmentHelper environment)
- {
- Requires.NotNull(environment, nameof(environment));
-
- _environment = environment;
- }
-
- public bool IsProjectOutputPaneEnabled
- {
- get
- {
-#if DEBUG
- return true;
-#else
- return IsEnabled("PROJECTSYSTEM_PROJECTOUTPUTPANEENABLED");
-#endif
- }
- }
-
- private bool IsEnabled(string variable)
- {
- string value = _environment.GetEnvironmentVariable(variable);
-
- return string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs
new file mode 100644
index 00000000000..ea98298d143
--- /dev/null
+++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs
@@ -0,0 +1,65 @@
+// 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;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.ProjectSystem.Utilities;
+using Microsoft.VisualStudio.Settings;
+using Microsoft.VisualStudio.Shell;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace Microsoft.VisualStudio.ProjectSystem.VS
+{
+ [Export(typeof(IProjectSystemOptions))]
+ internal class ProjectSystemOptions : IProjectSystemOptions
+ {
+ [Guid("9B164E40-C3A2-4363-9BC5-EB4039DEF653")]
+ private class SVsSettingsPersistenceManager { }
+
+ private const string FastUpToDateEnabledSettingKey = @"ManagedProjectSystem\FastUpToDateCheckEnabled";
+ private const string VerboseFastUpToDateLoggingSettingKey = @"ManagedProjectSystem\VerboseFastUpToDateLogging";
+
+ private readonly IVsOptionalService _settingsManager;
+ private readonly IEnvironmentHelper _environment;
+
+ [ImportingConstructor]
+ private ProjectSystemOptions(IEnvironmentHelper environment, IVsOptionalService settingsManager)
+ {
+ Requires.NotNull(environment, nameof(environment));
+
+ _environment = environment;
+ _settingsManager = settingsManager;
+ }
+
+ public bool IsProjectOutputPaneEnabled
+ {
+ get
+ {
+#if DEBUG
+ return true;
+#else
+ return IsEnabled("PROJECTSYSTEM_PROJECTOUTPUTPANEENABLED");
+#endif
+ }
+ }
+
+ public async Task GetIsFastUpToDateCheckEnabledAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ return _settingsManager.Value?.GetValueOrDefault(FastUpToDateEnabledSettingKey, true) ?? true;
+ }
+
+ public async Task GetVerboseFastUpToDateLoggingAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ return _settingsManager.Value?.GetValueOrDefault(VerboseFastUpToDateLoggingSettingKey, false) ?? false;
+ }
+
+ private bool IsEnabled(string variable)
+ {
+ string value = _environment.GetEnvironmentVariable(variable);
+
+ return string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/BuildUpToDateCheck.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/BuildUpToDateCheck.cs
new file mode 100644
index 00000000000..dfa2430a0d1
--- /dev/null
+++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/BuildUpToDateCheck.cs
@@ -0,0 +1,219 @@
+// 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 Microsoft.VisualStudio.ProjectSystem.Build;
+using Microsoft.VisualStudio.ProjectSystem.References;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Build.Framework;
+using System.Collections.Immutable;
+
+namespace Microsoft.VisualStudio.ProjectSystem
+{
+ [AppliesTo(ProjectCapability.CSharpOrVisualBasicOrFSharp)]
+ [Export(typeof(IBuildUpToDateCheckProvider))]
+ internal class BuildUpToDateCheck : IBuildUpToDateCheckProvider
+ {
+ private static string[] KnownOutputGroups = { "Symbols", "Built", "ContentFiles", "Documentation", "LocalizedResourceDlls" };
+
+ private readonly IProjectLockService _projectLockService;
+ private readonly IProjectSystemOptions _projectSystemOptions;
+ private readonly ConfiguredProject _configuredProject;
+ private readonly ProjectProperties _projectProperties;
+ private readonly IProjectItemProvider _projectItemProvider;
+ private readonly IProjectItemSchemaService _projectItemsSchema;
+ private readonly Lazy _fileTimestampCache;
+
+ [ImportingConstructor]
+ public BuildUpToDateCheck(
+ IProjectLockService projectLockService,
+ IProjectSystemOptions projectSystemOptions,
+ ConfiguredProject configuredProject,
+ ProjectProperties projectProperties,
+ [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider projectItemProvider,
+ IProjectItemSchemaService projectItemSchema,
+ Lazy fileTimestampCache)
+ {
+ _projectLockService = projectLockService;
+ _projectSystemOptions = projectSystemOptions;
+ _configuredProject = configuredProject;
+ _projectProperties = projectProperties;
+ _projectItemProvider = projectItemProvider;
+ _projectItemsSchema = projectItemSchema;
+ _fileTimestampCache = fileTimestampCache;
+ }
+
+ private void Log(TextWriter logger, string message, params object[] values) => logger?.WriteLine("FastUpToDate: " + string.Format(message, values));
+
+ private DateTime? GetLatestTimestamp(TextWriter logger, IEnumerable paths, IDictionary timestampCache)
+ {
+ var latestTime = DateTime.MinValue;
+
+ foreach (var path in paths)
+ {
+ if (!timestampCache.TryGetValue(path, out var time))
+ {
+ var info = new FileInfo(path);
+ if (info.Exists)
+ {
+ time = info.LastWriteTimeUtc;
+ timestampCache[path] = time;
+ Log(logger, "Path '{0}' had live timestamp '{1}'.", path, time);
+ }
+ else
+ {
+ Log(logger, "Path '{0}' did not exist.", path);
+ return null;
+ }
+ }
+ else
+ {
+ Log(logger, "Path '{0}' had cached timestamp '{1}'.", path, time);
+ }
+
+ if (latestTime < time)
+ {
+ latestTime = time;
+ }
+ }
+
+ return latestTime;
+ }
+
+ private async Task CheckReferencesAsync(TextWriter logger, string name, IResolvableReferencesService service, HashSet inputs)
+ where TUnresolvedReference : IProjectItem, TResolvedReference
+ where TResolvedReference : class, IReference
+ {
+ if (service != null)
+ {
+ Log(logger, "Checking reference type '{0}'.", name);
+
+ foreach (var resolvedReference in await service.GetResolvedReferencesAsync())
+ {
+ var fullPath = await resolvedReference.GetFullPathAsync();
+ Log(logger, "Adding input reference '{0}'.", fullPath);
+ inputs.Add(fullPath);
+ }
+ }
+ }
+
+ public async Task IsUpToDateAsync(BuildAction buildAction, TextWriter logger, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ EventHandler designTimeBuildNotifier = (sender, e) => { Log(logger, "Design time build started!"); };
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (buildAction != BuildAction.Build)
+ {
+ return false;
+ }
+
+ if (!await _projectSystemOptions.GetVerboseFastUpToDateLoggingAsync())
+ {
+ logger = null;
+ }
+
+ using (var access = await _projectLockService.ReadLockAsync(cancellationToken))
+ {
+ var project = await access.GetProjectAsync(_configuredProject, cancellationToken);
+ var timestampCache = _fileTimestampCache != null ? _fileTimestampCache.Value.TimestampCache : new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ Log(logger, "Starting check for project '{0}'.", project.FullPath);
+
+ ConfigurationGeneral general = await _projectProperties.GetConfigurationGeneralPropertiesAsync();
+
+ if ((bool?)await general.DisableFastUpToDateCheck.GetValueAsync() == true)
+ {
+ Log(logger, "Disabled because the 'DisableFastUpToDateCheckProperty' property is true.");
+ return false;
+ }
+
+ var inputs = new HashSet();
+
+ // add the project file
+ if (!string.IsNullOrEmpty(_configuredProject.UnconfiguredProject.FullPath))
+ {
+ inputs.Add(project.FullPath);
+ Log(logger, "Adding input project file {0}.", project.FullPath);
+ }
+
+ IEnumerable imports = project.Imports.Select(i => i.ImportedProject.FullPath);
+ foreach (var import in imports)
+ {
+ Log(logger, "Adding input project import {0}.", import);
+ inputs.Add(import);
+ }
+
+ var projectItemSchemaValue = (await _projectItemsSchema.GetSchemaAsync(cancellationToken)).Value;
+
+ foreach (var item in await _projectItemProvider.GetItemsAsync())
+ {
+ var itemType = projectItemSchemaValue.GetItemType(item);
+ if (itemType != null && itemType.UpToDateCheckInput)
+ {
+ var path = item.EvaluatedIncludeAsFullPath;
+ Log(logger, "Input item type '{0}' path '{1}'.", itemType.Name, path);
+ inputs.Add(path);
+ }
+ }
+
+ await CheckReferencesAsync(logger, nameof(_configuredProject.Services.AssemblyReferences), _configuredProject.Services.AssemblyReferences, inputs);
+ await CheckReferencesAsync(logger, nameof(_configuredProject.Services.ComReferences), _configuredProject.Services.ComReferences, inputs);
+ await CheckReferencesAsync(logger, nameof(_configuredProject.Services.PackageReferences), _configuredProject.Services.PackageReferences, inputs);
+ await CheckReferencesAsync(logger, nameof(_configuredProject.Services.ProjectReferences), _configuredProject.Services.ProjectReferences, inputs);
+ await CheckReferencesAsync(logger, nameof(_configuredProject.Services.SdkReferences), _configuredProject.Services.SdkReferences, inputs);
+ await CheckReferencesAsync(logger, nameof(_configuredProject.Services.WinRTReferences), _configuredProject.Services.WinRTReferences, inputs);
+
+ // UpToDateCheckInput is the special item group for customized projects to add explicit inputs
+ var upToDateCheckInputItems = project.GetItems("UpToDateCheckInput").Select(file => file.GetMetadataValue("FullPath"));
+
+ Log(logger, "Checking item type 'UpToDateCheckInput'.");
+ foreach (var upToDateCheckInputItem in upToDateCheckInputItems)
+ {
+ Log(logger, "Input item path '{0}'.", upToDateCheckInputItem);
+ inputs.Add(upToDateCheckInputItem);
+ }
+
+ var latestInput = GetLatestTimestamp(logger, inputs, timestampCache);
+ if (latestInput != null)
+ {
+ Log(logger, "Lastest write timestamp on input is {0}.", latestInput.Value);
+ }
+
+ var outputs = new HashSet();
+
+ outputs.AddRange(project.GetItems("UpToDateCheckOutput")
+ .Select(file => file.GetMetadataValue("FullPath")));
+
+ IOutputGroupsService outputGroupsService = _configuredProject.Services.OutputGroups;
+ foreach (var outputGroup in KnownOutputGroups)
+ {
+ Log(logger, "Checking known output group {0}.", outputGroup);
+
+ foreach (var output in (await outputGroupsService.GetOutputGroupAsync(outputGroup, cancellationToken)).Outputs)
+ {
+ Log(logger, "Output path '{0}'.", output.Key);
+ outputs.Add(output.Key);
+ }
+ }
+
+ var latestOutput = GetLatestTimestamp(logger, outputs, timestampCache);
+ if (latestOutput != null)
+ {
+ Log(logger, "Lastest write timestamp on output is {0}.", latestOutput.Value);
+ }
+
+ var isUpToDate = latestOutput != null && latestInput != null && latestOutput.Value >= latestInput.Value;
+ Log(logger, "Project is{0} up to date.", (!isUpToDate ? " not" : ""));
+
+ return isUpToDate;
+ }
+ }
+
+ public async Task IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken = default(CancellationToken)) =>
+ await _projectSystemOptions.GetIsFastUpToDateCheckEnabledAsync();
+ }
+}
diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs
index 87e2d806472..b22930fb268 100644
--- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs
+++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs
@@ -1,5 +1,7 @@
// 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.Threading.Tasks;
+
namespace Microsoft.VisualStudio.ProjectSystem
{
///
@@ -17,5 +19,21 @@ bool IsProjectOutputPaneEnabled
{
get;
}
+
+ ///
+ /// Gets a value indicating if the project fast up to date check is enabled.
+ ///
+ ///
+ /// if the project fast up to date check is enabled; otherwise,
+ ///
+ Task GetIsFastUpToDateCheckEnabledAsync();
+
+ ///
+ /// Gets a value indicating if fast up to date check logging should be verbose.
+ ///
+ ///
+ /// if the project fast up to date logging is verbose; otherwise,
+ ///
+ Task GetVerboseFastUpToDateLoggingAsync();
}
}
diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml
index 32584d3ef33..f5e0de66fb7 100644
--- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml
+++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml
@@ -55,6 +55,11 @@
+
+
+
+
+
diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectItemsSchema.xaml b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectItemsSchema.xaml
index 778f5f27eea..aa31df800a1 100644
--- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectItemsSchema.xaml
+++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectItemsSchema.xaml
@@ -58,7 +58,7 @@
-
+