-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Build hook spec #955
Build hook spec #955
Changes from 9 commits
43950e2
d75f6ab
13e9ed6
6f59908
4bc8cbd
5e01f81
f745db4
be58730
e1e8754
7fb1fce
ac5a7da
746a74d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
Build Hook Specification | ||
======================== | ||
|
||
Version 1: As of 2024-01-24 implemented behind `--enable-experiment=native-assets` | ||
|
||
### Concepts | ||
|
||
#### Asset | ||
|
||
An asset is a file together with metadata that tells the Dart and Flutter SDK how to bundle such asset in an application bundle. | ||
|
||
Currently, all such assets are dynamic libraries. | ||
|
||
An asset must have an `assetId`. Dart code that uses an asset, references the asset using the `assetId`. | ||
|
||
An asset must have a target os and target architecture. When accessing the asset at runtime, the asset for the current os and architecture is accessed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought there is not necessarily a target architecture in dry run mode? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not as input, but there is for the output. We should probably fix that in v2. |
||
|
||
An asset must have a link mode. Currently, the only supported link mode is dynamic. (Static linking of native code will be added later.) | ||
|
||
An asset must specify a path type for dynamic linking. | ||
|
||
* `absolute` paths are absolute paths in the output dir (see below). | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* `system` paths are expected to resolve on the target machine PATH. In this case the asset is not a file but only metadata. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* `process` "paths" have no path, symbols are resolved in the current process. In this case the asset is not a file but only metadata. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah so code assets are already much more involved than "a file". Code assets can be metadata only. And some variants of that metadata do not require any path. path_type is probably not the right name. Maybe it should be dynamic_library_type. But the executable and process one don't even have a dynamic library. So then it should be dynamic_linking_type. This also sounds weird, because the linking always is the same in a way. Maybe it should be dynamic_resolution_type. That gets closer. But the problem here is that we're also abstracting over the resolution partially. Dart and Flutter repackage dynamic libraries. So in this protocol for absolute paths, it's the absolute path at the point of this protocol. But for system paths, process, and executable the metadata gets passed in unmodified. So then it should maybe be something like: - asset_id: ...
dynamic_resolution:
type: bundled_dylib
# no path here, because that will be decided by Flutter/Dart when making the bundle.
path: ... # Not nested in dynamic_resolution, it's the path for the protocol.
- asset_id: ...
dynamic_resolution:
type: system
path: ... # Nested in dynamic_resolution, it's the metadata to be passed into linking.
- asset_id: ...
dynamic_resolution:
type: process
- asset_id: ...
dynamic_resolution:
type: executable wdyt? |
||
* `executable` "paths" have no path, symbols are resolved in the current executable. In this case the asset is not a file but only metadata. | ||
|
||
#### AssetId | ||
An asset must have an `assetId`. Dart code that uses an asset, references the asset using the `assetId`. | ||
|
||
A package must prefix all assetIds it defines with: `package:<package>/`, this ensures assets don't conflict between packages. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it seems an |
||
|
||
Conventionally, an asset referenced from `lib/src/foo.dart` in `package:foo` has `assetId = 'package:foo/src/foo.dart'`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the convention if two assets would be referenced from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, we don't have a convention yet. Maybe:
|
||
|
||
#### Target os and architecture | ||
|
||
A target specifies the operating system and architecture of the targeted platform. | ||
|
||
### `build.dart` | ||
To bundle native assets with a package, that package must define a `build.dart` | ||
script which accepts a `--config` option as follows: | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```console | ||
$ dart build.dart --config <build_config.yaml> | ||
``` | ||
|
||
The `--config` option specifies location of the `build_config.yaml` file. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The `build.dart` file MUST: | ||
* Read the `build_config.yaml` file, | ||
* Build assets using configuration from `build_config.yaml`. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* If `dry_run: true` in `build_config.yaml`, then this may be skipped. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So its not a MUST after all? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. With a linear doc it's a bit tricky to introduce concepts and then later nuance them. Open to suggestions. (Please feel free to push commits to rewrite this instead of leaving comments.) |
||
* Write assets into `out_dir` from `build_config.yaml`. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* If `dry_run: true` in `build_config.yaml`, then this may be skipped. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* Filename are unrelated to `assetId`. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* Arbitrary file names are fine, `build_output.yaml` will map `assetId` to files. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The file names of the assets don't really matter. It could be file1.foo, file2.foo, file3.foo. Because the only way assets are accessed from Dart code is through their assetId. However, we do want to have to files written to disk rather than having a buffer of bytes for files, because quite often the files are produces by an external |
||
* MUST avoid file name `build_output.yaml`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a name for an asset? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you write your asset to out_dir/build_output.yaml it's going to fail to write build_output.yaml Maybe in v2 we should consider writing the assets somewhere else than the build output. |
||
* Write `build_output.yaml` into `out_dir` (from `build_config.yaml`). | ||
* This maps `assetId` to assets previous written in `out_dir`. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* There may be multiple assets for a given `assetId` depending on | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might want to add something about the uniqueness of an |
||
characteristics like `target`, `link_mode`, etc. | ||
* If `dry_run: true` in `build_config.yaml`, the list of assets that would be | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
generated must this be enumerated. Mapping `assetId`s to non-existing files | ||
is expected. | ||
|
||
|
||
### `build_config.yaml` | ||
The Dart and Flutter SDK invoke `build.dart` of all packages in the transitive | ||
dependencies and pass a `build_config.yaml` as the `--config` option. | ||
|
||
```yaml | ||
# Build in dry-run mode. | ||
# | ||
# Running in dry-run mode `<out_dir>/build_output.yaml` must be written, but | ||
# the files it references need not exist. | ||
dry_run: true | false | ||
|
||
# Build Mode. | ||
# | ||
# A hint `build.dart` can use to determined which optimizations to enable and | ||
# whether or not to include debug symbols in the format relevant for the asset. | ||
# | ||
# Not provided on dry runs. | ||
build_mode: release | debug | ||
|
||
# Metadata as output from build.dart from direct dependencies. | ||
# | ||
# Not provided on dry runs. | ||
dependency_metadata: | ||
# package name of direct dependency. | ||
some_package_name: | ||
# key value pairs. | ||
some_key: some_value | ||
|
||
# Preferred link mode | ||
link_mode_preference: dynamic | static | prefer-dynamic | prefer-static | ||
|
||
# Path to output directory where assets should be placed. | ||
# | ||
# This is also where `build_output.yaml` should be written. | ||
# | ||
# Remark: Avoid using the name "build_output.yaml" for an asset file, this is | ||
# forbidden. | ||
out_dir: /absolute/path/to/out_dir/ | ||
|
||
# Name of the package that contains the `build.dart` | ||
# | ||
# Remark: This is entirely redundant since this is a config file specified to | ||
# `build.dart`, and the author of `build.dart` probably knows the name of the | ||
# package they are writing. | ||
package_name: my_package_with_native_assets | ||
|
||
# Path to root folder for the package that contains `build.dart`. | ||
# | ||
# This is useful if `build.dart` wishes to find source code files embedded in | ||
# its own package and compile them to an asset. | ||
# | ||
# Note that this will be most likely a path in the pub cache when the package | ||
# with a `build.dart` is a dependency of another package. | ||
package_root: /absolute/path/to/my_package_with_native_assets | ||
|
||
# Target architecture | ||
# | ||
# Combined with `target_os` this specifies the "target" for which assets | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# should be built. | ||
# | ||
# Not provided on dry runs. | ||
target_architecture: x64 | ia32 | arm | arm64 | riscv32 | riscv64 | ||
|
||
# Target operating system | ||
# | ||
# Combined with `target_architecture` this specifies the "target" for which | ||
# assets should be built. | ||
target_os: android | ios | linux | macos | windows | ||
|
||
# Schema version of this file. | ||
version: 1.0.0 | ||
``` | ||
|
||
### `build_output.yaml` | ||
Mapping from `assetId`s to files created by `build.dart`. | ||
|
||
```yaml | ||
# The list of assets. | ||
# | ||
# In dry runs, must contain assets for each architecture for the requested os. | ||
assets: | ||
- id: 'package:my_package_with_native_assets/src/foo.dart' | ||
link_mode: dynamic | static | prefer-dynamic | prefer-static | ||
path: | ||
path_type: absolute | system | process | executable | ||
# Only provided for path_type absolute and system. | ||
# | ||
# If path_type absolute: The absolute path to the file name. | ||
# | ||
# If path_type system: The path of the dynamic library as available on | ||
# the target machine's PATH. | ||
uri: /absolute/path/to/outdir/arbitrary_filename.whatever | ||
target: linux_x64 | ||
... | ||
|
||
# The files used by this build. | ||
# | ||
# If any of the files in [dependencies] are modified after [timestamp], the | ||
# build will be re-run. | ||
# | ||
# Not output on dry runs. | ||
dependencies: | ||
- /absolute/path/to/my_package_with_native_assets/build.dart | ||
... | ||
|
||
# The time the build this output is for started. | ||
# | ||
# Must be before any of the files in dependencies are read. | ||
timestamp: 2024-01-02 17:05:35.000 | ||
|
||
# Metadata usable for build.dart of packages directly depending on this package. | ||
# | ||
# Not output in dry runs. | ||
metadata: | ||
# Key value pairs. | ||
some_key: some_value | ||
|
||
# Schema version of this file. | ||
version: 1.0.0 | ||
``` | ||
|
||
|
||
### `.dart_tool/native_assets.yaml` | ||
|
||
(This is not part of the build.dart protocol.) | ||
|
||
This file is internal to the Dart SDK and Flutter SDK. | ||
It produced by `native_asset_builder` (as embedded in the SDKs). | ||
For each `target` it maps from `assetId` to a path type with an optional path. | ||
|
||
These paths are used to resolve `@Native() external` functions in Dart code. | ||
|
||
The path types in this file are as follows: | ||
|
||
* `absolute` paths are absolute paths on the system where the Dart executable is running. This path type is used when running in JIT mode on a developers' host machine, or in an Android app where the root path is the Android bundle. | ||
* `relative` paths are relative to the kernel or aot snapshot. This path type is used for shipping native assets in a directory together with a kernelsnapshot, aotsnapshot, or standalone executable (dartaotruntime+aotsnapshot). | ||
* `system` paths are expected to resolve on the target machine PATH. | ||
* `process` "paths" have no path, symbols are resolved in the current process. | ||
* `executable` "paths" have no path, symbols are resolved in the current executable. | ||
|
||
```yaml | ||
# Schema version of this file. | ||
format-version: [1, 0, 0] | ||
|
||
# A mapping from target and assetId to an asset. | ||
native-assets: | ||
# A <target>. | ||
linux_x64: | ||
# An <assetId>. | ||
'package:my_package_with_native_assets/src/foo.dart': | ||
# A list of path_type and optionally a path. | ||
- absolute | relative | system | process | executable | ||
- <url> # Only provided for absolute, relative, and system path types. | ||
... | ||
``` | ||
|
||
This file is used by the embedded SDK to resolve `assetId`s when running in | ||
development mode or build a deployable application (or standalone executable). | ||
|
||
## Wishes to improve for v2 | ||
|
||
* Multiple asset types. Rename `Asset` to `NativeCodeAsset` and introduce `DataAsset`s. | ||
* The path_types really only make sense for `link_mode: dynamic`. | ||
dcharkes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* Fix the mismatch between `target` for output but `os` and `architecture` for config. | ||
* Should the architecture field of native code assets be optional and be ommitted in dry runs? | ||
* Introduce a `link.dart` protocol with a link input and link output. | ||
mosuem marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and in the other items: You probably mean
the metadata must contain
? Also, what doesusing
mean? I assume just referencing, as an asset is a file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, we have conflated the
Asset
class that is metadata, and the file. This probs requires a full rewrite.Since we also have a "metadata" field in the build input and build output, we shoudl probably refrain from calling everything that is not the file metadata. Maybe we should call the file the "asset file".