Imported Upstream version 6.0.0.172

Former-commit-id: f3cc9b82f3e5bd8f0fd3ebc098f789556b44e9cd
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-04-12 14:10:50 +00:00
parent 8016999e4d
commit 64ac736ec5
32155 changed files with 3981439 additions and 75368 deletions

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using Mono.Linker;
using Mono.Linker.Steps;
using Mono.Cecil;
namespace ILLink.CustomSteps
{
public class ClearInitLocalsStep : BaseStep
{
HashSet<string> _assemblies;
protected override void Process ()
{
string parameterName = "ClearInitLocalsAssemblies";
if (Context.HasParameter (parameterName)) {
string parameter = Context.GetParameter (parameterName);
_assemblies = new HashSet<string> (parameter.Split(','), StringComparer.OrdinalIgnoreCase);
}
}
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
if ((_assemblies != null) && (!_assemblies.Contains (assembly.Name.Name))) {
return;
}
bool changed = false;
foreach (ModuleDefinition module in assembly.Modules) {
foreach (TypeDefinition type in module.Types) {
foreach (MethodDefinition method in type.Methods) {
if (method.Body != null) {
if (method.Body.InitLocals) {
method.Body.InitLocals = false;
changed = true;
}
}
}
}
}
if (changed && (Annotations.GetAction (assembly) == AssemblyAction.Copy))
Annotations.SetAction (assembly, AssemblyAction.Save);
}
}
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<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>
</PropertyGroup>
<ItemGroup>
<Compile Include="ClearInitLocals.cs" />
</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>
</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

@ -0,0 +1,37 @@
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

@ -0,0 +1,37 @@
using Microsoft.Build.Utilities; // Task
using Microsoft.Build.Framework; // ITaskItem
using Mono.Cecil;
namespace ILLink.Tasks
{
public class CheckEmbeddedRootDescriptor : Task
{
/// <summary>
/// Path to the assembly.
/// </summary>
[Required]
public ITaskItem AssemblyPath { get; set; }
/// <summary>
/// This will be set to true if the assembly has an embedded root descriptor.
/// </summary>
[Output]
public bool HasEmbeddedRootDescriptor { get; set; }
public override bool Execute()
{
ModuleDefinition module = ModuleDefinition.ReadModule (AssemblyPath.ItemSpec);
string assemblyName = module.Assembly.Name.Name;
string expectedResourceName = assemblyName + ".xml";
HasEmbeddedRootDescriptor = false;
foreach (var resource in module.Resources) {
if (resource.Name == expectedResourceName) {
HasEmbeddedRootDescriptor = true;
break;
}
}
return true;
}
}
}

View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
struct AssemblySizes
{
public long unlinkedSize;
public long linkedSize;
}
public class CompareAssemblySizes : Task
{
/// <summary>
/// Paths to managed assemblies before linking.
/// </summary>
[Required]
public ITaskItem[] UnlinkedAssemblies { get; set; }
/// <summary>
/// Paths to managed assemblies after linking. These
/// assembly names should be a subset of the
/// assembly names in UnlinkedAssemblies.
/// </summary>
[Required]
public ITaskItem[] LinkedAssemblies { get; set; }
public override bool Execute()
{
string[] unlinkedFiles = UnlinkedAssemblies.Select (i => i.ItemSpec).ToArray();
string[] linkedFiles = LinkedAssemblies.Select (i => i.ItemSpec).ToArray();
Dictionary<string, AssemblySizes> sizes = new Dictionary<string, AssemblySizes> ();
long totalUnlinked = 0;
foreach (string unlinkedFile in unlinkedFiles) {
if (!Utils.IsManagedAssembly (unlinkedFile)) {
continue;
}
string fileName = Path.GetFileName (unlinkedFile);
AssemblySizes assemblySizes = new AssemblySizes ();
assemblySizes.unlinkedSize = new System.IO.FileInfo (unlinkedFile).Length;
totalUnlinked += assemblySizes.unlinkedSize;
sizes[fileName] = assemblySizes;
}
long totalLinked = 0;
foreach (string linkedFile in linkedFiles) {
if (!Utils.IsManagedAssembly (linkedFile)) {
continue;
}
string fileName = Path.GetFileName (linkedFile);
if (!sizes.ContainsKey(fileName)) {
Console.WriteLine ($"{linkedFile} was specified as an assembly kept by the linker, but {fileName} was not specified as a managed publish assembly.");
continue;
}
AssemblySizes assemblySizes = sizes[fileName];
assemblySizes.linkedSize = new System.IO.FileInfo (linkedFile).Length;
totalLinked += assemblySizes.linkedSize;
sizes[fileName] = assemblySizes;
}
Console.WriteLine ("{0, -60} {1,-20:N0} {2, -20:N0} {3, -10:P}",
"",
"Before linking (B)",
"After linking (B)",
"Size decrease");
Console.WriteLine ("{0, -60} {1,-20:N0} {2, -20:N0} {3, -10:P}",
"-----------",
"-----------",
"-----------",
"-----------"
);
Console.WriteLine ("{0, -60} {1,-20:N0} {2, -20:N0} {3, -10:P}",
"Total size of assemblies",
totalUnlinked,
totalLinked,
((double)totalUnlinked - (double)totalLinked) / (double)totalUnlinked);
Console.WriteLine ("{0, -60} {1,-20:N0} {2, -20:N0} {3, -10:P}",
"-----------",
"-----------",
"-----------",
"-----------"
);
foreach (string assembly in sizes.Keys) {
Console.WriteLine ("{0, -60} {1,-20:N0} {2, -20:N0} {3, -10:P}",
assembly,
sizes[assembly].unlinkedSize,
sizes[assembly].linkedSize,
(double)(sizes[assembly].unlinkedSize - sizes[assembly].linkedSize)/(double)sizes[assembly].unlinkedSize);
}
return true;
}
public static long DirSize(DirectoryInfo d)
{
long size = 0;
// Add file sizes.
FileInfo[] fis = d.GetFiles ();
foreach (FileInfo fi in fis) {
size += fi.Length;
}
// Add subdirectory sizes.
DirectoryInfo[] dis = d.GetDirectories ();
foreach (DirectoryInfo di in dis) {
size += DirSize (di);
}
return size;
}
}
}

