Imported Upstream version 5.12.0.220

Former-commit-id: c477e03582759447177c6d4bf412cd2355aad476
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-04-24 09:31:23 +00:00
parent 8bd104cef2
commit 8fc30896db
1200 changed files with 29534 additions and 26161 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Mono.Linker;
using Mono.Linker.Steps;
@@ -8,17 +9,41 @@ namespace ILLink.CustomSteps
{
public class ClearInitLocalsStep : BaseStep
{
protected override void ProcessAssembly(AssemblyDefinition assembly)
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) {
method.Body.InitLocals = false;
if (method.Body.InitLocals) {
method.Body.InitLocals = false;
changed = true;
}
}
}
}
}
if (changed && (Annotations.GetAction (assembly) == AssemblyAction.Copy))
Annotations.SetAction (assembly, AssemblyAction.Save);
}
}
}

View File

@@ -45,6 +45,12 @@
<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

@@ -17,13 +17,13 @@ namespace ILLink.Tasks
/// dependencies.
/// </summary>
[Required]
public ITaskItem[] ManagedAssemblyPaths { get; set; }
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..
/// aren't found to be referenced by a managed assembly.
/// </summary>
public ITaskItem[] NativeDepsToKeep { get; set; }
public ITaskItem [] NativeDepsToKeep { get; set; }
/// <summary>
/// The paths to the available native dependencies. We
@@ -31,7 +31,7 @@ namespace ILLink.Tasks
/// native files.
/// </summary>
[Required]
public ITaskItem[] NativeDepsPaths { get; set; }
public ITaskItem [] NativeDepsPaths { get; set; }
/// <summary>
/// The set of native dependencies to keep, including those
@@ -40,67 +40,51 @@ namespace ILLink.Tasks
/// input NativeDepsToKeep.
/// </summary>
[Output]
public ITaskItem[] KeptNativeDepsPaths { get; set; }
public ITaskItem [] KeptNativeDepsPaths { get; set; }
public override bool Execute()
public override bool Execute ()
{
var allNative = new Dictionary<string, ITaskItem> ();
foreach (var n in NativeDepsPaths)
{
var fileName = Path.GetFileName(n.ItemSpec);
if (!allNative.ContainsKey(fileName))
{
allNative.Add(fileName, n);
}
}
var keptNative = new List<ITaskItem> ();
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 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 moduleRefCandidates = new[] { moduleName, moduleName + ".dll", moduleName + ".so", moduleName + ".dylib" };
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);
ITaskItem referencedNativeFile = null;
foreach (string moduleRefCandidate in moduleRefCandidates)
{
if (allNative.TryGetValue (moduleRefCandidate, out referencedNativeFile))
{
break;
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 (referencedNativeFile != null)
{
keptNative.Add(referencedNativeFile);
}
else
{
// DLLImport that wasn't satisfied
Log.LogMessage(MessageImportance.High, "unsatisfied DLLImport: " + managedAssembly + " -> " + moduleName);
}
if (!foundModuleRef)
Log.LogMessage("unsatisfied DLLImport: " + managedAssembly + " -> " + moduleName);
}
}
}
}
foreach (var n in NativeDepsToKeep)
{
ITaskItem nativeFile = null;
if (allNative.TryGetValue (n.ItemSpec, out nativeFile))
{
keptNative.Add(nativeFile);
}
var keptNativeDeps = new List<ITaskItem> ();
foreach (var nativeDep in NativeDepsPaths) {
var fileName = Path.GetFileName (nativeDep.ItemSpec);
if (keptNativeNames.Contains (fileName))
keptNativeDeps.Add (nativeDep);
}
KeptNativeDepsPaths = keptNative.ToArray();
KeptNativeDepsPaths = keptNativeDeps.ToArray ();
return true;
}
}

View File

@@ -97,6 +97,7 @@
</Target>
<ItemGroup>
<Compile Include="AdapterLogger.cs" />
<Compile Include="LinkTask.cs" />
<Compile Include="CompareSizes.cs" />
<Compile Include="ComputeManagedAssemblies.cs" />
@@ -195,7 +196,7 @@
<Target Name="SetCecilConfiguration"
AfterTargets="AssignProjectConfiguration">
<ItemGroup>
<ProjectReferenceWithConfiguration Condition=" '%(Filename)%(Extension)' == 'Mono.Cecil.csproj' Or '%(Filename)%(Extension)' == 'Mono.Cecil.Pdb.csproj' ">
<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>

View File

