diff --git a/corebuild/integration/ILLink.Tasks/ILLink.Tasks.targets b/corebuild/integration/ILLink.Tasks/ILLink.Tasks.targets
index ce76e7136831..70c051ec28e2 100644
--- a/corebuild/integration/ILLink.Tasks/ILLink.Tasks.targets
+++ b/corebuild/integration/ILLink.Tasks/ILLink.Tasks.targets
@@ -99,7 +99,7 @@
-
+
@@ -114,7 +114,7 @@
<_DebugSymbolsIntermediatePath Include="@(_LinkedDebugSymbols)" Condition=" '$(_DebugSymbolsProduced)' == 'true' " />
-
+
+
+
+
+
+ <_ReferencedLibraries Include="@(ReferencePath->'%(ResolvedPath)')" Exclude="@(_ReferencedLibrariesToExclude)" />
+ <_ManagedAssembliesToLink Include="@(_ReferencedLibraries)" />
+
+
@@ -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. -->
-
@@ -363,7 +384,7 @@
-
@@ -373,13 +394,19 @@
-
-
+
+
+
+
+
@@ -425,7 +452,7 @@
<_RemovedNativeDeps Include="@(_NativeResolvedDepsToPublish)" />
<_RemovedNativeDeps Remove="@(_NativeKeptDepsToPublish)" />
-
+
<_PublishConflictPackageFiles Include="@(_RemovedManagedAssemblies)" />
<_PublishConflictPackageFiles Include="@(_RemovedNativeDeps)" />
diff --git a/corebuild/integration/test/CommandRunner.cs b/corebuild/integration/test/CommandRunner.cs
index 58c4b3416743..efee6cbbd7f0 100644
--- a/corebuild/integration/test/CommandRunner.cs
+++ b/corebuild/integration/test/CommandRunner.cs
@@ -67,6 +67,7 @@ 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}");
@@ -74,7 +75,7 @@ public int Run(out string commandOutput)
outputHelper.WriteLine($"{command}");
}
if (!String.IsNullOrEmpty(workingDir)) {
- outputHelper.WriteLine("$working directory: {workingDir}");
+ outputHelper.WriteLine($"working directory: {workingDir}");
psi.WorkingDirectory = workingDir;
}
if (!String.IsNullOrEmpty(additionalPath)) {
diff --git a/corebuild/integration/test/HelloWorldTest.cs b/corebuild/integration/test/HelloWorldTest.cs
index 009601e8875c..23e4662c2062 100644
--- a/corebuild/integration/test/HelloWorldTest.cs
+++ b/corebuild/integration/test/HelloWorldTest.cs
@@ -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 @@ 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!"));
}
diff --git a/corebuild/integration/test/IntegrationTestBase.cs b/corebuild/integration/test/IntegrationTestBase.cs
index 951e5d575aeb..99218ba3e36a 100644
--- a/corebuild/integration/test/IntegrationTestBase.cs
+++ b/corebuild/integration/test/IntegrationTestBase.cs
@@ -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.
///
- public void BuildAndLink(string csproj, List rootFiles = null, Dictionary extraPublishArgs = null)
+ public string BuildAndLink(string csproj, List rootFiles = null, Dictionary 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);
@@ -83,28 +87,45 @@ public void BuildAndLink(string csproj, List 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;
}
diff --git a/corebuild/integration/test/MusicStoreTest.cs b/corebuild/integration/test/MusicStoreTest.cs
index 7aa53d414aff..a28e996d2489 100644
--- a/corebuild/integration/test/MusicStoreTest.cs
+++ b/corebuild/integration/test/MusicStoreTest.cs
@@ -10,8 +10,6 @@ namespace ILLink.Tests
{
public class MusicStoreTest : IntegrationTestBase
{
- public MusicStoreTest(ITestOutputHelper output) : base(output) {}
-
private static List rootFiles = new List { "MusicStoreReflection.xml" };
private static string gitRepo = "http://github.com/aspnet/JitBench";
@@ -34,40 +32,140 @@ public MusicStoreTest(ITestOutputHelper output) : base(output) {}
// The version of Microsoft.AspNetCore.All to publish with.
private static string aspNetVersion = "2.1.0-preview1-27654";
+ private static Dictionary versionPublishArgs;
+ private static Dictionary VersionPublishArgs
+ {
+ get {
+ if (versionPublishArgs != null) {
+ return versionPublishArgs;
+ }
+ versionPublishArgs = new Dictionary();
+ versionPublishArgs.Add("JITBENCH_FRAMEWORK_VERSION", runtimeVersion);
+ versionPublishArgs.Add("JITBENCH_ASPNET_VERSION", aspNetVersion);
+ return versionPublishArgs;
+ }
+ }
+
+ private static string csproj;
+
+ public MusicStoreTest(ITestOutputHelper output) : base(output) {
+ csproj = SetupProject();
+
+ // 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);
+ }
+
+ [Fact]
+ public void RunMusicStoreStandalone()
+ {
+ string executablePath = BuildAndLink(csproj, rootFiles, VersionPublishArgs, selfContained: true);
+ CheckOutput(executablePath, selfContained: true);
+ }
+
[Fact]
- public void RunMusicStore()
+ public void RunMusicStorePortable()
{
- string csproj = SetupProject();
+ Dictionary extraPublishArgs = new Dictionary(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);
+
+ 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
- string demoRoot= Path.GetDirectoryName(csproj);
CopyRootFiles(demoRoot);
// This is necessary because JitBench comes with a
// NuGet.Config that has a line, preventing
// NuGet.Config sources defined in outer directories from
// applying.
- string nugetConfig = Path.Combine("JitBench", "NuGet.config");
+ string nugetConfig = Path.Combine(repoName, "NuGet.config");
AddLocalNugetFeedAfterClear(nugetConfig);
AddLinkerReference(csproj);
- Dictionary extraPublishArgs = new Dictionary();
- extraPublishArgs.Add("JITBENCH_FRAMEWORK_VERSION", runtimeVersion);
- extraPublishArgs.Add("JITBENCH_ASPNET_VERSION", aspNetVersion);
- BuildAndLink(csproj, rootFiles, extraPublishArgs);
+ AddGlobalJson(repoName);
- 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);
+ 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 @@ string ObtainSDK(string rootDir, string repoDir)
}
}
- 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)
diff --git a/corebuild/integration/test/WebApiTest.cs b/corebuild/integration/test/WebApiTest.cs
index 2d948e951aec..c7e8683e0210 100644
--- a/corebuild/integration/test/WebApiTest.cs
+++ b/corebuild/integration/test/WebApiTest.cs
@@ -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 @@ public string SetupProject()
Assert.True(false);
}
- string csproj = Path.Combine(projectRoot, $"{projectRoot}.csproj");
+ PreventPublishFiltering(csproj);
+
+ AddLinkerReference(csproj);
+
return csproj;
}
@@ -49,18 +62,23 @@ private void PreventPublishFiltering(string csproj) {
}
[Fact]
- public void RunWebApi()
+ public void RunWebApiStandalone()
{
- string csproj = SetupProject();
-
- PreventPublishFiltering(csproj);
-
- AddLinkerReference(csproj);
+ string executablePath = BuildAndLink(csproj, selfContained: true);
+ CheckOutput(executablePath, selfContained: true);
+ }
- BuildAndLink(csproj);
+ [Fact]
+ public void RunWebApiPortable()
+ {
+ string target = BuildAndLink(csproj, selfContained: false);
+ CheckOutput(target, selfContained: false);
+ }
+ void CheckOutput(string target, bool selfContained = false)
+ {
string terminatingOutput = "Now listening on: http://localhost:5000";
- int ret = RunApp(csproj, out string commandOutput, 60000, terminatingOutput);
+ 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(terminatingOutput));
}