View File

@ -0,0 +1,31 @@
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class ComputeManagedAssemblies : Task
{
/// <summary>
/// Paths to assemblies.
/// </summary>
[Required]
public ITaskItem[] Assemblies { get; set; }
/// <summary>
/// This will contain the output list of managed
/// assemblies. Metadata from the input parameter
/// Assemblies is preserved.
/// </summary>
[Output]
public ITaskItem[] ManagedAssemblies { get; set; }
public override bool Execute()
{
ManagedAssemblies = Assemblies
.Where(f => Utils.IsManagedAssembly(f.ItemSpec))
.ToArray();
return true;
}
}
}

View File

@ -0,0 +1,44 @@
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class ComputeRemovedAssemblies : Task
{
/// <summary>
/// The paths to the inputs to the linker.
/// </summary>
[Required]
public ITaskItem[] InputAssemblies { get; set; }
/// <summary>
/// The paths to the linked assemblies.
/// </summary>
[Required]
public ITaskItem[] KeptAssemblies { get; set; }
/// <summary>
/// The set of assemblies in the inputs that weren't kept by
/// the linker. These items include the full metadata from
/// the input assemblies, and only the filenames of the
/// inputs are used to determine which assemblies were
/// removed.
/// </summary>
[Output]
public ITaskItem[] RemovedAssemblies { get; set; }
public override bool Execute()
{
var keptAssemblyNames = new HashSet<string> (
KeptAssemblies.Select(i => Path.GetFileName(i.ItemSpec))
);
RemovedAssemblies = InputAssemblies.Where(i =>
!keptAssemblyNames.Contains(Path.GetFileName(i.ItemSpec))
).ToArray();
return true;
}
}
}

View File

@ -0,0 +1,48 @@
using System.Xml;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class CreateRootDescriptorFile : Task
{
/// <summary>
/// Assembly names (without path or extension) to
/// include in the generated root file.
/// </summary>
[Required]
public ITaskItem[] AssemblyNames { get; set; }
/// <summary>
/// The path to the file to generate.
/// </summary>
[Required]
public ITaskItem RootDescriptorFilePath { get; set; }
public override bool Execute()
{
var roots = new XElement("linker");
foreach (var assemblyItem in AssemblyNames) {
var assemblyName = assemblyItem.ItemSpec;
roots.Add(new XElement("assembly",
new XAttribute("fullname", assemblyName),
new XElement("type",
new XAttribute("fullname", "*"),
new XAttribute("required", "true"))));
}
var xdoc = new XDocument(roots);
XmlWriterSettings xws = new XmlWriterSettings();
xws.Indent = true;
xws.OmitXmlDeclaration = true;
using (XmlWriter xw = XmlWriter.Create(RootDescriptorFilePath.ItemSpec, xws)) {
xdoc.Save(xw);
}
return true;
}
}
}