@@ -18,10 +18,15 @@
<LinkerDumpDependencies Condition=" '$(LinkerDumpDependencies)' == '' ">false</LinkerDumpDependencies>
<UsedApplicationAssemblyAction Condition=" '$(UsedApplicationAssemblyAction)' == '' ">Copy</UsedApplicationAssemblyAction>
<UnusedApplicationAssemblyAction Condition=" '$(UnusedApplicationAssemblyAction)' == '' ">Delete</UnusedApplicationAssemblyAction>
<UsedPlatformAssemblyAction Condition=" '$(UsedPlatformAssemblyAction)' == '' ">Link</UsedPlatformAssemblyAction>
<UnusedPlatformAssemblyAction Condition=" '$(UnusedPlatformAssemblyAction)' == '' ">Delete</UnusedPlatformAssemblyAction>
<UsedPlatformAssemblyAction Condition=" '$(UsedPlatformAssemblyAction)' == '' and '$(SelfContained)' == 'true' ">AddBypassNGen</UsedPlatformAssemblyAction>
<UsedPlatformAssemblyAction Condition=" '$(UsedPlatformAssemblyAction)' == '' ">Skip</UsedPlatformAssemblyAction>
<UnusedPlatformAssemblyAction Condition=" '$(UnusedPlatformAssemblyAction)' == '' and '$(SelfContained)' == 'true' ">Delete</UnusedPlatformAssemblyAction>
<UnusedPlatformAssemblyAction Condition=" '$(UnusedPlatformAssemblyAction)' == '' ">Skip</UnusedPlatformAssemblyAction>
<RootAllApplicationAssemblies Condition=" '$(RootAllApplicationAssemblies)' == '' ">true</RootAllApplicationAssemblies>
<RootAllApplicationAssemblies Condition=" '$(RootAllApplicationAssemblies)' != 'true' ">false</RootAllApplicationAssemblies>
<LinkerTrimNativeDeps Condition=" '$(LinkerTrimNativeDeps)' == '' ">true</LinkerTrimNativeDeps>
<LinkerTrimNativeDeps Condition=" '$(LinkerTrimNativeDeps)' != 'true' ">false</LinkerTrimNativeDeps>
<ClearInitLocals Condition=" '$(ClearInitLocals)' == '' ">false</ClearInitLocals>
</PropertyGroup>
<!-- This depends on LinkDuringPublish, so it needs to be imported
@@ -95,7 +100,7 @@
<ResolvedAssembliesToPublish Include="@(_NativeKeptDepsToPublish)" />
<ResolvedAssembliesToPublish Include="@(_LinkedResolvedAssemblies)" />
</ItemGroup>
<!-- Rewrite IntermediateAssembly, which is an input to
ComputeFilesToPublish. -->
<ItemGroup>
@@ -110,7 +115,7 @@
<_DebugSymbolsIntermediatePath Include="@(_LinkedDebugSymbols)" Condition=" '$(_DebugSymbolsProduced)' == 'true' " />
</ItemGroup>
</Target>
<!-- The SDK has a target called ComputeRefAssembliesToPublish that
runs after ComputeFilesToPublish and rewrites
ResolvedFileToPublish to include any reference assemblies that
@@ -203,7 +208,7 @@
<_ManagedAssembliesToLink Include="@(_ManagedAssembliesToLinkWithActions)" />
</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
@@ -218,13 +223,15 @@
the future we will want to generate these depending on the
scenario in which the linker is invoked. -->
<PropertyGroup>
<ExtraLinkerArgs Condition=" '$(ExtraLinkerArgs)' == '' ">-t -l none -b true</ExtraLinkerArgs>
<ExtraLinkerArgs Condition=" '$(ExtraLinkerArgs)' == '' ">-t -l none -b true --skip-unresolved true --verbose</ExtraLinkerArgs>
</PropertyGroup>
<ILLink AssemblyPaths="@(_ManagedAssembliesToLink)"
RootAssemblyNames="@(LinkerRootAssemblies)"
RootDescriptorFiles="@(LinkerRootDescriptors)"
OutputDirectory="$(IntermediateLinkDir)"
DumpDependencies="$(LinkerDumpDependencies)"
ClearInitLocals="$(ClearInitLocals)"
ClearInitLocalsAssemblies="$(ClearInitLocalsAssemblies)"
ExtraArgs="$(ExtraLinkerArgs)" />
<Touch Files="$(_LinkSemaphore)" AlwaysCreate="true">
@@ -241,6 +248,7 @@
<ItemGroup>
<_NativeResolvedDepsToPublish Include="@(ResolvedAssembliesToPublish)" />
<_NativeResolvedDepsToPublish Remove="@(_ManagedResolvedAssembliesToPublish)" />
<_NativeResolvedDepsToPublish Remove="@(_NativeResolvedDepsToPublish->WithMetadataValue('AssetType', 'resources'))" />
</ItemGroup>
<ItemGroup>
@@ -255,11 +263,15 @@
<_NativeDepsToAlwaysKeep Include="hostpolicy.dll;libhostpolicy.dylib;libhostpolicy.so" />
</ItemGroup>
<FindNativeDeps ManagedAssemblyPaths="@(_ManagedLinkedAssemblies)"
<FindNativeDeps Condition=" '$(LinkerTrimNativeDeps)' == 'true' "
ManagedAssemblyPaths="@(_ManagedLinkedAssemblies)"
NativeDepsPaths="@(_NativeResolvedDepsToPublish)"
NativeDepsToKeep="@(_NativeDepsToAlwaysKeep)">
<Output TaskParameter="KeptNativeDepsPaths" ItemName="_NativeKeptDepsToPublish" />
</FindNativeDeps>
<PropertyGroup>
<_NativeKeptDepsToPublish Condition=" '$(LinkerTrimNativeDeps)' != 'true' ">"@(_NativeResolvedDepsToPublish)"</_NativeKeptDepsToPublish>
</PropertyGroup>
</Target>
@@ -272,6 +284,26 @@
<_ManagedAssembliesToLink Include="@(IntermediateAssembly)" />
<_ManagedAssembliesToLink Include="@(_ManagedResolvedAssembliesToPublish)" />
</ItemGroup>
<!-- Portable publish: need to supply the linker with any needed
reference assemblies as well.
Sometimes assemblies have separate reference and
implementation assemblies. In these cases, prefer the
implementation assembly, and prevent inclusion of the ref
assembly by filtering on filename instead of the full
path. -->
<FilterByMetadata Items="@(ReferencePath->'%(ResolvedPath)')"
MetadataName="Filename"
MetadataValues="@(_ManagedAssembliesToLink->'%(Filename)')"
Condition=" '$(SelfContained)' != 'true' ">
<Output TaskParameter="FilteredItems" ItemName="_ReferencedLibrariesToExclude" />
</FilterByMetadata>
<ItemGroup Condition=" '$(SelfContained)' != 'true' ">
<_ReferencedLibraries Include="@(ReferencePath->'%(ResolvedPath)')" Exclude="@(_ReferencedLibrariesToExclude)" />
<_ManagedAssembliesToLink Include="@(_ReferencedLibraries)" />
</ItemGroup>
</Target>
@@ -298,7 +330,7 @@
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
@@ -330,12 +362,13 @@
to work on the publish output. -->
<Target Name="_ComputeLinkerRootAssemblies"
DependsOnTargets="_ComputeManagedResolvedAssembliesToPublish;_ComputePlatformLibraries;_CheckSystemPrivateCorelibEmbeddedRoots">
<!-- If RootAllApplicationAssemblies is true, roots are everything minus the framework
assemblies. This doesn't include the intermediate assembly,
because we root it separately using an xml file,
which lets us explicitly root everything.
<!-- If RootAllApplicationAssemblies is true, roots are everything
minus the framework assemblies. This doesn't include the
intermediate assembly, because we root it separately using an
xml file, which lets us explicitly root everything.
If RootAllApplicationAssemblies is false, only intermediate assembly's Main method is rooted.
If RootAllApplicationAssemblies is false, only intermediate
assembly's Main method is rooted.
System.Private.CoreLib is rooted unless it has an embedded
xml descriptor file. -->
@@ -354,7 +387,7 @@
</Target>
<UsingTask TaskName="CheckEmbeddedRootDescriptor" AssemblyFile="$(LinkTaskDllPath)" />
<Target Name="_CheckSystemPrivateCorelibEmbeddedRoots"
<Target Name="_CheckSystemPrivateCorelibEmbeddedRoots" Condition=" '$(SelfContained)' == 'true' "
DependsOnTargets="_ComputeManagedAssembliesToLink">
<CheckEmbeddedRootDescriptor AssemblyPath="@(_ManagedAssembliesToLink->WithMetadataValue('Filename', 'System.Private.CoreLib'))">
<Output TaskParameter="HasEmbeddedRootDescriptor" PropertyName="_SPCHasEmbeddedRootDescriptor" />
@@ -364,13 +397,19 @@
<!-- 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)"
<Target Name="_ComputePlatformLibraries"
DependsOnTargets="_ComputeManagedAssembliesToLink">
<GetRuntimeLibraries Condition=" '$(SelfContained)' == 'true' "
AssetsFilePath="$(ProjectAssetsFile)"
TargetFramework="$(TargetFrameworkMoniker)"
RuntimeIdentifier="$(RuntimeIdentifier)"
PackageNames="$(MicrosoftNETPlatformLibrary)">
<Output TaskParameter="RuntimeLibraries" ItemName="PlatformLibraries" />
</GetRuntimeLibraries>
<ItemGroup Condition=" '$(SelfContained)' != 'true' ">
<!-- Portable publish: computed referenced-not-published set in _ComputeManagedAssembliesToLink -->
<PlatformLibraries Include="@(_ReferencedLibraries)" />
</ItemGroup>
</Target>
@@ -416,7 +455,7 @@
<ItemGroup>
<_RemovedNativeDeps Include="@(_NativeResolvedDepsToPublish)" />
<_RemovedNativeDeps Remove="@(_NativeKeptDepsToPublish)" />
<_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" />
<_PublishConflictPackageFiles Include="@(_RemovedNativeDeps)" />
</ItemGroup>

