diff --git a/MSBuild.sln b/MSBuild.sln
index 63edfa67df2..da041af91bd 100644
--- a/MSBuild.sln
+++ b/MSBuild.sln
@@ -80,6 +80,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSBuild.VSSetup.Arm64", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Build.Analyzers", "src\Analyzers\Microsoft.Build.Analyzers.csproj", "{512E01F7-2899-433B-93E2-D63B43AF0420}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Build.Analyzers.UnitTests", "src\Analyzers.UnitTests\Microsoft.Build.Analyzers.UnitTests.csproj", "{B18BAE17-D78F-4F89-B7A4-808C05E64D73}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1384,6 +1386,46 @@ Global
{512E01F7-2899-433B-93E2-D63B43AF0420}.Release-MONO|x64.Build.0 = Release-MONO|x64
{512E01F7-2899-433B-93E2-D63B43AF0420}.Release-MONO|x86.ActiveCfg = Release-MONO|Any CPU
{512E01F7-2899-433B-93E2-D63B43AF0420}.Release-MONO|x86.Build.0 = Release-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|ARM64.ActiveCfg = Debug|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|ARM64.Build.0 = Debug|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|x64.ActiveCfg = Debug|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|x64.Build.0 = Debug|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug|x86.Build.0 = Debug|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|Any CPU.ActiveCfg = Debug-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|Any CPU.Build.0 = Debug-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|ARM64.ActiveCfg = Debug-MONO|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|ARM64.Build.0 = Debug-MONO|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|x64.ActiveCfg = Debug-MONO|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|x64.Build.0 = Debug-MONO|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|x86.ActiveCfg = Debug-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Debug-MONO|x86.Build.0 = Debug-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|Any CPU.ActiveCfg = MachineIndependent|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|Any CPU.Build.0 = MachineIndependent|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|ARM64.ActiveCfg = MachineIndependent|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|ARM64.Build.0 = MachineIndependent|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|x64.ActiveCfg = MachineIndependent|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|x64.Build.0 = MachineIndependent|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|x86.ActiveCfg = MachineIndependent|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.MachineIndependent|x86.Build.0 = MachineIndependent|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|ARM64.ActiveCfg = Release|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|ARM64.Build.0 = Release|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|x64.ActiveCfg = Release|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|x64.Build.0 = Release|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|x86.ActiveCfg = Release|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release|x86.Build.0 = Release|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|Any CPU.ActiveCfg = Release-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|Any CPU.Build.0 = Release-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|ARM64.ActiveCfg = Release-MONO|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|ARM64.Build.0 = Release-MONO|arm64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|x64.ActiveCfg = Release-MONO|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|x64.Build.0 = Release-MONO|x64
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|x86.ActiveCfg = Release-MONO|Any CPU
+ {B18BAE17-D78F-4F89-B7A4-808C05E64D73}.Release-MONO|x86.Build.0 = Release-MONO|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/eng/BootStrapMSBuild.props b/eng/BootStrapMSBuild.props
new file mode 100644
index 00000000000..e70bcb3489d
--- /dev/null
+++ b/eng/BootStrapMSBuild.props
@@ -0,0 +1,21 @@
+
+
+
+
+
+ $(ArtifactsBinDir)bootstrap\
+ $(BootstrapDestination)$(Platform)\
+ $(BootstrapDestination)$(TargetFramework.ToLowerInvariant())\MSBuild\
+
+
+
+ $(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin
+
+
+
+ $(BootstrapDestination)
+
+
diff --git a/eng/BootStrapMSBuild.targets b/eng/BootStrapMSBuild.targets
index e6fee0282f8..395a84d09b3 100644
--- a/eng/BootStrapMSBuild.targets
+++ b/eng/BootStrapMSBuild.targets
@@ -1,5 +1,7 @@
+
+
- $(ArtifactsBinDir)bootstrap\
- $(BootstrapDestination)$(Platform)\
- $(BootstrapDestination)$(TargetFramework.ToLowerInvariant())\MSBuild\
-
BootstrapFull
BootstrapNetCore
diff --git a/src/Analyzers.UnitTests/AssemblyInfo.cs b/src/Analyzers.UnitTests/AssemblyInfo.cs
new file mode 100644
index 00000000000..0f119a6530d
--- /dev/null
+++ b/src/Analyzers.UnitTests/AssemblyInfo.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+global using NativeMethodsShared = Microsoft.Build.Framework.NativeMethods;
+
+namespace Microsoft.Build.Analyzers.UnitTests;
+
+[System.AttributeUsage(System.AttributeTargets.Assembly)]
+internal sealed class BootstrapLocationAttribute(string bootstrapRoot, string bootstrapMsbuildBinaryLocation)
+ : System.Attribute
+{
+ public string BootstrapRoot { get; } = bootstrapRoot;
+ public string BootstrapMsbuildBinaryLocation { get; } = bootstrapMsbuildBinaryLocation;
+}
diff --git a/src/Analyzers.UnitTests/BootstrapRunner.cs b/src/Analyzers.UnitTests/BootstrapRunner.cs
new file mode 100644
index 00000000000..540abc8ac63
--- /dev/null
+++ b/src/Analyzers.UnitTests/BootstrapRunner.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Build.UnitTests.Shared;
+using Xunit.Abstractions;
+
+namespace Microsoft.Build.Analyzers.UnitTests
+{
+ internal static class BootstrapRunner
+ {
+ // This should ideally be part of RunnerUtilities - however then we'd need to enforce
+ // all test projects to import the BootStrapMSBuild.props file and declare the BootstrapLocationAttribute.
+ // Better solution would be to have a single test utility project - instead of linked code files.
+ public static string ExecBootstrapedMSBuild(string msbuildParameters, out bool successfulExit, bool shellExecute = false, ITestOutputHelper outputHelper = null)
+ {
+ var binaryFolder = Assembly.GetExecutingAssembly()
+ .GetCustomAttribute()!
+ .BootstrapMsbuildBinaryLocation;
+
+#if NET
+ string pathToExecutable = EnvironmentProvider.GetDotnetExePath()!;
+ msbuildParameters = Path.Combine(binaryFolder, "msbuild.dll") + " " + msbuildParameters;
+#else
+ string pathToExecutable =
+ Path.Combine(binaryFolder, "msbuild.exe");
+#endif
+ return RunnerUtilities.RunProcessAndGetOutput(pathToExecutable, msbuildParameters, out successfulExit, shellExecute, outputHelper);
+ }
+ }
+}
diff --git a/src/Build.UnitTests/Analyzers/EndToEndTests.cs b/src/Analyzers.UnitTests/EndToEndTests.cs
similarity index 94%
rename from src/Build.UnitTests/Analyzers/EndToEndTests.cs
rename to src/Analyzers.UnitTests/EndToEndTests.cs
index 79ecd1addc9..76a5479ebaa 100644
--- a/src/Build.UnitTests/Analyzers/EndToEndTests.cs
+++ b/src/Analyzers.UnitTests/EndToEndTests.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.UnitTests;
@@ -13,7 +14,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.Build.Engine.UnitTests.Analyzers
+namespace Microsoft.Build.Analyzers.UnitTests
{
public class EndToEndTests
{
@@ -27,8 +28,6 @@ public EndToEndTests(ITestOutputHelper output)
}
[Fact]
- // WARNING!: this test is using a bootstrap - so a build.cmd needs to be run prior this test can properly capture the
- // changes in the production code
public void SampleAnalyzerIntegrationTest()
{
using (TestEnvironment env = TestEnvironment.Create())
@@ -113,7 +112,7 @@ public void SampleAnalyzerIntegrationTest()
// env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
env.SetEnvironmentVariable("MSBUILDLOGPROPERTIESANDITEMSAFTEREVALUATION", "1");
// string output = RunnerUtilities.ExecMSBuild($"{projectFile.Path} /m:1 -nr:False", out bool success);
- string output = RunnerUtilities.ExecBootstrapedMSBuild($"{projectFile.Path} /m:1 -nr:False -restore", out bool success);
+ string output = BootstrapRunner.ExecBootstrapedMSBuild($"{projectFile.Path} /m:1 -nr:False -restore", out bool success);
_env.Output.WriteLine(output);
success.ShouldBeTrue();
// The conflicting outputs warning appears
diff --git a/src/Analyzers.UnitTests/Microsoft.Build.Analyzers.UnitTests.csproj b/src/Analyzers.UnitTests/Microsoft.Build.Analyzers.UnitTests.csproj
new file mode 100644
index 00000000000..dab550407fd
--- /dev/null
+++ b/src/Analyzers.UnitTests/Microsoft.Build.Analyzers.UnitTests.csproj
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+ $(RuntimeOutputTargetFrameworks)
+ $(RuntimeOutputPlatformTarget)
+ false
+ True
+ Microsoft.Build.Analyzers.UnitTests
+ Microsoft.Build.Analyzers.UnitTests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Shared\TestEnvironment.cs
+
+
+
+ Shared\FileUtilities.cs
+
+
+ Shared\TempFileUtilities.cs
+
+
+ Shared\ErrorUtilities.cs
+
+
+ Shared\EscapingUtilities.cs
+
+
+ Shared\BuildEnvironmentHelper.cs
+
+
+ Shared\ProcessExtensions.cs
+
+
+ Shared\ResourceUtilities.cs
+
+
+ Shared\ExceptionHandling.cs
+
+
+ Shared\FileUtilitiesRegex.cs
+
+
+ Shared\AssemblyResources.cs
+
+
+ Shared\RunnerUtilities.cs
+
+
+ Shared\EnvironmentProvider.cs
+
+
+
+
+
+ App.config
+ Designer
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+ <_Parameter1>$(ArtifactsBinDir)
+ <_Parameter2>$(BootstrapBinaryDestination)
+
+
+
diff --git a/src/Framework/Properties/AssemblyInfo.cs b/src/Framework/Properties/AssemblyInfo.cs
index 7de94ca1917..dfe95d4db15 100644
--- a/src/Framework/Properties/AssemblyInfo.cs
+++ b/src/Framework/Properties/AssemblyInfo.cs
@@ -46,6 +46,7 @@
[assembly: InternalsVisibleTo("Microsoft.Build.Conversion.Core, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
[assembly: InternalsVisibleTo("Microsoft.Build.Engine.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010015c01ae1f50e8cc09ba9eac9147cf8fd9fce2cfe9f8dce4f7301c4132ca9fb50ce8cbf1df4dc18dd4d210e4345c744ecb3365ed327efdbc52603faa5e21daa11234c8c4a73e51f03bf192544581ebe107adee3a34928e39d04e524a9ce729d5090bfd7dad9d10c722c0def9ccc08ff0a03790e48bcd1f9b6c476063e1966a1c4")]
+[assembly: InternalsVisibleTo("Microsoft.Build.Analyzers.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010015c01ae1f50e8cc09ba9eac9147cf8fd9fce2cfe9f8dce4f7301c4132ca9fb50ce8cbf1df4dc18dd4d210e4345c744ecb3365ed327efdbc52603faa5e21daa11234c8c4a73e51f03bf192544581ebe107adee3a34928e39d04e524a9ce729d5090bfd7dad9d10c722c0def9ccc08ff0a03790e48bcd1f9b6c476063e1966a1c4")]
// Ideally we wouldn't need to IVT to OM.UnitTests, which is supposed to test
// only the public surface area of Microsoft.Build. However, there's a bunch
// of shared code in Framework that's used there, and we can still avoid IVT
diff --git a/src/UnitTests.Shared/RunnerUtilities.cs b/src/UnitTests.Shared/RunnerUtilities.cs
index f836a7be6bd..3eb1026dfd2 100644
--- a/src/UnitTests.Shared/RunnerUtilities.cs
+++ b/src/UnitTests.Shared/RunnerUtilities.cs
@@ -33,24 +33,6 @@ public static string ExecMSBuild(string msbuildParameters, out bool successfulEx
return ExecMSBuild(PathToCurrentlyRunningMsBuildExe, msbuildParameters, out successfulExit, outputHelper: outputHelper);
}
- ///
- /// Invoke the currently running msbuild and return the stdout, stderr, and process exit status.
- /// This method may invoke msbuild via other runtimes.
- ///
- public static string ExecBootstrapedMSBuild(string msbuildParameters, out bool successfulExit, bool shellExecute = false, ITestOutputHelper outputHelper = null)
- {
- // TODO: use proper way of passing the bootsrtrap location: https://github.com/dotnet/msbuild/issues/9729
- string basePath = PathToCurrentlyRunningMsBuildExe.Substring(0, PathToCurrentlyRunningMsBuildExe.IndexOf(@"artifacts\bin", StringComparison.InvariantCultureIgnoreCase));
-#if NET
- string pathToExecutable = EnvironmentProvider.GetDotnetExePath();
- msbuildParameters = Path.Combine(basePath, @"artifacts\bin\bootstrap\net8.0\MSBuild\msbuild.dll") + " " + msbuildParameters;
-#else
- string pathToExecutable =
- Path.Combine(basePath, @"artifacts\bin\bootstrap\net472\MSBuild\Current\Bin\amd64\msbuild.exe");
-#endif
- return RunProcessAndGetOutput(pathToExecutable, msbuildParameters, out successfulExit, shellExecute, outputHelper);
- }
-
///
/// Invoke msbuild.exe with the given parameters and return the stdout, stderr, and process exit status.
/// This method may invoke msbuild via other runtimes.