Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 40 additions & 13 deletions corebuild/integration/ILLink.Tasks/ILLink.Tasks.targets
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<ResolvedAssembliesToPublish Include="@(_NativeKeptDepsToPublish)" />
<ResolvedAssembliesToPublish Include="@(_LinkedResolvedAssemblies)" />
</ItemGroup>

<!-- Rewrite IntermediateAssembly, which is an input to
ComputeFilesToPublish. -->
<ItemGroup>
Expand All @@ -114,7 +114,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
Expand Down Expand Up @@ -207,7 +207,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
Expand Down Expand Up @@ -281,6 +281,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>


Expand All @@ -307,7 +327,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
Expand Down Expand Up @@ -339,12 +359,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. -->
Expand All @@ -363,7 +384,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" />
Expand All @@ -373,13 +394,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>


Expand Down Expand Up @@ -425,7 +452,7 @@
<ItemGroup>
<_RemovedNativeDeps Include="@(_NativeResolvedDepsToPublish)" />
<_RemovedNativeDeps Remove="@(_NativeKeptDepsToPublish)" />

<_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" />
<_PublishConflictPackageFiles Include="@(_RemovedNativeDeps)" />
</ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion corebuild/integration/test/CommandRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,15 @@ public int Run(out string commandOutput)
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}");
outputHelper.WriteLine($"working directory: {workingDir}");
psi.WorkingDirectory = workingDir;
}
if (!String.IsNullOrEmpty(additionalPath)) {
Expand Down
34 changes: 26 additions & 8 deletions corebuild/integration/test/HelloWorldTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -24,20 +34,28 @@ public string SetupProject()
Assert.True(false);
}

string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
AddLinkerReference(csproj);

return csproj;
}

[Fact]
public void RunHelloWorld()
public void RunHelloWorldStandalone()
{
string csproj = SetupProject();

AddLinkerReference(csproj);
string executablePath = BuildAndLink(csproj, selfContained: true);
CheckOutput(executablePath, selfContained: true);
}

BuildAndLink(csproj, null);
[Fact]
public void RunHelloWorldPortable()
{
string target = BuildAndLink(csproj, selfContained: false);
CheckOutput(target, selfContained: false);
}

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!"));
}
Expand Down
59 changes: 40 additions & 19 deletions corebuild/integration/test/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,18 @@ protected int RunCommand(string command, string args, string workingDir, string
/// 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);
Expand All @@ -83,28 +87,45 @@ public void BuildAndLink(string csproj, List<string> rootFiles = null, Dictionar
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 terminatingOutput = null)
{
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, terminatingOutput);
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;
}

Expand Down
Loading