View File

@@ -46,6 +46,17 @@ namespace ILLink.Tasks
/// </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>
@@ -61,7 +72,8 @@ namespace ILLink.Tasks
string [] args = GenerateCommandLineCommands ();
var argsString = String.Join (" ", args);
Log.LogMessageFromText ($"illink {argsString}", MessageImportance.Normal);
int ret = Mono.Linker.Driver.Main (args);
var logger = new AdapterLogger (Log);
int ret = Mono.Linker.Driver.Execute (args, logger);
return ret == 0;
}
@@ -104,6 +116,17 @@ namespace ILLink.Tasks
args.Add (OutputDirectory.ItemSpec);
}
if (ClearInitLocals) {
args.Add ("-s");
// Version of ILLink.CustomSteps is passed as a workaround for msbuild issue #3016
args.Add ("ILLink.CustomSteps.ClearInitLocalsStep,ILLink.CustomSteps,Version=0.0.0.0:OutputStep");
if ((ClearInitLocalsAssemblies != null) && (ClearInitLocalsAssemblies.Length > 0)) {
args.Add ("-m");
args.Add ("ClearInitLocalsAssemblies");
args.Add (ClearInitLocalsAssemblies);
}
}
if (ExtraArgs != null) {
args.AddRange (ExtraArgs.Split (' '));
}

