buildSlintAot function

Future<void> buildSlintAot(
  1. BuildInput input,
  2. BuildOutputBuilder output
)

App build hook: AOT-compiles every ui/**.slint of the package into one staticlib — slint-build codegen plus generated C ABI glue, no slint-interpreter — and routes it to the app's link hook (ToLinkHook), which links the final dylib keeping only the components the app uses (see aot_link.dart). One code asset per .slint file, with id package:<app>/<path>.aot.g.dart matching the wrappers the slint_compiler builder generates.

Wire it up as the app's hook/build.dart (with hook/link.dart calling linkSlintAot):

import 'package:hooks/hooks.dart';
import 'package:slint_compiler/aot_build.dart';

void main(List<String> args) => build(args, buildSlintAot);

Implementation

Future<void> buildSlintAot(BuildInput input, BuildOutputBuilder output) async {
  if (!input.config.buildCodeAssets) return;
  // The AOT dylib ships only in release/profile builds; debug builds (incl.
  // `flutter test`) use the slint_interpreter package. In Flutter,
  // linkingEnabled == true exactly for the non-debug (AOT) modes.
  if (!input.config.linkingEnabled) return;

  // `.slint` files live under `ui/`, not `lib/`: they are not Dart, and the
  // release bundle must not ship them — see `slint_generator`'s builder.
  final uiDir = Directory.fromUri(input.packageRoot.resolve('ui/'));
  final slintFiles = !uiDir.existsSync()
      ? <File>[]
      : (uiDir
          .listSync(recursive: true, followLinks: false)
          .whereType<File>()
          .where((f) => f.path.endsWith('.slint'))
          .toList()
        ..sort((a, b) => a.path.compareTo(b.path)));
  if (slintFiles.isEmpty) return;

  final packageConfig = findPackageConfig(input);
  final compilerRoot = packageRootFromConfig(packageConfig, 'slint_compiler');
  final generatorRoot = packageRootFromConfig(packageConfig, 'slint_generator');
  final slintCoreCrate =
      packageRootFromConfig(packageConfig, 'slint').resolve('rust/');

  final files = <SlintAotFile>[];
  final assetNames = <String>[];
  final uiPath = uiDir.uri.toFilePath();
  for (final f in slintFiles) {
    final relative = f.path.substring(uiPath.length);
    final stemBase = relative.substring(0, relative.length - '.slint'.length);
    final stem = stemBase.replaceAll(Platform.pathSeparator, '_');
    // `a/b.slint` and `a_b.slint` map to the same module stem and would
    // silently overwrite each other's generated sources in the AOT crate.
    if (files.any((e) => e.stem == stem)) {
      throw StateError(
        'duplicate AOT module stem "$stem" from "$relative" — '
        'rename one of the .slint files',
      );
    }
    final schema = await introspectSlint(
      f.path,
      introspectManifest: generatorRoot.resolve('rust/Cargo.toml'),
    );
    files.add(SlintAotFile(
      stem: stem,
      source: f.readAsStringSync(),
      schema: schema,
    ));
    assetNames.add('$stemBase.aot.g.dart'.replaceAll(Platform.pathSeparator, '/'));
  }

  final crateDir =
      Directory.fromUri(input.outputDirectoryShared.resolve('slint_aot/'));
  emitAotCrate(
    crateDir,
    files: files,
    slintCoreCratePath: slintCoreCrate.toFilePath(),
    // Present inside this repo (the Cargo workspace root); absent for a pub
    // consumer, which then resolves afresh as before.
    lockfile: File.fromUri(compilerRoot.resolve('../../Cargo.lock')),
  );

  final build = await runCargoBuild(
    input,
    output,
    crateName: 'slint-dart-aot',
    manifestPath: crateDir.uri.resolve('Cargo.toml'),
    artifactKind: 'staticlib',
    // Makes rustc print the `native-static-libs:` note — the exact linker
    // line the staticlib needs — which the link hook replays when it links
    // the final dylib.
    extraEnv: const {'RUSTFLAGS': '--print=native-static-libs'},
    sourceDirs: [
      input.packageRoot.resolve('ui/'),
      // Regenerate when the glue generator or the introspect tool change.
      compilerRoot.resolve('lib/'),
      generatorRoot.resolve('rust/'),
    ],
  );

  final linkFlags = _persistedNativeLinkFlags(
    crateDir: crateDir,
    triple: rustTriple(input.config.code),
    cargoOutput: build.cargoOutput,
  );

  // Everything the link hook needs, written next to the routed staticlib.
  final manifest = {
    'assetIds': assetNames,
    'sharedSymbols': aotSharedSymbols,
    'linkFlags': linkFlags,
    'components': [
      for (var i = 0; i < files.length; i++)
        for (final c in files[i].schema.components)
          {
            'name': c.name,
            'library': 'package:${input.packageName}/${assetNames[i]}',
            'newExtern': aotNewExternName(c.name),
            'symbols': aotComponentSymbols(c.name),
          },
    ],
  };
  File.fromUri(input.outputDirectory.resolve(aotLinkManifestName))
      .writeAsStringSync(jsonEncode(manifest));

  output.assets.code.add(
    CodeAsset(
      package: input.packageName,
      name: assetNames.first,
      linkMode: StaticLinking(),
      file: build.artifact,
    ),
    routing: ToLinkHook(input.packageName),
  );
}