Skip to content

Commit

Permalink
Single file apps in .net 5 (#90)
Browse files Browse the repository at this point in the history
* Single file apps in .net 5

This change updates the design for single-file apps support, with upcoming changes in .net 5.
The .net core 3 design is summarized in design_3_0.md
  • Loading branch information
swaroop-sridhar authored Feb 20, 2020
1 parent 6cf87fd commit df6b2fb
Show file tree
Hide file tree
Showing 7 changed files with 458 additions and 408 deletions.
65 changes: 34 additions & 31 deletions accepted/single-file/bundler.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,57 @@
# The Single-file Bundler

### Requirements
## Requirements

Ideally, the bundler should be:

* Able to embed any file required to run a .Net Core app -- managed assemblies, native binaries, configuration files, data files, etc.
* Able to embed any file required to run a .NET Core app -- managed assemblies, native binaries, configuration files, data files, etc.
* Able to generate cross-platform bundles (ex: publish a single-file for Linux target from Windows)
* Deterministic (generate the exact same single-file on multiple runs)
* Amenable to post-processing (ex: signing tools)

### Bundle Transformation
## Bundle Layout

Given a .net core app published with respect to a specific runtime, the bundler transforms the `AppHost` binary to a single-file bundle by:
| Bundle Layout (ver 2.0) |
| ------------------------------------------------------------ |
| **AppHost**<br />`(Bundle-Marker)` |
| **Embedded Files**<br />`app.dll` <br />`app.deps.json`<br />`app.runtimeconfig.json`<br />`dependency.managed.dll`<br />`...`<br /> |
| **Bundle Header** <br />`2.0` (Version #)<br /> `#Number of Embedded Files`<br />`Bundle-ID` <br />`Needs Extraction?`<br />`deps.json file location (offset, size)`, if any.<br />`runtimeconfig.json file location (size,offset)`, if any. |
| **Bundle Manifest**<br />For each bundled file:<br /> `Location (Offset, Size)`<br /> `Type: IL, ReadyToRun, deps.json, runtimeconfig.json`, or `other` (extract) |

* Appending the managed app and its dependencies as a binary-blob at the end of the `AppHost` executable.
* Writing meta-data to identify the binary as a single-file bundle, and a manifest of the embedded files.
### Bundle Marker

#### Bundle Layout
Every `AppHost` has a static variable that identifies the location of the single-file header (if any). By default, this value is zero, which indicates that the `AppHost` is not a bundle. The bundler tool rewrites this value with the location of the bundle header.

The bundling tool will append the following contents to the `AppHost` binary:
Using a special marker for recognizing bundle-header (instead of simply writing the header at the end of the file) enables compatibility with other post-processing tools (such as `signtool`) which require their own content to be at the end of the file.

* The actual files to be published into the single file (including the managed app)
* A bundle header containing:
* The bundler tool version
* A bundle identifier: which is a *path-compatible* cryptographically strong name.
* This identifier is used to distinguish bundles for different versions of the same app.
* This identifier is used as part of the bundle extraction mechanism as described in [this document](extract.md).
* Currently, a new bundle identifier is generated for each bundle transformation.
In future, the bundle identifiers should be generated by hashing the contents of the bundle -- so that bundle transformation is deterministic.
* Offset of the bundle manifest
### Bundle Identifier

* A bundle manifest that describes:
* The location of embeded files (offset and size)
* The type of the embedded files: MSIL assemblies, ready-to-run assemblies, configuration files (ex: `app.deps.json` `app.runtimeconfig.json`), or others.
The bundle identifier is a *path-compatible* cryptographically strong name.

Every `AppHost` has a static variable that identifies the location of the single-file header (if any).
By default, this value is zero, which indicates that the `AppHost` is not a bundle.
The bundler tool rewrites this value with the location of the bundle header.
This ensures that the bundle-header is not position constrained (ex: at the end of the file), so that tools like `signtool` can post-process the bundle file.
* This identifier is used to distinguish bundles for different versions of the same app.
* This identifier is used as part of the bundle extraction mechanism as described in [this document](extract.md).
* A new bundle identifier is generated for each bundle transformation.

### Implementation
## Bundle Transformation

The bundler should ideally be located close to the core-host, since their implementation is closely related. Therefore, the bundler will be implemented in the `core-setup` repo.
Given a .NET Core app published with respect to a specific runtime, the bundler transforms the `AppHost` binary to a single-file bundle through the following actions:

The bundling tool is implemented as a library in the [Microsoft.NET.HostModel](https://www.nuget.org/packages/Microsoft.NET.HostModel/) package. This library is used by the SDK in order to publish a .net core app as a single-file.
* Append the managed app and its dependencies as a binary-blob at the end of the `AppHost` executable. This includes all files to be embedded located within the publish directory and any sub-directories. The path (relative to the bundle-root) of published files are noted in the bundle meta-data.
* Write meta-data headers and manifest that help locate the contents of the bundle.
* Set the bundle-indicator in the `AppHost` to the offset of the bundle-header.

### Further work
The bundler should generate the correct format of single-file bundles based on the target framework.

#### Codesign on Mac
## Implementation

Codesign tool on mac-os performs strict consistency checks, and cannot tolerate the additional files appended at the end of the `AppHost` executable.
[Further work](https://github.com/dotnet/core-setup/issues/7065) is necessary to update the binary headers to make the bundle file compatible for signing.
The bundling tool is implemented as a library in the [Microsoft.NET.HostModel](https://www.nuget.org/packages/Microsoft.NET.HostModel/) package in the [runtime](https://github.com/dotnet/runtime) repo.

* The bundler is located alongside the host components, since their implementations are closely related.
* Separating the bundler (and other AppHost transformers in HostModel) implementation from SDK repo aligns with code ownership, and facilitates maintenance of the library.
* The build targets/tasks that use the HostModel library are in the SDK repo. This facilitates the MSBuild tasks to be multi-targeted. It also helps generate localized error messages, since SDK repo has the localization infrastructure.

## Limitations

* Currently, a new bundle identifier is generated for each bundle transformation. In future, the bundle identifiers should be generated by hashing the contents of the bundle -- so that bundle transformation is deterministic.
* `Codesign` tool on mac-os performs strict consistency checks, and cannot tolerate the additional files appended at the end of the `AppHost` executable. [Further work](https://github.com/dotnet/core-setup/issues/7065) is necessary to update the binary headers to make the bundle file compatible for signing.

Loading

0 comments on commit df6b2fb

Please sign in to comment.