Imported Upstream version 6.4.0.137

Former-commit-id: 943baa9f16a098c33e129777827f3a9d20da00d6
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-07-26 19:53:28 +00:00
parent e9207cf623
commit ef583813eb
2712 changed files with 74169 additions and 40587 deletions

View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- net46 build is disabled until cecil uses SDK-style projects. -->
@@ -12,38 +11,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\linker\Mono.Linker.csproj">
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Debug' ">Configuration=illink_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Release' ">Configuration=illink_Release</SetConfiguration>
<Project>{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}</Project>
</ProjectReference>
<ProjectReference Include="..\..\external\cecil\Mono.Cecil.csproj">
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Debug' ">Configuration=netstandard_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Debug' And '$(TargetFramework)' == 'net46' ">Configuration=net_4_0_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Release' ">Configuration=netstandard_Release</SetConfiguration>
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Release' And '$(TargetFramework)' == 'net46' ">Configuration=net_4_0_Release</SetConfiguration>
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
</ProjectReference>
<ProjectReference Include="..\linker\Mono.Linker.csproj" />
<ProjectReference Include="..\..\external\cecil\Mono.Cecil.csproj" />
</ItemGroup>
<!-- The reference to the linker will cause Mono.Cecil.Pdb to be
built in the wrong configuration unless we apply this
workaround. -->
<Target Name="SetCecilConfiguration"
AfterTargets="AssignProjectConfiguration">
<ItemGroup>
<ProjectReferenceWithConfiguration Condition=" '%(Filename)%(Extension)' == 'Mono.Cecil.Pdb.csproj' ">
<SetConfiguration Condition=" '$(TargetFramework)' == 'net46' And '$(Configuration)' == 'illink_Debug' ">Configuration=net_4_0_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'netcoreapp2.0' And '$(Configuration)' == 'illink_Debug' ">Configuration=netstandard_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'net46' And '$(Configuration)' == 'illink_Release' ">Configuration=net_4_0_Release</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'netcoreapp2.0' And '$(Configuration)' == 'illink_Release' ">Configuration=netstandard_Release</SetConfiguration>
</ProjectReferenceWithConfiguration>
<ProjectReferenceWithConfiguration Condition=" '%(Filename)%(Extension)' == 'Mono.Cecil.Mdb.csproj' ">
<SetConfiguration Condition=" '$(TargetFramework)' == 'net46' And '$(Configuration)' == 'illink_Debug' ">Configuration=net_4_0_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'netcoreapp2.0' And '$(Configuration)' == 'illink_Debug' ">Configuration=netstandard_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'net46' And '$(Configuration)' == 'illink_Release' ">Configuration=net_4_0_Release</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'netcoreapp2.0' And '$(Configuration)' == 'illink_Release' ">Configuration=netstandard_Release</SetConfiguration>
</ProjectReferenceWithConfiguration>
</ItemGroup>
</Target>
</Project>

View File

@@ -1,37 +0,0 @@
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
class AdapterLogger : Mono.Linker.ILogger
{
private TaskLoggingHelper log;
public AdapterLogger (TaskLoggingHelper log)
{
this.log = log;
}
public void LogMessage (Mono.Linker.MessageImportance importance, string message, params object[] values)
{
Microsoft.Build.Framework.MessageImportance msBuildImportance;
switch (importance)
{
case Mono.Linker.MessageImportance.High:
msBuildImportance = MessageImportance.High;
break;
case Mono.Linker.MessageImportance.Normal:
msBuildImportance = MessageImportance.Normal;
break;
case Mono.Linker.MessageImportance.Low:
msBuildImportance = MessageImportance.Low;
break;
default:
throw new ArgumentException ($"Unrecognized importance level {importance}", nameof(importance));
}
log.LogMessageFromText (String.Format (message, values), msBuildImportance);
}
}
}

View File

