diff --git a/src/BuildPrediction/MsBuildHelpers.cs b/src/BuildPrediction/MsBuildHelpers.cs index f5f9c0a..b8ee2ae 100644 --- a/src/BuildPrediction/MsBuildHelpers.cs +++ b/src/BuildPrediction/MsBuildHelpers.cs @@ -235,16 +235,22 @@ public static bool ShouldCopyToOutputDirectory(this ProjectItemInstance item) } /// - /// Determins what the TargetPath metadata would be set to after calling the AssignTargetPath task. + /// Determines what the TargetPath metadata would be set to after calling the AssignTargetPath task. /// /// /// See: https://github.com/microsoft/msbuild/blob/master/src/Tasks/AssignTargetPath.cs. /// public static string GetTargetPath(this ProjectItemInstance item) { - string link = item.GetMetadataValue("Link"); + // If TargetPath is already set, it takes priority. + string targetPath = item.GetMetadataValue("TargetPath"); + if (!string.IsNullOrEmpty(targetPath)) + { + return targetPath; + } // If file has a link, use that. + string link = item.GetMetadataValue("Link"); if (!string.IsNullOrEmpty(link)) { return link; diff --git a/src/BuildPrediction/Predictors/ContentItemsPredictor.cs b/src/BuildPrediction/Predictors/ContentItemsPredictor.cs index 9198e4c..fe4b610 100644 --- a/src/BuildPrediction/Predictors/ContentItemsPredictor.cs +++ b/src/BuildPrediction/Predictors/ContentItemsPredictor.cs @@ -13,6 +13,7 @@ public sealed class ContentItemsPredictor : IProjectPredictor { internal const string OutDirPropertyName = "OutDir"; internal const string ContentItemName = "Content"; + internal const string ContentWithTargetPathItemName = "ContentWithTargetPath"; /// public void PredictInputsAndOutputs( @@ -34,6 +35,21 @@ public void PredictInputsAndOutputs( } } } + + foreach (ProjectItemInstance item in projectInstance.GetItems(ContentWithTargetPathItemName)) + { + predictionReporter.ReportInputFile(item.EvaluatedInclude); + + if (!string.IsNullOrEmpty(outDir) && item.ShouldCopyToOutputDirectory()) + { + // Note: Using TargetPath directly instead of GetTargetPath since ContentWithTargetPath items don't have AssignTargetPath called on them. + string targetPath = item.GetMetadataValue("TargetPath"); + if (!string.IsNullOrEmpty(targetPath)) + { + predictionReporter.ReportOutputFile(Path.Combine(outDir, targetPath)); + } + } + } } } } \ No newline at end of file diff --git a/src/BuildPrediction/Predictors/GetCopyToOutputDirectoryItemsGraphPredictor.cs b/src/BuildPrediction/Predictors/GetCopyToOutputDirectoryItemsGraphPredictor.cs index e9176f5..a2a7b27 100644 --- a/src/BuildPrediction/Predictors/GetCopyToOutputDirectoryItemsGraphPredictor.cs +++ b/src/BuildPrediction/Predictors/GetCopyToOutputDirectoryItemsGraphPredictor.cs @@ -57,6 +57,7 @@ private static void PredictInputsAndOutputs( // Process each item type considered in GetCopyToOutputDirectoryItems. Yes, Compile is considered. ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, ContentItemsPredictor.ContentItemName, outDir, predictionReporter); + ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, ContentItemsPredictor.ContentWithTargetPathItemName, outDir, predictionReporter); ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, EmbeddedResourceItemsPredictor.EmbeddedResourceItemName, outDir, predictionReporter); ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, CompileItemsPredictor.CompileItemName, outDir, predictionReporter); ReportCopyToOutputDirectoryItemsAsInputs(dependency.ProjectInstance, NoneItemsPredictor.NoneItemName, outDir, predictionReporter); diff --git a/src/BuildPrediction/Predictors/GetCopyToPublishDirectoryItemsGraphPredictor.cs b/src/BuildPrediction/Predictors/GetCopyToPublishDirectoryItemsGraphPredictor.cs index a3b390d..946cdbd 100644 --- a/src/BuildPrediction/Predictors/GetCopyToPublishDirectoryItemsGraphPredictor.cs +++ b/src/BuildPrediction/Predictors/GetCopyToPublishDirectoryItemsGraphPredictor.cs @@ -76,6 +76,7 @@ private static void ReportCopyToPublishDirectoryItems( { // Process each item type considered in GetCopyToPublishDirectoryItems. Yes, Compile is considered. ReportCopyToPublishDirectoryItems(projectInstance, ContentItemsPredictor.ContentItemName, publishDir, predictionReporter); + ReportCopyToPublishDirectoryItems(projectInstance, ContentItemsPredictor.ContentWithTargetPathItemName, publishDir, predictionReporter); ReportCopyToPublishDirectoryItems(projectInstance, EmbeddedResourceItemsPredictor.EmbeddedResourceItemName, publishDir, predictionReporter); ReportCopyToPublishDirectoryItems(projectInstance, CompileItemsPredictor.CompileItemName, publishDir, predictionReporter); ReportCopyToPublishDirectoryItems(projectInstance, NoneItemsPredictor.NoneItemName, publishDir, predictionReporter); diff --git a/src/BuildPredictionTests/MsBuildHelpersTests.cs b/src/BuildPredictionTests/MsBuildHelpersTests.cs index 6967508..9e4d500 100644 --- a/src/BuildPredictionTests/MsBuildHelpersTests.cs +++ b/src/BuildPredictionTests/MsBuildHelpersTests.cs @@ -34,21 +34,26 @@ public void ShouldCopyToOutputDirectory(string copyToOutputDirectoryValue, bool [Theory] [InlineData("Foo.xml", null, "Foo.xml")] - [InlineData("Foo.xml", @"Bar\Baz.xml", @"Bar\Baz.xml")] + [InlineData("Foo.xml", @"Link=Bar\Baz.xml", @"Bar\Baz.xml")] + [InlineData("Foo.xml", @"TargetPath=Bar\Baz.xml;Link=ShouldNotBeUsed.xml", @"Bar\Baz.xml")] [InlineData(@".\.\.\X\.\.\.\.\Foo.xml", null, @"X\Foo.xml")] [InlineData(@"{ProjectDir}\X\Foo.xml", null, @"X\Foo.xml")] [InlineData(@"{ProjectDir}\.\.\.\.\X\.\.\.\Foo.xml", null, @"X\Foo.xml")] [InlineData(@"{ProjectDir}\..\..\..\X\Y\Z\Foo.xml", null, @"Foo.xml")] - public void GetTargetPath(string itemIdentity, string linkValue, string expectedResult) + public void GetTargetPath(string itemIdentity, string properties, string expectedResult) { ProjectRootElement projectRootElement = ProjectRootElement.Create(); itemIdentity = itemIdentity.Replace("{ProjectDir}", projectRootElement.DirectoryPath, StringComparison.Ordinal); ProjectItemElement item = projectRootElement.AddItem("Foo", itemIdentity); - if (!string.IsNullOrEmpty(linkValue)) + if (properties is not null) { - item.AddMetadata("Link", linkValue); + foreach (string property in properties.Split(';')) + { + string[] propertyParts = property.Split(['='], 2); + item.AddMetadata(propertyParts[0], propertyParts[1]); + } } ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement); diff --git a/src/BuildPredictionTests/Predictors/ContentItemsPredictorTests.cs b/src/BuildPredictionTests/Predictors/ContentItemsPredictorTests.cs index 77cc1cd..3c367b8 100644 --- a/src/BuildPredictionTests/Predictors/ContentItemsPredictorTests.cs +++ b/src/BuildPredictionTests/Predictors/ContentItemsPredictorTests.cs @@ -15,15 +15,25 @@ public void NoCopy() { ProjectRootElement projectRootElement = ProjectRootElement.Create(); projectRootElement.AddProperty(ContentItemsPredictor.OutDirPropertyName, @"bin\"); - projectRootElement.AddItem(ContentItemsPredictor.ContentItemName, "Foo.xml"); + + ProjectItemElement item1 = projectRootElement.AddItem(ContentItemsPredictor.ContentItemName, "Foo.xml"); + + ProjectItemElement item2 = projectRootElement.AddItem(ContentItemsPredictor.ContentWithTargetPathItemName, "Bar.xml"); + item2.AddMetadata("TargetPath", @"bin\Bar.xml"); ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement); + PredictedItem[] expectedInputFiles = + [ + new PredictedItem("Foo.xml", nameof(ContentItemsPredictor)), + new PredictedItem("Bar.xml", nameof(ContentItemsPredictor)), + ]; + new ContentItemsPredictor() .GetProjectPredictions(projectInstance) .AssertPredictions( projectInstance, - new[] { new PredictedItem("Foo.xml", nameof(ContentItemsPredictor)) }, + expectedInputFiles, null, null, null); @@ -35,18 +45,34 @@ public void WithCopy() ProjectRootElement projectRootElement = ProjectRootElement.Create(); projectRootElement.AddProperty(ContentItemsPredictor.OutDirPropertyName, @"bin\"); - ProjectItemElement item = projectRootElement.AddItem(ContentItemsPredictor.ContentItemName, "Foo.xml"); - item.AddMetadata("CopyToOutputDirectory", "PreserveNewest"); + ProjectItemElement item1 = projectRootElement.AddItem(ContentItemsPredictor.ContentItemName, "Foo.xml"); + item1.AddMetadata("CopyToOutputDirectory", "PreserveNewest"); + + ProjectItemElement item2 = projectRootElement.AddItem(ContentItemsPredictor.ContentWithTargetPathItemName, "Bar.xml"); + item2.AddMetadata("CopyToOutputDirectory", "PreserveNewest"); + item2.AddMetadata("TargetPath", @"Bar\Bar.xml"); ProjectInstance projectInstance = TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement); + PredictedItem[] expectedInputFiles = + [ + new PredictedItem("Foo.xml", nameof(ContentItemsPredictor)), + new PredictedItem("Bar.xml", nameof(ContentItemsPredictor)), + ]; + + PredictedItem[] expectedOutputFiles = + [ + new PredictedItem(@"bin\Foo.xml", nameof(ContentItemsPredictor)), + new PredictedItem(@"bin\Bar\Bar.xml", nameof(ContentItemsPredictor)), + ]; + new ContentItemsPredictor() .GetProjectPredictions(projectInstance) .AssertPredictions( projectInstance, - new[] { new PredictedItem("Foo.xml", nameof(ContentItemsPredictor)) }, + expectedInputFiles, null, - new[] { new PredictedItem(@"bin\Foo.xml", nameof(ContentItemsPredictor)) }, + expectedOutputFiles, null); } }