提交 f32cfb90 编写于 作者: J Jared Parsons

Validate our bootstrap phase loads the correct binaries

Our bootstrap phase relies on loading Microsoft.Build.Tasks.CodeAnalysis from a specific place on disk.  There is no way in MSBuild to guarantee that suceeds.  It's possible for instance for MSBuild to have already loaded a DLL of the same name from a new location.  Or even if our DLL is loaded, it's possible that some of the downstream depenedencies fell prey to the same problem.

This adds a validation layer to our bootstrap phase to guard against that.  It verifies that our build task and dependencies are loading from the location that we expect them to load from.

Note: there is actually a bug in MSBuild right now that prevents us from correctly doing this in MSBuild 15.0.  This check will help us validate that bug was fixed properly. https://github.com/Microsoft/msbuild/issues/1183
上级 ea44dc5e
......@@ -201,6 +201,9 @@
<UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.Vbc"
AssemblyFile="$(BootstrapBuildPath)\Microsoft.Build.Tasks.CodeAnalysis.dll"
Condition="'$(BootstrapBuildPath)' != ''" />
<UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.ValidateBootstrap"
AssemblyFile="$(BootstrapBuildPath)\Microsoft.Build.Tasks.CodeAnalysis.dll"
Condition="'$(BootstrapBuildPath)' != ''" />
<PropertyGroup Condition="'$(BootstrapBuildPath)' != ''">
<UseSharedCompilation>true</UseSharedCompilation>
......@@ -208,6 +211,9 @@
<VisualBasicCoreTargetsPath>$(BootstrapBuildPath)\Microsoft.VisualBasic.Core.targets</VisualBasicCoreTargetsPath>
</PropertyGroup>
<Target Name="CheckBootstrapState" Condition="'$(BootstrapBuildPath)' != ''" BeforeTargets="CoreCompile">
<ValidateBootstrap BootstrapPath="$(BootstrapBuildPath)" />
</Target>
<!-- Common project settings -->
<PropertyGroup>
......@@ -290,6 +296,9 @@
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition="'$(InitialDefineConstants)' != ''">$(InitialDefineConstants)</DefineConstants>
</PropertyGroup>
</When>
<!-- C# specific settings -->
......@@ -314,6 +323,9 @@
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition="'$(InitialDefineConstants)' != ''">$(DefineConstants);$(InitialDefineConstants)</DefineConstants>
</PropertyGroup>
</When>
<!-- C++ specific settings -->
......
......@@ -61,7 +61,7 @@ set bindir=%RoslynRoot%Binaries
if not exist "%bindir%" mkdir "%bindir%" || goto :BuildFailed
REM Build with the real assembly version, since that's what's contained in the bootstrap compiler redirects
msbuild %MSBuildAdditionalCommandLineArgs% /p:UseShippingAssemblyVersion=true "%RoslynRoot%build\Toolset.sln" /p:NuGetRestorePackages=false /p:Configuration=%BuildConfiguration% /fileloggerparameters:LogFile="%bindir%\Bootstrap.log" || goto :BuildFailed
msbuild %MSBuildAdditionalCommandLineArgs% /p:UseShippingAssemblyVersion=true /p:InitialDefineConstants=BOOTSTRAP "%RoslynRoot%build\Toolset.sln" /p:NuGetRestorePackages=false /p:Configuration=%BuildConfiguration% /fileloggerparameters:LogFile="%bindir%\Bootstrap.log" || goto :BuildFailed
powershell -noprofile -executionPolicy RemoteSigned -file "%RoslynRoot%\build\scripts\check-msbuild.ps1" "%bindir%\Bootstrap.log" || goto :BuildFailed
if not exist "%bindir%\Bootstrap" mkdir "%bindir%\Bootstrap" || goto :BuildFailed
......
......@@ -46,6 +46,7 @@
<Link>DesktopBuildClient.cs</Link>
</Compile>
<Compile Include="CanonicalError.cs" />
<Compile Include="ValidateBootstrap.cs" />
<Compile Include="CommandLineBuilderExtension.cs" />
<Compile Include="Csc.cs" />
<Compile Include="Csi.cs" />
......
......@@ -476,14 +476,19 @@ private static string TryGetClientDir()
?.GetMethod.Invoke(buildTask, parameters: null);
if (inGac != false)
{
return null;
}
var codeBase = (string)typeof(Assembly)
.GetTypeInfo()
.GetDeclaredProperty("CodeBase")
?.GetMethod.Invoke(buildTask, parameters: null);
if (codeBase == null) return null;
if (codeBase == null)
{
return null;
}
var uri = new Uri(codeBase);
......@@ -499,15 +504,15 @@ private static string TryGetClientDir()
.GetDeclaredMethod("GetCallingAssembly")
?.Invoke(null, null);
var location = (string)typeof(Assembly)
.GetTypeInfo()
.GetDeclaredProperty("Location")
?.GetMethod.Invoke(callingAssembly, parameters: null);
if (location == null) return null;
var location = Utilities.GetLocation(callingAssembly);
if (location == null)
{
return null;
}
assemblyPath = location;
}
return Path.GetDirectoryName(assemblyPath);
}
......
......@@ -7,6 +7,7 @@
using Microsoft.Build.Framework;
using Microsoft.CodeAnalysis.CommandLine;
using System.Runtime.InteropServices;
using System.Reflection;
namespace Microsoft.CodeAnalysis.BuildTasks
{
......@@ -147,5 +148,16 @@ internal static void DeleteNoThrow(string path)
{
return new ArgumentException(string.Format(CultureInfo.CurrentCulture, errorString, args));
}
internal static string GetLocation(Assembly assembly)
{
var method = typeof(Assembly).GetTypeInfo().GetDeclaredProperty("Location")?.GetMethod;
if (method == null)
{
return null;
}
return (string)method.Invoke(assembly, parameters: null);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Build.Utilities;
using System.IO;
using System.Reflection;
using Microsoft.CodeAnalysis.CSharp;
using System;
namespace Microsoft.CodeAnalysis.BuildTasks
{
#if DEBUG || BOOTSTRAP
/// <summary>
/// This task exists to help us validate our bootstrap building phase is executing correctly. It
/// is very easy for us, or MSBuild, to accidentally load DLLs from locations that we are
/// not expecting. This task takes steps to validate the bootstrap phase is executing as expected.
/// </summary>
public sealed class ValidateBootstrap : Task
{
private string _bootstrapPath;
public string BootstrapPath
{
get { return _bootstrapPath; }
set { _bootstrapPath = NormalizePath(value); }
}
public ValidateBootstrap()
{
}
public override bool Execute()
{
if (_bootstrapPath == null)
{
Log.LogError($"{nameof(ValidateBootstrap)} task must have a {nameof(BootstrapPath)} parameter.");
return false;
}
var dependencies = new[]
{
typeof(ValidateBootstrap).GetTypeInfo().Assembly,
typeof(CSharpCompilation).GetTypeInfo().Assembly,
typeof(Compilation).GetTypeInfo().Assembly,
};
var allGood = true;
var comparer = StringComparer.OrdinalIgnoreCase;
foreach (var dependency in dependencies)
{
var path = GetDirectory(dependency);
path = NormalizePath(path);
if (!comparer.Equals(path, _bootstrapPath))
{
Log.LogError($"Bootstrap assembly {dependency.GetName().Name} incorrectly loaded from {path} instead of {_bootstrapPath}");
allGood = false;
}
}
return allGood;
}
private static string NormalizePath(string path)
{
if (path == null)
{
return path;
}
if (path.EndsWith("/"))
{
path = path.Substring(0, path.Length - 1);
}
return path;
}
private string GetDirectory(Assembly assembly) => Path.GetDirectoryName(Utilities.GetLocation(assembly));
}
#endif
}
......@@ -12,6 +12,7 @@
"System.IO.FileSystem": "4.0.1",
"System.IO.FileSystem.DriveInfo": "4.0.0",
"System.IO.Pipes": "4.0.0",
"System.Reflection": "4.1.0",
"System.Runtime.InteropServices.RuntimeInformation": "4.0.0",
"System.Security.AccessControl": "4.0.0",
"System.Security.Cryptography.Algorithms": "4.2.0",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册