tree: 3adbec53668026fed849c7b4fbd2240e29f67d0b [search]
  1. example/
  2. lib/
  3. test/
  4. analysis_options.yaml
  5. CHANGELOG.md
  6. LICENSE
  7. pubspec.yaml
  8. README.md
pkgs/prebuilt_code_assets/README.md

package:prebuilt_code_assets

Shared build and link hook infrastructure for Dart packages that distribute prebuilt native code assets (via GitHub Releases or bundled prebuilt/ directories), support compiling from source through a toolchain-agnostic buildFromSource callback (e.g. CBuilder from package:native_toolchain_c, CMake, or Cargo), and tree-shake static libraries in hook/link.dart via @RecordUse.

Modeled after CLibrary in package:native_toolchain_c, a single PrebuiltLibrary specification is defined once and shared across hook/build.dart, hook/link.dart, tool/precompile_binaries.dart, and tool/regenerate_hashes.dart.

Features

  • Unified PrebuiltLibrary specification:
    • library.build(input: input, output: output) for hook/build.dart.
    • library.link(input: input, output: output) for hook/link.dart.
    • library.buildStandalone(...) (via BuildInputBuilder) for standalone CI scripts.
  • Standardized build modes (buildMode under hooks.user_defines.<package_name>):
    • fetch (default): Uses a pub-bundled prebuilt/ binary if present, or downloads the prebuilt binary, verifies its SHA-256 digest, and caches it in outputDirectoryShared (in an ABI-specific subdirectory preserving the canonical OS library filename for iOS/macOS XCFrameworks). Downloads are streamed, retried on transient failures, and written atomically. Optionally falls back to buildFromSource if no binary is available.
    • build (alias checkout): Compiles from source using buildFromSource (optionally from checkoutPath).
    • local: Bundles a pre-existing dynamic library from localPath.
  • Tree-shaking link hook (treeshake under hooks.user_defines.<package_name>):
    • Resolves used symbols via usedSymbols, such as SymbolsResolvers.fromRecordUseMapping (ffigen) or SymbolsResolvers.fromMethodPrefix (e.g. Diplomat). Without usedSymbols, the library is never tree-shaken, and the build hook bundles the dynamic library directly.
    • auto (default): Tree-shakes when possible. If no static library is released for a target, or if linking fails in fetch mode (e.g. no C toolchain for the target), prints a warning and bundles the prebuilt dynamic library instead.
    • on: Always tree-shakes, and fails the build if that is not possible.
    • off: Never tree-shakes; bundles the dynamic library directly without running the C linker.
    • If the application uses none of the library's functions, the link hook bundles no library at all.
    • If record use is disabled, it is unknown which functions the application uses, so nothing can be tree-shaken. Then fetch mode bundles the prebuilt dynamic library. When building from source, the link hook links the static library keeping all functions, except on Windows, where a DLL only exports the functions it lists; that fails the build (use treeshake: off instead).
    • On Windows, package:native_toolchain_c only exports the used functions that the .lib defines.
    • On Windows, don't mark functions with __declspec(dllexport) when compiling the static library: the linker then exports, and so keeps, every such function in each object file it links, even unused ones. The .def file already determines the exports.
    • Any other assets routed to the package's link hook are forwarded unchanged.
  • Maintainer CLI runners (package:prebuilt_code_assets/tools.dart):
    • runPrecompileBinariesCli for tool/precompile_binaries.dart.
    • runRegenerateHashesCli for tool/regenerate_hashes.dart (supports both local artifact directories and remote release URLs, and refuses to write an incomplete manifest when downloads fail).

Usage

1. Define your PrebuiltLibrary (lib/src/hook_helpers/library.dart)

import 'package:prebuilt_code_assets/prebuilt_code_assets.dart';
import 'package:record_use/record_use.dart' as record_use;

import '../bindings/record_use_mapping.g.dart';
import 'hashes.dart';

final myLibrary = PrebuiltLibrary(
  name: 'my_lib',
  assetName: 'my_package.dart',
  releaseConfig: PrebuiltReleaseConfig.github(
    owner: 'my-org',
    repo: 'my_package',
    version: version,
    fileHashes: fileHashes,
    libraryName: 'my_lib',
  ),
  buildFromSource: (input, output, {required static, checkoutPath}) async {
    // Compile with your toolchain (e.g. `CBuilder`, CMake, or Cargo), add the
    // sources you read to `output.dependencies`, and return the Uri of the
    // built static (if `static`) or dynamic library.
  },
  usedSymbols: SymbolsResolvers.fromRecordUseMapping(
    const record_use.Library('package:my_package/src/bindings/bindings.g.dart'),
    recordUseMapping,
  ),
);

For release layouts other than <repo>-<target>-<libraryFileName>, pass a custom resolveAssetName to PrebuiltReleaseConfig.github, or use the PrebuiltReleaseConfig constructor directly.

2. Wire up hook/build.dart and hook/link.dart

// hook/build.dart
import 'package:hooks/hooks.dart';
import 'package:my_package/src/hook_helpers/library.dart';

Future<void> main(List<String> args) async {
  await build(args, (input, output) async {
    await myLibrary.build(input: input, output: output);
  });
}
// hook/link.dart
import 'package:hooks/hooks.dart';
import 'package:my_package/src/hook_helpers/library.dart';

Future<void> main(List<String> args) async {
  await link(args, (input, output) async {
    await myLibrary.link(input: input, output: output);
  });
}

Both methods accept an optional Logger; by default messages are printed to the hook's stdout/stderr.

3. Configuring user_defines in pubspec.yaml

Consumers of your package configure how the native library is obtained and whether it is tree-shaken under hooks.user_defines.<package_name> in their workspace or application pubspec.yaml:

hooks:
  user_defines:
    my_package:
      # 'fetch' (default), 'build' (alias 'checkout'), or 'local'
      buildMode: fetch
      # 'auto' (default), 'on', or 'off'
      treeshake: auto
      # Path to a local source checkout (used when buildMode is 'build')
      # checkoutPath: ../path/to/checkout
      # Path to a pre-existing dynamic library on disk (required when buildMode is 'local')
      # localPath: /absolute/or/relative/path/to/libmy_lib.so
KeyTypeDescription
buildModeString'fetch' (default: use bundled prebuilt/ or download from release URL), 'build' / 'checkout' (compile from source), or 'local' (use a binary at localPath).
treeshakeString or bool'auto' (default), 'on' / true, or 'off' / false. See Features.
local_buildboolShorthand used by dart-lang/native examples: local_build: true means buildMode: build. Ignored if buildMode is set.
checkoutPathString (path)Optional path to a local source directory when buildMode is 'build'.
localPathString (path)Path to a pre-built dynamic library file when buildMode is 'local'.

Relative paths are resolved against the pubspec.yaml that defines them. Unknown values fail the build with an explanation (set strictBuildOptions: false on PrebuiltLibrary to fall back to the defaults instead).