View File

@@ -0,0 +1,146 @@
using System;
using System.Diagnostics;
using System.Text;
using Xunit;
using Xunit.Abstractions;
namespace ILLink.Tests
{
public class CommandRunner
{
protected readonly ITestOutputHelper outputHelper;
private string command;
private string args;
private string workingDir;
private string additionalPath;
private int timeout = Int32.MaxValue;
private string terminatingOutput;
public CommandRunner(string command, ITestOutputHelper outputHelper) {
this.command = command;
this.outputHelper = outputHelper;
}
public CommandRunner WithArguments(string args) {
this.args = args;
return this;
}
public CommandRunner WithWorkingDir(string workingDir) {
this.workingDir = workingDir;
return this;
}
public CommandRunner WithAdditionalPath(string additionalPath) {
this.additionalPath = additionalPath;
return this;
}
public CommandRunner WithTimeout(int timeout) {
this.timeout = timeout;
return this;
}
public CommandRunner WithTerminatingOutput(string terminatingOutput) {
this.terminatingOutput = terminatingOutput;
return this;
}
public int Run()
{
return Run(out string commandOutputUnused);
}
public int Run(out string commandOutput)
{
if (String.IsNullOrEmpty(command)) {
throw new Exception("No command was specified specified.");
}
if (outputHelper == null) {
throw new Exception("No output helper present.");
}
var psi = new ProcessStartInfo
{
FileName = command,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
outputHelper.WriteLine($"caller working directory: {Environment.CurrentDirectory}");
if (!String.IsNullOrEmpty(args)) {
psi.Arguments = args;
outputHelper.WriteLine($"{command} {args}");
} else {
outputHelper.WriteLine($"{command}");
}
if (!String.IsNullOrEmpty(workingDir)) {
outputHelper.WriteLine($"working directory: {workingDir}");
psi.WorkingDirectory = workingDir;
}
if (!String.IsNullOrEmpty(additionalPath)) {
string path = psi.Environment["PATH"];
psi.Environment["PATH"] = path + ";" + additionalPath;
}
var process = new Process();
process.StartInfo = psi;
// dotnet sets some environment variables that
// may cause problems in the child process.
psi.Environment.Remove("MSBuildExtensionsPath");
psi.Environment.Remove("MSBuildLoadMicrosoftTargetsReadOnly");
psi.Environment.Remove("MSBuildSDKsPath");
psi.Environment.Remove("VbcToolExe");
psi.Environment.Remove("CscToolExe");
psi.Environment.Remove("MSBUILD_EXE_PATH");
outputHelper.WriteLine("environment:");
foreach (var item in psi.Environment) {
outputHelper.WriteLine($"\t{item.Key}={item.Value}");
}
StringBuilder processOutput = new StringBuilder();
DataReceivedEventHandler handler = (sender, e) => {
processOutput.Append(e.Data);
processOutput.AppendLine();
};
StringBuilder processError = new StringBuilder();
DataReceivedEventHandler ehandler = (sender, e) => {
processError.Append(e.Data);
processError.AppendLine();
};
process.OutputDataReceived += handler;
process.ErrorDataReceived += ehandler;
// terminate process if output contains specified string
if (!String.IsNullOrEmpty(terminatingOutput)) {
DataReceivedEventHandler terminatingOutputHandler = (sender, e) => {
if (!String.IsNullOrEmpty(e.Data) && e.Data.Contains(terminatingOutput)) {
process.Kill();
}
};
process.OutputDataReceived += terminatingOutputHandler;
}
// start the process
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (!process.WaitForExit(timeout)) {
outputHelper.WriteLine($"killing process after {timeout} ms");
process.Kill();
}
// WaitForExit with timeout doesn't guarantee
// that the async output handlers have been
// called, so WaitForExit needs to be called
// afterwards.
process.WaitForExit();
string processOutputStr = processOutput.ToString();
string processErrorStr = processError.ToString();
outputHelper.WriteLine(processOutputStr);
outputHelper.WriteLine(processErrorStr);
commandOutput = processOutputStr;
return process.ExitCode;
}
}
}

View File

@@ -7,11 +7,21 @@ namespace ILLink.Tests
{
public class HelloWorldTest : IntegrationTestBase
{
public HelloWorldTest(ITestOutputHelper output) : base(output) {}
private string csproj;
public HelloWorldTest(ITestOutputHelper output) : base(output) {
csproj = SetupProject();
}
public string SetupProject()
{
string projectRoot = "helloworld";
string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
if (File.Exists(csproj)) {
output.WriteLine($"using existing project {csproj}");
return csproj;
}
if (Directory.Exists(projectRoot)) {
Directory.Delete(projectRoot, true);
@@ -24,20 +34,28 @@ namespace ILLink.Tests
Assert.True(false);
}
string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
AddLinkerReference(csproj);
return csproj;
}
[Fact]
public void RunHelloWorld()
public void RunHelloWorldStandalone()
{
string csproj = SetupProject();
string executablePath = BuildAndLink(csproj, selfContained: true);
CheckOutput(executablePath, selfContained: true);
}
AddLinkerReference(csproj);
[Fact]
public void RunHelloWorldPortable()
{
string target = BuildAndLink(csproj, selfContained: false);
CheckOutput(target, selfContained: false);
}
BuildAndLink(csproj, null);
int ret = RunApp(csproj, out string commandOutput);
void CheckOutput(string target, bool selfContained = false)
{
int ret = RunApp(target, out string commandOutput, selfContained: selfContained);
Assert.True(ret == 0);
Assert.True(commandOutput.Contains("Hello World!"));
}

View File

@@ -30,7 +30,7 @@ namespace ILLink.Tests
protected int Dotnet(string args, string workingDir, string additionalPath = null)
{
return RunCommand(Path.GetFullPath(context.DotnetToolPath), args,
workingDir, additionalPath, out string commandOutput);
workingDir, additionalPath, out string commandOutput);
}
protected int RunCommand(string command, string args, int timeout = Int32.MaxValue)
@@ -43,71 +43,16 @@ namespace ILLink.Tests
return RunCommand(command, args, workingDir, null, out string commandOutput);
}
protected int RunCommand(string command, string args, string workingDir, string additionalPath, out string commandOutput, int timeout = Int32.MaxValue)
protected int RunCommand(string command, string args, string workingDir, string additionalPath,
out string commandOutput, int timeout = Int32.MaxValue, string terminatingOutput = 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();
process.StartInfo = psi;
// dotnet sets some environment variables that
// may cause problems in the child process.
psi.Environment.Remove("MSBuildExtensionsPath");
psi.Environment.Remove("MSBuildLoadMicrosoftTargetsReadOnly");
psi.Environment.Remove("MSBuildSDKsPath");
psi.Environment.Remove("VbcToolExe");
psi.Environment.Remove("CscToolExe");
psi.Environment.Remove("MSBUILD_EXE_PATH");
StringBuilder processOutput = new StringBuilder();
DataReceivedEventHandler handler = (sender, e) => {
processOutput.Append(e.Data);
processOutput.AppendLine();
};
StringBuilder processError = new StringBuilder();
DataReceivedEventHandler ehandler = (sender, e) => {
processError.Append(e.Data);
processError.AppendLine();
};
process.OutputDataReceived += handler;
process.ErrorDataReceived += ehandler;
output.WriteLine("environment:");
foreach (var item in psi.Environment) {
output.WriteLine($"\t{item.Key}={item.Value}");
}
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (!process.WaitForExit(timeout)) {
output.WriteLine($"killing process after {timeout} ms");
process.Kill();
}
// WaitForExit with timeout doesn't guarantee
// that the async output handlers have been
// called, so WaitForExit needs to be called
// afterwards.
process.WaitForExit();
string processOutputStr = processOutput.ToString();
string processErrorStr = processError.ToString();
output.WriteLine(processOutputStr);
output.WriteLine(processErrorStr);
commandOutput = processOutputStr;
return process.ExitCode;
return (new CommandRunner(command, output))
.WithArguments(args)
.WithWorkingDir(workingDir)
.WithAdditionalPath(additionalPath)
.WithTimeout(timeout)
.WithTerminatingOutput(terminatingOutput)
.Run(out commandOutput);
}
/// <summary>
@@ -115,14 +60,18 @@ namespace ILLink.Tests
/// that the project already contains a reference to the
/// linker task package.
/// Optionally takes a list of root descriptor files.
/// Returns the path to the built app, either the renamed
/// host for self-contained publish, or the dll containing
/// the entry point.
/// </summary>
public void BuildAndLink(string csproj, List<string> rootFiles = null, Dictionary<string, string> extraPublishArgs = null)
public string BuildAndLink(string csproj, List<string> rootFiles = null, Dictionary<string, string> extraPublishArgs = null, bool selfContained = false)
{
string rid = context.RuntimeIdentifier;
string config = context.Configuration;
string demoRoot = Path.GetDirectoryName(csproj);
string publishArgs = $"publish -r {rid} -c {config} /v:n /p:ShowLinkerSizeComparison=true";
string publishArgs = $"publish -c {context.Configuration} /v:n /p:ShowLinkerSizeComparison=true";
if (selfContained) {
publishArgs += $" -r {context.RuntimeIdentifier}";
}
string rootFilesStr;
if (rootFiles != null && rootFiles.Any()) {
rootFilesStr = String.Join(";", rootFiles);
@@ -138,28 +87,45 @@ namespace ILLink.Tests
if (ret != 0) {
output.WriteLine("publish failed, returning " + ret);
Assert.True(false);
return;
}
}
public int RunApp(string csproj, out string processOutput, int timeout = Int32.MaxValue)
{
string demoRoot = Path.GetDirectoryName(csproj);
// detect the target framework for which the app was published
string tfmDir = Path.Combine(demoRoot, "bin", context.Configuration);
string tfm = Directory.GetDirectories(tfmDir).Select(p => Path.GetFileName(p)).Single();
string executablePath = Path.Combine(tfmDir, tfm,
context.RuntimeIdentifier, "publish",
Path.GetFileNameWithoutExtension(csproj)
);
if (context.RuntimeIdentifier.Contains("win")) {
executablePath += ".exe";
string builtApp = Path.Combine(tfmDir, tfm);
if (selfContained) {
builtApp = Path.Combine(builtApp, context.RuntimeIdentifier);
}
Assert.True(File.Exists(executablePath));
builtApp = Path.Combine(builtApp, "publish",
Path.GetFileNameWithoutExtension(csproj));
if (selfContained) {
if (context.RuntimeIdentifier.Contains("win")) {
builtApp += ".exe";
}
} else {
builtApp += ".dll";
}
Assert.True(File.Exists(builtApp));
return builtApp;
}
int ret = RunCommand(executablePath, null,
Directory.GetParent(executablePath).FullName,
null, out processOutput, timeout);
public int RunApp(string target, out string processOutput, int timeout = Int32.MaxValue,
string terminatingOutput = null, bool selfContained = false)
{
Assert.True(File.Exists(target));
int ret;
if (selfContained) {
ret = RunCommand(
target, null,
Directory.GetParent(target).FullName,
null, out processOutput, timeout, terminatingOutput);
} else {
ret = RunCommand(
Path.GetFullPath(context.DotnetToolPath),
Path.GetFullPath(target),
Directory.GetParent(target).FullName,
null, out processOutput, timeout, terminatingOutput);
}
return ret;
}

View File

@@ -10,8 +10,6 @@ namespace ILLink.Tests
{
public class MusicStoreTest : IntegrationTestBase
{
public MusicStoreTest(ITestOutputHelper output) : base(output) {}
private static List<string> rootFiles = new List<string> { "MusicStoreReflection.xml" };
private static string gitRepo = "http://github.com/aspnet/JitBench";
@@ -34,40 +32,140 @@ namespace ILLink.Tests
// The version of Microsoft.AspNetCore.All to publish with.
private static string aspNetVersion = "2.1.0-preview1-27654";
[Fact]
public void RunMusicStore()
private static Dictionary<string, string> versionPublishArgs;
private static Dictionary<string, string> VersionPublishArgs
{
string csproj = SetupProject();
get {
if (versionPublishArgs != null) {
return versionPublishArgs;
}
versionPublishArgs = new Dictionary<string, string>();
versionPublishArgs.Add("JITBENCH_FRAMEWORK_VERSION", runtimeVersion);
versionPublishArgs.Add("JITBENCH_ASPNET_VERSION", aspNetVersion);
return versionPublishArgs;
}
}
// Copy root files into the project directory
string demoRoot= Path.GetDirectoryName(csproj);
CopyRootFiles(demoRoot);
private static string csproj;
// 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);
public MusicStoreTest(ITestOutputHelper output) : base(output) {
csproj = SetupProject();
AddLinkerReference(csproj);
// MusicStore targets .NET Core 2.1, so it must be built
// using an SDK that can target 2.1. We obtain that SDK
// here.
context.DotnetToolPath = ObtainSDK(context.TestBin, repoName);
}
Dictionary<string, string> extraPublishArgs = new Dictionary<string, string>();
extraPublishArgs.Add("JITBENCH_FRAMEWORK_VERSION", runtimeVersion);
extraPublishArgs.Add("JITBENCH_ASPNET_VERSION", aspNetVersion);
BuildAndLink(csproj, rootFiles, extraPublishArgs);
[Fact]
public void RunMusicStoreStandalone()
{
string executablePath = BuildAndLink(csproj, rootFiles, VersionPublishArgs, selfContained: true);
CheckOutput(executablePath, selfContained: true);
}
[Fact]
public void RunMusicStorePortable()
{
Dictionary<string, string> extraPublishArgs = new Dictionary<string, string>(VersionPublishArgs);
extraPublishArgs.Add("PublishWithAspNetCoreTargetManifest", "false");
string target = BuildAndLink(csproj, null, extraPublishArgs, selfContained: false);
CheckOutput(target, selfContained: false);
}
void CheckOutput(string target, bool selfContained = false)
{
int ret = RunApp(target, out string commandOutput, selfContained: selfContained);
int ret = RunApp(csproj, out string commandOutput);
Assert.True(commandOutput.Contains("Starting request to http://localhost:5000"));
Assert.True(commandOutput.Contains("Response: OK"));
Assert.True(commandOutput.Contains("Running 100 requests"));
Assert.True(ret == 0);
}
// returns path to .csproj project file
string SetupProject()
{
int ret;
string demoRoot = Path.Combine(repoName, Path.Combine("src", "MusicStore"));
string csproj = Path.Combine(demoRoot, "MusicStore.csproj");
if (File.Exists(csproj)) {
output.WriteLine($"using existing project {csproj}");
return csproj;
}
if (Directory.Exists(repoName)) {
Directory.Delete(repoName, true);
}
ret = RunCommand("git", $"clone {gitRepo} {repoName}");
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 {gitRevision}", demoRoot);
if (ret != 0) {
output.WriteLine($"problem checking out revision {gitRevision}");
Assert.True(false);
}
// Copy root files into the project directory
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(repoName, "NuGet.config");
AddLocalNugetFeedAfterClear(nugetConfig);
AddLinkerReference(csproj);
AddGlobalJson(repoName);
return csproj;
}
void AddGlobalJson(string repoDir)
{
string globalJson = Path.Combine(repoDir, "global.json");
string globalJsonContents = "{ \"sdk\": { \"version\": \"" + sdkVersion + "\" } }\n";
File.WriteAllText(globalJson, globalJsonContents);
}
string GetDotnetToolPath(string dotnetDir)
{
string dotnetToolName = Directory.GetFiles(dotnetDir)
.Select(p => Path.GetFileName(p))
.Where(p => p.Contains("dotnet"))
.Single();
string dotnetToolPath = Path.Combine(dotnetDir, dotnetToolName);
if (!File.Exists(dotnetToolPath)) {
output.WriteLine("repo-local dotnet tool does not exist.");
Assert.True(false);
}
return dotnetToolPath;
}
string ObtainSDK(string rootDir, string repoDir)
{
int ret;
string dotnetDirName = ".dotnet";
string dotnetDir = Path.Combine(rootDir, dotnetDirName);
if (Directory.Exists(dotnetDir)) {
return GetDotnetToolPath(dotnetDir);
}
string dotnetInstall = Path.Combine(Path.GetFullPath(repoDir), "dotnet-install");
if (context.RuntimeIdentifier.Contains("win")) {
dotnetInstall += ".ps1";
@@ -103,57 +201,7 @@ namespace ILLink.Tests
}
}
string dotnetDir = Path.Combine(rootDir, dotnetDirName);
string dotnetToolName = Directory.GetFiles(dotnetDir)
.Select(p => Path.GetFileName(p))
.Where(p => p.Contains("dotnet"))
.Single();
string dotnetToolPath = Path.Combine(dotnetDir, dotnetToolName);
if (!File.Exists(dotnetToolPath)) {
output.WriteLine("repo-local dotnet tool does not exist.");
Assert.True(false);
}
string globalJson = Path.Combine(repoDir, "global.json");
string globalJsonContents = "{ \"sdk\": { \"version\": \"" + sdkVersion + "\" } }\n";
File.WriteAllText(globalJson, globalJsonContents);
return dotnetToolPath;
}
// returns path to .csproj project file
string SetupProject()
{
int ret;
if (Directory.Exists(repoName)) {
Directory.Delete(repoName, true);
}
ret = RunCommand("git", $"clone {gitRepo}");
if (ret != 0) {
output.WriteLine("git failed");
Assert.True(false);
}
string demoRoot = Path.Combine("JitBench", Path.Combine("src", "MusicStore"));
if (!Directory.Exists(demoRoot)) {
output.WriteLine($"{demoRoot} does not exist");
Assert.True(false);
}
ret = RunCommand("git", $"checkout {gitRevision}", demoRoot);
if (ret != 0) {
output.WriteLine($"problem checking out revision {gitRevision}");
Assert.True(false);
}
// MusicStore targets .NET Core 2.1, so it must be built
// using an SDK that can target 2.1. We obtain that SDK
// here.
context.DotnetToolPath = ObtainSDK(context.TestBin, repoName);
string csproj = Path.Combine(demoRoot, "MusicStore.csproj");
return csproj;
return GetDotnetToolPath(dotnetDir);
}
static void CopyRootFiles(string demoRoot)

View File

@@ -8,11 +8,21 @@ namespace ILLink.Tests
{
public class WebApiTest : IntegrationTestBase
{
public WebApiTest(ITestOutputHelper output) : base(output) {}
private string csproj;
public WebApiTest(ITestOutputHelper output) : base(output) {
csproj = SetupProject();
}
public string SetupProject()
{
string projectRoot = "webapi";
string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
if (File.Exists(csproj)) {
output.WriteLine($"using existing project {csproj}");
return csproj;
}
if (Directory.Exists(projectRoot)) {
Directory.Delete(projectRoot, true);
@@ -25,7 +35,10 @@ namespace ILLink.Tests
Assert.True(false);
}
string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
PreventPublishFiltering(csproj);
AddLinkerReference(csproj);
return csproj;
}
@@ -49,19 +62,25 @@ namespace ILLink.Tests
}
[Fact]
public void RunWebApi()
public void RunWebApiStandalone()
{
string csproj = SetupProject();
string executablePath = BuildAndLink(csproj, selfContained: true);
CheckOutput(executablePath, selfContained: true);
}
PreventPublishFiltering(csproj);
[Fact]
public void RunWebApiPortable()
{
string target = BuildAndLink(csproj, selfContained: false);
CheckOutput(target, selfContained: false);
}
AddLinkerReference(csproj);
BuildAndLink(csproj);
int ret = RunApp(csproj, out string commandOutput, 10000);
void CheckOutput(string target, bool selfContained = false)
{
string terminatingOutput = "Now listening on: http://localhost:5000";
int ret = RunApp(target, out string commandOutput, 60000, terminatingOutput, selfContained: selfContained);
Assert.True(commandOutput.Contains("Application started. Press Ctrl+C to shut down."));
Assert.True(commandOutput.Contains("Now listening on: http://localhost:5000"));
Assert.True(commandOutput.Contains(terminatingOutput));
}
}
}

View File

@@ -22,6 +22,7 @@
</Target>
<ItemGroup>
<Compile Include="CommandRunner.cs" />
<Compile Include="IntegrationTestBase.cs" />
<Compile Include="TestContext.cs" />
<Compile Include="MusicStoreTest.cs" />

View File

@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "..\cecil\Mono
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil.Pdb", "..\cecil\symbols\pdb\Mono.Cecil.Pdb.csproj", "{63E6915C-7EA4-4D76-AB28-0D7191EEA626}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil.Mdb", "..\cecil\symbols\mdb\Mono.Cecil.Mdb.csproj", "{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILLink.CustomSteps", "integration\ILLink.CustomSteps\ILLink.CustomSteps.csproj", "{275C1D10-168A-4AC4-8F3E-AD969F580B9C}"
EndProject
Global
@@ -59,6 +61,18 @@ Global
{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
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Debug|Any CPU.ActiveCfg = netstandard_Debug|Any CPU
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Debug|Any CPU.Build.0 = netstandard_Debug|Any CPU
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Debug|x64.ActiveCfg = netstandard_Debug|x64
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Debug|x64.Build.0 = netstandard_Debug|x64
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Debug|x86.ActiveCfg = netstandard_Debug|x86
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Debug|x86.Build.0 = netstandard_Debug|x86
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Release|Any CPU.ActiveCfg = netstandard_Release|Any CPU
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Release|Any CPU.Build.0 = netstandard_Release|Any CPU
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Release|x64.ActiveCfg = netstandard_Release|x64
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Release|x64.Build.0 = netstandard_Release|x64
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Release|x86.ActiveCfg = netstandard_Release|x86
{8559DD7F-A16F-46D0-A05A-9139FAEBA8FD}.Release|x86.Build.0 = netstandard_Release|x86
{275C1D10-168A-4AC4-8F3E-AD969F580B9C}.Debug|Any CPU.ActiveCfg = illink_Debug|Any CPU
{275C1D10-168A-4AC4-8F3E-AD969F580B9C}.Debug|Any CPU.Build.0 = illink_Debug|Any CPU
{275C1D10-168A-4AC4-8F3E-AD969F580B9C}.Debug|x64.ActiveCfg = illink_Debug|x64