Skip to content

Commit

Permalink
[release/5.0.1xx-preview4] Single-File: Close AppHost stream when reu…
Browse files Browse the repository at this point in the history
…sing extraction

Customer Scenario
Self-contained Apps published as a single-file fail at run-time.

Problem

Single-file bundles are now processed in the framework rather than the apphost (dotnet/runtime#34274).
This means that hostpolicy and hostfxr DLLs are excluded from being bundled themselves.
In the case of self-contained single-file apps, these files need to be separate files until static-apphost is available.
This needs to be ensured by the SDK; otherwise app execution will fail.

Solution

This change fixes the problem by adapting the SDK to:
* Pass the correct target-OS settings (otherwise cross-targetting builds will not work correctly)
* Copy files excluded by the Bundler to the publish directory.

The stage-0 netcoreapp is updated to preview4, because preview2 apphost is not compatible with preview4 bundler.

Risk
Low
  • Loading branch information
swaroop-sridhar committed Apr 30, 2020
1 parent 6680411 commit 6c4007a
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 10 deletions.
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"tools": {
"dotnet": "5.0.100-preview.2.20152.7",
"dotnet": "5.0.100-preview.4.20229.10",
"runtimes": {
"dotnet": [
"$(MicrosoftNETCoreAppRuntimePackageVersion)"
Expand Down
21 changes: 20 additions & 1 deletion src/Tasks/Microsoft.NET.Build.Tasks/GenerateBundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Microsoft.NET.HostModel.Bundle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace Microsoft.NET.Build.Tasks
{
Expand All @@ -18,13 +20,24 @@ public class GenerateBundle : TaskBase
public bool IncludeSymbols { get; set; }
[Required]
public string OutputDir { get; set; }
[Required]
public string TargetFrameworkVersion { get; set; }
[Required]
public string RuntimeIdentifier { get; set; }

[Required]
public bool ShowDiagnosticOutput { get; set; }

[Output]
public ITaskItem[] ExcludedFiles { get; set; }

protected override void ExecuteCore()
{
OSPlatform targetOS = RuntimeIdentifier.StartsWith("win") ? OSPlatform.Windows :
RuntimeIdentifier.StartsWith("osx") ? OSPlatform.OSX : OSPlatform.Linux;

BundleOptions options = BundleOptions.BundleAllContent | (IncludeSymbols ? BundleOptions.BundleSymbolFiles : BundleOptions.None);
var bundler = new Bundler(AppHostName, OutputDir, options, diagnosticOutput: ShowDiagnosticOutput);
var bundler = new Bundler(AppHostName, OutputDir, options, targetOS, new Version(TargetFrameworkVersion), ShowDiagnosticOutput);
var fileSpec = new List<FileSpec>(FilesToBundle.Length);

foreach (var item in FilesToBundle)
Expand All @@ -34,6 +47,12 @@ protected override void ExecuteCore()
}

bundler.GenerateBundle(fileSpec);

// Certain files are excluded from the bundle, based on BundleOptions.
// For example, native files and contents files are excluded by default.
// Return the set of excluded files in ExcludedFiles, so that they can be placed in the publish directory.

ExcludedFiles = FilesToBundle.Zip(fileSpec, (item, spec) => (spec.Excluded) ? item : null).Where(x => x != null).ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -945,11 +945,6 @@ Copyright (c) .NET Foundation. All rights reserved.
<PublishedSingleFilePath>$(PublishDir)$(PublishedSingleFileName)</PublishedSingleFilePath>
</PropertyGroup>

<ItemGroup>
<ResolvedFileToPublish Remove="@(_FilesToBundle)"/>
<!-- ResolvedFileToPublish shouldn't include PublishedSingleFilePath, since the single-file bundle is written directly to the publish directory -->
</ItemGroup>

</Target>

<UsingTask TaskName="GenerateBundle" AssemblyFile="$(MicrosoftNETBuildTasksAssembly)" />
Expand All @@ -963,8 +958,17 @@ Copyright (c) .NET Foundation. All rights reserved.
AppHostName="$(PublishedSingleFileName)"
IncludeSymbols="$(IncludeSymbolsInSingleFile)"
OutputDir="$(PublishDir)"
ShowDiagnosticOutput="false"/>
TargetFrameworkVersion="$(_TargetFrameworkVersionWithoutV)"
RuntimeIdentifier="$(RuntimeIdentifier)"
ShowDiagnosticOutput="false">
<Output TaskParameter="ExcludedFiles" ItemName="_FilesExcludedFromBundle"/>
</GenerateBundle>

<ItemGroup>
<ResolvedFileToPublish Remove="@(_FilesToBundle)"/>
<ResolvedFileToPublish Include="@(_FilesExcludedFromBundle)"/>
<!-- ResolvedFileToPublish shouldn't include PublishedSingleFilePath, since the single-file bundle is written directly to the publish directory -->
</ItemGroup>
</Target>

<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ private PublishCommand GetPublishCommand()
return new PublishCommand(Log, testAsset.TestRoot);
}

private DirectoryInfo GetPublishDirectory(PublishCommand publishCommand)
private DirectoryInfo GetPublishDirectory(PublishCommand publishCommand, string targetFramework = "netcoreapp3.0")
{
return publishCommand.GetOutputDirectory(targetFramework: "netcoreapp3.0",
return publishCommand.GetOutputDirectory(targetFramework: targetFramework,
runtimeIdentifier: RuntimeInformation.RuntimeIdentifier);
}

Expand Down Expand Up @@ -352,5 +352,41 @@ public void It_rewrites_the_apphost_for_non_single_file_publish()

appHostSize.Should().BeLessThan(singleFileSize);
}

[CoreMSBuildOnlyTheory]
[InlineData("netcoreapp3.0", false)]
[InlineData("netcoreapp3.0", true)]
[InlineData("netcoreapp3.1", false)]
[InlineData("netcoreapp3.1", true)]
[InlineData("netcoreapp5.0", false)]
[InlineData("netcoreapp5.0", true)]
public void It_runs_single_file_apps(string targetFramework, bool selfContained)
{
var testProject = new TestProject()
{
Name = "SingleFileTest",
TargetFrameworks = targetFramework,
IsSdkProject = true,
IsExe = true,
};
testProject.AdditionalProperties.Add("SelfContained", $"{selfContained}");

var testAsset = _testAssetsManager.CreateTestProject(testProject);
var publishCommand = new PublishCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name));

publishCommand.Execute(PublishSingleFile, RuntimeIdentifier)
.Should()
.Pass();

var publishDir = GetPublishDirectory(publishCommand, targetFramework).FullName;
var singleFilePath = Path.Combine(publishDir, $"{testProject.Name}{Constants.ExeSuffix}");

var command = new RunExeCommand(Log, singleFilePath);
command.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");
}
}
}

0 comments on commit 6c4007a

Please sign in to comment.