Pre-trim Mono BCL (#1382)

* Add "ilwipe" build command

* Perform BCL wipe as part of build

* Simplify by converting ilwipe command to process entire directories

* Clean up the build
This commit is contained in:
Steve Sanderson 2018-09-05 10:03:54 +01:00 committed by GitHub
parent b004ffdc24
commit facc1e466e
15 changed files with 498 additions and 35 deletions

View File

@ -8,9 +8,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B867E038-B3CE-43E3-9292-61568C46CDEB}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B867E038-B3CE-43E3-9292-61568C46CDEB}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mono", "mono", "{7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mono", "mono", "{7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D}"
ProjectSection(SolutionItems) = preProject
src\mono\mono.targets = src\mono\mono.targets
EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}"
EndProject EndProject
@ -125,6 +122,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{F380
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "modules\jsinterop\test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{ECF02708-4CA4-44B3-B23F-274F4B417FA5}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "modules\jsinterop\test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{ECF02708-4CA4-44B3-B23F-274F4B417FA5}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mono", "src\mono\mono.csproj", "{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -456,6 +455,14 @@ Global
{ECF02708-4CA4-44B3-B23F-274F4B417FA5}.Release|Any CPU.Build.0 = Release|Any CPU {ECF02708-4CA4-44B3-B23F-274F4B417FA5}.Release|Any CPU.Build.0 = Release|Any CPU
{ECF02708-4CA4-44B3-B23F-274F4B417FA5}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU {ECF02708-4CA4-44B3-B23F-274F4B417FA5}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{ECF02708-4CA4-44B3-B23F-274F4B417FA5}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU {ECF02708-4CA4-44B3-B23F-274F4B417FA5}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Release|Any CPU.Build.0 = Release|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -512,6 +519,7 @@ Global
{4A5D7F9D-9CED-44C1-983E-054D8E99A292} = {1386F99B-3862-40C2-B24D-796C07DC7921} {4A5D7F9D-9CED-44C1-983E-054D8E99A292} = {1386F99B-3862-40C2-B24D-796C07DC7921}
{1386F99B-3862-40C2-B24D-796C07DC7921} = {F380B6B6-9486-42BC-9B24-C388F8BF13A3} {1386F99B-3862-40C2-B24D-796C07DC7921} = {F380B6B6-9486-42BC-9B24-C388F8BF13A3}
{ECF02708-4CA4-44B3-B23F-274F4B417FA5} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E} {ECF02708-4CA4-44B3-B23F-274F4B417FA5} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E} = {7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3} SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}

View File

@ -5,18 +5,20 @@
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<DefaultItemExcludes>${DefaultItemExcludes};node_modules\**</DefaultItemExcludes> <DefaultItemExcludes>${DefaultItemExcludes};node_modules\**</DefaultItemExcludes>
<IsPackable>false</IsPackable>
<!-- VS's FastUpToDateCheck doesn't consider .ts file changes, so it's necessary to disable it to get incremental builds to work correctly (albeit not as fast as if FastUpToDateCheck did work for them) --> <!-- VS's FastUpToDateCheck doesn't consider .ts file changes, so it's necessary to disable it to get incremental builds to work correctly (albeit not as fast as if FastUpToDateCheck did work for them) -->
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck> <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.BuildTools\Microsoft.AspNetCore.Blazor.BuildTools.csproj" PrivateAssets="all" ReferenceOutputAssembly="false" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(AspNetCorePackageVersion)" /> <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(AspNetCorePackageVersion)" />
<WebpackInputs Include="**\*.ts" Exclude="node_modules\**" /> <WebpackInputs Include="**\*.ts" Exclude="node_modules\**" />
<WebpackInputs Include="..\..\modules\jsinterop\src\Microsoft.JSInterop.JS\src\**" /> <WebpackInputs Include="..\..\modules\jsinterop\src\Microsoft.JSInterop.JS\src\**" />
</ItemGroup> </ItemGroup>
<Import Project="..\Microsoft.AspNetCore.Blazor.BuildTools\ReferenceFromSource.props" /> <Import Project="..\Microsoft.AspNetCore.Blazor.BuildTools\BuildToolsExe.props" />
<Target Name="EnsureNpmRestored" Condition="!Exists('node_modules')"> <Target Name="EnsureNpmRestored" Condition="!Exists('node_modules')">
<Exec Command="$(BlazorBuildToolsExe) checknodejs -v 8.3.0" /> <Exec Command="$(BlazorBuildToolsExe) checknodejs -v 8.3.0" />