@@ -1,102 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(MSBuildThisFileDirectory)../../eng/Versions.props" Condition=" '$(ArcadeBuild)' != 'true' " />
<PropertyGroup>
<!-- net46 build is disabled until cecil uses SDK-style projects. -->
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.0;net472</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<BaseOutputPath>bin/</BaseOutputPath>
<PackageOutputPath>$(BaseOutputPath)nupkgs</PackageOutputPath>
<IsPackable>true</IsPackable>
<!-- IsTool true causes the build output to be placed in the
package's tools folder. This allows projects to reference the
tasks package without including the tasks dll in their
output. -->
<!-- TODO: This has no effect currently, because we are using a
custom .nuspec with the tools path hardcoded. Uncomment this
once we are able to remove the custom .nuspec workaround. -->
<!-- <IsTool>true</IsTool> -->
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>MSBuild tasks for running the IL Linker</Description>
<Authors>$(AssemblyName)</Authors>
<!-- Don't include the build output. Instead, we want to include
the TargetFramework-specific publish output. -->
<IncludeBuildOutput>false</IncludeBuildOutput>
<!-- We want to package the tasks package together with its
package dependencies, the linker, and the linker's
dependencies, in order to prevent projects that consume the
tasks package from pulling in the linker. To do this, we need
to include project references and package references in the
package, and prevent any of these references from being
marked as dependencies in the tasks package.
transitive dependencies and the linker, without marking them
as dependencies in the tasks package.
To include the linker in the package, we want to package the
tasks project together with its project references. This is
not supported by the pack targets
(https://github.com/dotnet/cli/issues/1290,
https://github.com/dotnet/cli/issues/3959), so we work around
this by explicitly setting the package path to include the
build output. Using the publish directory will also cause
dependencies from package references to be packaged.
To prevent the linker from being marked as a package
dependency, we can't use PrivateAssets="All", because this
removes it from the publish output as well, due to an issue
in the SDK (https://github.com/dotnet/sdk/issues/952). To
work around this, we use a custom .nuspec that doesn't
declare any dependencies. This also prevents package
references from being marked as dependencies. -->
<!-- TODO: Remove the custom .nuspec once the P2P PrivateAssets
issue is fixed. -->
<NuspecFileName>ILLink.Tasks.nuspec</NuspecFileName>
<NuspecFile>$(BaseOutputPath)$(NuspecFileName)</NuspecFile>
<NuspecProperties>id=$(AssemblyName);authors=$(AssemblyName);description=linker tasks;</NuspecProperties>
Pack doesn't support including project references
(https://github.com/NuGet/Home/issues/3891), so we work
around this by explicitly including the publish output in the
package. -->
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_LayoutPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<!-- TODO: Remove this workaround once we're able to avoid using a
custom .nuspec. We may still need a similar workaround to
dynamically include the publish output in the package contents.
We can't specify the output path to package in the static
nuspec properties, because the project's output path gets set
at a later point. To work around this, we add the output path
to the nuspec properties dynamically as part of the pack
target. We use the same workaround to set the version in the
.nuspec file.
We can't insert this into the pack target by modifying
PackDependsOn, since GenerateNuspec is always prepended to
PackDependsOn, which would cause the nuspec file to be
generated before our extra properties are added.
Instead, we use GenerateNuspecDependsOn. We could probably also
use BeforeTargets="GenerateNuspec". -->
<PropertyGroup>
<GenerateNuspecDependsOn>SetDynamicNuspecProperties;BinPlacePackageDeps;$(GenerateNuspecDependsOn)</GenerateNuspecDependsOn>
</PropertyGroup>
<Target Name="SetDynamicNuspecProperties"
DependsOnTargets="LayoutPackage">
<PropertyGroup>
<NuspecProperties>$(NuspecProperties)version=$(Version);</NuspecProperties>
</PropertyGroup>
</Target>
<!-- This target is necessary because the .nuspec includes the full
path of the selected dlls in the package layout. We want the
assets to be included in the package without the bin prefix, so
we place the .nuspec and the included targets alongside the
publish directories in the bin directory. -->
<Target Name="BinPlacePackageDeps">
<Copy SourceFiles="$(NuspecFileName)" DestinationFolder="$(BaseOutputPath)" />
<Copy SourceFiles="ILLink.Tasks.targets" DestinationFolder="$(BaseOutputPath)" />
</Target>
<Target Name="LayoutPackage">
<ItemGroup>
<TFMsToPublish Include="$(TargetFrameworks)" />
<ProjectsToPublish Include="$(MSBuildProjectFile)">
<AdditionalProperties>TargetFramework=%(TFMsToPublish.Identity);PublishDir=$(BaseOutputPath)%(TFMsToPublish.Identity)</AdditionalProperties>
</ProjectsToPublish>
</ItemGroup>
<MSBuild Projects="@(ProjectsToPublish)" Targets="Publish" />
</Target>
<ItemGroup>
<Compile Include="AdapterLogger.cs" />
<Compile Include="LinkTask.cs" />
<Compile Include="CheckEmbeddedRootDescriptor.cs" />
<Compile Include="CompareSizes.cs" />
@@ -109,114 +37,72 @@
<Compile Include="FindNativeDeps.cs" />
<Compile Include="SetAssemblyActions.cs" />
<Compile Include="Utils.cs" />
<Content Include="Sdk\Sdk.props">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</ItemGroup>
<ItemGroup>
<Content Include="ILLink.Tasks.targets">
<PackagePath>build</PackagePath>
</Content>
<Content Include="Sdk/Sdk.props">
<PackagePath>Sdk</PackagePath>
</Content>
</ItemGroup>
<!-- TODO: Uncomment this once we can avoid hard-coding this in a
custom .nuspec. -->
<!-- Targets under the build directory of the package automatically
get included in the consumer's build.
-->
<!--
<ItemGroup>
<Content Include="ILLink.Tasks.targets">
<PackagePath>build</PackagePath>
</Content>
</ItemGroup>
-->
<Target Name="_LayoutPackage">
<!-- TODO: Use this to set the package contents once we can avoid
using a custom .nuspec. -->
<!-- We can't glob everything in the output path for two reasons:
<PropertyGroup>
<PublishDir>$(BaseOutputPath)$(TargetFramework)</PublishDir>
</PropertyGroup>
1. Content gets expanded before "Build" is called during
"Pack". We could work around this by creating our own target to
call build and then pack, but it would be nice to avoid
changing the build/package pipeline.
2. We'll try to include the tasks dll twice. This only causes a
warning during pack, but should be avoided.
<Content Include="$(OutputPath)*.dll;$(OutputPath)*.json">
<PackagePath>tools</PackagePath>
</Content>
There may also be a better ItemGroup to use than
Content. Content semantics mean to output with the build, which
isn't our goal. Instead, we want to include these dependencies
in the package without necessarily including them in the build.
-->
<!--
<ItemGroup>
<Content Include="$(OutputPath)illink.dll;$(OutputPath)Mono.Cecil.dll">
<PackagePath>tools</PackagePath>
</Content>
</ItemGroup>
-->
<ItemGroup>
<!-- TODO: Once https://github.com/dotnet/sdk/issues/952 is fixed,
use PrivateAssets="All" to prevent this project reference
from being marked as a dependency of the tasks package (while
still including it in the publish output). -->
<ProjectReference Include="../linker/Mono.Linker.csproj">
<!-- SetConfiguration isn't required when the configuration is
already set in the solution. Setting it here allows packing
the tasks csproj on its own. This lets us avoid some of the
strange behavior that shows up when trying to build from a
.sln file.
There is a nuget bug that prevents this from working
properly during restore
(https://github.com/NuGet/Home/issues/4873). For the
moment, the linker has a workaround for this issue.
However, this still won't work properly because the build
target doesn't propagate this information properly either -
this is probably another bug. Building from the .csproj
would cause cecil to be built twice, once through the
linker with the netstandard configuration and once directly
from the tasks project in the default configuration info
(because some target gets its reference information from
the lock file, which doesn't have configuration info).
-->
<SetConfiguration>Configuration=illink_$(Configuration)</SetConfiguration>
</ProjectReference>
<ProjectReference Include="../../external/cecil/Mono.Cecil.csproj" />
<ProjectReference Include="../ILLink.CustomSteps/ILLink.CustomSteps.csproj">
<SetConfiguration>Configuration=illink_$(Configuration)</SetConfiguration>
</ProjectReference>
</ItemGroup>
<!-- Workaround for the SetConfiguration issue described above. -->
<Target Name="SetCecilConfiguration"
AfterTargets="AssignProjectConfiguration">
<ItemGroup>
<ProjectReferenceWithConfiguration Condition=" '%(Filename)%(Extension)' == 'Mono.Cecil.csproj' Or '%(Filename)%(Extension)' == 'Mono.Cecil.Pdb.csproj' Or '%(Filename)%(Extension)' == 'Mono.Cecil.Mdb.csproj' ">
<SetConfiguration Condition=" '$(TargetFramework)' == 'net46' ">Configuration=net_4_0_$(Configuration)</SetConfiguration>
<SetConfiguration Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">Configuration=netstandard_$(Configuration)</SetConfiguration>
</ProjectReferenceWithConfiguration>
<ProjectsToPublish Include="$(MSBuildProjectFile)">
<AdditionalProperties>TargetFramework=$(TargetFramework);PublishDir=$(PublishDir)</AdditionalProperties>
</ProjectsToPublish>
</ItemGroup>
<!-- Clean the publish directory in case there are any left-over
artifacts (publish does not work incrementally). -->
<ItemGroup>
<_FilesToDelete Remove="@(_FilesToDelete)" />
<_FilesToDelete Include="$(PublishDir)/*.dll" />
<_FilesToDelete Include="$(PublishDir)/*.json" />
</ItemGroup>
<Delete Files="@(_FilesToDelete)" />
<MSBuild Projects="@(ProjectsToPublish)" Targets="Publish" />
<ItemGroup>
<TfmSpecificPackageFile Include="$(PublishDir)/*.dll" PackagePath="tools\$(TargetFramework)" />
<TfmSpecificPackageFile Include="$(PublishDir)/*.json" PackagePath="tools\$(TargetFramework)" />
</ItemGroup>
</Target>
<ItemGroup>
<ProjectReference Include="../linker/Mono.Linker.csproj"
PrivateAssets="All"
Condition=" '$(TargetFramework)' == 'netcoreapp2.0' " />
<ProjectReference Include="../../external/cecil/Mono.Cecil.csproj"
PrivateAssets="All" />
<ProjectReference Include="../ILLink.CustomSteps/ILLink.CustomSteps.csproj"
PrivateAssets="All"
Condition=" '$(TargetFramework)' == 'netcoreapp2.0' " />
</ItemGroup>
<ItemGroup>
<!-- TODO: Once we can avoid using a custom .nuspec, we should be
able to set PrivateAssets="All" on these packages to prevent
them from becoming package dependencies, and use an msbuild
itemgroup to include their assets in the package instead of
passing the publish path to the .nuspec. -->
<!-- We use private assets for the Microsoft.Build packages to
prevent them from being published with the tasks dll, because
these are already a part of the SDK. -->
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.1012"
PrivateAssets="All" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.1.1012"
PrivateAssets="All" />
<PackageReference Include="System.Reflection.Metadata" Version="1.3.0"
PrivateAssets="All" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkVersion)"
PrivateAssets="All"
ExcludeAssets="Runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCoreVersion)"
PrivateAssets="All"
ExcludeAssets="Runtime" />
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)"
Condition=" '$(TargetFramework)' == 'net472' "
PrivateAssets="All"
Publish="True" />
</ItemGroup>
</Project>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>$id$</id>
<version>$version$</version>
<authors>$authors$</authors>
<description>$description$</description>
</metadata>
<files>
<file src="ILLink.Tasks.targets" target="build" />
<file src="netcoreapp2.0/**/*.dll" target="tools" />
<file src="netcoreapp2.0/**/*.json" target="tools" />
<file src="netcoreapp2.0/Sdk/Sdk.props" target="Sdk/Sdk.props" />
</files>
</package>

View File

@@ -12,12 +12,7 @@ namespace ILLink.Tasks
{
/// <summary>
/// Paths to the assembly files that should be considered as
/// input to the linker. Currently the linker will
/// additionally be able to resolve any assemblies in the
/// same directory as an assembly in AssemblyPaths, but this
/// behavior should not be relied upon. Instead, work under
/// the assumption that only the AssemblyPaths given will be
/// resolved.
/// input to the linker.
/// Each path can also have an "action" metadata,
/// which will set the illink action to take for
/// that assembly.
@@ -25,6 +20,12 @@ namespace ILLink.Tasks
[Required]
public ITaskItem [] AssemblyPaths { get; set; }
/// <summary>
/// Paths to assembly files that are reference assemblies,
/// representing the surface area for compilation.
/// </summary>
public ITaskItem [] ReferenceAssemblyPaths { get; set; }
/// <summary>
/// The names of the assemblies to root. This should contain
/// assembly names without an extension, not file names or
@@ -82,14 +83,12 @@ namespace ILLink.Tasks
get
{
if (!String.IsNullOrEmpty (_dotnetPath))
{
return _dotnetPath;
}
_dotnetPath = Environment.GetEnvironmentVariable (DotNetHostPathEnvironmentName);
if (String.IsNullOrEmpty (_dotnetPath))
{
throw new InvalidOperationException ($"{DotNetHostPathEnvironmentName} is not set");
}
return _dotnetPath;
}
}
@@ -106,71 +105,98 @@ namespace ILLink.Tasks
public string ILLinkPath {
get {
if (!String.IsNullOrEmpty (_illinkPath))
{
return _illinkPath;
}
var taskDirectory = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location);
_illinkPath = Path.Combine (taskDirectory, "illink.dll");
// The linker always runs on .NET Core, even when using desktop MSBuild to host ILLink.Tasks.
_illinkPath = Path.Combine (Path.GetDirectoryName (taskDirectory), "netcoreapp2.0", "illink.dll");
return _illinkPath;
}
set => _illinkPath = value;
}
private static string Quote (string path)
{
return $"\"{path.TrimEnd('\\')}\"";
}
protected override string GenerateCommandLineCommands ()
{
var args = new StringBuilder ();
args.Append (ILLinkPath);
if (RootDescriptorFiles != null) {
foreach (var rootFile in RootDescriptorFiles) {
args.Append (" -x ").Append (rootFile.ItemSpec);
}
}
foreach (var assemblyItem in RootAssemblyNames) {
args.Append (" -a ").Append (assemblyItem.ItemSpec);
}
HashSet<string> directories = new HashSet<string> ();
foreach (var assembly in AssemblyPaths) {
var assemblyPath = assembly.ItemSpec;
var dir = Path.GetDirectoryName (assemblyPath);
if (!directories.Contains (dir)) {
directories.Add (dir);
args.Append (" -d ").Append (dir);
}
string action = assembly.GetMetadata ("action");
if ((action != null) && (action.Length > 0)) {
args.Append (" -p ");
args.Append (action);
args.Append (" ").Append (Path.GetFileNameWithoutExtension (assemblyPath));
}
}
if (OutputDirectory != null) {
args.Append (" -out ").Append (OutputDirectory.ItemSpec);
}
if (ClearInitLocals) {
args.Append (" -s ");
// Version of ILLink.CustomSteps is passed as a workaround for msbuild issue #3016
args.Append ("LLink.CustomSteps.ClearInitLocalsStep,ILLink.CustomSteps,Version=0.0.0.0:OutputStep");
if ((ClearInitLocalsAssemblies != null) && (ClearInitLocalsAssemblies.Length > 0)) {
args.Append (" -m ClearInitLocalsAssemblies ");
args.Append (ClearInitLocalsAssemblies);
}
}
if (ExtraArgs != null) {
args.Append (" ").Append (ExtraArgs);
}
if (DumpDependencies)
args.Append (" --dump-dependencies");
args.Append (Quote (ILLinkPath));
return args.ToString ();
}
protected override string GenerateResponseFileCommands ()
{
var args = new StringBuilder ();
if (RootDescriptorFiles != null) {
foreach (var rootFile in RootDescriptorFiles)
args.Append ("-x ").AppendLine (Quote (rootFile.ItemSpec));
}
foreach (var assemblyItem in RootAssemblyNames)
args.Append ("-a ").AppendLine (Quote (assemblyItem.ItemSpec));
HashSet<string> assemblyNames = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
foreach (var assembly in AssemblyPaths) {
var assemblyPath = assembly.ItemSpec;
var assemblyName = Path.GetFileNameWithoutExtension (assemblyPath);
// If there are multiple paths with the same assembly name, only use the first one.
if (!assemblyNames.Add (assemblyName))
continue;
args.Append ("-reference ").AppendLine (Quote (assemblyPath));
string action = assembly.GetMetadata ("action");
if ((action != null) && (action.Length > 0)) {
args.Append ("-p ");
args.Append (action);
args.Append (" ").AppendLine (Quote (assemblyName));
}
}
if (ReferenceAssemblyPaths != null) {
foreach (var assembly in ReferenceAssemblyPaths) {
var assemblyPath = assembly.ItemSpec;
var assemblyName = Path.GetFileNameWithoutExtension (assemblyPath);
// Don't process references for which we already have
// implementation assemblies.
if (assemblyNames.Contains (assemblyName))
continue;
args.Append ("-reference ").AppendLine (Quote (assemblyPath));
// Treat reference assemblies as "skip". Ideally we
// would not even look at the IL, but only use them to
// resolve surface area.
args.Append ("-p skip ").AppendLine (Quote (assemblyName));
}
}
if (OutputDirectory != null)
args.Append ("-out ").AppendLine (Quote (OutputDirectory.ItemSpec));
if (ClearInitLocals) {
args.Append ("-s ");
// Version of ILLink.CustomSteps is passed as a workaround for msbuild issue #3016
args.AppendLine ("ILLink.CustomSteps.ClearInitLocalsStep,ILLink.CustomSteps,Version=0.0.0.0:OutputStep");
if ((ClearInitLocalsAssemblies != null) && (ClearInitLocalsAssemblies.Length > 0)) {
args.Append ("-m ClearInitLocalsAssemblies ");
args.AppendLine (ClearInitLocalsAssemblies);
}
}
if (ExtraArgs != null)
args.AppendLine (ExtraArgs);
if (DumpDependencies)
args.AppendLine ("--dump-dependencies");
return args.ToString ();
}
}
}

