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:
parent
b004ffdc24
commit
facc1e466e
14
Blazor.sln
14
Blazor.sln
|
|
@ -8,9 +8,6 @@ EndProject
|
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B867E038-B3CE-43E3-9292-61568C46CDEB}"
|
||||
EndProject
|
||||
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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}"
|
||||
EndProject
|
||||
|
|
@ -125,6 +122,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{F380
|
|||
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}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mono", "src\mono\mono.csproj", "{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.ReleaseNoVSIX|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -512,6 +519,7 @@ Global
|
|||
{4A5D7F9D-9CED-44C1-983E-054D8E99A292} = {1386F99B-3862-40C2-B24D-796C07DC7921}
|
||||
{1386F99B-3862-40C2-B24D-796C07DC7921} = {F380B6B6-9486-42BC-9B24-C388F8BF13A3}
|
||||
{ECF02708-4CA4-44B3-B23F-274F4B417FA5} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
||||
{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E} = {7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,20 @@
|
|||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
||||
<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) -->
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.BuildTools\Microsoft.AspNetCore.Blazor.BuildTools.csproj" PrivateAssets="all" ReferenceOutputAssembly="false" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(AspNetCorePackageVersion)" />
|
||||
<WebpackInputs Include="**\*.ts" Exclude="node_modules\**" />
|
||||
<WebpackInputs Include="..\..\modules\jsinterop\src\Microsoft.JSInterop.JS\src\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\Microsoft.AspNetCore.Blazor.BuildTools\ReferenceFromSource.props" />
|
||||
<Import Project="..\Microsoft.AspNetCore.Blazor.BuildTools\BuildToolsExe.props" />
|
||||
|
||||
<Target Name="EnsureNpmRestored" Condition="!Exists('node_modules')">
|
||||
<Exec Command="$(BlazorBuildToolsExe) checknodejs -v 8.3.0" />
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@
|
|||
|
||||
<Import Project="$(MSBuildThisFileDirectory)targets/All.props" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)targets/All.targets" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)../mono/mono.targets" />
|
||||
|
||||
<Target Name="ProvisionMono" DependsOnTargets="OptimizeMono" BeforeTargets="BuildBlazorBuildBinary" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="$(RazorPackageVersion)"/>
|
||||
|
|
@ -37,9 +34,11 @@
|
|||
<Target
|
||||
Name="BuildBlazorBuildBinary"
|
||||
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>
|
||||
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" />
|
||||
</Target>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,13 +25,4 @@
|
|||
<BlazorBootJsonOutputPath>$(BaseBlazorRuntimeOutputPath)$(BlazorBootJsonName)</BlazorBootJsonOutputPath>
|
||||
</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>
|
||||
|
|
@ -157,6 +157,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Static content to copy to the output folder">
|
||||
<MonoAsmjsFile Include="$(MonoAsmjsRuntimePath)**/*.*" />
|
||||
<MonoWasmFile Include="$(MonoWasmRuntimePath)**/*.*" />
|
||||
<BlazorJsFile Include="$(BlazorJsPath)" />
|
||||
<BlazorItemOutput Include="@(MonoAsmjsFile)">
|
||||
<TargetOutputPath>$(TargetDir)$(BaseBlazorRuntimeAsmjsOutputPath)%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>AsmJs</Type>
|
||||
|
|
@ -424,9 +427,10 @@
|
|||
4) Add the file we just created to the list of file writes, to support incremental builds.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<_MonoBaseClassLibraryFolder Include="$(MonoBaseClassLibraryPath);$(MonoBaseClassLibraryFacadesPath)" />
|
||||
<_BlazorAssembliesToLink Include="@(_BlazorDependencyInput->'-a "%(Identity)"')" />
|
||||
<_BlazorAssembliesToLink Include="@(IntermediateAssembly->'-a "%(FullPath)"')" />
|
||||
<_BlazorFolderLookupPaths Include="@(MonoBaseClassLibraryFolder->'-d "%(Identity)"')" />
|
||||
<_BlazorFolderLookupPaths Include="@(_MonoBaseClassLibraryFolder->'-d "%(Identity)"')" />
|
||||
<_BlazorAssemblyDescriptorFiles
|
||||
Include="@(BlazorLinkerDescriptor->'-x "%(FullPath)"')" Condition="'@(BlazorLinkerDescriptor)' != ''" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<BlazorBuildToolsExe>dotnet "$(ArtifactsBinDir)Microsoft.AspNetCore.Blazor.BuildTools/netcoreapp2.1/Microsoft.AspNetCore.Blazor.BuildTools.dll"</BlazorBuildToolsExe>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.BuildTools.Cli.Commands;
|
||||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Blazor.BuildTools
|
|||
app.HelpOption("-?|-h|--help");
|
||||
|
||||
app.Command("checknodejs", CheckNodeJsInstalledCommand.Command);
|
||||
app.Command("ilwipe", ILWipeCommand.Command);
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
|
|
@ -6,12 +6,9 @@
|
|||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="tools\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.10.0-beta7" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<BlazorBuildToolsExe>dotnet "$(ArtifactsBinDir)Microsoft.AspNetCore.Blazor.BuildTools/netcoreapp2.1/Microsoft.AspNetCore.Blazor.BuildTools.dll"</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>
|
||||
Loading…
Reference in New Issue