Imported Upstream version 5.4.0.167

Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2017-08-21 15:34:15 +00:00
parent e49d6f06c0
commit 536cd135cc
12856 changed files with 563812 additions and 223249 deletions

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
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) {
try {
AssemblyName.GetAssemblyName (unlinkedFile);
}
catch (BadImageFormatException) {
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) {
try {
AssemblyName.GetAssemblyName (linkedFile);
}
catch (BadImageFormatException) {
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}",
"Total size of assemblies",
totalUnlinked,
totalLinked,
((double)totalUnlinked - (double)totalLinked) / (double)totalUnlinked);
Console.WriteLine ("-----------");
Console.WriteLine ("Details");
Console.WriteLine ("-----------");
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,47 @@
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.Build.Utilities; // Task
using Microsoft.Build.Framework; // MessageImportance
using Microsoft.NET.Build.Tasks; // LockFileCache
using NuGet.ProjectModel; // LockFileTargetLibrary
using NuGet.Frameworks; // NuGetFramework.Parse(targetframework)
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()
{
var managedAssemblies = new List<ITaskItem>();
foreach (var f in Assemblies) {
try {
AssemblyName.GetAssemblyName(f.ItemSpec);
managedAssemblies.Add(f);
} catch (BadImageFormatException) {
}
}
ManagedAssemblies = managedAssemblies.ToArray();
return true;
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.Build.Utilities; // Task
using Microsoft.Build.Framework; // MessageImportance
using Microsoft.NET.Build.Tasks; // LockFileCache
using NuGet.ProjectModel; // LockFileTargetLibrary
using NuGet.Frameworks; // NuGetFramework.Parse(targetframework)
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,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using System.IO;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
namespace ILLink.Tasks
{
/// <summary>
/// This class exists as a workaround. It strips the publish
/// dependency file of assemblies excluded from the publish
/// output by the linker. Ideally we would pass appropriate
/// parameters to the task that generates the deps file in
/// the first place, instead of rewriting it. We may be
/// ablee to do this once
/// https://github.com/dotnet/sdk/pull/1052 is merged.
/// </summary>
public class DepsJsonLinker : Task
{
[Required]
public ITaskItem InputDepsFilePath { get; set; }
[Required]
public ITaskItem OutputDepsFilePath { get; set; }
[Required]
public ITaskItem[] ManagedPublishAssemblies { get; set; }
[Required]
public ITaskItem[] KeptAssemblies { get; set; }
public override bool Execute()
{
string inputFile = InputDepsFilePath.ItemSpec;
string outputFile = OutputDepsFilePath.ItemSpec;
string[] keptAssemblies = KeptAssemblies.Select(a => a.ItemSpec).ToArray();
string[] allAssemblies = ManagedPublishAssemblies.Select(a => a.ItemSpec).ToArray();
string[] removedAssemblies = allAssemblies.Except(keptAssemblies).ToArray();
var removedAssembliesSet = new HashSet<string> (removedAssemblies, StringComparer.InvariantCultureIgnoreCase);
JObject o = JObject.Parse (File.ReadAllText (inputFile));
JObject targets = (JObject)o["targets"];
// Remove targets
foreach (JProperty target in targets.Children()) {
JEnumerable<JToken> children = target.Value.Children ();
for (int i = 0; i < children.Count(); ++i) {
//foreach (JProperty subtarget in target.Value.Children()) {
var subtarget = (JProperty) children.ElementAt (i);
string name = subtarget.Name.Substring (0, subtarget.Name.IndexOf ('/'));
if (removedAssembliesSet.Contains (name + ".dll")) {
subtarget.Remove ();
i--;
continue;
}
// Remove dependencies
var dependencies = subtarget.Value["dependencies"];
if (dependencies != null) {
for (int j = 0; j < dependencies.Count (); ++j) {
var dependency = ((JProperty)dependencies.ElementAt (j));
if (removedAssembliesSet.Contains (dependency.Name + ".dll")) {
dependency.Remove ();
j--;
continue;
}
}
}
// Remove runtimes
var runtimes = subtarget.Value["runtime"];
if (runtimes != null) {
for (int j = 0; j < runtimes.Count (); ++j) {
var runtime = ((JProperty)runtimes.ElementAt (j));
string runtimeFileName = runtime.Name.Substring (runtime.Name.LastIndexOf ('/') + 1);
if (removedAssembliesSet.Contains (runtimeFileName)) {
runtime.Remove ();
j--;
continue;
}
}
}
}
}
// Remove libraries
JObject libraries = (JObject)o["libraries"];
JEnumerable<JToken> libraryChildren = libraries.Children ();
for (int i = 0; i < libraryChildren.Count (); ++i) {
var library = (JProperty)libraryChildren.ElementAt (i);
string name = library.Name.Substring (0, library.Name.IndexOf ('/'));
if (removedAssembliesSet.Contains (name + ".dll")) {
library.Remove ();
i--;
continue;
}
}
File.WriteAllText (outputFile, o.ToString ());
return true;
}
}
}

View File

@@ -0,0 +1,175 @@
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.Build.Utilities; // Task
using Microsoft.Build.Framework; // MessageImportance
using Microsoft.NET.Build.Tasks; // LockFileCache
using NuGet.ProjectModel; // LockFileTargetLibrary
using NuGet.Frameworks; // NuGetFramework.Parse(targetframework)
namespace ILLink.Tasks
{
public class GenerateRoots : Task
{
[Required]
public string AssetsFilePath { get; set; }
[Required]
public string TargetFramework { get; set; }
[Required]
public string RuntimeIdentifier { get; set; }
[Required]
public string PublishDir { get; set; }
[Required]
public ITaskItem SingleRootXmlFilePath { get; set; }
[Required]
public string MainAssemblyName { get; set; }
[Output]
public ITaskItem[] RootAssemblies { get; private set; }
[Output]
public ITaskItem[] FrameworkAssemblies { get; private set; }
[Output]
public ITaskItem[] PublishAssemblies { get; private set; }
[Output]
public ITaskItem[] UnmanagedFileAssets { get; private set; }
private List<string> publishLibs;
private List<string> unmanagedFileAssets;
private List<string> rootLibs;
private List<string> frameworkLibs;
private void WriteSingleRootXmlFile()
{
var xdoc = new XDocument(new XElement("linker",
new XElement("assembly",
new XAttribute("fullname", MainAssemblyName),
new XElement("type",
new XAttribute("fullname", "*"),
new XAttribute("required", "true")))));
XmlWriterSettings xws = new XmlWriterSettings();
xws.Indent = true;
xws.OmitXmlDeclaration = true;
using (XmlWriter xw = XmlWriter.Create(SingleRootXmlFilePath.ItemSpec, xws))
{
xdoc.Save(xw);
}
}
private void GetAssembliesAndFiles()
{
unmanagedFileAssets = new List<string>();
publishLibs = new List<string>();
foreach (var f in Directory.GetFiles(PublishDir))
{
try
{
AssemblyName.GetAssemblyName(f);
publishLibs.Add(f);
}
catch (BadImageFormatException)
{
unmanagedFileAssets.Add(f);
}
}
}
private void PopulateOutputItems()
{
FrameworkAssemblies = frameworkLibs.Select(l => new TaskItem(l)).ToArray();
RootAssemblies = rootLibs.Select(l => Path.GetFileNameWithoutExtension(l))
.Select(l => new TaskItem(l)).ToArray();
UnmanagedFileAssets = unmanagedFileAssets.Select(f => Path.GetFileName(f))
.Select(f => new TaskItem(f)).ToArray();
PublishAssemblies = publishLibs.Select(l => Path.GetFileName(l))
.Select(l => new TaskItem(l)).ToArray();
}
public override bool Execute()
{
if (!Directory.Exists(PublishDir))
{
Log.LogMessageFromText($"Publish directory {PublishDir} does not exist. Run dotnet publish before dotnet link.", MessageImportance.High); return false;
}
// TODO: make this a separate msbuild task
WriteSingleRootXmlFile();
// TODO: make this a separate msbuild task
GetAssembliesAndFiles();
// TODO: make this a separate msbuild task
GetFrameworkLibraries();
rootLibs = publishLibs.Select(l => Path.GetFileName(l)).Except(frameworkLibs).ToList();
PopulateOutputItems();
return true;
}
private void GetFrameworkLibraries()
{
var lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath);
var lockFileTarget = lockFile.GetTarget(NuGetFramework.Parse(TargetFramework), RuntimeIdentifier);
if (lockFileTarget == null)
{
var targetString = string.IsNullOrEmpty(RuntimeIdentifier) ? TargetFramework : $"{TargetFramework}/{RuntimeIdentifier}";
throw new Exception($"Missing target section {targetString} from assets file {AssetsFilePath}. Ensure you have restored this project previously.");
}
var netCoreAppPackage = lockFileTarget.Libraries.Single(l => l.Name == "Microsoft.NETCore.App");
Dictionary<string, LockFileTargetLibrary> packages = new Dictionary<string, LockFileTargetLibrary>(lockFileTarget.Libraries.Count, StringComparer.OrdinalIgnoreCase);
foreach (var lib in lockFileTarget.Libraries)
{
packages.Add(lib.Name, lib);
}
var packageQueue = new Queue<LockFileTargetLibrary>();
packageQueue.Enqueue(netCoreAppPackage);
var libraries = new List<string>();
while (packageQueue.Count > 0)
{
var package = packageQueue.Dequeue();
foreach (var lib in package.RuntimeAssemblies)
{
libraries.Add(lib.ToString());
}
foreach (var dep in package.Dependencies.Select(d => d.Id))
{
packageQueue.Enqueue(packages[dep]);
}
}
frameworkLibs = libraries.Select(l => Path.GetFileName(l)).ToList();
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.Build.Utilities; // Task
using Microsoft.Build.Framework; // MessageImportance
using Microsoft.NET.Build.Tasks; // LockFileCache
using NuGet.ProjectModel; // LockFileTargetLibrary
using NuGet.Frameworks; // NuGetFramework.Parse(targetframework)
namespace ILLink.Tasks
{
public class GetRuntimeLibraries : Task
{
/// <summary>
/// Path to the assets file.
/// </summary>
[Required]
public ITaskItem AssetsFilePath { get; set; }
/// <summary>
/// Target framework for which to get the platform
/// libraries.
/// </summary>
[Required]
public string TargetFramework { get; set; }
/// <summary>
/// Runtime identifier for which to get the platform
/// libraries.
/// </summary>
[Required]
public string RuntimeIdentifier { get; set; }
/// <summary>
/// Name of the library to consider the "platform"
/// library.
/// </summary>
[Required]
public string[] PackageNames { get; set; }
[Output]
public ITaskItem[] RuntimeLibraries { get; private set; }
public override bool Execute()
{
var lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath.ItemSpec);
var lockFileTarget = lockFile.GetTarget(NuGetFramework.Parse(TargetFramework), RuntimeIdentifier);
if (lockFileTarget == null) {
var targetString = string.IsNullOrEmpty(RuntimeIdentifier) ? TargetFramework : $"{TargetFramework}/{RuntimeIdentifier}";
throw new Exception($"Missing target section {targetString} from assets file {AssetsFilePath}. Ensure you have restored this project previously.");
}
Dictionary<string, LockFileTargetLibrary> packages = new Dictionary<string, LockFileTargetLibrary>(lockFileTarget.Libraries.Count, StringComparer.OrdinalIgnoreCase);
foreach (var lib in lockFileTarget.Libraries) {
packages.Add(lib.Name, lib);
}
HashSet<string> packageNames = new HashSet<string>(PackageNames);
var rootPackages = lockFileTarget.Libraries.Where(l => packageNames.Contains(l.Name));
var packageQueue = new Queue<LockFileTargetLibrary>(rootPackages);
var libraries = new List<string>();
while (packageQueue.Count > 0) {
var package = packageQueue.Dequeue();
foreach (var lib in package.RuntimeAssemblies) {
libraries.Add(lib.ToString());
}
foreach (var dep in package.Dependencies.Select(d => d.Id)) {
packageQueue.Enqueue(packages[dep]);
}
}
RuntimeLibraries = libraries.Select(l => new TaskItem(l)).ToArray();
return true;
}
}
}

View File

@@ -0,0 +1,171 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>0.1.4-preview</VersionPrefix>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0-beta-001509-00</RuntimeFrameworkVersion>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<PackageOutputPath>../nupkgs</PackageOutputPath>
<!-- 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 contsume 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. -->
<NuspecFile>ILLink.Tasks.nuspec</NuspecFile>
<NuspecProperties>id=$(AssemblyName);authors=$(AssemblyName);description=linker tasks;tfm=$(TargetFramework);</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>Publish;SetDynamicNuspecProperties;$(GenerateNuspecDependsOn)</GenerateNuspecDependsOn>
</PropertyGroup>
<Target Name="SetDynamicNuspecProperties">
<PropertyGroup>
<NuspecProperties>$(NuspecProperties)output=$(PublishDir);version=$(Version);</NuspecProperties>
</PropertyGroup>
</Target>
<ItemGroup>
<Compile Include="LinkTask.cs" />
<Compile Include="DepsJsonLinker.cs" />
<Compile Include="CompareSizes.cs" />
<Compile Include="ComputeManagedAssemblies.cs" />
<Compile Include="GetRuntimeLibraries.cs" />
<Compile Include="CreateRootDescriptorFile.cs" />
<Compile Include="Microsoft.NET.Build.Tasks/LockFileCache.cs" />
<Compile Include="Microsoft.NET.Build.Tasks/BuildErrorException.cs" />
</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. However, it should be possible
to set it here to allow packing the tasks csproj on its
own. This would let 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=netcore_$(Configuration)</SetConfiguration>
-->
</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. -->
<PackageReference Include="Microsoft.Build.Framework" Version="0.1.0-preview-00028-160627" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="0.1.0-preview-00028-160627" />
<PackageReference Include="NuGet.ProjectModel" Version="4.3.0-preview1-2500" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
<?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="$output$*.dll" target="tools/$tfm$" />
</files>
</package>

View File

@@ -0,0 +1,294 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LinkTaskDllPath Condition=" '$(LinkTaskDllPath)' == '' ">$(MSBuildThisFileDirectory)../tools/$(TargetFramework)/ILLink.Tasks.dll</LinkTaskDllPath>
</PropertyGroup>
<PropertyGroup>
<!-- LinkDuringPublish allows the linker to be disabled by passing
a property on the command-line. (/p:LinkDuringPublish=false)
-->
<LinkDuringPublish Condition=" '$(LinkDuringPublish)' == '' ">true</LinkDuringPublish>
<LinkDuringPublish Condition=" '$(LinkDuringPublish)' != 'true' ">false</LinkDuringPublish>
<ShowLinkerSizeComparison Condition=" '$(ShowLinkerSizeComparison)' == '' ">false</ShowLinkerSizeComparison>
</PropertyGroup>
<ItemGroup>
<!-- LinkerRootDescriptors (the ItemGroup) is set to contain
LinkerRootDescriptors (the Property), which allows adding
linker roots on the command-line
(/p:LinkerRootDescriptors=path/to/a.xml;path/to/b.xml;). These
are relative paths from the project to the xml file.
LinkerRootDescriptors (the ItemGroup) can also be set in the
project file, and these will be included in addition to those
specified on the command-line. -->
<LinkerRootDescriptors Include="$(LinkerRootDescriptors)" />
</ItemGroup>
<!-- By default, the linker will generate roots for the intermediate
assembly that root everything in the assembly. -->
<PropertyGroup>
<_IntermediateRootDescriptorName Condition=" '$(_IntermediateRootDescriptorName)' == '' ">IntermediateAssemblyRoots.xml</_IntermediateRootDescriptorName>
<_IntermediateRootDescriptorPath Condition=" '$(_IntermediateRootDescriptorPath)' == '' ">$(IntermediateOutputPath)$(_IntermediateRootDescriptorName)</_IntermediateRootDescriptorPath>
</PropertyGroup>
<!-- The linker will place linked assemblies into an intermediate
directory, and hook into the rest of the publish pipeline to
publish the linked assemblies instead of those given as input
to the linker. -->
<PropertyGroup>
<IntermediateLinkDirName Condition=" '$(IntermediateLinkDirName)' == '' ">linked</IntermediateLinkDirName>
<IntermediateLinkDir Condition=" '$(IntermediateLinkDir)' == '' ">$(IntermediateOutputPath)$(IntermediateLinkDirName)</IntermediateLinkDir>
</PropertyGroup>
<!-- Used to enable incremental build for the link target. -->
<PropertyGroup>
<_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore</_LinkSemaphore>
<_LinkDepsSemaphore>$(IntermediateOutputPath)LinkDeps.semaphore</_LinkDepsSemaphore>
</PropertyGroup>
<!--
This target runs the linker during the publish pipeline. The
publish pipeline has a target called ComputeFilesToPublish,
which computes the ItemGroup ResolvedFileToPublish. To extend
this target, we insert a target before
ComputeFilesToPublish. Our target rewrites the relevant inputs
(@(IntermediateAssembly) and
@(ResolvedAssembliesToPublish)). This lets ComputeFilesToPublish
be ignorant of the linker, but changes the meaning of
IntermediateAssembly and ResolvedAssembliesToPublish.
-->
<!-- DependsOnTargets here doesn't include the targets that compute
ResolvedAssembliesToPublish or IntermediateAssembly, because
ComputeFilesToPublish already depends on
these. BeforeTargets="ComputeFilesToPublish" ensures that
ComputeLinkedFilesToPublish will run before
ComputeFilesToPublish, but after all of its dependencies. -->
<Target Name="ComputeLinkedFilesToPublish"
BeforeTargets="ComputeFilesToPublish"
DependsOnTargets="_ComputeLinkedAssemblies"
Condition=" '$(LinkDuringPublish)' == 'true' ">
<!-- Rewrite ResolvedAssembliesToPublish, which is an input to
ComputeFilesToPublish. -->
<ItemGroup>
<ResolvedAssembliesToPublish Remove="@(_ManagedResolvedAssembliesToPublish)" />
<ResolvedAssembliesToPublish Include="@(_LinkedResolvedAssemblies)" />
</ItemGroup>
<!-- Rewrite IntermediateAssembly, which is an input to
ComputeFilesToPublish. -->
<ItemGroup>
<IntermediateAssembly Remove="@(IntermediateAssembly)" />
<IntermediateAssembly Include="@(_LinkedIntermediateAssembly)" />
</ItemGroup>
</Target>
<!-- Print out a size comparison report for the linked
assemblies. This is disabled by default, but can be turned on
by setting $(ShowLinkerSizeComparison) to true. This runs after
the top-level link target, ComputeLinkedFilesToPublish, so it
is output even during incremental builds. -->
<UsingTask TaskName="CompareAssemblySizes" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="_CompareLinkedAssemblySizes"
AfterTargets="ComputeLinkedFilesToPublish"
DependsOnTargets="_ComputeManagedAssembliesToLink;_ComputeLinkedAssemblies"
Condition=" '$(LinkDuringPublish)' == 'true' And '$(ShowLinkerSizeComparison)' == 'true' ">
<CompareAssemblySizes UnlinkedAssemblies="@(_ManagedAssembliesToLink)"
LinkedAssemblies="@(_LinkedIntermediateAssembly);@(_LinkedResolvedAssemblies)" />
</Target>
<!-- Computes _LinkedResolvedAssemblies and
_LinkedIntermediateAssembly. _LinkedResolvedAssemblies needs to
keep metadata from _ManagedResolvedAssembliesToPublish, since
this is used by ComputeFilesToPublish. -->
<Target Name="_ComputeLinkedAssemblies"
DependsOnTargets="_ComputeManagedResolvedAssembliesToPublish;ILLink">
<ItemGroup>
<__LinkedResolvedAssemblies Include="@(_ManagedResolvedAssembliesToPublish->'$(IntermediateLinkDir)/%(Filename)%(Extension)')" />
<_LinkedResolvedAssemblies Include="@(__LinkedResolvedAssemblies)" Condition="Exists('%(Identity)')" />
</ItemGroup>
<ItemGroup>
<__LinkedIntermediateAssembly Include="@(IntermediateAssembly->'$(IntermediateLinkDir)/%(Filename)%(Extension)')" />
<_LinkedIntermediateAssembly Include="@(__LinkedIntermediateAssembly)" Condition="Exists('%(Identity)')" />
</ItemGroup>
</Target>
<!-- This calls the linker. Inputs are the managed assemblies to
link, and root specifications. The semaphore enables msbuild to
skip linking during an incremental build, when the semaphore is
up to date with respect to _ManagedAssembliesToLink. -->
<UsingTask TaskName="ILLink" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="ILLink"
DependsOnTargets="_ComputeManagedAssembliesToLink;_ComputeLinkerRootAssemblies;_ComputeLinkerRootDescriptors"
Inputs="@(_ManagedAssembliesToLink);@(LinkerRootDescriptors);$(MSBuildAllProjects)"
Outputs="$(_LinkSemaphore)">
<!-- These extra arguments have been hard-coded for now, as this
is what we want to use when linking a self-contained app. In
the future we will want to generate these depending on the
scenario in which the linker is invoked. -->
<PropertyGroup>
<ExtraLinkerArgs Condition=" '$(ExtraLinkerArgs)' == '' ">-t -c link -l none</ExtraLinkerArgs>
</PropertyGroup>
<ILLink AssemblyPaths="@(_ManagedAssembliesToLink)"
RootAssemblyNames="@(LinkerRootAssemblies)"
RootDescriptorFiles="@(LinkerRootDescriptors)"
OutputDirectory="$(IntermediateLinkDir)"
ExtraArgs="$(ExtraLinkerArgs)" />
<Touch Files="$(_LinkSemaphore)" AlwaysCreate="true">
<Output TaskParameter="TouchedFiles" ItemName="FileWrites" />
</Touch>
</Target>
<!-- Computes the managed assemblies that are input to the
linker. Includes managed assemblies from
ResolvedAssembliesToPublish, and IntermediateAssembly. -->
<Target Name="_ComputeManagedAssembliesToLink"
DependsOnTargets="_ComputeManagedResolvedAssembliesToPublish">
<ItemGroup>
<_ManagedAssembliesToLink Include="@(IntermediateAssembly)" />
<_ManagedAssembliesToLink Include="@(_ManagedResolvedAssembliesToPublish)" />
</ItemGroup>
</Target>
<!-- Computes the managed subset of
ResolvedAssembliesToPublish. This needs to preserve metadata,
because it is later used to compute the linked assemblies with
metadata that gets used by the rest of the publish
pipeline. This needs to run even during incremental build,
because we use the list of managed assemblies to filter the
publish output. -->
<UsingTask TaskName="ComputeManagedAssemblies" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="_ComputeManagedResolvedAssembliesToPublish">
<!-- TODO: Is there a better way to get the managed assemblies
from ResolvedAssembliesToPublish? We may be able to check for
AssetType="runtime" on managed assemblies - would that give
the same set of assemblies? -->
<ComputeManagedAssemblies Assemblies="@(ResolvedAssembliesToPublish)">
<Output TaskParameter="ManagedAssemblies" ItemName="_ManagedResolvedAssembliesToPublish" />
</ComputeManagedAssemblies>
<!-- For now, hard-code System.Private.CoreLib.ni, the only .ni
file we've been encountering. This is a special case matching
how we compute the linker roots by default - we always root
System.Private.CoreLib, so we want to keep it in the publish
output and the generated deps.json file. Excluding it from
_ManagedResolvedAssembliesToPublish will prevent it from
getting filtered out of the publish output later.
In the future we may want to detect ngen assemblies and
filter them more robustly. -->
<!-- TODO: Which .ni files do we expect to be in
ResolvedAssembliesToPublish? -->
<ItemGroup>
<_ManagedResolvedAssembliesToPublish Remove="@(_ManagedResolvedAssembliesToPublish->WithMetadataValue('Filename', 'System.Private.CoreLib.ni'))" />
</ItemGroup>
</Target>
<!-- Compute the assemblies the linker should mark as roots. By
default, these are passed as "-a", but this is an
implementation detail of the link task and should not be relied
upon in general. In the future we may add other inputs to the
link task to control how these are rooted.
-->
<!-- We may want to add an additional mode in which we root only the
assembly we have built, rooting all entry points visible from
the outside of the assembly. We need to discuss whether this
even makes sense during publish, since typically libraries get
packaged via "dotnet pack", and the pack task takes input from
build, not from publish. Thus linking the library itself should
be part of the build step or pack step, OR pack could be made
to work on the publish output. -->
<Target Name="_ComputeLinkerRootAssemblies"
DependsOnTargets="_ComputeManagedResolvedAssembliesToPublish;_ComputePlatformLibraries">
<!-- By default, roots are everything minus the framework
assemblies (except for System.Private.CoreLib, which we
always root for now). This doesn't include the intermediate
assembly, because we root it separately using an xml file,
which lets us explicitly root everything. -->
<ItemGroup>
<LinkerRootAssemblies Include="@(_ManagedResolvedAssembliesToPublish->'%(Filename)')" />
<LinkerRootAssemblies Remove="@(PlatformLibraries->'%(Filename)')" />
<LinkerRootAssemblies Include="System.Private.CoreLib" />
</ItemGroup>
</Target>
<!-- Platform libraries are the managed runtime assets needed by the
"platform", currently Microsoft.NETCore.App. -->
<UsingTask TaskName="GetRuntimeLibraries" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="_ComputePlatformLibraries">
<GetRuntimeLibraries AssetsFilePath="$(ProjectAssetsFile)"
TargetFramework="$(TargetFrameworkMoniker)"
RuntimeIdentifier="$(RuntimeIdentifier)"
PackageNames="$(MicrosoftNETPlatformLibrary)">
<Output TaskParameter="RuntimeLibraries" ItemName="PlatformLibraries" />
</GetRuntimeLibraries>
</Target>
<!-- This target prepares the LinkerRootDescriptors itemgroup for
input to the link task. In addition to including user-specified
LinkerRootDescriptors in the ItemGroup statically above, we
dynamically include the generated descriptor files for the
intermediate assembly. -->
<Target Name="_ComputeLinkerRootDescriptors"
DependsOnTargets="_GenerateIntermediateRootDescriptor">
<ItemGroup>
<LinkerRootDescriptors Include="$(_IntermediateRootDescriptorPath)" />
</ItemGroup>
</Target>
<!-- Generates the xml root descriptor file that completely roots
the intermediate assembly. -->
<UsingTask TaskName="CreateRootDescriptorFile" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="_GenerateIntermediateRootDescriptor"
Inputs="@(IntermediateAssembly)"
Outputs="$(_IntermediateRootDescriptorPath)">
<CreateRootDescriptorFile AssemblyNames="@(IntermediateAssembly->'%(Filename)')"
RootDescriptorFilePath="$(_IntermediateRootDescriptorPath)" />
</Target>
<!-- This target needs to remove from the publish deps file those
assemblies that were excluded from the publish output by the
linker. Currently it does so by rewriting the publish
dependency file (as opposed to generating one without the
excluded assemblies in the first place).
TODO: update this to pass FilesToSkip to
GeneratePublishDependencyFile once
https://github.com/dotnet/sdk/pull/1052 is merged.
-->
<UsingTask TaskName="DepsJsonLinker" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="_GenerateLinkedPublishDependencyFile"
DependsOnTargets="_ComputeManagedAssembliesToLink;_ComputeLinkedAssemblies"
AfterTargets="GeneratePublishDependencyFile"
Condition=" '$(LinkDuringPublish)' == 'true' "
Inputs="@(_ManagedResolvedAssembliesToPublish);@(_LinkedResolvedAssemblies);$(PublishDepsFilePath)"
Outputs="$(_LinkDepsSemaphore)">
<!-- DepsJsonLinker expects inputs in the form of filename.dll. -->
<!-- We pass _ManagedResolvedAssembliesToPublish, which doesn't
contain any .ni files. This correctly prevents stripping of
the .ni files (which we want to continue publishing at the
moment). -->
<!-- This doesn't filter any assemblies from IntermediateAssembly,
which should currently always be rooted by default. -->
<DepsJsonLinker InputDepsFilePath="$(PublishDepsFilePath)"
OutputDepsFilePath="$(PublishDepsFilePath)"
ManagedPublishAssemblies="@(_ManagedResolvedAssembliesToPublish->'%(Filename)%(Extension)')"
KeptAssemblies="@(_LinkedResolvedAssemblies->'%(Filename)%(Extension)')" />
<Touch Files="$(_LinkDepsSemaphore)" AlwaysCreate="true">
<Output TaskParameter="TouchedFiles" ItemName="FileWrites" />
</Touch>
</Target>
</Project>

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Text;
using System.IO;
using System.Linq;
namespace ILLink.Tasks
{
public class ILLink : Task
{
/// <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.
/// </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>
/// Extra arguments to pass to illink, delimited by spaces.
/// </summary>
public string ExtraArgs { get; set; }
public override bool Execute()
{
string[] args = GenerateCommandLineCommands();
var argsString = String.Join(" ", args);
Log.LogMessageFromText($"illink {argsString}", MessageImportance.Normal);
int ret = Mono.Linker.Driver.Main(args);
return ret == 0;
}
private string[] GenerateCommandLineCommands()
{
var args = new List<string>();
if (RootDescriptorFiles != null) {
foreach (var rootFile in RootDescriptorFiles) {
args.Add("-x");
args.Add(rootFile.ItemSpec);
}
}
foreach (var assemblyItem in RootAssemblyNames) {
args.Add("-a");
args.Add(assemblyItem.ItemSpec);
}
var assemblyDirs = AssemblyPaths.Select(p => Path.GetDirectoryName(p.ItemSpec))
.GroupBy(d => d).Select(ds => ds.First());
foreach (var dir in assemblyDirs) {
args.Add("-d");
args.Add(dir);
}
if (OutputDirectory != null) {
args.Add("-out");
args.Add(OutputDirectory.ItemSpec);
}
if (ExtraArgs != null) {
args.AddRange(ExtraArgs.Split(' '));
}
return args.ToArray();
}
}
}

View File

@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
namespace Microsoft.NET.Build.Tasks
{
/// <summary>
/// Represents an error that is neither avoidable in all cases nor indicative of a bug in this library.
/// It will be logged as a plain build error without the exception type or stack.
/// </summary>
internal class BuildErrorException : Exception
{
public BuildErrorException()
{
}
public BuildErrorException(string message) : base(message)
{
}
public BuildErrorException(string message, Exception innerException) : base(message, innerException)
{
}
public BuildErrorException(string format, params string[] args)
: this(string.Format(CultureInfo.CurrentCulture, format, args))
{
}
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using Microsoft.Build.Framework;
using NuGet.Common;
using NuGet.ProjectModel;
namespace Microsoft.NET.Build.Tasks
{
internal class LockFileCache
{
private IBuildEngine4 _buildEngine;
public LockFileCache(IBuildEngine4 buildEngine)
{
_buildEngine = buildEngine;
}
public LockFile GetLockFile(string path)
{
if (!Path.IsPathRooted(path))
{
throw new BuildErrorException("Assets file path '{0}' is not rooted. Only full paths are supported.", path);
}
string lockFileKey = GetTaskObjectKey(path);
LockFile result;
object existingLockFileTaskObject = _buildEngine.GetRegisteredTaskObject(lockFileKey, RegisteredTaskObjectLifetime.Build);
if (existingLockFileTaskObject == null)
{
result = LoadLockFile(path);
_buildEngine.RegisterTaskObject(lockFileKey, result, RegisteredTaskObjectLifetime.Build, true);
}
else
{
result = (LockFile)existingLockFileTaskObject;
}
return result;
}
private static string GetTaskObjectKey(string lockFilePath)
{
return $"{nameof(LockFileCache)}:{lockFilePath}";
}
private LockFile LoadLockFile(string path)
{
if (!File.Exists(path))
{
throw new BuildErrorException("Assets file '{0}' not found. Run a NuGet package restore to generate this file.", path);
}
// TODO - https://github.com/dotnet/sdk/issues/18 adapt task logger to Nuget Logger
return LockFileUtilities.GetLockFile(path, NullLogger.Instance);
}
}
}

View File

@@ -0,0 +1,9 @@
$tasksFolder="~\.nuget\packages\illink.tasks"
If (Test-Path $tasksFolder) {
Remove-Item -r $tasksFolder
}
$dotNetTool = Join-Path $PSScriptRoot "..\corebuild\dotnet.ps1"
# create integration packages
& $dotNetTool restore (Join-Path $PSScriptRoot "linker.sln")
& $dotNetTool pack (Join-Path $PSScriptRoot "linker.sln")

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
__scriptpath=$(cd "$(dirname "$0")"; pwd -P)
tasksFolder=~/.nuget/packages/illink.tasks
if [ -d $tasksFolder ]
then
rm -r $tasksFolder
fi
dotNetTool=$__scriptpath/../corebuild/dotnet.sh
# create integration packages
$dotNetTool restore $__scriptpath/linker.sln
$dotNetTool pack $__scriptpath/linker.sln

View File

@@ -0,0 +1,76 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILLink.Tasks", "ILLink.Tasks\ILLink.Tasks.csproj", "{42050FFF-50CB-41D5-B617-68D1624FE716}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Linker", "..\..\linker\Mono.Linker.csproj", "{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "..\..\cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil.Pdb", "..\..\cecil\symbols\pdb\Mono.Cecil.Pdb.csproj", "{63E6915C-7EA4-4D76-AB28-0D7191EEA626}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{42050FFF-50CB-41D5-B617-68D1624FE716}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42050FFF-50CB-41D5-B617-68D1624FE716}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42050FFF-50CB-41D5-B617-68D1624FE716}.Debug|x64.ActiveCfg = Debug|x64
{42050FFF-50CB-41D5-B617-68D1624FE716}.Debug|x64.Build.0 = Debug|x64
{42050FFF-50CB-41D5-B617-68D1624FE716}.Debug|x86.ActiveCfg = Debug|x86
{42050FFF-50CB-41D5-B617-68D1624FE716}.Debug|x86.Build.0 = Debug|x86
{42050FFF-50CB-41D5-B617-68D1624FE716}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42050FFF-50CB-41D5-B617-68D1624FE716}.Release|Any CPU.Build.0 = Release|Any CPU
{42050FFF-50CB-41D5-B617-68D1624FE716}.Release|x64.ActiveCfg = Release|x64
{42050FFF-50CB-41D5-B617-68D1624FE716}.Release|x64.Build.0 = Release|x64
{42050FFF-50CB-41D5-B617-68D1624FE716}.Release|x86.ActiveCfg = Release|x86
{42050FFF-50CB-41D5-B617-68D1624FE716}.Release|x86.Build.0 = Release|x86
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Debug|Any CPU.ActiveCfg = netcore_Debug|Any CPU
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Debug|Any CPU.Build.0 = netcore_Debug|Any CPU
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Debug|x64.ActiveCfg = netcore_Debug|x64
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Debug|x64.Build.0 = netcore_Debug|x64
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Debug|x86.ActiveCfg = netcore_Debug|x86
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Debug|x86.Build.0 = netcore_Debug|x86
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Release|Any CPU.ActiveCfg = netcore_Release|Any CPU
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Release|Any CPU.Build.0 = netcore_Release|Any CPU
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Release|x64.ActiveCfg = netcore_Release|x64
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Release|x64.Build.0 = netcore_Release|x64
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Release|x86.ActiveCfg = netcore_Release|x86
{DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46}.Release|x86.Build.0 = netcore_Release|x86
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|Any CPU.ActiveCfg = netstandard_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|Any CPU.Build.0 = netstandard_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x64.ActiveCfg = netstandard_Debug|x64
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x64.Build.0 = netstandard_Debug|x64
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x86.ActiveCfg = netstandard_Debug|x86
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x86.Build.0 = netstandard_Debug|x86
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|Any CPU.ActiveCfg = netstandard_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|Any CPU.Build.0 = netstandard_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x64.ActiveCfg = netstandard_Release|x64
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x64.Build.0 = netstandard_Release|x64
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x86.ActiveCfg = netstandard_Release|x86
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x86.Build.0 = netstandard_Release|x86
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Debug|Any CPU.ActiveCfg = netstandard_Debug|Any CPU
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Debug|Any CPU.Build.0 = netstandard_Debug|Any CPU
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Debug|x64.ActiveCfg = netstandard_Debug|x64
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Debug|x64.Build.0 = netstandard_Debug|x64
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Debug|x86.ActiveCfg = netstandard_Debug|x86
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Debug|x86.Build.0 = netstandard_Debug|x86
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Release|Any CPU.ActiveCfg = netstandard_Release|Any CPU
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Release|Any CPU.Build.0 = netstandard_Release|Any CPU
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Release|x64.ActiveCfg = netstandard_Release|x64
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Release|x64.Build.0 = netstandard_Release|x64
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Release|x86.ActiveCfg = netstandard_Release|x86
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}.Release|x86.Build.0 = netstandard_Release|x86
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,41 @@
using System;
using System.IO;
using Xunit;
using Xunit.Abstractions;
namespace ILLink.Tests
{
public class HelloWorldTest : IntegrationTestBase
{
public HelloWorldTest(ITestOutputHelper output) : base(output) {}
public string SetupProject()
{
string projectRoot = "helloworld";
if (Directory.Exists(projectRoot)) {
Directory.Delete(projectRoot, true);
}
Directory.CreateDirectory(projectRoot);
int ret = Dotnet("new console", projectRoot);
if (ret != 0) {
output.WriteLine("dotnet new failed");
Assert.True(false);
}
string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
return csproj;
}
[Fact]
public void RunHelloWorld()
{
string csproj = SetupProject();
AddLinkerReference(csproj);
BuildAndLink(csproj, null);
}
}
}

View File

@@ -0,0 +1,147 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Xunit;
using Xunit.Abstractions;
namespace ILLink.Tests
{
public class IntegrationTestBase
{
protected readonly ITestOutputHelper output;
protected readonly TestContext context;
public IntegrationTestBase(ITestOutputHelper output)
{
this.output = output;
// This sets up the context with some values specific to
// the setup of the linker repository. A different context
// should be used in order to run tests in a different
// environment.
this.context = TestContext.CreateDefaultContext();
}
protected int Dotnet(string args, string workingDir, string additionalPath = null)
{
return RunCommand(context.DotnetToolPath, args, workingDir, additionalPath);
}
protected int RunCommand(string command, string args, string workingDir, string additionalPath = null)
{
output.WriteLine($"{command} {args}");
if (workingDir != null)
output.WriteLine($"working directory: {workingDir}");
var psi = new ProcessStartInfo
{
FileName = command,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = workingDir,
};
if (additionalPath != null) {
string path = psi.Environment["PATH"];
psi.Environment["PATH"] = path + ";" + additionalPath;
}
var process = new Process
{
StartInfo = psi,
};
process.Start();
string capturedOutput = process.StandardOutput.ReadToEnd();
output.WriteLine(capturedOutput);
string capturedError = process.StandardError.ReadToEnd();
output.WriteLine(capturedError);
process.WaitForExit();
return process.ExitCode;
}
/// <summary>
/// Run the linker on the specified project. This assumes
/// that the project already contains a reference to the
/// linker task package.
/// Optionally takes a list of root descriptor files.
/// </summary>
public void BuildAndLink(string csproj, List<string> rootFiles = null)
{
string rid = context.RuntimeIdentifier;
string config = context.Configuration;
string demoRoot = Path.GetDirectoryName(csproj);
int ret = Dotnet($"restore -r {rid}", demoRoot);
if (ret != 0) {
output.WriteLine("restore failed");
Assert.True(false);
return;
}
string publishArgs = $"publish -r {rid} -c {config} /v:n /p:ShowLinkerSizeComparison=true";
string rootFilesStr;
if (rootFiles != null && rootFiles.Any()) {
rootFilesStr = String.Join(";", rootFiles);
publishArgs += $" /p:LinkerRootDescriptors={rootFilesStr}";
}
ret = Dotnet(publishArgs, demoRoot);
if (ret != 0) {
output.WriteLine("publish failed");
Assert.True(false);
return;
}
}
protected void AddLinkerReference(string csproj)
{
var xdoc = XDocument.Load(csproj);
var ns = xdoc.Root.GetDefaultNamespace();
bool added = false;
foreach (var el in xdoc.Root.Elements(ns + "ItemGroup")) {
if (el.Elements(ns + "PackageReference").Any()) {
el.Add(new XElement(ns+"PackageReference",
new XAttribute("Include", context.TasksPackageName),
new XAttribute("Version", context.TasksPackageVersion)));
added = true;
break;
}
}
if (!added) {
xdoc.Root.Add(new XElement(ns + "ItemGroup",
new XElement(ns + "PackageReference",
new XAttribute("Include", context.TasksPackageName),
new XAttribute("Version", context.TasksPackageVersion))));
added= true;
}
using (var fs = new FileStream(csproj, FileMode.Create)) {
xdoc.Save(fs);
}
}
static void AddLinkerRoots(string csproj, List<string> rootFiles)
{
var xdoc = XDocument.Load(csproj);
var ns = xdoc.Root.GetDefaultNamespace();
var rootsItemGroup = new XElement(ns+"ItemGroup");
foreach (var rootFile in rootFiles) {
rootsItemGroup.Add(new XElement(ns+"LinkerRootFiles",
new XAttribute("Include", rootFile)));
}
var propertyGroup = xdoc.Root.Elements(ns + "PropertyGroup").First();
propertyGroup.AddAfterSelf(rootsItemGroup);
using (var fs = new FileStream(csproj, FileMode.Create)) {
xdoc.Save(fs);
}
}
}
}

View File

@@ -0,0 +1,20 @@
<linker>
<!--- Called by Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions:.cctor -->
<assembly fullname="System.Linq.Queryable">
<type fullname="System.Linq.Queryable" required="true" />
</assembly>
<!--- Called by System.Linq.Expressions.Expression:CreateLambda -->
<assembly fullname="System.Linq.Expressions">
<type fullname="System.Linq.Expressions.Expression`1" required="true" />
</assembly>
<assembly fullname="System.Dynamic.Runtime">
<!--- Called by [System.Dynamic.Runtime]System.Runtime.CompilerServices.CallSite<>.CreateCustomNoMatchDelegate and [System.Dynamic.Runtime]System.Runtime.CompilerServices.CallSite<>.CreateCustomUpdateDelegate-->
<type fullname="System.Runtime.CompilerServices.CallSiteOps" required="true" />
<!--- Called by [System.Dynamic.Runtime]System.Runtime.CompilerServices.CallSiteBinder.Stitch -->
<type fullname="System.Runtime.CompilerServices.CallSite" required="true" />
<!--- Called by [System.Dynamic.Runtime]System.Runtime.CompilerServices.CallSiteBinder.Stitch -->
<type fullname="System.Runtime.CompilerServices.CallSite`1" required="true" />
</assembly>
</linker>

View File

@@ -0,0 +1,94 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Xunit;
using Xunit.Abstractions; // ITestOutputHelper
namespace ILLink.Tests
{
public class MusicStoreTest : IntegrationTestBase
{
public MusicStoreTest(ITestOutputHelper output) : base(output) {}
private static List<string> rootFiles = new List<string> { "MusicStoreReflection.xml" };
[Fact]
public void RunMusicStore()
{
string csproj = SetupProject();
// Copy root files into the project directory
string demoRoot= Path.GetDirectoryName(csproj);
CopyRootFiles(demoRoot);
// This is necessary because JitBench comes with a
// NuGet.Config that has a <clear /> line, preventing
// NuGet.Config sources defined in outer directories from
// applying.
string nugetConfig = Path.Combine("JitBench", "NuGet.config");
AddLocalNugetFeedAfterClear(nugetConfig);
AddLinkerReference(csproj);
BuildAndLink(csproj, rootFiles);
}
// returns path to .csproj project file
string SetupProject()
{
string gitRepo = "http://github.com/aspnet/JitBench";
string repoName = "JitBench";
string gitBranch = "dev";
string demoRoot = Path.Combine("JitBench", Path.Combine("src", "MusicStore"));
int ret;
if (Directory.Exists(repoName)) {
Directory.Delete(repoName, true);
}
ret = RunCommand("git", $"clone {gitRepo}", null, null);
if (ret != 0) {
output.WriteLine("git failed");
Assert.True(false);
}
if (!Directory.Exists(demoRoot)) {
output.WriteLine($"{demoRoot} does not exist");
Assert.True(false);
}
ret = RunCommand("git", $"checkout {gitBranch}", demoRoot, null);
if (ret != 0) {
output.WriteLine($"problem checking out branch {gitBranch}");
Assert.True(false);
}
string csproj = Path.Combine(demoRoot, "MusicStore.csproj");
return csproj;
}
static void CopyRootFiles(string demoRoot)
{
foreach (var rf in rootFiles) {
File.Copy(rf, Path.Combine(demoRoot, rf));
}
}
private void AddLocalNugetFeedAfterClear(string nugetConfig)
{
string localPackagePath = Path.GetFullPath(context.PackageSource);
var xdoc = XDocument.Load(nugetConfig);
var ns = xdoc.Root.GetDefaultNamespace();
var clear = xdoc.Root.Element(ns+"packageSources").Element(ns+"clear");
clear.Parent.Add(new XElement(ns+"add",
new XAttribute("key", "local linker feed"),
new XAttribute("value", localPackagePath)));
using (var fs = new FileStream(nugetConfig, FileMode.Create)) {
xdoc.Save(fs);
}
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<!-- When the tests are run from the repo's test bin directory,
this NuGet.config should be read to find the local package
directory. -->
<add key="local linker packages" value="../nupkgs" />
</packageSources>
</configuration>

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