View File

@ -21,9 +21,6 @@
<Import Project="$(MSBuildThisFileDirectory)targets/All.props" /> <Import Project="$(MSBuildThisFileDirectory)targets/All.props" />
<Import Project="$(MSBuildThisFileDirectory)targets/All.targets" /> <Import Project="$(MSBuildThisFileDirectory)targets/All.targets" />
<Import Project="$(MSBuildThisFileDirectory)../mono/mono.targets" />
<Target Name="ProvisionMono" DependsOnTargets="OptimizeMono" BeforeTargets="BuildBlazorBuildBinary" />
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="$(RazorPackageVersion)"/> <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="$(RazorPackageVersion)"/>
@ -37,9 +34,11 @@
<Target <Target
Name="BuildBlazorBuildBinary" Name="BuildBlazorBuildBinary"
BeforeTargets="BlazorGenerateDeclaration"> BeforeTargets="BlazorGenerateDeclaration">
<!-- Ensures this project is built before the consuming project, but without <!-- Ensures these projects are built before the consuming project, but without
adding a runtime dependency on the .dll (to be equivalent to a <PackageDependency> adding a runtime dependency on the .dll (to be equivalent to a <PackageDependency>
given that the packed version of this project wouldn't add a .dll reference) --> given that the packed version of this project wouldn't add a .dll reference) -->
<MSBuild Projects="$(MSBuildThisFileDirectory)..\Microsoft.AspNetCore.Blazor.BuildTools\Microsoft.AspNetCore.Blazor.BuildTools.csproj" Targets="Build" />
<MSBuild Projects="$(MSBuildThisFileDirectory)..\mono\mono.csproj" Targets="Build" />
<MSBuild Projects="$(MSBuildThisFileDirectory)Microsoft.AspNetCore.Blazor.Build.csproj" Targets="Build" /> <MSBuild Projects="$(MSBuildThisFileDirectory)Microsoft.AspNetCore.Blazor.Build.csproj" Targets="Build" />
</Target> </Target>

View File

@ -25,13 +25,4 @@
<BlazorBootJsonOutputPath>$(BaseBlazorRuntimeOutputPath)$(BlazorBootJsonName)</BlazorBootJsonOutputPath> <BlazorBootJsonOutputPath>$(BaseBlazorRuntimeOutputPath)$(BlazorBootJsonName)</BlazorBootJsonOutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<MonoBCLFile Include="$(MonoBaseClassLibraryPath)*.dll" />
<MonoBCLFacadeFile Include="$(MonoBaseClassLibraryFacadesPath)*.dll" />
<MonoAsmjsFile Include="$(MonoAsmjsRuntimePath)**/*.*" />
<MonoWasmFile Include="$(MonoWasmRuntimePath)**/*.*" />
<BlazorJsFile Include="$(BlazorJsPath)" />
<MonoBaseClassLibraryFolder Include="$(MonoBaseClassLibraryPath);$(MonoBaseClassLibraryFacadesPath)" />
</ItemGroup>
</Project> </Project>

View File

@ -157,6 +157,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Static content to copy to the output folder"> <ItemGroup Label="Static content to copy to the output folder">
<MonoAsmjsFile Include="$(MonoAsmjsRuntimePath)**/*.*" />
<MonoWasmFile Include="$(MonoWasmRuntimePath)**/*.*" />
<BlazorJsFile Include="$(BlazorJsPath)" />
<BlazorItemOutput Include="@(MonoAsmjsFile)"> <BlazorItemOutput Include="@(MonoAsmjsFile)">
<TargetOutputPath>$(TargetDir)$(BaseBlazorRuntimeAsmjsOutputPath)%(FileName)%(Extension)</TargetOutputPath> <TargetOutputPath>$(TargetDir)$(BaseBlazorRuntimeAsmjsOutputPath)%(FileName)%(Extension)</TargetOutputPath>
<Type>AsmJs</Type> <Type>AsmJs</Type>
@ -424,9 +427,10 @@
4) Add the file we just created to the list of file writes, to support incremental builds. 4) Add the file we just created to the list of file writes, to support incremental builds.
--> -->
<ItemGroup> <ItemGroup>
<_MonoBaseClassLibraryFolder Include="$(MonoBaseClassLibraryPath);$(MonoBaseClassLibraryFacadesPath)" />
<_BlazorAssembliesToLink Include="@(_BlazorDependencyInput->'-a &quot;%(Identity)&quot;')" /> <_BlazorAssembliesToLink Include="@(_BlazorDependencyInput->'-a &quot;%(Identity)&quot;')" />
<_BlazorAssembliesToLink Include="@(IntermediateAssembly->'-a &quot;%(FullPath)&quot;')" /> <_BlazorAssembliesToLink Include="@(IntermediateAssembly->'-a &quot;%(FullPath)&quot;')" />
<_BlazorFolderLookupPaths Include="@(MonoBaseClassLibraryFolder->'-d &quot;%(Identity)&quot;')" /> <_BlazorFolderLookupPaths Include="@(_MonoBaseClassLibraryFolder->'-d &quot;%(Identity)&quot;')" />
<_BlazorAssemblyDescriptorFiles <_BlazorAssemblyDescriptorFiles
Include="@(BlazorLinkerDescriptor->'-x &quot;%(FullPath)&quot;')" Condition="'@(BlazorLinkerDescriptor)' != ''" /> Include="@(BlazorLinkerDescriptor->'-x &quot;%(FullPath)&quot;')" Condition="'@(BlazorLinkerDescriptor)' != ''" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<BlazorBuildToolsExe>dotnet &quot;$(ArtifactsBinDir)Microsoft.AspNetCore.Blazor.BuildTools/netcoreapp2.1/Microsoft.AspNetCore.Blazor.BuildTools.dll&quot;</BlazorBuildToolsExe>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,110 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe;
using Microsoft.Extensions.CommandLineUtils;
namespace Microsoft.AspNetCore.Blazor.BuildTools.Cli.Commands
{
static class ILWipeCommand
{
public static void Command(CommandLineApplication command)
{
command.Description = "Wipes code from .NET assemblies.";
command.HelpOption("-?|-h|--help");
var inputDirOption = command.Option(
"-i|--input",
"The directory containing assemblies from which code should be wiped.",
CommandOptionType.SingleValue);
var specFileOption = command.Option(
"-s|--spec",
"The directory containing spec files describing which members to wipe from the assemblies.",
CommandOptionType.SingleValue);
var verboseOption = command.Option(
"-v|--verbose",
"If set, logs additional information to the console.",
CommandOptionType.NoValue);
var listOption = command.Option(
"-l|--list",
"If set, just writes lists the assembly contents to disk.",
CommandOptionType.NoValue);
var outputOption = command.Option(
"-o|--output",
"The directory to which the wiped assembly files should be written.",
CommandOptionType.SingleValue);
command.OnExecute(() =>
{
var inputDir = GetRequiredOptionValue(inputDirOption);
var outputDir = GetRequiredOptionValue(outputOption);
var specDir = GetRequiredOptionValue(specFileOption);
var specFiles = Directory.EnumerateFiles(
specDir, "*.txt",
new EnumerationOptions { RecurseSubdirectories = true });
foreach (var specFilePath in specFiles)
{
var specFileRelativePath = Path.GetRelativePath(specDir, specFilePath);
var assemblyRelativePath = Path.ChangeExtension(specFileRelativePath, ".dll");
var inputAssemblyPath = Path.Combine(inputDir, assemblyRelativePath);
var outputAssemblyPath = Path.Combine(outputDir, assemblyRelativePath);
var inputAssemblySize = new FileInfo(inputAssemblyPath).Length;
if (listOption.HasValue())
{
var outputList = AssemblyItem
.ListContents(inputAssemblyPath)
.Select(item => item.ToString());
File.WriteAllLines(
Path.ChangeExtension(outputAssemblyPath, ".txt"),
outputList);
}
else
{
WipeAssembly.Exec(
inputAssemblyPath,
outputAssemblyPath,
specFilePath,
verboseOption.HasValue());
Console.WriteLine(
$"{assemblyRelativePath.PadRight(40)} " +
$"{FormatFileSize(inputAssemblySize)} ==> " +
$"{FormatFileSize(outputAssemblyPath)}");
}
}
return 0;
});
}
private static string FormatFileSize(string path)
{
return FormatFileSize(new FileInfo(path).Length);
}
private static string FormatFileSize(long length)
{
return string.Format("{0:0.000} MB", ((double)length) / (1024*1024));
}
private static string GetRequiredOptionValue(CommandOption option)
{
if (!option.HasValue())
{
throw new InvalidOperationException($"Missing value for required option '{option.LongName}'.");
}
return option.Value();
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.BuildTools.Cli.Commands; using Microsoft.AspNetCore.Blazor.BuildTools.Cli.Commands;
@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Blazor.BuildTools
app.HelpOption("-?|-h|--help"); app.HelpOption("-?|-h|--help");
app.Command("checknodejs", CheckNodeJsInstalledCommand.Command); app.Command("checknodejs", CheckNodeJsInstalledCommand.Command);
app.Command("ilwipe", ILWipeCommand.Command);
if (args.Length > 0) if (args.Length > 0)
{ {

View File

@ -0,0 +1,80 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Mono.Cecil;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil.Cil;
namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
{
class AssemblyItem
{
public static IEnumerable<AssemblyItem> ListContents(string assemblyPath)
{
var moduleDefinition = ModuleDefinition.ReadModule(assemblyPath);
return ListContents(moduleDefinition);
}
public static IEnumerable<AssemblyItem> ListContents(ModuleDefinition moduleDefinition)
{
return moduleDefinition.Types
.SelectMany(GetNestedTypesRecursive)
.SelectMany(type => type.Methods)
.Select(method => new AssemblyItem(method))
.OrderBy(item => item.ToString(), StringComparer.Ordinal);
}
public MethodDefinition Method { get; }
public AssemblyItem(MethodDefinition method)
{
Method = method ?? throw new ArgumentNullException(nameof(method));
}
public void WipeFromAssembly(MethodDefinition createMethodWipedException)
{
if (!Method.HasBody)
{
return; // Nothing to do
}
// We don't want to actually remove the method definition from the assembly, because
// then you'd have an assembly that was invalid (it could contain calls to the method
// that no longer exists). Instead, remove all the instructions from its body, and
// replace it with "throw CreateMethodWipedException()". Then:
// [1] The method body is very short, while still definitely being valid (still OK for
// it to have any return type)
// [2] We've removed its references to other methods/types, so they are more likely
// to be actually removed fully by a subsequent IL linker pass
// [3] If the method is actually invoked at runtime, the stack trace will make clear
// which method is being excessively wiped
var il = Method.Body.GetILProcessor();
il.Body.Instructions.Clear();
il.Body.Variables.Clear();
il.Body.ExceptionHandlers.Clear();
il.Append(il.Create(OpCodes.Call, createMethodWipedException));
il.Append(il.Create(OpCodes.Throw));
}
public override string ToString()
{
var result = Method.ToString();
return result.Substring(result.IndexOf(' ') + 1);
}
public int CodeSize
=> Method.Body?.CodeSize ?? 0;
private static IEnumerable<TypeDefinition> GetNestedTypesRecursive(TypeDefinition type)
{
yield return type;
foreach (var descendant in type.NestedTypes.SelectMany(GetNestedTypesRecursive))
{
yield return descendant;
}
}
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
{
static class MethodWipedExceptionMethod
{
public static MethodDefinition AddToAssembly(ModuleDefinition moduleDefinition)
{
// Adds the following method to the assembly:
// namespace ILWipe
// {
// internal static class ILWipeHelpers
// {
// public static Exception CreateMethodWipedException()
// {
// return new NotImplementedException("Cannot call method because it was wiped. See stack trace for details.");
// }
// }
// }
var ilWipeHelpersType = new TypeDefinition("ILWipe", "ILWipeHelpers",
TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
moduleDefinition.Types.Add(ilWipeHelpersType);
var methodAttributes =
MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.Static;
var createMethodWipedExceptionMethod = new MethodDefinition(
"CreateMethodWipedException",
methodAttributes,
ImportEquivalentTypeFromMscorlib(moduleDefinition, typeof(Exception)));
ilWipeHelpersType.Methods.Add(createMethodWipedExceptionMethod);
var notImplExceptionType = ImportEquivalentTypeFromMscorlib(moduleDefinition, typeof(NotImplementedException));
var notImplExceptionCtor = new MethodReference(".ctor", moduleDefinition.TypeSystem.Void, notImplExceptionType);
notImplExceptionCtor.Parameters.Add(new ParameterDefinition(moduleDefinition.TypeSystem.String));
var il = createMethodWipedExceptionMethod.Body.GetILProcessor();
il.Append(il.Create(OpCodes.Ldstr, "Cannot invoke method because it was wiped. See stack trace for details."));
il.Append(il.Create(OpCodes.Newobj, notImplExceptionCtor));
il.Append(il.Create(OpCodes.Ret));
return createMethodWipedExceptionMethod;
}
static TypeReference ImportEquivalentTypeFromMscorlib(ModuleDefinition module, Type type)
{
// We have to do this instead of module.ImportReference(type), because the latter
// would try to reference it in System.Private.CoreLib because this tool itself
// compiles to target netcoreapp rather than netstandard
IMetadataScope mscorlibScope;
if (module.TryGetTypeReference(typeof(object).FullName, out var objectRef))
{
mscorlibScope = objectRef.Scope;
}
else if (module.Name == "mscorlib.dll")
{
mscorlibScope = module;
}
else
{
throw new InvalidOperationException($"Could not resolve System.Object type within '{module.FileName}'.");
}
var typeRef = new TypeReference(type.Namespace, type.Name, module, mscorlibScope, type.IsValueType);
return module.ImportReference(typeRef);
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
{
class SpecList
{
private List<SpecListEntry> _itemSpecs;
public SpecList(string[] fromSpecLines)
{
var linesToUse = fromSpecLines.Where(
line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#'));
_itemSpecs = new List<SpecListEntry>(
linesToUse.Select(line => new SpecListEntry(line)));
}
public bool IsEmpty
{
get => _itemSpecs.Count == 0;
}
public bool Match(AssemblyItem item)
{
// If this needs to be faster, consider implementing some kind of matching tree.
var lastMatchingSpec = _itemSpecs.LastOrDefault(spec => spec.IsMatch(item));
return lastMatchingSpec == null ? false : !lastMatchingSpec.Negated;
}
}
}

View File

@ -0,0 +1,121 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
{
class SpecListEntry
{
public bool Negated { get; }
public string TypeName { get; }
public string MethodName { get; }
public string Args { get; }
public SpecListEntry(string parseSpecLine)
{
parseSpecLine = parseSpecLine.Trim();
if (parseSpecLine.StartsWith('!'))
{
Negated = true;
parseSpecLine = parseSpecLine.Substring(1);
}
var colonsPos = parseSpecLine.IndexOf("::");
if (colonsPos < 0)
{
TypeName = parseSpecLine;
}
else
{
TypeName = parseSpecLine.Substring(0, colonsPos);
parseSpecLine = parseSpecLine.Substring(colonsPos + 2);
var bracketPos = parseSpecLine.IndexOf('(');
if (bracketPos < 0)
{
MethodName = parseSpecLine;
}
else
{
MethodName = parseSpecLine.Substring(0, bracketPos);
Args = parseSpecLine.Substring(bracketPos + 1, parseSpecLine.Length - bracketPos - 2);
}
}
}
public bool IsMatch(AssemblyItem item)
{
return MatchesType(item)
&& MatchesMethod(item)
&& MatchesArgs(item);
}
private bool MatchesArgs(AssemblyItem item)
{
if (Args == null)
{
return true;
}
else
{
var methodString = item.Method.ToString();
var bracketPos = methodString.IndexOf('(');
var argsString = methodString.Substring(bracketPos + 1, methodString.Length - bracketPos - 2);
return string.Equals(argsString, Args, StringComparison.Ordinal);
}
}
private bool MatchesMethod(AssemblyItem item)
{
if (MethodName == null)
{
return true;
}
else if (MethodName.EndsWith('*'))
{
return item.Method.Name.StartsWith(
MethodName.Substring(0, MethodName.Length - 1),
StringComparison.Ordinal);
}
else
{
return string.Equals(item.Method.Name, MethodName, StringComparison.Ordinal);
}
}
private bool MatchesType(AssemblyItem item)
{
var declaringTypeFullName = item.Method.DeclaringType.FullName;
if (TypeName.EndsWith('*'))
{
// Wildcard match
return declaringTypeFullName.StartsWith(
TypeName.Substring(0, TypeName.Length - 1),
StringComparison.Ordinal);
}
else
{
// Exact match
if (string.Equals(
item.Method.DeclaringType.FullName,
TypeName,
StringComparison.Ordinal))
{
return true;
}
// If we're matching all members of the type, include nested types
if (MethodName == null && declaringTypeFullName.StartsWith(
$"{TypeName}/",
StringComparison.Ordinal))
{
return true;
}
return false;
}
}
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Mono.Cecil;
using System.Linq;
namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
{
static class WipeAssembly
{
public static void Exec(string inputPath, string outputPath, string specFilePath, bool logVerbose)
{
if (string.IsNullOrEmpty(outputPath))
{
outputPath = Path.ChangeExtension(inputPath, ".wiped" + Path.GetExtension(inputPath));
}
var specLines = File.ReadAllLines(specFilePath);
var wipeSpecList = new SpecList(specLines);
var moduleDefinition = ModuleDefinition.ReadModule(inputPath);
if (!wipeSpecList.IsEmpty)
{
var createMethodWipedException = MethodWipedExceptionMethod.AddToAssembly(moduleDefinition);
var contents = AssemblyItem.ListContents(moduleDefinition).ToList();
foreach (var contentItem in contents)
{
var shouldWipe = wipeSpecList.Match(contentItem)
&& contentItem.Method != createMethodWipedException;
if (logVerbose)
{
Console.WriteLine($"{(shouldWipe ? "Wiping" : "Retaining")}: {contentItem}");
}
if (shouldWipe)
{
contentItem.WipeFromAssembly(createMethodWipedException);
}
}
}
moduleDefinition.Write(outputPath);
}
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
@ -6,12 +6,9 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="tools\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageReference Include="Mono.Cecil" Version="0.10.0-beta7" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,11 +0,0 @@
<Project>
<PropertyGroup>
<BlazorBuildToolsExe>dotnet &quot;$(ArtifactsBinDir)Microsoft.AspNetCore.Blazor.BuildTools/netcoreapp2.1/Microsoft.AspNetCore.Blazor.BuildTools.dll&quot;</BlazorBuildToolsExe>
</PropertyGroup>
<ItemGroup>
<!-- Ensures BuildTools itself is built before the consuming project, but without
adding a runtime dependency on the .dll -->
<ProjectReference Include="$(MSBuildThisFileDirectory)Microsoft.AspNetCore.Blazor.BuildTools.csproj" ReferenceOutputAssembly="false" PrivateAssets="all" />
</ItemGroup>
</Project>