View File

@@ -1,10 +1,83 @@
# ILLink.Tasks
ILLink.Tasks is a package containing MSBuild tasks and targets that
will run the linker to run during publish of a .NET Core app.
ILLink.Tasks contains MSBuild tasks that run the linker for .NET Core. It runs illink.dll, built from the same [sources](../linker/) that are used to build monolinker.exe. ILLink.Tasks is shipped as part of the .NET Core 3.0 SDK.
ILLink.Tasks provides an MSBuild task called ILLink that makes it easy
to run the linker from an MSBuild project file:
Note: in previous versions of .NET Core, ILLink.Tasks was shipped as an external nuget package. This is no longer supported - please update to the latest 3.0 SDK and try the new experience!
## Usage
To use this tool, set `PublishTrimmed` to `true` in your project and publish a self-contained app:
```
dotnet publish -r <rid> -c Release
```
The publish output will include a subset of the framework libraries, depending on what the application code calls. For a "hello world" app, this reduces the size from ~68MB to ~28MB.
Applications or frameworks (including ASP<span />.NET Core and WPF) that use reflection or related dynamic features will often break when trimmed, because the linker does not know about this dynamic behavior, and can not determine in general which framework types will be required for reflection at runtime. To trim such apps, you will need to tell the linker about any types needed by reflection in your code, and in packages or frameworks that you depend on. Be sure to test your apps after trimming.
## How it works
The IL linker scans the IL of your application to detect which code is actually required, and trims unused framework libraries. This can significantly reduce the size of some apps. Typically small tool-like console apps benefit the most as they tend to use fairly small subsets of the framework, and are usually more amenable to trimming. Applications that use reflection may not work with this approach.
## Default behavior
By default, the linker will operate in a conservative mode that keeps
all managed assemblies that aren't part of the framework (they are
kept intact, and the linker simply copies them). This means that any reflection calls to non-framework code should continue to work. Reflection calls to code in the framework can potentially break if the target of the call is removed. Any framework assemblies that aren't predicted to be used at runtime will be removed from the publish output. Used framework assemblies will be kept entirely.
# Adding reflection roots
If your app or its dependencies use reflection, you may need to tell the linker to keep reflection targets explicitly. For example, dependency injection in ASP<span />.NET Core apps will activate
types depending on what is present at runtime, and therefore may fail
if the linker has removed assemblies that would otherwise be
present. Similarly, WPF apps may call into framework code depending on
the features used. If you know beforehand what your app will require
at runtime, you can tell the linker about this in a few ways.
For example, an app may reflect over `System.IO.File`:
```csharp
Type file = System.Type.GetType("System.IO.File,System.IO.FileSystem");
```
To ensure that this works with `PublishTrimmed=true`:
- You can include a direct reference to the required type in your code
somewhere, for example by using `typeof(System.IO.File)`.
- You can tell the linker to explicitly keep an assembly by adding it
to your csproj (use the assembly name *without* extension):
```xml
<ItemGroup>
<TrimmerRootAssembly Include="System.IO.FileSystem" />
</ItemGroup>
```
- You can give the linker a more specific list of types/methods,
etc. to include using an xml file, using the format described at
http://github.com/mono/linker
`.csproj`:
```xml
<ItemGroup>
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>
```
`TrimmerRoots.xml`:
```xml
<linker>
<assembly fullname="System.IO.FileSystem">
<type fullname="System.IO.File" />
</assembly>
</linker>
```
# MSBuild task
The linker can be invoked as an MSBuild task, `ILLink`. We recommend not using the task directly, because the SDK has built-in logic that handles computing the right set of reference assemblies as inputs, incremental linking, and similar logic. If you would like to use the [advanced options](../linker/README.md), you can invoke the msbuild task directly and pass any extra arguments like this:
```xml
<ILLink AssemblyPaths="@(AssemblyFilesToLink)"
@@ -14,83 +87,31 @@ to run the linker from an MSBuild project file:
ExtraArgs="-t -c link -l none" />
```
For a description of the options that this task supports, see the
For a full description of the inputs that this task supports, see the
comments in [LinkTask.cs](LinkTask.cs).
In addition, ILLink.Tasks contains MSBuild logic that makes the linker
run automatically during `dotnet publish` for .NET Core apps. This
will:
# Building
- Determine the assemblies and options to pass to illink.
- Remove unused native files from the publish output.
The full set of options is described below.
## Building
To build ILLink.Tasks:
```
linker> ./corebuild/dotnet.{sh/ps1} restore illink.sln
linker> ./corebuild/dotnet.{sh/ps1} pack src/ILLink.Tasks/ILLink.Tasks.csproj
linker> dotnet restore illink.sln
linker> dotnet pack illink.sln
```
The output package will be placed in
`corebuild/integration/bin/nupkgs`. If you are building on unix, you
will need to adjust
[ILLink.Tasks.nuspec](ILLink.Tasks.nuspec). Replace
the dll file includes with the following:
To produce a package:
```
linker> ./eng/dotnet.{sh/ps1} pack illink.sln
```
`<file src="netcoreapp2.0/**/*.dll" target="tools/netcoreapp2.0" />`
In .NET Core 3.0, this package is shipped with the SDK.
## Using ILLink.Tasks
Add a package reference to the linker. Ensure that either the
[dotnet-core](https://dotnet.myget.org/gallery/dotnet-core) myget feed
or the path to the locally-built linker package path exists in the
project's nuget.config. If using myget, you probably want to ensure
that you're using the latest version available at
https://dotnet.myget.org/feed/dotnet-core/package/nuget/ILLink.Tasks.
After adding the package, linking will be turned on during `dotnet
publish`. The publish output will contain the linked assemblies.
## Default behavior
By default, the linker will operate in a conservative mode that keeps
all managed assemblies that aren't part of the framework (they are
kept intact, and the linker simply copies them). It also analyzes all
non-framework assemblies to find and keep code used by them (they are
roots for the analysis). This means that unanalyzed reflection calls
within the app should continue to work after linking. Reflection calls
to code in the framework can potentially break when using the linker,
if the target of the call is removed.
For portable publish, framework assemblies usually do not get
published with the app. In this case they will not be analyzed or
linked.
For self-contained publish, framework assemblies are part of the
publish output, and are analyzed by the linker. Any framework
assemblies that aren't predicted to be used at runtime based on the
linker analysis will be removed from the publish output. Used
framework assemblies will be kept, and any used code within these
assemblies will be compiled to native code. Unused parts of used
framework assemblies are kept as IL, so that reflection calls will
continue to work, with runtime JIT compilation.
Native dependencies that aren't referenced by any of the kept managed
assemblies will be removed from the publish output as well.
## Caveats
You should make sure to test the publish output before deploying your
code, because the linker can potentially break apps that use
reflection.
# Caveats
The linker does not analyze reflection calls, so any reflection
targets outside of the kept assemblies will need to be rooted
explicitly using either `LinkerRootAssemblies` or
`LinkerRootDescriptors` (see below).
explicitly (see above).
Sometimes an application may include multiple versions of the same
assembly. This may happen when portable apps include platform-specific
@@ -99,76 +120,3 @@ publish output. In such cases, the linker will pick one of the
duplicate assemblies to analyze. This means that dependencies of the
un-analyzed duplicates may not be included in the application, so you
may need to root such dependencies manually.
## Options
The following MSBuild properties can be used to control the behavior
of the linker, from the command-line (via `dotnet publish
/p:PropertyName=PropertyValue`), or from the .csproj file (via
`<PropertyName>PropertyValue</PropertyName>`). They are defined and
used in
[ILLink.Tasks.targets](ILLink.Tasks.targets).
- `LinkDuringPublish` (default `true`) - Set to `false` to disable
linking.
- `ShowLinkerSizeComparison` (default `false`) - Set to `true` to
print out a table showing the size impact of the linker.
- `RootAllApplicationAssemblies` (default `true`) - If `true`, all
application assemblies are rooted by the linker. This means they are
kept in their entirety, and analyzed for dependencies. If `false`,
only the app dll's entry point is rooted.
- `LinkerRootAssemblies` - The set of assemblies to root. The default
depends on the value of `RootAllApplicationAssemblies`. Additional
assemblies can be rooted by adding them to this ItemGroup.
- `LinkerRootDescriptors` - The set of [xml descriptors](../linker#syntax-of-xml-descriptor)
specifying additional roots within assemblies. The default is to
include a generated descriptor that roots everything in the
application assembly if `RootAllApplicationAssemblies` is
`true`. Additional roots from descriptors can be included by adding
the descriptor files to this ItemGroup.
- `ExtraLinkerArgs` - Extra arguments to pass to the linker. The
default sets some flags that output symbols, tolerate resolution
errors, log warnings, skip mono-specific localization assemblies,
and keep type-forwarder assemblies. See
[ILLink.Tasks.targets](ILLink.Tasks.targets).
Setting this will override the defaults.
- Assembly actions: illink has the ability to specify an [action](../linker#actions-on-the-assemblies) to
take per-assembly. ILLink.Tasks provides high-level switches that
control the action to take for a set of assemblies. The set of
managed files that make up the application are split into
"application" and "platform" assemblies. The "platform" represents
the .NET framework, while the "application" represents the rest of
the application and its other dependencies. The assembly action can
be set for each of these groups independently, for assemblies that
are analyzed as used and as unused, with the following switches:
- `UsedApplicationAssemblyAction` - The default is to `Copy` any used
application assemblies to the output, leaving them as-is.
- `UnusedApplicationAssemblyAction` - The default is to `Delete` (not
publish) unused application assemblies.
- `UsedPlatformAssemblyAction` - For self-contained publish, the
default is `AddBypassNGen`, which will add the BypassNGenAttribute
to unused code in used platform assemblies. This causes the native
compilation step to compile only parts of these assemblies that
are used. For portable publish, the default is to `Skip` these,
because the platform assemblies are generally not published with
the app.
- `UnusedPlatformAssemblyAction` - For self-contained publish, the
default is to `Delete` (not publish) unused platform
assemblies. For portable publish, the default is to `Skip`.
The full list of assembly actions is described in
[AssemblyAction.cs](../linker/Linker/AssemblyAction.cs) Some
combinations of actions may be disallowed if they do not make
sense. For more details, see
[SetAssemblyActions.cs](SetAssemblyActions.cs).
- `LinkerTrimNativeDeps` (default `true`) - If `true`, enable
detection and removal of unused native dependencies. If `false`, all
native dependencies are kept.

View File

@@ -11,6 +11,15 @@ Copyright (c) .NET Foundation. All rights reserved.
-->
<Project ToolsVersion="14.0">
<UsingTask TaskName="ILLink.Tasks.ILLink" AssemblyFile="$(MSBuildThisFileDirectory)..\tools\netcoreapp2.0\ILLink.Tasks.dll" />
<PropertyGroup>
<_ILLinkTasksDirectoryRoot Condition=" '$(_ILLinkTasksDirectoryRoot)' == '' ">$(MSBuildThisFileDirectory)../tools/</_ILLinkTasksDirectoryRoot>
<_ILLinkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core' ">netcoreapp2.0</_ILLinkTasksTFM>
<_ILLinkTasksTFM Condition=" '$(_ILLinkTasksTFM)' == '' ">net472</_ILLinkTasksTFM>
<_ILLinkTasksDirectory>$(_ILLinkTasksDirectoryRoot)$(_ILLinkTasksTFM)/</_ILLinkTasksDirectory>
<ILLinkTasksAssembly Condition=" '$(ILLinkTasksAssembly)' == '' ">$(_ILLinkTasksDirectory)ILLink.Tasks.dll</ILLinkTasksAssembly>
</PropertyGroup>
<UsingTask TaskName="ILLink.Tasks.ILLink" AssemblyFile="$(ILLinkTasksAssembly)" />
<UsingTask TaskName="ILLink.Tasks.ComputeManagedAssemblies" AssemblyFile="$(ILLinkTasksAssembly)" />
</Project>

View File

@@ -1,6 +1,5 @@
using System;
using Mono.Cecil;
using Mono.Linker;
public static class Utils
{
@@ -13,14 +12,4 @@ public static class Utils
return false;
}
}
public static bool IsCrossgenedAssembly (string fileName)
{
try {
ModuleDefinition module = ModuleDefinition.ReadModule (fileName);
return module.IsCrossgened ();
} catch (BadImageFormatException) {
return false;
}
}
}

