diff --git a/eng/BootStrapMsBuild.targets b/eng/BootStrapMsBuild.targets index dcff8617638..d4330ba658d 100644 --- a/eng/BootStrapMsBuild.targets +++ b/eng/BootStrapMsBuild.targets @@ -35,7 +35,8 @@ <_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Newtonsoft.Json'))' == 'true'" /> <_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('NuGetSdkResolver'))' == 'true'" /> <_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Microsoft.Extensions.'))' == 'true'" /> - + <_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Microsoft.VisualStudio.SolutionPersistence'))' == 'true'" /> + <_NuGetRuntimeDependencies Include="%(RuntimeTargetsCopyLocalItems.Identity)" Condition="'@(RuntimeTargetsCopyLocalItems->Contains('NuGet.'))' == 'true'" /> @@ -48,7 +49,7 @@ - - - - - - - - - - - - - - - - - @(BravoProjectOutputs) - @(CharlieProjectOutputs) - @(DeltaProjectOutputs) - - - - - - - - - - - - - - - Count())' != '3' ` Text='Overall sln outputs must include outputs of each referenced project (there should be 3).' /> - AnyHaveMetadataValue('Identity', '$(StringifiedBravoProjectOutputs)'))' != 'true'` Text='Overall sln outputs must include outputs of normal project build of project B.' /> - AnyHaveMetadataValue('Identity', '$(StringifiedCharlieProjectOutputs)'))' != 'true' ` Text='Overall sln outputs must include outputs of normal project build of project C.' /> - AnyHaveMetadataValue('Identity', '$(StringifiedDeltaProjectOutputs)'))' != 'true' ` Text='Overall sln outputs must include outputs of normal project build of project D.' /> - -"; + """; + const string automaticProjectFileContents = + """ + + + + + + + + + + + + + + + + + + + + @(BravoProjectOutputs) + @(CharlieProjectOutputs) + @(DeltaProjectOutputs) + + + + + + + + + + + + + + + Count())' != '3' ` Text='Overall sln outputs must include outputs of each referenced project (there should be 3).' /> + AnyHaveMetadataValue('Identity', '$(StringifiedBravoProjectOutputs)'))' != 'true'` Text='Overall sln outputs must include outputs of normal project build of project B.' /> + AnyHaveMetadataValue('Identity', '$(StringifiedCharlieProjectOutputs)'))' != 'true' ` Text='Overall sln outputs must include outputs of normal project build of project C.' /> + AnyHaveMetadataValue('Identity', '$(StringifiedDeltaProjectOutputs)'))' != 'true' ` Text='Overall sln outputs must include outputs of normal project build of project D.' /> + + + """; #endregion var logger = new MockLogger(output); @@ -1039,11 +1080,13 @@ public void SolutionConfigurationWithDependenciesRelaysItsOutputs() /// /// Test the SolutionProjectGenerator.AddPropertyGroupForSolutionConfiguration method /// - [Fact] - public void TestAddPropertyGroupForSolutionConfiguration() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TestAddPropertyGroupForSolutionConfiguration(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ClassLibrary1', 'ClassLibrary1\ClassLibrary1.csproj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' @@ -1063,9 +1106,9 @@ public void TestAddPropertyGroupForSolutionConfiguration() {A6F99D27-47B9-4EA4-BFC9-25157CBDC281}.Debug|Mixed Platforms.Build.0 = VCConfig1|Win32 EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser); ProjectRootElement projectXml = ProjectRootElement.Create(); @@ -1112,11 +1155,13 @@ public void TestAddPropertyGroupForSolutionConfiguration() /// /// Make sure that BuildProjectInSolution is set to true of the Build.0 entry is in the solution configuration. /// - [Fact] - public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionSet() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionSet(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ClassLibrary1', 'ClassLibrary1\ClassLibrary1.csproj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' @@ -1131,9 +1176,9 @@ public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionSe {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Mixed Platforms.Build.0 = CSConfig1|Any CPU EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser); ProjectRootElement projectXml = ProjectRootElement.Create(); @@ -1156,11 +1201,13 @@ public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionSe /// /// Make sure that BuildProjectInSolution is set to false of the Build.0 entry is in the solution configuration. /// - [Fact] - public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionNotSet() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionNotSet(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ClassLibrary1', 'ClassLibrary1\ClassLibrary1.csproj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' @@ -1174,9 +1221,9 @@ public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionNo {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Mixed Platforms.ActiveCfg = CSConfig1|Any CPU EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser); ProjectRootElement projectXml = ProjectRootElement.Create(); @@ -1200,10 +1247,12 @@ public void TestAddPropertyGroupForSolutionConfigurationBuildProjectInSolutionNo /// In this bug, SkipNonexistentProjects was always set to 'Build'. It should be 'Build' for metaprojects and 'True' for everything else. /// The repro below has one of each case. WebProjects can't build so they are set as SkipNonexistentProjects='Build' /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void Regress751742_SkipNonexistentProjects() + public void Regress751742_SkipNonexistentProjects(bool useNewParser) { if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) { @@ -1212,7 +1261,7 @@ public void Regress751742_SkipNonexistentProjects() } var solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ClassLibrary1', 'ClassLibrary1\ClassLibrary1.csproj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' @@ -1232,39 +1281,43 @@ public void Regress751742_SkipNonexistentProjects() {A6F99D27-47B9-4EA4-BFC9-25157CBDC281}.Debug|Mixed Platforms.Build.0 = VCConfig1|Win32 EndGlobalSection EndGlobal - "; + """; - // We're not passing in a /tv:xx switch, so the solution project will have tools version 2.0 - var solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + // We're not passing in a /tv:xx switch, so the solution project will have tools version 2.0 + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - var instance = SolutionProjectGenerator.Generate(solution, null, ObjectModelHelpers.MSBuildDefaultToolsVersion, _buildEventContext, CreateMockLoggingService())[0]; + var instance = SolutionProjectGenerator.Generate(solution, null, ObjectModelHelpers.MSBuildDefaultToolsVersion, _buildEventContext, CreateMockLoggingService())[0]; - foreach (ITaskItem item in instance.Items) - { - string skipNonexistentProjects = item.GetMetadata("SkipNonexistentProjects"); - if (item.ItemSpec.EndsWith("ClassLibrary1.csproj")) - { - Assert.Equal("False", skipNonexistentProjects); - } - else if (item.ItemSpec.EndsWith("MainApp.metaproj")) + foreach (ITaskItem item in instance.Items) { - Assert.Equal("Build", skipNonexistentProjects); - } - else if (item.ItemSpec == "Debug|Mixed Platforms") - { - Assert.Equal("Debug", item.GetMetadata("Configuration")); - Assert.Equal("Mixed Platforms", item.GetMetadata("Platform")); - Assert.Contains("", item.GetMetadata("Content")); - } - else if (item.ItemSpec == "Release|Any CPU") - { - Assert.Equal("Release", item.GetMetadata("Configuration")); - Assert.Equal("Any CPU", item.GetMetadata("Platform")); - Assert.Contains("", item.GetMetadata("Content")); - } - else - { - Assert.Fail("Unexpected project seen:" + item.ItemSpec); + string skipNonexistentProjects = item.GetMetadata("SkipNonexistentProjects"); + if (item.ItemSpec.EndsWith("ClassLibrary1.csproj")) + { + Assert.Equal("False", skipNonexistentProjects); + } + else if (item.ItemSpec.EndsWith("MainApp.metaproj")) + { + Assert.Equal("Build", skipNonexistentProjects); + } + else if (item.ItemSpec == "Debug|Mixed Platforms") + { + Assert.Equal("Debug", item.GetMetadata("Configuration")); + Assert.Equal("Mixed Platforms", item.GetMetadata("Platform")); + Assert.Contains("", item.GetMetadata("Content")); + } + else if (item.ItemSpec == "Release|Any CPU") + { + Assert.Equal("Release", item.GetMetadata("Configuration")); + Assert.Equal("Any CPU", item.GetMetadata("Platform")); + Assert.Contains("", item.GetMetadata("Content")); + } + else + { + Assert.Fail("Unexpected project seen:" + item.ItemSpec); + } } } } @@ -1274,11 +1327,13 @@ public void Regress751742_SkipNonexistentProjects() /// if set when building a solution, will be specified as the ToolsVersion on the MSBuild task when /// building the projects contained within the solution. /// - [Fact] - public void ToolsVersionOverrideShouldBeSpecifiedOnMSBuildTaskInvocations() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ToolsVersionOverrideShouldBeSpecifiedOnMSBuildTaskInvocations(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ClassLibrary1', 'ClassLibrary1\ClassLibrary1.csproj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' @@ -1298,41 +1353,45 @@ public void ToolsVersionOverrideShouldBeSpecifiedOnMSBuildTaskInvocations() {A6F99D27-47B9-4EA4-BFC9-25157CBDC281}.Debug|Mixed Platforms.Build.0 = VCConfig1|Win32 EndGlobalSection EndGlobal - "; + """; - // We're not passing in a /tv:xx switch, so the solution project will have tools version 2.0 - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + // We're not passing in a /tv:xx switch, so the solution project will have tools version 2.0 + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, ObjectModelHelpers.MSBuildDefaultToolsVersion, _buildEventContext, CreateMockLoggingService()); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, ObjectModelHelpers.MSBuildDefaultToolsVersion, _buildEventContext, CreateMockLoggingService()); - int i = 0; - foreach (ProjectInstance instance in instances) - { - if (i == 0) + int i = 0; + foreach (ProjectInstance instance in instances) { - continue; - } + if (i == 0) + { + continue; + } - foreach (ProjectTargetInstance target in instance.Targets.Values) - { - foreach (ProjectTaskInstance childNode in target.Tasks) + foreach (ProjectTargetInstance target in instance.Targets.Values) { - if (String.Equals(childNode.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + foreach (ProjectTaskInstance childNode in target.Tasks) { - string projectsParameter = childNode.GetParameter("Projects"); - if (projectsParameter != "@(ProjectReference)") + if (String.Equals(childNode.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) { - // we found an MSBuild task invocation, now let's verify that it has the correct - // ToolsVersion parameter set - string toolsVersionParameter = childNode.GetParameter("ToolsVersion"); - - Assert.Equal(toolsVersionParameter, instances[0].GetPropertyValue("ProjectToolsVersion")); + string projectsParameter = childNode.GetParameter("Projects"); + if (projectsParameter != "@(ProjectReference)") + { + // we found an MSBuild task invocation, now let's verify that it has the correct + // ToolsVersion parameter set + string toolsVersionParameter = childNode.GetParameter("ToolsVersion"); + + Assert.Equal(toolsVersionParameter, instances[0].GetPropertyValue("ProjectToolsVersion")); + } } } } - } - i++; + i++; + } } } @@ -1340,68 +1399,74 @@ public void ToolsVersionOverrideShouldBeSpecifiedOnMSBuildTaskInvocations() /// /// Make sure that whatever the solution ToolsVersion is, it gets mapped to all its metaprojs, too. /// - [Fact] - public void SolutionWithDependenciesHasCorrectToolsVersionInMetaprojs() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SolutionWithDependenciesHasCorrectToolsVersionInMetaprojs(bool useNewParser) { string solutionFileContents = - @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ConsoleApplication2', 'ConsoleApplication2\ConsoleApplication2.csproj', '{5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}' - ProjectSection(ProjectDependencies) = postProject - {E0D295A1-CAFA-4E68-9929-468657DAAC6C} = {E0D295A1-CAFA-4E68-9929-468657DAAC6C} - EndProjectSection -EndProject -Project('{F184B08F-C81C-45F6-A57F-5ABD9991F28F}') = 'ConsoleApplication1', 'ConsoleApplication1\ConsoleApplication1.vbproj', '{E0D295A1-CAFA-4E68-9929-468657DAAC6C}' -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Release|Any CPU.Build.0 = Release|Any CPU - {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal - "; + """ + Microsoft Visual Studio Solution File, Format Version 12.00 + Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ConsoleApplication2', 'ConsoleApplication2\ConsoleApplication2.csproj', '{5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}' + ProjectSection(ProjectDependencies) = postProject + {E0D295A1-CAFA-4E68-9929-468657DAAC6C} = {E0D295A1-CAFA-4E68-9929-468657DAAC6C} + EndProjectSection + EndProject + Project('{F184B08F-C81C-45F6-A57F-5ABD9991F28F}') = 'ConsoleApplication1', 'ConsoleApplication1\ConsoleApplication1.vbproj', '{E0D295A1-CAFA-4E68-9929-468657DAAC6C}' + EndProject + Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B97A3C7-3DEE-47A4-870F-5CB6384FE6A4}.Release|Any CPU.Build.0 = Release|Any CPU + {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0D295A1-CAFA-4E68-9929-468657DAAC6C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + EndGlobal + """; - // We're not passing in a /tv:xx switch, so the solution project will have tools version 2.0 - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + // We're not passing in a /tv:xx switch, so the solution project will have tools version 2.0 + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - string[] solutionToolsVersions = { "4.0", ObjectModelHelpers.MSBuildDefaultToolsVersion }; + string[] solutionToolsVersions = { "4.0", ObjectModelHelpers.MSBuildDefaultToolsVersion }; - foreach (string solutionToolsVersion in solutionToolsVersions) - { - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, solutionToolsVersion, _buildEventContext, CreateMockLoggingService()); + foreach (string solutionToolsVersion in solutionToolsVersions) + { + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, solutionToolsVersion, _buildEventContext, CreateMockLoggingService()); - Assert.Equal(2, instances.Length); + Assert.Equal(2, instances.Length); - // Solution metaproj - Assert.Equal(solutionToolsVersion, instances[0].ToolsVersion); + // Solution metaproj + Assert.Equal(solutionToolsVersion, instances[0].ToolsVersion); - ICollection projectReferences = instances[0].GetItems("ProjectReference"); + ICollection projectReferences = instances[0].GetItems("ProjectReference"); - foreach (ProjectItemInstance projectReference in projectReferences) - { - // If this is the reference to the metaproj, its ToolsVersion metadata needs to match - // the solution ToolsVersion -- that's how the build knows which ToolsVersion to use. - if (projectReference.EvaluatedInclude.EndsWith(".metaproj", StringComparison.OrdinalIgnoreCase)) + foreach (ProjectItemInstance projectReference in projectReferences) { - Assert.Equal(solutionToolsVersion, projectReference.GetMetadataValue("ToolsVersion")); + // If this is the reference to the metaproj, its ToolsVersion metadata needs to match + // the solution ToolsVersion -- that's how the build knows which ToolsVersion to use. + if (projectReference.EvaluatedInclude.EndsWith(".metaproj", StringComparison.OrdinalIgnoreCase)) + { + Assert.Equal(solutionToolsVersion, projectReference.GetMetadataValue("ToolsVersion")); + } } - } - // Project metaproj for project with dependencies - Assert.Equal(solutionToolsVersion, instances[1].ToolsVersion); + // Project metaproj for project with dependencies + Assert.Equal(solutionToolsVersion, instances[1].ToolsVersion); + } } } #endif @@ -1409,11 +1474,13 @@ public void SolutionWithDependenciesHasCorrectToolsVersionInMetaprojs() /// /// Test the SolutionProjectGenerator.Generate method has its toolset redirected correctly. /// - [Fact] - public void ToolsVersionOverrideCausesToolsetRedirect() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ToolsVersionOverrideCausesToolsetRedirect(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'ClassLibrary1', 'ClassLibrary1\ClassLibrary1.csproj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' @@ -1433,8 +1500,10 @@ public void ToolsVersionOverrideCausesToolsetRedirect() {A6F99D27-47B9-4EA4-BFC9-25157CBDC281}.Debug|Mixed Platforms.Build.0 = VCConfig1|Win32 EndGlobalSection EndGlobal - "; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + """; + + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser); bool caughtException = false; try @@ -1454,11 +1523,13 @@ public void ToolsVersionOverrideCausesToolsetRedirect() /// /// Test the SolutionProjectGenerator.AddPropertyGroupForSolutionConfiguration method /// - [Fact] - public void TestDisambiguateProjectTargetName() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TestDisambiguateProjectTargetName(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'Build', 'Build\Build.csproj', '{21397922-C38F-4A0E-B950-77B3FBD51881}' @@ -1478,55 +1549,59 @@ public void TestDisambiguateProjectTargetName() HideSolutionNode = FALSE EndGlobalSection EndGlobal - "; - - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); - - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); - - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Build", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Clean", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Rebuild", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Publish", StringComparison.OrdinalIgnoreCase))); - - ProjectTargetInstance buildTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Build", StringComparison.OrdinalIgnoreCase)).First().Value; - ProjectTargetInstance cleanTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Clean", StringComparison.OrdinalIgnoreCase)).First().Value; - ProjectTargetInstance rebuildTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Rebuild", StringComparison.OrdinalIgnoreCase)).First().Value; - ProjectTargetInstance publishTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Publish", StringComparison.OrdinalIgnoreCase)).First().Value; - - // Check that the appropriate target is being passed to the child projects - Assert.Null(buildTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Targets")); + """; - Assert.Equal("Clean", cleanTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Targets")); - - Assert.Equal("Rebuild", rebuildTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Targets")); - - Assert.Equal("Publish", publishTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Targets")); - - // Check that the child projects in question are the members of the "ProjectReference" item group - Assert.Equal("@(ProjectReference)", buildTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Projects")); - - Assert.Equal("@(ProjectReference->Reverse())", cleanTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Projects")); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - Assert.Equal("@(ProjectReference)", rebuildTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Projects")); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); - Assert.Equal("@(ProjectReference)", publishTarget.Tasks.Where( - task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) - .First().GetParameter("Projects")); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Build", StringComparison.OrdinalIgnoreCase))); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Clean", StringComparison.OrdinalIgnoreCase))); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Rebuild", StringComparison.OrdinalIgnoreCase))); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Publish", StringComparison.OrdinalIgnoreCase))); + + ProjectTargetInstance buildTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Build", StringComparison.OrdinalIgnoreCase)).First().Value; + ProjectTargetInstance cleanTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Clean", StringComparison.OrdinalIgnoreCase)).First().Value; + ProjectTargetInstance rebuildTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Rebuild", StringComparison.OrdinalIgnoreCase)).First().Value; + ProjectTargetInstance publishTarget = instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Publish", StringComparison.OrdinalIgnoreCase)).First().Value; + + // Check that the appropriate target is being passed to the child projects + Assert.Null(buildTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Targets")); + + Assert.Equal("Clean", cleanTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Targets")); + + Assert.Equal("Rebuild", rebuildTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Targets")); + + Assert.Equal("Publish", publishTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Targets")); + + // Check that the child projects in question are the members of the "ProjectReference" item group + Assert.Equal("@(ProjectReference)", buildTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Projects")); + + Assert.Equal("@(ProjectReference->Reverse())", cleanTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Projects")); + + Assert.Equal("@(ProjectReference)", rebuildTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Projects")); + + Assert.Equal("@(ProjectReference)", publishTarget.Tasks.Where( + task => String.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase)) + .First().GetParameter("Projects")); + } // We should have only the four standard targets plus the two validation targets (ValidateSolutionConfiguration and ValidateToolsVersions). } @@ -1534,11 +1609,13 @@ public void TestDisambiguateProjectTargetName() /// /// Tests the algorithm for choosing default configuration/platform values for solutions /// + /// This test would only work for the old parser. In the new parser SolutionConfigurations are not available, + /// and constructed from projects configurations. [Fact] public void TestConfigurationPlatformDefaults1() { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1550,9 +1627,9 @@ public void TestConfigurationPlatformDefaults1() Release|Win32 = Release|Win32 EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = SolutionFile_OldParser_Tests.ParseSolutionHelper(solutionFileContents); // These used to exist on the engine, but now need to be passed in explicitly IDictionary globalProperties = new Dictionary(); @@ -1572,11 +1649,13 @@ public void TestConfigurationPlatformDefaults1() /// /// Tests the algorithm for choosing default configuration/platform values for solutions /// + /// This test would only work for the old parser. In the new parser SolutionConfigurations are not available, + /// and constructed from projects configurations. [Fact] public void TestConfigurationPlatformDefaults2() { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1586,9 +1665,9 @@ public void TestConfigurationPlatformDefaults2() Other|Win32 = Other|Win32 EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = SolutionFile_OldParser_Tests.ParseSolutionHelper(solutionFileContents); ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); @@ -1602,10 +1681,12 @@ public void TestConfigurationPlatformDefaults2() /// /// Tests the algorithm for choosing default Venus configuration values for solutions /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void TestVenusConfigurationDefaults() + public void TestVenusConfigurationDefaults(bool useNewParser) { if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) { @@ -1615,13 +1696,13 @@ public void TestVenusConfigurationDefaults() Dictionary globalProperties = new Dictionary(); globalProperties["Configuration"] = "Debug"; - ProjectInstance msbuildProject = CreateVenusSolutionProject(globalProperties); + ProjectInstance msbuildProject = CreateVenusSolutionProject(globalProperties, useNewParser); // ASP.NET configuration should match the selected solution configuration Assert.Equal("Debug", msbuildProject.GetPropertyValue("AspNetConfiguration")); globalProperties["Configuration"] = "Release"; - msbuildProject = CreateVenusSolutionProject(globalProperties); + msbuildProject = CreateVenusSolutionProject(globalProperties, useNewParser); Assert.Equal("Release", msbuildProject.GetPropertyValue("AspNetConfiguration")); // Check that the two standard Asp.net configurations are represented on the targets @@ -1632,10 +1713,12 @@ public void TestVenusConfigurationDefaults() /// /// Tests that the correct value for TargetFrameworkVersion gets set when creating Venus solutions /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void VenusSolutionDefaultTargetFrameworkVersion() + public void VenusSolutionDefaultTargetFrameworkVersion(bool useNewParser) { if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) { @@ -1644,7 +1727,7 @@ public void VenusSolutionDefaultTargetFrameworkVersion() } // v4.0 by default - ProjectInstance msbuildProject = CreateVenusSolutionProject(); + ProjectInstance msbuildProject = CreateVenusSolutionProject(useNewParser); Assert.Equal("v4.0", msbuildProject.GetPropertyValue("TargetFrameworkVersion")); if (FrameworkLocationHelper.PathToDotNetFrameworkV35 == null) @@ -1654,34 +1737,36 @@ public void VenusSolutionDefaultTargetFrameworkVersion() } // v3.5 if MSBuildToolsVersion is 3.5 - msbuildProject = CreateVenusSolutionProject("3.5"); + msbuildProject = CreateVenusSolutionProject("3.5", useNewParser); Assert.Equal("v3.5", msbuildProject.GetPropertyValue("TargetFrameworkVersion")); // v2.0 if MSBuildToolsVersion is 2.0 - msbuildProject = CreateVenusSolutionProject("2.0"); + msbuildProject = CreateVenusSolutionProject("2.0", useNewParser); Assert.Equal("v2.0", msbuildProject.GetPropertyValue("TargetFrameworkVersion")); // may be user defined IDictionary globalProperties = new Dictionary(); globalProperties.Add("TargetFrameworkVersion", "userdefined"); - msbuildProject = CreateVenusSolutionProject(globalProperties); + msbuildProject = CreateVenusSolutionProject(globalProperties, useNewParser); Assert.Equal("userdefined", msbuildProject.GetPropertyValue("TargetFrameworkVersion")); } /// /// Tests the algorithm for choosing target framework paths for ResolveAssemblyReferences for Venus /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void TestTargetFrameworkPaths0() + public void TestTargetFrameworkPaths0(bool useNewParser) { if (FrameworkLocationHelper.PathToDotNetFrameworkSdkV20 != null) { IDictionary globalProperties = new Dictionary(); globalProperties.Add("TargetFrameworkVersion", "v2.0"); - ProjectInstance msbuildProject = CreateVenusSolutionProject("2.0"); + ProjectInstance msbuildProject = CreateVenusSolutionProject("2.0", useNewParser); // ToolsVersion is 2.0, TargetFrameworkVersion is v2.0 --> one item pointing to v2.0 Assert.Equal("2.0", msbuildProject.ToolsVersion); @@ -1696,10 +1781,12 @@ public void TestTargetFrameworkPaths0() /// /// Tests the algorithm for choosing target framework paths for ResolveAssemblyReferences for Venus /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void TestTargetFrameworkPaths1() + public void TestTargetFrameworkPaths1(bool useNewParser) { if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) { @@ -1707,7 +1794,7 @@ public void TestTargetFrameworkPaths1() return; } - ProjectInstance msbuildProject = CreateVenusSolutionProject(); + ProjectInstance msbuildProject = CreateVenusSolutionProject(useNewParser); // ToolsVersion is 4.0, TargetFrameworkVersion is v2.0 --> one item pointing to v2.0 msbuildProject.SetProperty("TargetFrameworkVersion", "v2.0"); @@ -1722,10 +1809,12 @@ public void TestTargetFrameworkPaths1() /// /// Tests the algorithm for choosing target framework paths for ResolveAssemblyReferences for Venus /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void TestTargetFrameworkPaths2() + public void TestTargetFrameworkPaths2(bool useNewParser) { if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) { @@ -1733,7 +1822,7 @@ public void TestTargetFrameworkPaths2() return; } - ProjectInstance msbuildProject = CreateVenusSolutionProject(); + ProjectInstance msbuildProject = CreateVenusSolutionProject(useNewParser); // ToolsVersion is 4.0, TargetFrameworkVersion is v4.0 --> items for v2.0 and v4.0 msbuildProject.SetProperty("TargetFrameworkVersion", "v4.0"); @@ -1771,11 +1860,13 @@ public void TestTargetFrameworkPaths2() /// /// Test the PredictActiveSolutionConfigurationName method /// + /// This test would only work for the old parser. + /// In the new parser SolutionConfigurations are not available, and constructed from projects configurations. [Fact] public void TestPredictSolutionConfigurationName() { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1785,9 +1876,9 @@ public void TestPredictSolutionConfigurationName() Debug|Win32 = Debug|Win32 EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = SolutionFile_OldParser_Tests.ParseSolutionHelper(solutionFileContents); IDictionary globalProperties = new Dictionary(); @@ -1806,11 +1897,13 @@ public void TestPredictSolutionConfigurationName() /// /// Verifies that the SolutionProjectGenerator will correctly escape project file paths /// - [Fact] - public void SolutionGeneratorEscapingProjectFilePaths() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SolutionGeneratorEscapingProjectFilePaths(bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{F184B08F-C81C-45F6-A57F-5ABD9991F28F}') = 'ConsoleApplication1', '%abtest\ConsoleApplication1.vbproj', '{AB3413A6-D689-486D-B7F0-A095371B3F13}' @@ -1830,27 +1923,33 @@ public void SolutionGeneratorEscapingProjectFilePaths() HideSolutionNode = FALSE EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - // Creating a ProjectRootElement shouldn't affect the ProjectCollection at all - Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); + // Creating a ProjectRootElement shouldn't affect the ProjectCollection at all + Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); - Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); + Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); - // Ensure that the value has been correctly stored in the ProjectReference item list - // Since there is only one project in the solution, there will be only one project reference - Assert.Contains("%abtest", instances[0].GetItems("ProjectReference").ElementAt(0).EvaluatedInclude); + // Ensure that the value has been correctly stored in the ProjectReference item list + // Since there is only one project in the solution, there will be only one project reference + Assert.Contains("%abtest", instances[0].GetItems("ProjectReference").ElementAt(0).EvaluatedInclude); + } } /// /// Verifies that the SolutionProjectGenerator will emit a solution file. /// - [Fact] - public void SolutionGeneratorCanEmitSolutions() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SolutionGeneratorCanEmitSolutions(bool useNewParser) { string oldValueForMSBuildEmitSolution = Environment.GetEnvironmentVariable("MSBuildEmitSolution"); @@ -1858,7 +1957,7 @@ public void SolutionGeneratorCanEmitSolutions() ProjectCollection.GlobalProjectCollection.UnloadAllProjects(); string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{F184B08F-C81C-45F6-A57F-5ABD9991F28F}') = 'ConsoleApplication1', 'ConsoleApplication1\ConsoleApplication1.vbproj', '{AB3413A6-D689-486D-B7F0-A095371B3F13}' @@ -1878,7 +1977,7 @@ public void SolutionGeneratorCanEmitSolutions() HideSolutionNode = FALSE EndGlobalSection EndGlobal - "; + """; SolutionFile solution = null; using ProjectCollection collection = new ProjectCollection(); @@ -1887,12 +1986,16 @@ public void SolutionGeneratorCanEmitSolutions() { Environment.SetEnvironmentVariable("MSBuildEmitSolution", "1"); - solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - // Creating a ProjectRootElement shouldn't affect the ProjectCollection at all - Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); + // Creating a ProjectRootElement shouldn't affect the ProjectCollection at all + Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService()); + } // Instantiating the Assert.Empty(ProjectCollection.GlobalProjectCollection.LoadedProjects); @@ -1919,16 +2022,18 @@ public void SolutionGeneratorCanEmitSolutions() /// Make sure that we output a warning and don't build anything when we're given an invalid /// solution configuration and SkipInvalidConfigurations is set to true. /// - [Fact] + [Theory] + [InlineData(false)] + [InlineData(true)] [Trait("Category", "netcore-osx-failing")] [Trait("Category", "netcore-linux-failing")] - public void TestSkipInvalidConfigurationsCase() + public void TestSkipInvalidConfigurationsCase(bool useNewParser) { string tmpFileName = FileUtilities.GetTemporaryFileName(); string projectFilePath = tmpFileName + ".sln"; - string solutionContents = - @" + string solutionFileContents = + """ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2005 Project('{E24C65DC-7377-472B-9ABA-BC803B73C61A}') = 'C:\solutions\WebSite2\', '..\..\solutions\WebSite2\', '{F90528C4-6989-4D33-AFE8-F53173597CC2}' @@ -1959,7 +2064,8 @@ public void TestSkipInvalidConfigurationsCase() {F90528C4-6989-4D33-AFE8-F53173597CC2}.Debug|Any CPU.ActiveCfg = Debug|.NET {F90528C4-6989-4D33-AFE8-F53173597CC2}.Debug|Any CPU.Build.0 = Debug|.NET EndGlobalSection - EndGlobal"; + EndGlobal + """; try { @@ -1969,12 +2075,16 @@ public void TestSkipInvalidConfigurationsCase() globalProperties["Configuration"] = "Nonexistent"; globalProperties["SkipInvalidConfigurations"] = "true"; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionContents.Replace('\'', '"')); - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, CreateMockLoggingService()); - ProjectInstance msbuildProject = instances[0]; + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, CreateMockLoggingService()); + ProjectInstance msbuildProject = instances[0]; - // Build should complete successfully even with an invalid solution config if SkipInvalidConfigurations is true - Assert.True(msbuildProject.Build(new ILogger[] { logger })); + // Build should complete successfully even with an invalid solution config if SkipInvalidConfigurations is true + Assert.True(msbuildProject.Build(new ILogger[] { logger })); + } // We should get the invalid solution configuration warning Assert.Single(logger.Warnings); @@ -2173,50 +2283,52 @@ public void BadFrameworkMonkierExpectBuildToFail2() /// Bug indicated that when a target framework version greater than 4.0 was used then the solution project generator would crash. /// this test is to make sure the fix is not regressed. /// - [Fact] - public void TestTargetFrameworkVersionGreaterThan4() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TestTargetFrameworkVersionGreaterThan4(bool useNewParser) { string tmpFileName = FileUtilities.GetTemporaryFileName(); string projectFilePath = tmpFileName + ".sln"; string solutionFileContents = - @" -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project('{E24C65DC-7377-472B-9ABA-BC803B73C61A}') = 'WebSite1', '..\WebSite1\', '{6B8F98F2-C976-4029-9321-5CCD73A174DA}' - ProjectSection(WebsiteProperties) = preProject - TargetFrameworkMoniker = '.NETFramework,Version=v4.34' - Debug.AspNetCompiler.VirtualPath = '/WebSite1' - Debug.AspNetCompiler.PhysicalPath = '..\WebSite1\' - Debug.AspNetCompiler.TargetPath = 'PrecompiledWeb\WebSite1\' - Debug.AspNetCompiler.Updateable = 'true' - Debug.AspNetCompiler.ForceOverwrite = 'true' - Debug.AspNetCompiler.FixedNames = 'false' - Debug.AspNetCompiler.Debug = 'True' - Release.AspNetCompiler.VirtualPath = '/WebSite1' - Release.AspNetCompiler.PhysicalPath = '..\WebSite1\' - Release.AspNetCompiler.TargetPath = 'PrecompiledWeb\WebSite1\' - Release.AspNetCompiler.Updateable = 'true' - Release.AspNetCompiler.ForceOverwrite = 'true' - Release.AspNetCompiler.FixedNames = 'false' - Release.AspNetCompiler.Debug = 'False' - VWDPort = '45602' - DefaultWebSiteLanguage = 'Visual Basic' - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6B8F98F2-C976-4029-9321-5CCD73A174DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B8F98F2-C976-4029-9321-5CCD73A174DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal - "; + """ + Microsoft Visual Studio Solution File, Format Version 11.00 + # Visual Studio 2010 + Project('{E24C65DC-7377-472B-9ABA-BC803B73C61A}') = 'WebSite1', '..\WebSite1\', '{6B8F98F2-C976-4029-9321-5CCD73A174DA}' + ProjectSection(WebsiteProperties) = preProject + TargetFrameworkMoniker = '.NETFramework,Version=v4.34' + Debug.AspNetCompiler.VirtualPath = '/WebSite1' + Debug.AspNetCompiler.PhysicalPath = '..\WebSite1\' + Debug.AspNetCompiler.TargetPath = 'PrecompiledWeb\WebSite1\' + Debug.AspNetCompiler.Updateable = 'true' + Debug.AspNetCompiler.ForceOverwrite = 'true' + Debug.AspNetCompiler.FixedNames = 'false' + Debug.AspNetCompiler.Debug = 'True' + Release.AspNetCompiler.VirtualPath = '/WebSite1' + Release.AspNetCompiler.PhysicalPath = '..\WebSite1\' + Release.AspNetCompiler.TargetPath = 'PrecompiledWeb\WebSite1\' + Release.AspNetCompiler.Updateable = 'true' + Release.AspNetCompiler.ForceOverwrite = 'true' + Release.AspNetCompiler.FixedNames = 'false' + Release.AspNetCompiler.Debug = 'False' + VWDPort = '45602' + DefaultWebSiteLanguage = 'Visual Basic' + EndProjectSection + EndProject + Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6B8F98F2-C976-4029-9321-5CCD73A174DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B8F98F2-C976-4029-9321-5CCD73A174DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + EndGlobal + """; try { @@ -2226,18 +2338,22 @@ public void TestTargetFrameworkVersionGreaterThan4() globalProperties["Configuration"] = "Release"; globalProperties["SkipInvalidConfigurations"] = "true"; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents.Replace('\'', '"')); - using ProjectCollection collection = new ProjectCollection(); - collection.RegisterLogger(logger); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); + + using ProjectCollection collection = new ProjectCollection(); + collection.RegisterLogger(logger); #pragma warning disable format #if !FEATURE_ASPNET_COMPILER - Assert.Throws(() => - { + Assert.Throws(() => + { #endif - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, collection.LoggingService); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, collection.LoggingService); #if !FEATURE_ASPNET_COMPILER - }); + }); #endif #pragma warning restore format @@ -2246,6 +2362,7 @@ public void TestTargetFrameworkVersionGreaterThan4() string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("AspNetCompiler.TargetingHigherFrameworksDefaultsTo40", solution.ProjectsInOrder[0].ProjectName, ver.ToString()); logger.AssertLogContains(message); #endif + } } finally { @@ -2256,14 +2373,16 @@ public void TestTargetFrameworkVersionGreaterThan4() /// /// Verifies that when target names are specified they end up in the metaproj. /// - [Fact] - public void CustomTargetNamesAreInInMetaproj() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CustomTargetNamesAreInInMetaproj(bool useNewParser) { - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper( - @" - Microsoft Visual Studio Solution File, Format Version 14.00 + string solutionFileContents = + """ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2015 - Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ClassLibrary1"", ""ClassLibrary1.csproj"", ""{6185CC21-BE89-448A-B3C0-D1C27112E595}"" + Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1.csproj", "{6185CC21-BE89-448A-B3C0-D1C27112E595}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -2275,43 +2394,51 @@ public void CustomTargetNamesAreInInMetaproj() {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = CSConfig2|Any CPU EndGlobalSection EndGlobal - "); + """; - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "One" }); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "One", StringComparison.OrdinalIgnoreCase))); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "One" }); - instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "Two", "Three", "Four" }); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "One", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Two", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Three", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Four", StringComparison.OrdinalIgnoreCase))); + instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "Two", "Three", "Four" }); - instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "Build" }); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Two", StringComparison.OrdinalIgnoreCase))); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Three", StringComparison.OrdinalIgnoreCase))); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Four", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Build", StringComparison.OrdinalIgnoreCase))); + instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "Build" }); - instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "Five", "Rebuild" }); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Build", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Five", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Rebuild", StringComparison.OrdinalIgnoreCase))); + instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "Five", "Rebuild" }); - instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "My_Project:Six" }); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Five", StringComparison.OrdinalIgnoreCase))); + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Rebuild", StringComparison.OrdinalIgnoreCase))); - Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Six", StringComparison.OrdinalIgnoreCase))); + instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), new List { "My_Project:Six" }); + + Assert.Single(instances[0].Targets.Where(target => String.Equals(target.Value.Name, "Six", StringComparison.OrdinalIgnoreCase))); + } } /// /// Verifies that disambiguated target names are used when a project name matches a standard solution entry point. /// - [Fact] - public void DisambiguatedTargetNamesAreInInMetaproj() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void DisambiguatedTargetNamesAreInMetaproj(bool useNewParser) { - foreach(string projectName in ProjectInSolution.projectNamesToDisambiguate) + foreach (string projectName in ProjectInSolution.projectNamesToDisambiguate) { - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper( - $$""" - Microsoft Visual Studio Solution File, Format Version 14.00 + string solutionFileContents = + $$""" + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2015 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "{{projectName}}", "{{projectName}}.csproj", "{6185CC21-BE89-448A-B3C0-D1C27112E595}" EndProject @@ -2326,21 +2453,27 @@ public void DisambiguatedTargetNamesAreInInMetaproj() {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal - """); + """; - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), null); - - foreach (string targetName in ProjectInSolution.projectNamesToDisambiguate) + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) { - // The entry point still exists normally. - Assert.True(instances[0].Targets.ContainsKey(targetName)); - - // The traversal target should be disambiguated with a "Solution:" prefix. - // Note: The default targets are used instead of "Build". - string traversalTargetName = targetName.Equals("Build", StringComparison.OrdinalIgnoreCase) - ? $"Solution:{projectName}" - : $"Solution:{projectName}:{targetName}"; - Assert.True(instances[0].Targets.ContainsKey(traversalTargetName)); + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); + + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, null, null, BuildEventContext.Invalid, CreateMockLoggingService(), null); + + foreach (string targetName in ProjectInSolution.projectNamesToDisambiguate) + { + // The entry point still exists normally. + Assert.True(instances[0].Targets.ContainsKey(targetName)); + + // The traversal target should be disambiguated with a "Solution:" prefix. + // Note: The default targets are used instead of "Build". + string traversalTargetName = targetName.Equals("Build", StringComparison.OrdinalIgnoreCase) + ? $"Solution:{projectName}" + : $"Solution:{projectName}:{targetName}"; + Assert.True(instances[0].Targets.ContainsKey(traversalTargetName)); + } } } } @@ -2349,73 +2482,81 @@ public void DisambiguatedTargetNamesAreInInMetaproj() /// Verifies that illegal user target names (the ones already used internally) don't crash the SolutionProjectGenerator /// [Theory] - [InlineData(false)] - [InlineData(true)] - public void IllegalUserTargetNamesDoNotThrow(bool forceCaseDifference) + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public void IllegalUserTargetNamesDoNotThrow(bool forceCaseDifference, bool useNewParser) { - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper( - @" - Microsoft Visual Studio Solution File, Format Version 14.00 + string solutionFileContents = + """ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2015 - Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ClassLibrary1"", ""ClassLibrary1.csproj"", ""{6185CC21-BE89-448A-B3C0-D1C27112E595}"" + Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1.csproj", "{6185CC21-BE89-448A-B3C0-D1C27112E595}" EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection EndGlobal - "); + """; - ProjectInstance[] instances; + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - // Avoid any unexpected targets getting pulled in - var globalProperties = new Dictionary { { "ImportByWildcardBeforeSolution", "false" } }; + ProjectInstance[] instances; - foreach (string builtInTargetName in new[] - { - null, - "Build", - "Rebuild", - "Clean", - "Publish", - "ClassLibrary1", - "ClassLibrary1:Clean", - "ClassLibrary1:Rebuild", - "GetSolutionConfigurationContents", - "ValidateProjects", - }) - { - string[] targetNames; + // Avoid any unexpected targets getting pulled in + var globalProperties = new Dictionary { { "ImportByWildcardBeforeSolution", "false" } }; - if (builtInTargetName == null) + foreach (string builtInTargetName in new[] { - targetNames = null; - } - else + null, + "Build", + "Rebuild", + "Clean", + "Publish", + "ClassLibrary1", + "ClassLibrary1:Clean", + "ClassLibrary1:Rebuild", + "GetSolutionConfigurationContents", + "ValidateProjects", + }) { - string targetName = forceCaseDifference ? builtInTargetName.ToUpperInvariant() : builtInTargetName; - targetNames = new[] { targetName }; - } + string[] targetNames; - instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, CreateMockLoggingService(), targetNames); + if (builtInTargetName == null) + { + targetNames = null; + } + else + { + string targetName = forceCaseDifference ? builtInTargetName.ToUpperInvariant() : builtInTargetName; + targetNames = new[] { targetName }; + } - Assert.Single(instances); + instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, CreateMockLoggingService(), targetNames); - Assert.Equal(12, instances[0].TargetsCount); - } + Assert.Single(instances); - instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, CreateMockLoggingService(), new[] { "Foo" }); + Assert.Equal(12, instances[0].TargetsCount); + } + + instances = SolutionProjectGenerator.Generate(solution, globalProperties, null, BuildEventContext.Invalid, CreateMockLoggingService(), new[] { "Foo" }); - Assert.Single(instances); + Assert.Single(instances); - Assert.Equal(14, instances[0].TargetsCount); + Assert.Equal(14, instances[0].TargetsCount); + } } /// @@ -2426,31 +2567,34 @@ public void AfterTargetsComeFromImport() { string baseDirectory = Guid.NewGuid().ToString("N"); - string solutionFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"{Guid.NewGuid():N}.sln"), @" - Microsoft Visual Studio Solution File, Format Version 14.00 + string solutionFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"{Guid.NewGuid():N}.sln"), + """ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2015 - Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ClassLibrary1"", ""ClassLibrary1.csproj"", ""{6185CC21-BE89-448A-B3C0-D1C27112E595}"" + Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1.csproj", "{6185CC21-BE89-448A-B3C0-D1C27112E595}" EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection EndGlobal - "); + """); - ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"after.{Path.GetFileName(solutionFilePath)}.targets"), @" - - + ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"after.{Path.GetFileName(solutionFilePath)}.targets"), + """ + + - "); + + """); try { @@ -2482,31 +2626,34 @@ public void BeforeTargetsFromImportCanHookDynamicTarget() { string baseDirectory = Guid.NewGuid().ToString("N"); - string solutionFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"{Guid.NewGuid():N}.sln"), @" - Microsoft Visual Studio Solution File, Format Version 14.00 + string solutionFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"{Guid.NewGuid():N}.sln"), + """ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2015 - Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ClassLibrary1"", ""ClassLibrary1.csproj"", ""{6185CC21-BE89-448A-B3C0-D1C27112E595}"" + Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1.csproj", "{6185CC21-BE89-448A-B3C0-D1C27112E595}" EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection EndGlobal - "); + """); - ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"after.{Path.GetFileName(solutionFilePath)}.targets"), @" - - - + ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"after.{Path.GetFileName(solutionFilePath)}.targets"), + """ + + + - "); + + """); try { @@ -2557,48 +2704,55 @@ public void DirectorySolutionPropsTest(string projectName, bool enable) string baseDirectory = Guid.NewGuid().ToString("N"); - string solutionFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"{Guid.NewGuid():N}.sln"), @" - Microsoft Visual Studio Solution File, Format Version 14.00 + string solutionFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, $"{Guid.NewGuid():N}.sln"), + """ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2015 - Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ClassLibrary1"", ""ClassLibrary1.csproj"", ""{6185CC21-BE89-448A-B3C0-D1C27112E595}"" + Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1.csproj", "{6185CC21-BE89-448A-B3C0-D1C27112E595}" EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection EndGlobal - "); + """); - string projectPath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, projectName), $@" - + string projectPath = ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, projectName), + $$""" + - {expectedPropertyValue} + {{expectedPropertyValue}} - "); + + """); if (projectPath.StartsWith("Custom", StringComparison.OrdinalIgnoreCase)) { // If a custom file name was given, create a Directory.Solution.props and Directory.Build.targets to ensure that they aren't imported - ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, "Directory.Solution.props"), $@" - - - This file should not be imported - - "); - - ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, "Directory.Solution.targets"), $@" - - - This file should not be imported - - "); + ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, "Directory.Solution.props"), + """ + + + This file should not be imported + + + """); + + ObjectModelHelpers.CreateFileInTempProjectDirectory(Path.Combine(baseDirectory, "Directory.Solution.targets"), + """ + + + This file should not be imported + + + """); } try @@ -2640,20 +2794,23 @@ public void DirectorySolutionPropsTest(string projectName, bool enable) /// Regression test for https://github.com/dotnet/msbuild/issues/6236 /// [Theory] - [InlineData("http://localhost:8080")] - [InlineData("a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-a-really-long-string-")] - public void AbsolutePathWorksForUnsupportedPaths(string relativePath) + [InlineData("http://localhost:8080", false)] + [InlineData("http://localhost:8080", true)] + [InlineData(_longLineString, false)] + [InlineData(_longLineString, true)] + public void AbsolutePathWorksForUnsupportedPaths(string relativePath, bool useNewParser) { string solutionFileContents = - $@" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31025.194 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{{E24C65DC-7377-472B-9ABA-BC803B73C61A}}"") = ""WebSite1"", ""{relativePath}"", ""{{{{96E0707C-2E9C-4704-946F-FA583147737F}}}}"" -EndProject"; + $$""" + Microsoft Visual Studio Solution File, Format Version 12.00 + # Visual Studio Version 16 + VisualStudioVersion = 16.0.31025.194 + MinimumVisualStudioVersion = 10.0.40219.1 + Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "WebSite1", "{{relativePath}}", "{96E0707C-2E9C-4704-946F-FA583147737F}" + EndProject + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser); ProjectInSolution projectInSolution = solution.ProjectsInOrder.ShouldHaveSingleItem(); @@ -2665,25 +2822,25 @@ public void AbsolutePathWorksForUnsupportedPaths(string relativePath) /// /// Create a Project derived from a Venus solution /// - private ProjectInstance CreateVenusSolutionProject() + private ProjectInstance CreateVenusSolutionProject(bool useNewParser) { - return CreateVenusSolutionProject(null, null); + return CreateVenusSolutionProject(null, null, useNewParser); } /// /// Create a Project derived from a Venus solution /// - private ProjectInstance CreateVenusSolutionProject(IDictionary globalProperties) + private ProjectInstance CreateVenusSolutionProject(IDictionary globalProperties, bool useNewParser) { - return CreateVenusSolutionProject(globalProperties, null); + return CreateVenusSolutionProject(globalProperties, null, useNewParser); } /// /// Create a Project derived from a Venus solution /// - private ProjectInstance CreateVenusSolutionProject(string toolsVersion) + private ProjectInstance CreateVenusSolutionProject(string toolsVersion, bool useNewParser) { - return CreateVenusSolutionProject(null, toolsVersion); + return CreateVenusSolutionProject(null, toolsVersion, useNewParser); } /// @@ -2692,10 +2849,10 @@ private ProjectInstance CreateVenusSolutionProject(string toolsVersion) /// /// The dictionary of global properties. May be null. /// The ToolsVersion override value. May be null. - private ProjectInstance CreateVenusSolutionProject(IDictionary globalProperties, string toolsVersion) + private ProjectInstance CreateVenusSolutionProject(IDictionary globalProperties, string toolsVersion, bool useNewParser) { string solutionFileContents = - @" + """ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project('{E24C65DC-7377-472B-9ABA-BC803B73C61A}') = 'C:\solutions\WebSite2\', '..\..\solutions\WebSite2\', '{F90528C4-6989-4D33-AFE8-F53173597CC2}' @@ -2727,14 +2884,18 @@ private ProjectInstance CreateVenusSolutionProject(IDictionary g {F90528C4-6989-4D33-AFE8-F53173597CC2}.Debug|Any CPU.Build.0 = Debug|.NET EndGlobalSection EndGlobal - "; + """; - SolutionFile solution = SolutionFile_Tests.ParseSolutionHelper(solutionFileContents); + // SolutionProjectGenerator.Generate calls SolutionFile.UseNewParser, so we need TestEnvironment with the environment variable available. + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + SolutionFile solution = ParseSolutionHelper(solutionFileContents, useNewParser, testEnvironment); - ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, globalProperties, toolsVersion, BuildEventContext.Invalid, CreateMockLoggingService()); + ProjectInstance[] instances = SolutionProjectGenerator.Generate(solution, globalProperties, toolsVersion, BuildEventContext.Invalid, CreateMockLoggingService()); - // Index 0 is the traversal project, which will reference the sole Venus project. - return instances[1]; + // Index 0 is the traversal project, which will reference the sole Venus project. + return instances[1]; + } } private ILoggingService CreateMockLoggingService() @@ -2776,6 +2937,25 @@ private void AssertProjectItemNameCount(ProjectInstance msbuildProject, string i Assert.Equal(count, itemGroup.Count()); } + private static SolutionFile ParseSolutionHelper(string solutionFileContents, bool isOptInSlnParsingWithNewParser) + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + return ParseSolutionHelper(solutionFileContents, isOptInSlnParsingWithNewParser, testEnvironment); + } + } + + private static SolutionFile ParseSolutionHelper(string solutionFileContents, bool isOptInSlnParsingWithNewParser, TestEnvironment testEnvironment) + { + solutionFileContents = solutionFileContents.Replace('\'', '"'); + if (isOptInSlnParsingWithNewParser) + { + testEnvironment.SetEnvironmentVariable("MSBUILD_PARSE_SLN_WITH_SOLUTIONPERSISTENCE", "1"); + } + TransientTestFile sln = testEnvironment.CreateFile(FileUtilities.GetTemporaryFileName(".sln"), solutionFileContents); + return SolutionFile.Parse(sln.Path); + } + #endregion // Helper Functions } } diff --git a/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs b/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs index b941649ad74..d6344240f1a 100644 --- a/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs +++ b/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs @@ -402,14 +402,14 @@ public void SolutionWithoutAllConfigurations() // Slashes here (and in the .slnf) are hardcoded as backslashes intentionally to support the common case. TransientTestFile solutionFile = testEnvironment.CreateFile(folder, "SimpleProject.sln", - @" + """ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29326.124 MinimumVisualStudioVersion = 10.0.40219.1 - Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""Project1"", ""1\1\1.csproj"", ""{79B5EBA6-5D27-4976-BC31-14422245A59A}"" + Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project1", "1\1\1.csproj", "{79B5EBA6-5D27-4976-BC31-14422245A59A}" EndProject - Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""2"", ""2\2\2.proj"", ""{8EFCCA22-9D51-4268-90F7-A595E11FCB2D}"" + Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "2", "2\2\2.proj", "{8EFCCA22-9D51-4268-90F7-A595E11FCB2D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -434,7 +434,7 @@ public void SolutionWithoutAllConfigurations() SolutionGuid = {DE7234EC-0C4D-4070-B66A-DCF1B4F0CFEF} EndGlobalSection EndGlobal - "); + """); ProjectCollection projectCollection = testEnvironment.CreateProjectCollection().Collection; MockLogger logger = new(); diff --git a/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs b/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs index 431ea412875..6d535479b1e 100644 --- a/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs +++ b/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs @@ -59,7 +59,8 @@ public void GraphConstructionFailsOnNonExistentSolution() new ProjectGraph("nonExistent.sln"); }); - exception.Message.ShouldContain("The project file could not be loaded. Could not find file"); + exception.Message.ShouldContain("The project file could not be loaded."); + exception.Message.ShouldContain("Could not find file"); } [Fact] @@ -646,28 +647,6 @@ IEnumerable GetIncomingEdgeItemsToNode(ProjectGraphNode nod } } - [Fact] - public void GraphConstructionShouldThrowOnMissingSolutionDependencies() - { - var solutionContents = SolutionFileBuilder.FromGraphEdges( - _env, - new Dictionary { { 1, null }, { 2, null } }, - new[] { ("1", new[] { Guid.NewGuid().ToString("B") }) }).BuildSolution(); - - var solutionFile = _env.CreateFile( - "solution.sln", - solutionContents) - .Path; - - var exception = Should.Throw( - () => - { - new ProjectGraph(solutionFile); - }); - - exception.Message.ShouldContain("but a project with this GUID was not found in the .SLN file"); - } - private static bool IsSolutionItemReference(ProjectItemInstance edgeItem) { return edgeItem.ItemType == GraphBuilder.SolutionItemReference; diff --git a/src/Build/Construction/Solution/SolutionFile.cs b/src/Build/Construction/Solution/SolutionFile.cs index b549e97da5a..d472078d160 100644 --- a/src/Build/Construction/Solution/SolutionFile.cs +++ b/src/Build/Construction/Solution/SolutionFile.cs @@ -193,7 +193,7 @@ internal int VisualStudioVersion internal bool UseNewParser => ShouldUseNewParser(_solutionFile); - internal static bool ShouldUseNewParser(string solutionFile) => FileUtilities.IsSolutionXFilename(solutionFile); + internal static bool ShouldUseNewParser(string solutionFile) => Traits.Instance.SlnParsingWithSolutionPersistenceOptIn || FileUtilities.IsSolutionXFilename(solutionFile); /// /// All projects in this solution, in the order they appeared in the solution file @@ -221,6 +221,10 @@ internal string FullPath set { + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14) && string.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(nameof(FullPath)); + } // Should already be canonicalized to a full path ErrorUtilities.VerifyThrowInternalRooted(value); // To reduce code duplication, this should be diff --git a/src/Framework/Traits.cs b/src/Framework/Traits.cs index 046933f38f0..bcdc4ac195c 100644 --- a/src/Framework/Traits.cs +++ b/src/Framework/Traits.cs @@ -145,6 +145,7 @@ public Traits() // for VS17.14 public readonly bool TelemetryOptIn = IsEnvVarOneOrTrue("MSBUILD_TELEMETRY_OPTIN"); + public readonly bool SlnParsingWithSolutionPersistenceOptIn = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILD_PARSE_SLN_WITH_SOLUTIONPERSISTENCE")); public static void UpdateFromEnvironment() {