diff --git a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs index 66dbf6d4ea0..9bf009491fb 100644 --- a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs @@ -661,6 +661,55 @@ public void UsePropertyBeforeSet() } } + /// + /// Log when a property is being assigned a new value. + /// + [Fact] + public void LogPropertyAssignments() + { + string testtargets = ObjectModelHelpers.CleanupFileContents(@" + + + OldValue + NewValue + + + + "); + + string tempPath = Path.GetTempPath(); + string targetDirectory = Path.Combine(tempPath, "LogPropertyAssignments"); + string testTargetPath = Path.Combine(targetDirectory, "test.proj"); + + bool originalValue = BuildParameters.WarnOnUninitializedProperty; + try + { + BuildParameters.WarnOnUninitializedProperty = true; + Directory.CreateDirectory(targetDirectory); + File.WriteAllText(testTargetPath, testtargets); + + MockLogger logger = new MockLogger(); + logger.Verbosity = LoggerVerbosity.Diagnostic; + ProjectCollection pc = new ProjectCollection(); + pc.RegisterLogger(logger); + Project project = pc.LoadProject(testTargetPath); + + bool result = project.Build(); + Assert.Equal(true, result); + logger.AssertLogContains("Evaluation started"); + logger.AssertLogContains("Property reassignment"); + logger.AssertLogContains("Evaluation finished"); + logger.AssertLogContains("Prop"); + logger.AssertLogContains("OldValue"); + logger.AssertLogContains("NewValue"); + } + finally + { + BuildParameters.WarnOnUninitializedProperty = originalValue; + FileUtilities.DeleteWithoutTrailingBackslash(targetDirectory, true); + } + } + /// /// If we use a property twice make sure we warn and dont crash due to the dictionary which is holding the used but uninitialized variables.. /// diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index e8d47821def..67a48530d6f 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -777,6 +777,8 @@ private IDictionary Evaluate() #endif string projectFile = String.IsNullOrEmpty(_projectRootElement.ProjectFileLocation.File) ? "(null)" : _projectRootElement.ProjectFileLocation.File; + _loggingService.LogComment(_buildEventContext, MessageImportance.Low, "EvaluationStarted", projectFile); + #if MSBUILDENABLEVSPROFILING string endPass0 = String.Format(CultureInfo.CurrentCulture, "Evaluate Project {0} - End Pass 0 (Initial properties)", projectFile); DataCollection.CommentMarkProfile(8816, endPass0); @@ -929,6 +931,8 @@ private IDictionary Evaluate() _data.FinishEvaluation(); + _loggingService.LogComment(_buildEventContext, MessageImportance.Low, "EvaluationFinished", projectFile); + #if FEATURE_MSBUILD_DEBUGGER return _projectLevelLocalsForBuild; #else @@ -1013,7 +1017,7 @@ element is ProjectOtherwiseElement } implicitImports.Add(ProjectImportElement.CreateImplicit("Sdk.props", currentProjectOrImport, ImplicitImportLocation.Top, sdkName)); - + implicitImports.Add(ProjectImportElement.CreateImplicit("Sdk.targets", currentProjectOrImport, ImplicitImportLocation.Bottom, sdkName)); } } @@ -1627,7 +1631,7 @@ private void EvaluatePropertyElement(ProjectPropertyElement propertyElement) string evaluatedValue = _expander.ExpandIntoStringLeaveEscaped(propertyElement.Value, ExpanderOptions.ExpandProperties, propertyElement.Location); - // If we are goign to set a property to a value other than null or empty we need to check to see if it has been used + // If we are going to set a property to a value other than null or empty we need to check to see if it has been used // during evaluation. if (evaluatedValue.Length > 0 && _expander.WarnForUninitializedProperties) { @@ -1649,6 +1653,11 @@ private void EvaluatePropertyElement(ProjectPropertyElement propertyElement) P property = _data.SetProperty(propertyElement, evaluatedValue, predecessor); + if (predecessor != null) + { + LogPropertyReassignment(predecessor, property, propertyElement.Location.LocationString); + } + #if FEATURE_MSBUILD_DEBUGGER if (DebuggerManager.DebuggingEnabled) { @@ -1657,6 +1666,24 @@ private void EvaluatePropertyElement(ProjectPropertyElement propertyElement) #endif } + private void LogPropertyReassignment(P predecessor, P property, string location) + { + string newValue = property.EvaluatedValue; + string oldValue = predecessor.EvaluatedValue; + + if (newValue != oldValue) + { + _loggingService.LogComment( + _buildEventContext, + MessageImportance.Low, + "PropertyReassignment", + property.Name, + newValue, + oldValue, + location); + } + } + private void EvaluateItemElement(bool itemGroupConditionResult, ProjectItemElement itemElement, LazyItemEvaluator lazyEvaluator) { #if FEATURE_MSBUILD_DEBUGGER @@ -2290,7 +2317,7 @@ private List ExpandAndLoadImports(string directoryOfImportin { project = Path.Combine(BuildEnvironmentHelper.Instance.MSBuildSDKsPath, importElement.Sdk, "Sdk", project); } - + var newExpandedImportPath = project.Replace(extensionPropertyRefAsString, extensionPathExpanded); _loggingService.LogComment(_buildEventContext, MessageImportance.Low, "TryingExtensionsPath", newExpandedImportPath, extensionPathExpanded); @@ -2351,7 +2378,7 @@ private List ExpandAndLoadImports(string directoryOfImportin /// requests can be satisfied without re-parsing it. /// private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(string directoryOfImportingFile, ProjectImportElement importElement, string unescapedExpression, - out List projects, bool throwOnFileNotExistsError=true) + out List projects, bool throwOnFileNotExistsError = true) { if (!EvaluateConditionCollectingConditionedProperties(importElement, ExpanderOptions.ExpandProperties, ParserOptions.AllowProperties, _projectRootElementCache)) { @@ -2490,7 +2517,7 @@ private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(stri importFileUnescaped, new ReadOnlyConvertingDictionary( _data.GlobalPropertiesDictionary, - instance => ((IProperty) instance).EvaluatedValueEscaped), + instance => ((IProperty)instance).EvaluatedValueEscaped), _data.ExplicitToolsVersion, _loggingService, _projectRootElementCache, @@ -2768,7 +2795,7 @@ private void ThrowForImportedProjectWithSearchPathsNotFound(ProjectImportPathMat private static string StringifyList(IList strings) { var sb = new StringBuilder(); - for (int i = 0; i < strings.Count - 1; i ++) + for (int i = 0; i < strings.Count - 1; i++) { if (i > 0) { diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index f31af2366a5..aa1852d6460 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -339,6 +339,12 @@ The <{0}> tag is no longer supported as a child of the <Project> element. Place this tag within a target, and add the name of the target to the "InitialTargets" attribute of the <Project> element. + + Evaluation started ("{0}") + + + Evaluation finished ("{0}") + Launching task "{0}" from assembly "{1}" in an external task host with a runtime of "{2}" and a process architecture of "{3}". @@ -740,6 +746,9 @@ MSB4148: The name of a property stored under the registry key "{0}" has zero length. {StrBegin="MSB4148: "} + + Property reassignment: $({0})="{1}" (previous value: "{2}") at {3} + MSB4043: The item metadata reference "{0}" is invalid because it is qualified with an item name. Item metadata referenced in transforms do not need to be qualified, because the item name is automatically deduced from the items being transformed. Change "{0}" to "%({1})". {StrBegin="MSB4043: "}UE: This message is shown when the user does something like this: @(foo->'%(foo.metadata)'). There is no need to specify