View File

@ -0,0 +1,349 @@
using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using Microsoft.Build.Utilities; // Task
using Microsoft.Build.Framework; // ITaskItem
namespace ILLink.Tasks
{
public class CreateRuntimeRootILLinkDescriptorFile : Task
{
/// <summary>
/// The path to namespace.h.
/// </summary>
[Required]
public ITaskItem NamespaceFilePath { get; set; }
/// <summary>
/// The path to mscorlib.h.
/// </summary>
[Required]
public ITaskItem MscorlibFilePath { get; set; }
/// <summary>
/// The path to cortypeinfo.h.
/// </summary>
[Required]
public ITaskItem CortypeFilePath { get; set; }
/// <summary>
/// The path to rexcep.h.
/// </summary>
[Required]
public ITaskItem RexcepFilePath { get; set; }
/// <summary>
/// The path to ILLinkTrim.xml.
/// </summary>
[Required]
public ITaskItem ILLinkTrimXmlFilePath { get; set; }
/// <summary>
/// The path to the file to generate.
/// </summary>
[Required]
public ITaskItem RuntimeRootDescriptorFilePath { get; set; }
class ClassMembers
{
public bool keepAllFields;
public HashSet<string> methods;
public HashSet<string> fields;
}
Dictionary<string, string> namespaceDictionary = new Dictionary<string, string> ();
Dictionary<string, string> classIdsToClassNames = new Dictionary<string, string> ();
Dictionary<string, ClassMembers> classNamesToClassMembers = new Dictionary<string, ClassMembers> ();
public override bool Execute ()
{
var namespaceFilePath = NamespaceFilePath.ItemSpec;
if (!File.Exists (namespaceFilePath)) {
Log.LogError ("File " + namespaceFilePath + " doesn't exist.");
return false;
}
var mscorlibFilePath = MscorlibFilePath.ItemSpec;
if (!File.Exists (mscorlibFilePath)) {
Log.LogError ("File " + mscorlibFilePath + " doesn't exist.");
return false;
}
var cortypeFilePath = CortypeFilePath.ItemSpec;
if (!File.Exists (cortypeFilePath)) {
Log.LogError ("File " + cortypeFilePath + " doesn't exist.");
return false;
}
var rexcepFilePath = RexcepFilePath.ItemSpec;
if (!File.Exists (rexcepFilePath)) {
Log.LogError ("File " + rexcepFilePath + " doesn't exist.");
return false;
}
var iLLinkTrimXmlFilePath = ILLinkTrimXmlFilePath.ItemSpec;
if (!File.Exists (iLLinkTrimXmlFilePath)) {
Log.LogError ("File " + iLLinkTrimXmlFilePath + " doesn't exist.");
return false;
}
ProcessNamespaces (namespaceFilePath);
ProcessMscorlib (mscorlibFilePath);
ProcessCoreTypes (cortypeFilePath);
ProcessExceptionTypes (rexcepFilePath);
OutputXml (iLLinkTrimXmlFilePath, RuntimeRootDescriptorFilePath.ItemSpec);
return true;
}
void ProcessNamespaces (string namespaceFile)
{
string [] namespaces = File.ReadAllLines (namespaceFile);
// Process definitions of the form
// #define g_SystemNS "System"
// from namespace.h
foreach (string namespaceDef in namespaces) {
if (namespaceDef.StartsWith ("#define")) {
char [] separators = { '"', ' ', '\t' };
string [] namespaceDefElements = namespaceDef.Split (separators, StringSplitOptions.RemoveEmptyEntries);
int startIndex = "g_".Length;
// E.g., if namespaceDefElements [1] is "g_RuntimeNS", lhs is "Runtime".
string lhs = namespaceDefElements [1].Substring (startIndex, namespaceDefElements [1].LastIndexOf ('N') - startIndex);
if (namespaceDefElements.Length == 3) {
// E.G., #define g_SystemNS "System"
// "System" --> "System"
namespaceDictionary [lhs] = namespaceDefElements [2];
}
else {
// E.g., #define g_RuntimeNS g_SystemNS ".Runtime"
// "Runtime" --> "System.Runtime"
string prefix = namespaceDefElements [2].Substring (startIndex, namespaceDefElements [2].LastIndexOf ('N') - startIndex);
namespaceDictionary [lhs] = namespaceDictionary [prefix] + namespaceDefElements [3];
}
}
}
}
void ProcessMscorlib (string typeFile)
{
string [] types = File.ReadAllLines (typeFile);
string classId = null;
foreach (string def in types) {
string [] defElements = null;
if (def.StartsWith ("DEFINE_") || def.StartsWith ("// DEFINE_")) {
char [] separators = { ',', '(', ')', ' ', '\t', '/' };
defElements = def.Split (separators, StringSplitOptions.RemoveEmptyEntries);
}
if (def.StartsWith ("DEFINE_CLASS(") || def.StartsWith ("// DEFINE_CLASS(")) {
// E.g., DEFINE_CLASS(APP_DOMAIN, System, AppDomain)
classId = defElements [1]; // APP_DOMAIN
string classNamespace = defElements [2]; // System
string className = defElements [3]; // AppDomain
AddClass (classNamespace, className, classId);
}
else if (def.StartsWith ("DEFINE_CLASS_U(")) {
// E.g., DEFINE_CLASS_U(System, AppDomain, AppDomainBaseObject)
string classNamespace = defElements [1]; // System
string className = defElements [2]; // AppDomain
classId = defElements [3]; // AppDomainBaseObject
// For these classes the sizes of managed and unmanaged classes and field offsets
// are compared so we need to preserve all fields.
const bool keepAllFields = true;
AddClass (classNamespace, className, classId, keepAllFields);
}
else if (def.StartsWith ("DEFINE_FIELD(")) {
// E.g., DEFINE_FIELD(ACCESS_VIOLATION_EXCEPTION, IP, _ip)
classId = defElements [1]; // ACCESS_VIOLATION_EXCEPTION
string fieldName = defElements [3]; // _ip
AddField (fieldName, classId);
}
else if (def.StartsWith ("DEFINE_METHOD(")) {
// E.g., DEFINE_METHOD(APP_DOMAIN, ON_ASSEMBLY_LOAD, OnAssemblyLoadEvent, IM_Assembly_RetVoid)
string methodName = defElements [3]; // OnAssemblyLoadEvent
classId = defElements [1]; // APP_DOMAIN
AddMethod (methodName, classId);
}
else if (def.StartsWith ("DEFINE_PROPERTY(") || def.StartsWith ("DEFINE_STATIC_PROPERTY(")) {
// E.g., DEFINE_PROPERTY(ARRAY, LENGTH, Length, Int)
// or DEFINE_STATIC_PROPERTY(THREAD, CURRENT_THREAD, CurrentThread, Thread)
string propertyName = defElements [3]; // Length or CurrentThread
classId = defElements [1]; // ARRAY or THREAD
AddMethod ("get_" + propertyName, classId);
}
else if (def.StartsWith ("DEFINE_SET_PROPERTY(")) {
// E.g., DEFINE_SET_PROPERTY(THREAD, UI_CULTURE, CurrentUICulture, CultureInfo)
string propertyName = defElements [3]; // CurrentUICulture
classId = defElements [1]; // THREAD
AddMethod ("get_" + propertyName, classId);
AddMethod ("set_" + propertyName, classId);
}
}
}
public void ProcessCoreTypes (string corTypeFile)
{
string [] corTypes = File.ReadAllLines (corTypeFile);
foreach (string def in corTypes) {
// E.g., TYPEINFO(ELEMENT_TYPE_VOID, "System", "Void", 0, TYPE_GC_NONE, false, true, false, false, false) // 0x01
if (def.StartsWith ("TYPEINFO(")) {
char [] separators = { ',', '(', ')', '"', ' ', '\t' };
string [] defElements = def.Split (separators, StringSplitOptions.RemoveEmptyEntries);
string classId = null;
string classNamespace = defElements [2]; // System
string className = defElements [3]; // Void
AddClass (classNamespace, className, classId);
}
}
}
public void ProcessExceptionTypes (string excTypeFile)
{
string [] excTypes = File.ReadAllLines (excTypeFile);
foreach (string def in excTypes) {
// E.g., DEFINE_EXCEPTION(g_InteropNS, MarshalDirectiveException, false, COR_E_MARSHALDIRECTIVE)
if (def.StartsWith ("DEFINE_EXCEPTION(")) {
char [] separators = { ',', '(', ')', ' ', '\t' };
string [] defElements = def.Split (separators, StringSplitOptions.RemoveEmptyEntries);
string classId = null;
string classNamespace = defElements [1]; // g_InteropNS
string className = defElements [2]; // MarshalDirectiveException
AddClass (classNamespace, className, classId);
AddMethod (".ctor", classId, classNamespace, className);
}
}
}
void OutputXml (string iLLinkTrimXmlFilePath, string outputFileName)
{
XmlDocument doc = new XmlDocument ();
doc.Load (iLLinkTrimXmlFilePath);
XmlNode linkerNode = doc ["linker"];
XmlNode assemblyNode = linkerNode ["assembly"];
foreach (string typeName in classNamesToClassMembers.Keys) {
XmlNode typeNode = doc.CreateElement ("type");
XmlAttribute typeFullName = doc.CreateAttribute ("fullname");
typeFullName.Value = typeName;
typeNode.Attributes.Append (typeFullName);
ClassMembers members = classNamesToClassMembers [typeName];
// We need to keep everyting in System.Runtime.InteropServices.WindowsRuntime and
// System.Threading.Volatile.
if (!typeName.StartsWith ("System.Runtime.InteropServices.WindowsRuntime") &&
!typeName.StartsWith ("System.Threading.Volatile")) {
if (members.keepAllFields) {
XmlAttribute preserve = doc.CreateAttribute ("preserve");
preserve.Value = "fields";
typeNode.Attributes.Append (preserve);
}
else if ((members.fields == null) && (members.methods == null)) {
XmlAttribute preserve = doc.CreateAttribute ("preserve");
preserve.Value = "nothing";
typeNode.Attributes.Append (preserve);
}
if (!members.keepAllFields && (members.fields != null)) {
foreach (string field in members.fields) {
XmlNode fieldNode = doc.CreateElement ("field");
XmlAttribute fieldName = doc.CreateAttribute ("name");
fieldName.Value = field;
fieldNode.Attributes.Append (fieldName);
typeNode.AppendChild (fieldNode);
}
}
if (members.methods != null) {
foreach (string method in members.methods) {
XmlNode methodNode = doc.CreateElement ("method");
XmlAttribute methodName = doc.CreateAttribute ("name");
methodName.Value = method;
methodNode.Attributes.Append (methodName);
typeNode.AppendChild (methodNode);
}
}
}
assemblyNode.AppendChild (typeNode);
}
doc.Save (outputFileName);
}
void AddClass (string classNamespace, string className, string classId, bool keepAllFields = false)
{
string fullClassName = GetFullClassName (classNamespace, className);
if (fullClassName != null) {
if ((classId != null) && (classId != "NoClass")) {
classIdsToClassNames [classId] = fullClassName;
}
ClassMembers members;
if (!classNamesToClassMembers.TryGetValue (fullClassName, out members)) {
members = new ClassMembers ();
classNamesToClassMembers [fullClassName] = members;
}
members.keepAllFields |= keepAllFields;
}
}
void AddField (string fieldName, string classId)
{
string className = classIdsToClassNames [classId];
ClassMembers members = classNamesToClassMembers [className];
if (members.fields == null) {
members.fields = new HashSet<string> ();
}
members.fields.Add (fieldName);
}
void AddMethod (string methodName, string classId, string classNamespace = null, string className = null)
{
string fullClassName;
if (classId != null) {
fullClassName = classIdsToClassNames [classId];
}
else {
fullClassName = GetFullClassName (classNamespace, className);
}
ClassMembers members = classNamesToClassMembers [fullClassName];
if (members.methods == null) {
members.methods = new HashSet<string> ();
}
members.methods.Add (methodName);
}
string GetFullClassName (string classNamespace, string className)
{
string prefixToRemove = "g_";
if (classNamespace.StartsWith (prefixToRemove)) {
classNamespace = classNamespace.Substring (prefixToRemove.Length);
}
string suffixToRemove = "NS";
if (classNamespace.EndsWith (suffixToRemove)) {
classNamespace = classNamespace.Substring (0, classNamespace.Length - suffixToRemove.Length);
}
if ((classNamespace == "NULL") && (className == "NULL")) {
return null;
}
if (!namespaceDictionary.ContainsKey (classNamespace)) {
Log.LogError ("Unknown namespace: " + classNamespace);
}
return namespaceDictionary [classNamespace] + "." + className;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class FilterByMetadata : Task
{
/// <summary>
/// Items to filter.
/// </summary>
[Required]
public ITaskItem[] Items { get; set; }
/// <summary>
/// Name of metadata to filter on.
/// </summary>
[Required]
public String MetadataName { get; set; }
/// <summary>
/// The set of metadata values to include.
/// </summary>
[Required]
public ITaskItem[] MetadataValues { get; set; }
/// <summary>
/// Filtered items: the input items for which the
/// specified metadata was one of the allowed
/// values.
/// </summary>
[Output]
public ITaskItem[] FilteredItems { get; set; }
public override bool Execute()
{
var metadataValues = new HashSet<string>(MetadataValues.Select(v => v.ItemSpec));
FilteredItems = Items
.Where(i => metadataValues.Contains(i.GetMetadata(MetadataName)))
.ToArray();
return true;
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class FindDuplicatesByMetadata : Task
{
/// <summary>
/// Items to scan.
/// </summary>
[Required]
public ITaskItem [] Items { get; set; }
/// <summary>
/// Name of metadata to scan for.
/// </summary>
[Required]
public String MetadataName { get; set; }
/// <summary>
/// Duplicate items: the input items for which the
/// specified metadata was shared by multiple input
/// items.
/// </summary>
[Output]
public ITaskItem [] DuplicateItems { get; set; }
/// <summary>
/// Duplicate representatives: includes one input
/// item from each set of duplicates.
/// </summary>
[Output]
public ITaskItem [] DuplicateRepresentatives { get; set; }
public override bool Execute ()
{
var duplicateGroups = Items.GroupBy (i => i.GetMetadata (MetadataName))
.Where (g => g.Count () > 1);
DuplicateItems = duplicateGroups.SelectMany (g => g).ToArray ();
DuplicateRepresentatives = duplicateGroups.Select (g => g.First ()).ToArray ();
return true;
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class FindNativeDeps : Task
{
/// <summary>
/// The managed assemblies to scan for references to native
/// dependencies.
/// </summary>
[Required]
public ITaskItem [] ManagedAssemblyPaths { get; set; }
/// <summary>
/// The set of native dependencies to keep even if they
/// aren't found to be referenced by a managed assembly.
/// </summary>
public ITaskItem [] NativeDepsToKeep { get; set; }
/// <summary>
/// The paths to the available native dependencies. We
/// expect that all references found point to existing
/// native files.
/// </summary>
[Required]
public ITaskItem [] NativeDepsPaths { get; set; }
/// <summary>
/// The set of native dependencies to keep, including those
/// found in the analysis, and those explicitly marked keep
/// by NativeDepsToKeep. This includes metadata from the
/// input NativeDepsToKeep.
/// </summary>
[Output]
public ITaskItem [] KeptNativeDepsPaths { get; set; }
public override bool Execute ()
{
var allNativeNames = new HashSet<string> ();
foreach (var nativeDep in NativeDepsPaths)
allNativeNames.Add (Path.GetFileName (nativeDep.ItemSpec));
var keptNativeNames = new HashSet<string> ();
foreach (var nativeDep in NativeDepsToKeep)
keptNativeNames.Add (Path.GetFileName (nativeDep.ItemSpec));
var managedAssemblies = ManagedAssemblyPaths.Select (i => i.ItemSpec).ToArray ();
foreach (string managedAssembly in managedAssemblies) {
using (var peReader = new PEReader(new FileStream (managedAssembly, FileMode.Open, FileAccess.Read, FileShare.Read))) {
if (peReader.HasMetadata) {
var reader = peReader.GetMetadataReader ();
for (int i = 1, count = reader.GetTableRowCount (TableIndex.ModuleRef); i <= count; i++) {
var moduleRef = reader.GetModuleReference (MetadataTokens.ModuleReferenceHandle (i));
var moduleName = reader.GetString (moduleRef.Name);
var moduleRefCandidates = new [] { moduleName, moduleName + ".dll", moduleName + ".so", moduleName + ".dylib" };
bool foundModuleRef = false;
foreach (string moduleRefCandidate in moduleRefCandidates) {
if (allNativeNames.Contains (moduleRefCandidate)) {
keptNativeNames.Add (moduleRefCandidate);
foundModuleRef = true;
}
}
if (!foundModuleRef)
Log.LogMessage("unsatisfied DLLImport: " + managedAssembly + " -> " + moduleName);
}
}
}
}
var keptNativeDeps = new List<ITaskItem> ();
foreach (var nativeDep in NativeDepsPaths) {
var fileName = Path.GetFileName (nativeDep.ItemSpec);
if (keptNativeNames.Contains (fileName))
keptNativeDeps.Add (nativeDep);
}
KeptNativeDepsPaths = keptNativeDeps.ToArray ();
return true;
}
}
}

View File

@ -0,0 +1,222 @@
<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>
<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> -->
<!-- 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.
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>
</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" />
<Compile Include="ComputeManagedAssemblies.cs" />
<Compile Include="ComputeRemovedAssemblies.cs" />
<Compile Include="CreateRootDescriptorFile.cs" />
<Compile Include="CreateRuntimeRootDescriptorFile.cs" />
<Compile Include="FilterByMetadata.cs" />
<Compile Include="FindDuplicatesByMetadata.cs" />
<Compile Include="FindNativeDeps.cs" />
<Compile Include="SetAssemblyActions.cs" />
<Compile Include="Utils.cs" />
<Content Include="Sdk\Sdk.props">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</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>
-->
<!-- 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:
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>
</ItemGroup>
</Target>
<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" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<?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>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class ILLink : ToolTask
{
/// <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.
/// Each path can also have an "action" metadata,
/// which will set the illink action to take for
/// that assembly.
/// </summary>
[Required]
public ITaskItem [] AssemblyPaths { get; set; }
/// <summary>
/// The names of the assemblies to root. This should contain
/// assembly names without an extension, not file names or
/// paths. Exactly which parts of the assemblies get rooted
/// is subject to change. Currently these get passed to
/// illink with "-a", which roots the entry point for
/// executables, and everything for libraries. To control
/// the linker more explicitly, either pass descriptor
/// files, or pass extra arguments for illink.
/// </summary>
[Required]
public ITaskItem [] RootAssemblyNames { get; set; }
/// <summary>
/// The directory in which to place linked assemblies.
/// </summary>
[Required]
public ITaskItem OutputDirectory { get; set; }
/// <summary>
/// A list of XML root descriptor files specifying linker
/// roots at a granular level. See the mono/linker
/// documentation for details about the format.
/// </summary>
public ITaskItem [] RootDescriptorFiles { get; set; }
/// <summary>
/// Boolean specifying whether to clear initlocals flag on methods.
/// </summary>
public bool ClearInitLocals { get; set; }
/// <summary>
/// A comma-separated list of assemblies whose methods
/// should have initlocals flag cleared if ClearInitLocals is true.
/// </summary>
public string ClearInitLocalsAssemblies { get; set; }
/// <summary>
/// Extra arguments to pass to illink, delimited by spaces.
/// </summary>
public string ExtraArgs { get; set; }
/// <summary>
/// Make illink dump dependencies file for linker analyzer tool.
/// </summary>
public bool DumpDependencies { get; set; }
private static string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH";
private string _dotnetPath;
private string DotNetPath
{
get
{
if (!String.IsNullOrEmpty (_dotnetPath))
{
return _dotnetPath;
}
_dotnetPath = Environment.GetEnvironmentVariable (DotNetHostPathEnvironmentName);
if (String.IsNullOrEmpty (_dotnetPath))
{
throw new InvalidOperationException ($"{DotNetHostPathEnvironmentName} is not set");
}
return _dotnetPath;
}
}
/// ToolTask implementation
protected override string ToolName => Path.GetFileName (DotNetPath);
protected override string GenerateFullPathToTool () => DotNetPath;
private string _illinkPath = "";
public string ILLinkPath {
get {
if (!String.IsNullOrEmpty (_illinkPath))
{
return _illinkPath;
}
var taskDirectory = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location);
_illinkPath = Path.Combine (taskDirectory, "illink.dll");
return _illinkPath;
}
set => _illinkPath = value;
}
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");
return args.ToString ();
}
}
}

View File

@ -0,0 +1,174 @@
# 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 provides an MSBuild task called ILLink that makes it easy
to run the linker from an MSBuild project file:
```xml
<ILLink AssemblyPaths="@(AssemblyFilesToLink)"
RootAssemblyNames="@(LinkerRootAssemblies)"
RootDescriptorFiles="@(LinkerRootDescriptors)"
OutputDirectory="output"
ExtraArgs="-t -c link -l none" />
```
For a description of the options 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:
- 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
```
linker> ./corebuild/dotnet.{sh/ps1} restore illink.sln
linker> ./corebuild/dotnet.{sh/ps1} pack src/ILLink.Tasks/ILLink.Tasks.csproj
```
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:
`<file src="netcoreapp2.0/**/*.dll" target="tools/netcoreapp2.0" />`
## 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.
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).
Sometimes an application may include multiple versions of the same
assembly. This may happen when portable apps include platform-specific
managed code, which gets placed in the `runtimes` directory of the
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

@ -0,0 +1,16 @@
<!--
***********************************************************************************************
Sdk.props
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
<Project ToolsVersion="14.0">
<UsingTask TaskName="ILLink.Tasks.ILLink" AssemblyFile="$(MSBuildThisFileDirectory)..\tools\netcoreapp2.0\ILLink.Tasks.dll" />
</Project>

View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.IO;
namespace ILLink.Tasks
{
public class SetAssemblyActions : Task
{
/// <summary>
/// Paths to the assembly files that should be considered as
/// input to the linker.
/// </summary>
[Required]
public ITaskItem [] AssemblyPaths { get; set; }
/// <summary>
/// Application assembly names.
/// </summary>
[Required]
public ITaskItem [] ApplicationAssemblyNames { get; set; }
/// <summary>
/// Platform assembly names.
/// </summary>
[Required]
public ITaskItem [] PlatformAssemblyNames { get; set; }
/// <summary>
/// Action to perform on used application assemblies.
/// </summary>
[Required]
public ITaskItem UsedApplicationAssemblyAction { get; set; }
/// <summary>
/// Action to perform on unused application assemblies.
/// </summary>
[Required]
public ITaskItem UnusedApplicationAssemblyAction { get; set; }
/// <summary>
/// Action to perform on used platform assemblies.
/// </summary>
[Required]
public ITaskItem UsedPlatformAssemblyAction { get; set; }
/// <summary>
/// Action to perform on unused platform assemblies.
/// </summary>
[Required]
public ITaskItem UnusedPlatformAssemblyAction { get; set; }
[Output]
public ITaskItem [] AssemblyPathsWithActions { get; set; }
public override bool Execute ()
{
string applicationAssemblyAction;
string usedApplicationAssemblyAction = UsedApplicationAssemblyAction.ItemSpec;
string unusedApplicationAssemblyAction = UnusedApplicationAssemblyAction.ItemSpec;
if (!GetAssemblyAction (usedApplicationAssemblyAction.ToLower (), unusedApplicationAssemblyAction.ToLower (), out applicationAssemblyAction)) {
Log.LogError ("Unsupported combination of application assembly actions: {0}, {1}.",
usedApplicationAssemblyAction, unusedApplicationAssemblyAction);
return false;
}
string platformAssemblyAction;
string usedPlatformAssemblyAction = UsedPlatformAssemblyAction.ItemSpec;
string unusedPlatformAssemblyAction = UnusedPlatformAssemblyAction.ItemSpec;
if (!GetAssemblyAction (usedPlatformAssemblyAction.ToLower (), unusedPlatformAssemblyAction.ToLower (), out platformAssemblyAction)) {
Log.LogError ("Unsupported combination of platform assembly actions: {0}, {1}.",
usedPlatformAssemblyAction, unusedPlatformAssemblyAction);
return false;
}
List<ITaskItem> resultAssemblies = new List<ITaskItem> ();
AddAssemblyActionMetadata (ApplicationAssemblyNames, applicationAssemblyAction, resultAssemblies);
AddAssemblyActionMetadata (PlatformAssemblyNames, platformAssemblyAction, resultAssemblies);
AssemblyPathsWithActions = resultAssemblies.ToArray ();
return true;
}
bool GetAssemblyAction (string usedAssemblyAction, string unusedAssemblyAction, out string assemblyAction)
{
assemblyAction = "illegal";
if ((unusedAssemblyAction != usedAssemblyAction) && (unusedAssemblyAction != "delete")) {
return false;
}
switch (usedAssemblyAction) {
case "link":
assemblyAction = "link";
break;
case "copy":
assemblyAction = (unusedAssemblyAction == "delete") ? "copyused" : "copy";
break;
case "addbypassngen":
assemblyAction = (unusedAssemblyAction == "delete") ? "addbypassngenused" : "addbypassngen";
break;
case "skip":
if (unusedAssemblyAction != usedAssemblyAction) {
return false;
}
assemblyAction = "skip";
break;
default:
return false;
}
return true;
}
void AddAssemblyActionMetadata (ITaskItem [] assemblies, string action, List<ITaskItem> resultList)
{
HashSet<string> assemblyHashSet = new HashSet<string> ();
foreach (var assembly in assemblies) {
assemblyHashSet.Add (assembly.ItemSpec);
}
foreach (var assembly in AssemblyPaths) {
if (assemblyHashSet.Contains (Path.GetFileNameWithoutExtension (assembly.ItemSpec))) {
assembly.SetMetadata ("action", action);
resultList.Add (assembly);
}
}
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using Mono.Cecil;
using Mono.Linker;
public static class Utils
{
public static bool IsManagedAssembly (string fileName)
{
try {
ModuleDefinition module = ModuleDefinition.ReadModule (fileName);
return true;
} catch (BadImageFormatException) {
return false;
}
}
public static bool IsCrossgenedAssembly (string fileName)
{
try {
ModuleDefinition module = ModuleDefinition.ReadModule (fileName);
return module.IsCrossgened ();
} catch (BadImageFormatException) {
return false;
}
}
}

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