View File

@@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- net46 build is disabled until cecil uses SDK-style projects. -->
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<DefineConstants>$(DefineConstants);FEATURE_ILLINK</DefineConstants>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AssemblyName>illink</AssemblyName>
<!-- OutputType is Exe in Mono.Linker.csproj, but it needs to be set before importing SDK targets. -->
<OutputType>Exe</OutputType>
</PropertyGroup>
<!-- When publishing illink as a dependency of ILLink.Tasks, we want
to include files needed to run it as an application in the
publish output. See https://github.com/dotnet/sdk/issues/1675. -->
<Target Name="AddRuntimeDependenciesToContent"
BeforeTargets="GetCopyToOutputDirectoryItems"
DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles"
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" />
</ItemGroup>
</Target>
</Project>

View File

@@ -45,33 +45,34 @@ namespace Mono.Linker.Steps {
}
}
void RewriteBodyToLinkedAway (MethodDefinition method)
protected virtual void RewriteBodyToLinkedAway (MethodDefinition method)
{
method.ImplAttributes &= ~(MethodImplAttributes.AggressiveInlining | MethodImplAttributes.Synchronized);
method.ImplAttributes |= MethodImplAttributes.NoInlining;
method.Body = CreateThrowLinkedAwayBody (method);
ClearDebugInformation (method);
method.ClearDebugInformation();
}
void RewriteBodyToStub (MethodDefinition method)
protected virtual void RewriteBodyToStub (MethodDefinition method)
{
if (!method.IsIL)
throw new NotImplementedException ();
method.Body = CreateStubBody (method);
ClearDebugInformation (method);
method.ClearDebugInformation();
}
void RewriteBodyToFalse (MethodDefinition method)
protected virtual void RewriteBodyToFalse (MethodDefinition method)
{
if (!method.IsIL)
throw new NotImplementedException ();
method.Body = CreateReturnFalseBody (method);
ClearDebugInformation (method);
method.ClearDebugInformation();
}
MethodBody CreateThrowLinkedAwayBody (MethodDefinition method)
@@ -80,7 +81,7 @@ namespace Mono.Linker.Steps {
var il = body.GetILProcessor ();
// import the method into the current assembly
var ctor = Context.MarkedKnownMembers.NotSupportedExceptionCtorString;
MethodReference ctor = Context.MarkedKnownMembers.NotSupportedExceptionCtorString;
ctor = assembly.MainModule.ImportReference (ctor);
il.Emit (OpCodes.Ldstr, "Linked away");
@@ -98,7 +99,7 @@ namespace Mono.Linker.Steps {
var il = body.GetILProcessor ();
if (method.IsInstanceConstructor ()) {
var base_ctor = GetDefaultInstanceConstructor (method.DeclaringType.BaseType);
var base_ctor = method.DeclaringType.BaseType.GetDefaultInstanceConstructor();
base_ctor = assembly.MainModule.ImportReference (base_ctor);
il.Emit (OpCodes.Ldarg_0);
@@ -130,33 +131,5 @@ namespace Mono.Linker.Steps {
il.Emit (OpCodes.Ret);
return body;
}
static MethodReference GetDefaultInstanceConstructor (TypeReference type)
{
foreach (var m in type.GetMethods ()) {
if (m.HasParameters)
continue;
var definition = m.Resolve ();
if (!definition.IsDefaultConstructor ())
continue;
return m;
}
throw new NotImplementedException ();
}
static void ClearDebugInformation (MethodDefinition method)
{
// TODO: This always allocates, update when Cecil catches up
var di = method.DebugInformation;
di.SequencePoints.Clear ();
if (di.Scope != null) {
di.Scope.Variables.Clear ();
di.Scope.Constants.Clear ();
di.Scope = null;
}
}
}
}

View File

@@ -46,6 +46,7 @@ namespace Mono.Linker.Steps {
protected Queue<AttributeProviderPair> _assemblyLevelAttributes;
protected Queue<AttributeProviderPair> _lateMarkedAttributes;
protected List<TypeDefinition> _typesWithInterfaces;
protected List<MethodBody> _unreachableBodies;
public AnnotationStore Annotations {
get { return _context.Annotations; }
@@ -64,6 +65,7 @@ namespace Mono.Linker.Steps {
_assemblyLevelAttributes = new Queue<AttributeProviderPair> ();
_lateMarkedAttributes = new Queue<AttributeProviderPair> ();
_typesWithInterfaces = new List<TypeDefinition> ();
_unreachableBodies = new List<MethodBody> ();
}
public virtual void Process (LinkContext context)
@@ -72,6 +74,7 @@ namespace Mono.Linker.Steps {
Initialize ();
Process ();
Complete ();
}
void Initialize ()
@@ -93,6 +96,13 @@ namespace Mono.Linker.Steps {
}
}
void Complete ()
{
foreach (var body in _unreachableBodies) {
Annotations.SetAction (body.Method, MethodAction.ConvertToThrow);
}
}
void InitializeType (TypeDefinition type)
{
if (type.HasNestedTypes) {
@@ -188,6 +198,7 @@ namespace Mono.Linker.Steps {
ProcessQueue ();
ProcessVirtualMethods ();
ProcessMarkedTypesWithInterfaces ();
ProcessPendingBodies ();
DoAdditionalProcessing ();
}
@@ -242,6 +253,17 @@ namespace Mono.Linker.Steps {
}
}
void ProcessPendingBodies ()
{
for (int i = 0; i < _unreachableBodies.Count; i++) {
var body = _unreachableBodies [i];
if (Annotations.IsInstantiated (body.Method.DeclaringType)) {
MarkMethodBody (body);
_unreachableBodies.RemoveAt (i--);
}
}
}
void ProcessVirtualMethod (MethodDefinition method)
{
var overrides = Annotations.GetOverrides (method);
@@ -449,7 +471,7 @@ namespace Mono.Linker.Steps {
continue;
if (signature == null) {
MarkMethod (m);
MarkIndirectlyCalledMethod (m);
marked = true;
continue;
}
@@ -469,7 +491,7 @@ namespace Mono.Linker.Steps {
if (i < 0)
continue;
MarkMethod (m);
MarkIndirectlyCalledMethod (m);
marked = true;
}
@@ -813,6 +835,9 @@ namespace Mono.Linker.Steps {
bool ProcessLazyAttributes ()
{
if (Annotations.HasMarkedAnyIndirectlyCalledMethods () && MarkDisablePrivateReflectionAttribute ())
return true;
var startingQueueCount = _assemblyLevelAttributes.Count;
if (startingQueueCount == 0)
return false;
@@ -1035,6 +1060,7 @@ namespace Mono.Linker.Steps {
_typesWithInterfaces.Add (type);
if (type.HasMethods) {
MarkMethodsIf (type.Methods, IsVirtualNeededByTypeDueToPreservedScope);
if (ShouldMarkTypeStaticConstructor (type))
MarkStaticConstructor (type);
@@ -1346,7 +1372,38 @@ namespace Mono.Linker.Steps {
MarkType (constraint);
}
bool IsVirtualAndHasPreservedParent (MethodDefinition method)
bool IsVirtualNeededByTypeDueToPreservedScope (MethodDefinition method)
{
if (!method.IsVirtual)
return false;
var base_list = Annotations.GetBaseMethods (method);
if (base_list == null)
return false;
foreach (MethodDefinition @base in base_list) {
// Just because the type is marked does not mean we need interface methods.
// if the type is never instantiated, interfaces will be removed
if (@base.DeclaringType.IsInterface)
continue;
// If the type is marked, we need to keep overrides of abstract members defined in assemblies
// that are copied. However, if the base method is virtual, then we don't need to keep the override
// until the type could be instantiated
if (!@base.IsAbstract)
continue;
if (IgnoreScope (@base.DeclaringType.Scope))
return true;
if (IsVirtualNeededByTypeDueToPreservedScope (@base))
return true;
}
return false;
}
bool IsVirtualNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method)
{
if (!method.IsVirtual)
return false;
@@ -1359,7 +1416,7 @@ namespace Mono.Linker.Steps {
if (IgnoreScope (@base.DeclaringType.Scope))
return true;
if (IsVirtualAndHasPreservedParent (@base))
if (IsVirtualNeededByTypeDueToPreservedScope (@base))
return true;
}
@@ -1683,6 +1740,12 @@ namespace Mono.Linker.Steps {
MarkMethod (method);
}
protected void MarkIndirectlyCalledMethod (MethodDefinition method)
{
MarkMethod (method);
Annotations.MarkIndirectlyCalledMethod (method);
}
protected virtual MethodDefinition MarkMethod (MethodReference reference)
{
reference = GetOriginalMethod (reference);
@@ -1841,7 +1904,7 @@ namespace Mono.Linker.Steps {
foreach (var method in type.Methods) {
if (method.IsFinalizer ())
MarkMethod (method);
else if (IsVirtualAndHasPreservedParent (method))
else if (IsVirtualNeededByInstantiatedTypeDueToPreservedScope (method))
MarkMethod (method);
}
@@ -1887,23 +1950,47 @@ namespace Mono.Linker.Steps {
break;
case MethodAction.ConvertToThrow:
if (_context.MarkedKnownMembers.NotSupportedExceptionCtorString != null)
break;
var nse = BCL.FindPredefinedType ("System", "NotSupportedException", _context);
if (nse == null)
throw new NotSupportedException ("Missing predefined 'System.NotSupportedException' type");
MarkType (nse);
var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString);
if (nseCtor == null)
throw new MarkException ($"Could not find constructor on '{nse.FullName}'");
_context.MarkedKnownMembers.NotSupportedExceptionCtorString = nseCtor;
MarkAndCacheConvertToThrowExceptionCtor ();
break;
}
}
}
protected virtual void MarkAndCacheConvertToThrowExceptionCtor ()
{
if (_context.MarkedKnownMembers.NotSupportedExceptionCtorString != null)
return;
var nse = BCL.FindPredefinedType ("System", "NotSupportedException", _context);
if (nse == null)
throw new NotSupportedException ("Missing predefined 'System.NotSupportedException' type");
MarkType (nse);
var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString);
if (nseCtor == null)
throw new MarkException ($"Could not find constructor on '{nse.FullName}'");
_context.MarkedKnownMembers.NotSupportedExceptionCtorString = nseCtor;
}
bool MarkDisablePrivateReflectionAttribute ()
{
if (_context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor != null)
return false;
var nse = BCL.FindPredefinedType ("System.Runtime.CompilerServices", "DisablePrivateReflectionAttribute", _context);
if (nse == null)
throw new NotSupportedException ("Missing predefined 'System.Runtime.CompilerServices.DisablePrivateReflectionAttribute' type");
MarkType (nse);
var ctor = MarkMethodIf (nse.Methods, MethodDefinitionExtensions.IsDefaultConstructor);
if (ctor == null)
throw new MarkException ($"Could not find constructor on '{nse.FullName}'");
_context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor = ctor;
return true;
}
void MarkBaseMethods (MethodDefinition method)
{
@@ -2036,6 +2123,12 @@ namespace Mono.Linker.Steps {
protected virtual void MarkMethodBody (MethodBody body)
{
if (_context.IsOptimizationEnabled (CodeOptimizations.UnreachableBodies) && IsUnreachableBody (body)) {
MarkAndCacheConvertToThrowExceptionCtor ();
_unreachableBodies.Add (body);
return;
}
foreach (VariableDefinition var in body.Variables)
MarkType (var.VariableType);
@@ -2053,6 +2146,14 @@ namespace Mono.Linker.Steps {
PostMarkMethodBody (body);
}
bool IsUnreachableBody (MethodBody body)
{
return !body.Method.IsStatic
&& !Annotations.IsInstantiated (body.Method.DeclaringType)
&& MethodBodyScanner.IsWorthConvertingToThrow (body);
}
partial void PostMarkMethodBody (MethodBody body);
void MarkInterfacesNeededByBodyStack (MethodBody body)
@@ -2226,7 +2327,7 @@ namespace Mono.Linker.Steps {
if ((bindingFlags == BindingFlags.Default || bindingFlags.IsSet(BindingFlags.Public) == method.IsPublic) && method.Name == ".ctor") {
Tracer.Push ($"Reflection-{method}");
try {
MarkMethod (method);
MarkIndirectlyCalledMethod (method);
} finally {
Tracer.Pop ();
}
@@ -2244,7 +2345,7 @@ namespace Mono.Linker.Steps {
&& method.Name == name) {
Tracer.Push ($"Reflection-{method}");
try {
MarkMethod (method);
MarkIndirectlyCalledMethod (method);
} finally {
Tracer.Pop ();
}
@@ -2264,8 +2365,13 @@ namespace Mono.Linker.Steps {
// It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used.
// Be conservative and mark everything for the property.
MarkProperty (property);
MarkMethodIfNotNull (property.GetMethod);
MarkMethodIfNotNull (property.SetMethod);
if (property.GetMethod != null)
MarkIndirectlyCalledMethod (property.GetMethod);
if (property.SetMethod != null)
MarkIndirectlyCalledMethod (property.SetMethod);
} finally {
Tracer.Pop ();
}

View File

@@ -155,7 +155,10 @@ namespace Mono.Linker.Steps {
WriterParameters SaveSymbols (AssemblyDefinition assembly)
{
var parameters = new WriterParameters ();
var parameters = new WriterParameters {
DeterministicMvid = Context.DeterministicOutput
};
if (!Context.LinkSymbols)
return parameters;

View File

@@ -63,6 +63,7 @@ namespace Mono.Linker.Steps {
context.Annotations.AddPreservedMethod (calendar, ctor);
// we need to mark the type or the above won't be processed
context.Annotations.Mark (calendar);
context.Annotations.MarkIndirectlyCalledMethod (ctor);
return;
}
}

View File

@@ -0,0 +1,67 @@
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public class ReflectionBlockedStep : BaseStep
{
AssemblyDefinition assembly;
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
this.assembly = assembly;
foreach (var type in assembly.MainModule.Types)
ProcessType (type);
}
void ProcessType (TypeDefinition type)
{
if (!HasIndirectCallers (type)) {
AddCustomAttribute (type);
return;
}
//
// We mark everything, otherwise we would need to check full visibility
// hierarchy (e.g. public method inside private type)
//
foreach (var method in type.Methods) {
if (!Annotations.IsIndirectlyCalled (method)) {
AddCustomAttribute (method);
}
}
foreach (var nested in type.NestedTypes)
ProcessType (nested);
}
bool HasIndirectCallers (TypeDefinition type)
{
foreach (var method in type.Methods) {
if (Annotations.IsIndirectlyCalled (method))
return true;
}
if (type.HasNestedTypes) {
foreach (var nested in type.NestedTypes) {
if (HasIndirectCallers (nested))
return true;
}
}
return false;
}
void AddCustomAttribute (ICustomAttributeProvider caProvider)
{
// We are using DisableReflectionAttribute which is not exact match but it's quite
// close to what we need and it already exists in the BCL
MethodReference ctor = Context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor;
ctor = assembly.MainModule.ImportReference (ctor);
var ca = new CustomAttribute (ctor);
caProvider.CustomAttributes.Add (ca);
Annotations.Mark (ca);
}
}
}

View File

@@ -12,6 +12,7 @@ namespace Mono.Linker.Steps
public bool FeatureCOM { get; set; }
public bool FeatureETW { get; set; }
public bool FeatureSRE { get; set; }
//
// Manually overrides System.Globalization.Invariant mode
@@ -57,6 +58,20 @@ namespace Mono.Linker.Steps
ExcludeEventSourceImplementation (type);
}
if (FeatureSRE) {
if (type.Namespace == "System" && type.Name == "RuntimeType") {
foreach (var method in type.Methods) {
if (method.Name == "MakeTypeBuilderInstantiation") {
Annotations.SetAction (method, MethodAction.ConvertToThrow);
break;
}
}
}
}
if (FeatureGlobalization)
ExcludeGlobalization (type);
if (RemoveCustomAttributes (type)) {
if (FeatureCOM && type.IsImport) {
type.IsImport = false;
@@ -76,6 +91,66 @@ namespace Mono.Linker.Steps
ProcessType (nested);
}
void ExcludeGlobalization (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Globalization":
switch (type.Name) {
case "CalendarData":
foreach (var method in type.Methods) {
switch (method.Name) {
case "GetJapaneseEraNames":
case "GetJapaneseEnglishEraNames":
Annotations.SetAction (method, MethodAction.ConvertToThrow);
break;
}
}
break;
case "DateTimeFormatInfo":
foreach (var method in type.Methods) {
switch (method.Name) {
case "PopulateSpecialTokenHashTable":
case "GetJapaneseCalendarDTFI":
case "GetTaiwanCalendarDTFI":
case "IsJapaneseCalendar":
case "TryParseHebrewNumber":
Annotations.SetAction (method, MethodAction.ConvertToThrow);
break;
}
}
break;
}
break;
case "System":
switch (type.Name) {
case "DateTimeFormat":
foreach (var method in type.Methods) {
switch (method.Name) {
case "HebrewFormatDigits":
case "FormatHebrewMonthName":
Annotations.SetAction (method, MethodAction.ConvertToThrow);
break;
}
}
break;
case "DateTimeParse":
foreach (var method in type.Methods) {
switch (method.Name) {
case "GetJapaneseCalendarDefaultInstance":
case "GetTaiwanCalendarDefaultInstance":
case "ProcessHebrewTerminalState":
case "GetHebrewDayOfNM":
case "MatchHebrewDigits":
Annotations.SetAction (method, MethodAction.ConvertToThrow);
break;
}
}
break;
}
break;
}
}
void ExcludeEventSource (TypeDefinition type)
{
var annotations = Context.Annotations;
@@ -100,7 +175,8 @@ namespace Mono.Linker.Steps
continue;
}
annotations.SetAction (method, MethodAction.ConvertToThrow);
if (MethodBodyScanner.IsWorthConvertingToThrow (method.Body))
annotations.SetAction (method, MethodAction.ConvertToThrow);
}
}
@@ -149,7 +225,7 @@ namespace Mono.Linker.Steps
continue;
}
if (!skip)
if (!skip && MethodBodyScanner.IsWorthConvertingToThrow (method.Body))
annotations.SetAction (method, MethodAction.ConvertToThrow);
}
}
@@ -166,7 +242,8 @@ namespace Mono.Linker.Steps
{
foreach (var method in type.Methods)
{
annotations.SetAction(method, MethodAction.ConvertToThrow);
if (MethodBodyScanner.IsWorthConvertingToThrow (method.Body))
annotations.SetAction(method, MethodAction.ConvertToThrow);
}
}

View File

@@ -133,6 +133,7 @@ namespace Mono.Linker.Steps
void MarkMethod (MethodDefinition method)
{
InternalMark (method);
Annotations.MarkIndirectlyCalledMethod (method);
Annotations.SetAction (method, MethodAction.Parse);
}
}

View File

@@ -434,6 +434,7 @@ namespace Mono.Linker.Steps {
Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {method.FullName}");
Annotations.Mark (method);
Annotations.MarkIndirectlyCalledMethod (method);
Tracer.AddDirectDependency (this, method);
Annotations.SetAction (method, MethodAction.Parse);
}

View File

@@ -285,6 +285,9 @@ namespace Mono.Linker.Steps {
if (type.HasCustomAttributes)
SweepCustomAttributes (type);
if (type.HasGenericParameters)
SweepCustomAttributeCollection (type.GenericParameters);
if (type.HasProperties)
SweepCustomAttributeCollection (type.Properties);
@@ -312,8 +315,10 @@ namespace Mono.Linker.Steps {
{
for (int i = type.Interfaces.Count - 1; i >= 0; i--) {
var iface = type.Interfaces [i];
if (Annotations.IsMarked (iface))
if (Annotations.IsMarked (iface)) {
SweepCustomAttributes (iface);
continue;
}
InterfaceRemoved (type, iface);
type.Interfaces.RemoveAt (i);
}
@@ -398,13 +403,16 @@ namespace Mono.Linker.Steps {
SweepDebugInfo (methods);
foreach (var method in methods) {
if (method.HasGenericParameters)
SweepCustomAttributeCollection (method.GenericParameters);
SweepCustomAttributes (method.MethodReturnType);
if (!method.HasParameters)
continue;
foreach (var parameter in method.Parameters)
SweepCustomAttributes (parameter);
SweepCustomAttributes (method.MethodReturnType);
}
}

View File

@@ -55,6 +55,8 @@ namespace Mono.Linker {
protected readonly HashSet<CustomAttribute> marked_attributes = new HashSet<CustomAttribute> ();
readonly HashSet<TypeDefinition> marked_types_with_cctor = new HashSet<TypeDefinition> ();
protected readonly HashSet<TypeDefinition> marked_instantiated = new HashSet<TypeDefinition> ();
protected readonly HashSet<MethodDefinition> indirectly_called = new HashSet<MethodDefinition>();
public AnnotationStore (LinkContext context) => this.context = context;
@@ -142,6 +144,24 @@ namespace Mono.Linker {
return marked_attributes.Contains (attribute);
}
public void MarkIndirectlyCalledMethod (MethodDefinition method)
{
if (!context.AddReflectionAnnotations)
return;
indirectly_called.Add (method);
}
public bool HasMarkedAnyIndirectlyCalledMethods ()
{
return indirectly_called.Count != 0;
}
public bool IsIndirectlyCalled (MethodDefinition method)
{
return indirectly_called.Contains (method);
}
public void MarkInstantiated (TypeDefinition type)
{
marked_instantiated.Add (type);

View File

@@ -30,14 +30,7 @@ using System;
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle ("Mono.Linker")]
[assembly: AssemblyDescription ("Mono CIL Linker")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("(C) 2006, Jb Evain")]
[assembly: AssemblyCulture ("")]
[assembly: CLSCompliant (false)]
[assembly: ComVisible (false)]
[assembly: AssemblyVersion ("0.2.0.0")]

Some files were not shown because too many files have changed in this diff Show More