Skip to content

Commit

Permalink
[native_assets_builder] Refactor API to ProtocolExtensions (#2089)
Browse files Browse the repository at this point in the history
This PR introduces a concept of protocol extensions, which package together everything belonging to a protocol extension.

Closes: #2088

As a bonus, this better abstraction reduces LOC.

The `validation` methods are now all internal to the extensions. The `setup` methods stay exported because they are used extensively in hook-helper-packages (e.g. `native_toolchain_c`).
  • Loading branch information
dcharkes authored Mar 11, 2025
1 parent 2dba4f5 commit 0cd2e55
Show file tree
Hide file tree
Showing 33 changed files with 293 additions and 357 deletions.
10 changes: 1 addition & 9 deletions pkgs/native_assets_builder/lib/native_assets_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

export 'package:native_assets_builder/src/build_runner/build_runner.dart'
show
ApplicationAssetValidator,
BuildInputCreator,
BuildInputValidator,
BuildValidator,
LinkInputCreator,
LinkInputValidator,
LinkValidator,
NativeAssetsBuildRunner;
show BuildInputCreator, LinkInputCreator, NativeAssetsBuildRunner;
export 'package:native_assets_builder/src/model/build_result.dart'
show BuildResult;
export 'package:native_assets_builder/src/model/kernel_assets.dart';
Expand Down
99 changes: 48 additions & 51 deletions pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,6 @@ typedef LinkInputCreator = LinkInputBuilder Function();
typedef _HookValidator =
Future<ValidationErrors> Function(HookInput input, HookOutput output);

// A callback that validates the invariants of the [BuildInput].
typedef BuildInputValidator =
Future<ValidationErrors> Function(BuildInput input);

// A callback that validates the invariants of the [LinkInput].
typedef LinkInputValidator = Future<ValidationErrors> Function(LinkInput input);

// A callback that validates the output of a `hook/link.dart` invocation is
// valid (it may valid asset-type specific information).
typedef BuildValidator =
Future<ValidationErrors> Function(BuildInput input, BuildOutput outup);

// A callback that validates the output of a `hook/link.dart` invocation is
// valid (it may valid asset-type specific information).
typedef LinkValidator =
Future<ValidationErrors> Function(LinkInput input, LinkOutput output);

// A callback that validates assets emitted across all packages are valid / can
// be used together (it may valid asset-type specific information - e.g. that
// there are no classes in shared library filenames).
typedef ApplicationAssetValidator =
Future<ValidationErrors> Function(List<EncodedAsset> assets);

/// The programmatic API to be used by Dart launchers to invoke native builds.
///
/// These methods are invoked by launchers such as dartdev (for `dart run`)
Expand Down Expand Up @@ -98,18 +75,14 @@ class NativeAssetsBuildRunner {
/// This method is invoked by launchers such as dartdev (for `dart run`) and
/// flutter_tools (for `flutter run` and `flutter build`).
///
/// The given [applicationAssetValidator] is only used if the build is
/// performed without linking (i.e. [linkingEnabled] is `false`).
///
/// The native assets build runner does not support reentrancy for identical
/// [BuildInput] and [LinkInput]! For more info see:
/// https://github.com/dart-lang/native/issues/1319
///
/// The base protocol can be extended with [extensions]. See
/// [ProtocolExtension] for more documentation.
Future<BuildResult?> build({
required BuildInputCreator inputCreator,
required BuildInputValidator inputValidator,
required BuildValidator buildValidator,
required ApplicationAssetValidator applicationAssetValidator,
required List<String> buildAssetTypes,
required List<ProtocolExtension> extensions,
required bool linkingEnabled,
}) async {
final (buildPlan, packageGraph) = await _makePlan(
Expand All @@ -132,11 +105,15 @@ class NativeAssetsBuildRunner {
targetMetadata: globalMetadata,
)?.forEach((key, value) => metadata[key] = value);

final inputBuilder =
inputCreator()
..config.setupShared(buildAssetTypes: buildAssetTypes)
..config.setupBuild(linkingEnabled: linkingEnabled)
..setupBuildInput(metadata: metadata);
final inputBuilder = BuildInputBuilder();
inputBuilder.config.setupShared(
buildAssetTypes: [for (final e in extensions) ...e.buildAssetTypes],
);
for (final e in extensions) {
e.setupBuildInput(inputBuilder);
}
inputBuilder.config.setupBuild(linkingEnabled: linkingEnabled);
inputBuilder.setupBuildInput(metadata: metadata);

final (buildDirUri, outDirUri, outDirSharedUri) = await _setupDirectories(
Hook.build,
Expand All @@ -155,7 +132,7 @@ class NativeAssetsBuildRunner {
final input = BuildInput(inputBuilder.json);
final errors = [
...await validateBuildInput(input),
...await inputValidator(input),
for (final e in extensions) ...await e.validateBuildInput(input),
];
if (errors.isNotEmpty) {
return _printErrors(
Expand All @@ -167,8 +144,13 @@ class NativeAssetsBuildRunner {
final result = await _runHookForPackageCached(
Hook.build,
input,
(input, output) =>
buildValidator(input as BuildInput, output as BuildOutput),
(input, output) async => [
for (final e in extensions)
...await e.validateBuildOutput(
input as BuildInput,
output as BuildOutput,
),
],
null,
);
if (result == null) return null;
Expand All @@ -182,7 +164,10 @@ class NativeAssetsBuildRunner {
// in the link step if linking is enableD).
if (linkingEnabled) return hookResult;

final errors = await applicationAssetValidator(hookResult.encodedAssets);
final errors = [
for (final e in extensions)
...await e.validateApplicationAssets(hookResult.encodedAssets),
];
if (errors.isEmpty) return hookResult;

_printErrors('Application asset verification failed', errors);
Expand All @@ -195,13 +180,12 @@ class NativeAssetsBuildRunner {
/// The native assets build runner does not support reentrancy for identical
/// [BuildInput] and [LinkInput]! For more info see:
/// https://github.com/dart-lang/native/issues/1319
///
/// The base protocol can be extended with [extensions]. See
/// [ProtocolExtension] for more documentation.
Future<LinkResult?> link({
required LinkInputCreator inputCreator,
required LinkInputValidator inputValidator,
required LinkValidator linkValidator,
required ApplicationAssetValidator applicationAssetValidator,
required List<ProtocolExtension> extensions,
Uri? resourceIdentifiers,
required List<String> buildAssetTypes,
required BuildResult buildResult,
}) async {
final (buildPlan, packageGraph) = await _makePlan(
Expand All @@ -212,8 +196,13 @@ class NativeAssetsBuildRunner {

var hookResult = HookResult(encodedAssets: buildResult.encodedAssets);
for (final package in buildPlan) {
final inputBuilder =
inputCreator()..config.setupShared(buildAssetTypes: buildAssetTypes);
final inputBuilder = LinkInputBuilder();
inputBuilder.config.setupShared(
buildAssetTypes: [for (final e in extensions) ...e.buildAssetTypes],
);
for (final e in extensions) {
e.setupLinkInput(inputBuilder);
}

final (buildDirUri, outDirUri, outDirSharedUri) = await _setupDirectories(
Hook.link,
Expand Down Expand Up @@ -243,7 +232,7 @@ class NativeAssetsBuildRunner {
final input = LinkInput(inputBuilder.json);
final errors = [
...await validateLinkInput(input),
...await inputValidator(input),
for (final e in extensions) ...await e.validateLinkInput(input),
];
if (errors.isNotEmpty) {
print(input.assets.encodedAssets);
Expand All @@ -256,16 +245,24 @@ class NativeAssetsBuildRunner {
final result = await _runHookForPackageCached(
Hook.link,
input,
(input, output) =>
linkValidator(input as LinkInput, output as LinkOutput),
(input, output) async => [
for (final e in extensions)
...await e.validateLinkOutput(
input as LinkInput,
output as LinkOutput,
),
],
resourceIdentifiers,
);
if (result == null) return null;
final (hookOutput, hookDeps) = result;
hookResult = hookResult.copyAdd(hookOutput, hookDeps);
}

final errors = await applicationAssetValidator(hookResult.encodedAssets);
final errors = [
for (final e in extensions)
...await e.validateApplicationAssets(hookResult.encodedAssets),
];
if (errors.isEmpty) return hookResult;

_printErrors('Application asset verification failed', errors);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ void main() async {
dartExecutable,
capturedLogs: logMessages,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(
logMessages.join('\n'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ void main() async {
createCapturingLogger(logMessages, level: Level.SEVERE),
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
);
final fullLog = logMessages.join('\n');
expect(result, isNull);
Expand Down Expand Up @@ -56,9 +53,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
);
expect(result, isNotNull);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ void main() async {
createCapturingLogger(logMessages, level: Level.SEVERE),
dartExecutable,
buildAssetTypes: [],
inputValidator: (input) async => [],
buildValidator: (input, output) async => [],
applicationAssetValidator: validateCodeAssetInApplication,
);
final fullLog = logMessages.join('\n');
expect(result, isNull);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ void main() async {
dartExecutable,
capturedLogs: logMessages,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(
logMessages.join('\n'),
Expand All @@ -56,9 +53,6 @@ void main() async {
dartExecutable,
capturedLogs: logMessages,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
final hookUri = packageUri.resolve('hook/build.dart');
expect(
Expand Down Expand Up @@ -104,9 +98,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
await expectSymbols(
asset: CodeAsset.fromEncoded(result.encodedAssets.single),
Expand All @@ -127,9 +118,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;

final cUri = packageUri.resolve('src/').resolve('native_add.c');
Expand Down Expand Up @@ -166,9 +154,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
{
final compiledHook =
Expand Down Expand Up @@ -199,9 +184,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;

final hookUri = packageUri.resolve('hook/build.dart');
Expand Down Expand Up @@ -237,9 +219,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
hookEnvironment:
modifiedEnvKey == 'PATH'
? null
Expand Down Expand Up @@ -272,9 +251,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(logMessages.join('\n'), contains('hook.dill'));
expect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ void main() async {
createCapturingLogger(logMessages, level: Level.SEVERE),
dartExecutable,
buildAssetTypes: [],
inputValidator: (input) async => [],
buildValidator: (input, output) async => [],
applicationAssetValidator: (_) async => [],
);
final fullLog = logMessages.join('\n');
expect(result, isNull);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(result.encodedAssets.length, 1);
await expectSymbols(
Expand All @@ -54,9 +51,6 @@ void main() async {
createCapturingLogger(logMessages, level: Level.SEVERE),
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
);
final fullLog = logMessages.join('\n');
expect(result, isNull);
Expand Down Expand Up @@ -87,9 +81,6 @@ void main() async {
logger,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(result.encodedAssets.length, 1);
await expectSymbols(
Expand Down Expand Up @@ -121,9 +112,6 @@ void main() async {
capturedLogs: logMessages,
dartExecutable,
buildAssetTypes: [CodeAsset.type],
inputValidator: validateCodeAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
);
Matcher stringContainsBuildHookCompilation(String packageName) =>
stringContainsInOrder([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ void main() async {
capturedLogs: logMessages,
runPackageName: 'some_dev_dep',
buildAssetTypes: [CodeAsset.type],
inputValidator: validateDataAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(result.encodedAssets, isEmpty);
expect(result.dependencies, isEmpty);
Expand All @@ -47,9 +44,6 @@ void main() async {
capturedLogs: logMessages,
runPackageName: 'native_add',
buildAssetTypes: [CodeAsset.type],
inputValidator: validateDataAssetBuildInput,
buildValidator: validateCodeAssetBuildOutput,
applicationAssetValidator: validateCodeAssetInApplication,
))!;
expect(result.encodedAssets, isNotEmpty);
expect(
Expand Down
Loading

0 comments on commit 0cd2e55

Please sign in to comment.