Skip to content

[native_assets_cli] Add user_defines #2165

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

Merged
merged 3 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/native.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ jobs:
- run: dart pub get -C test_data/reuse_dynamic_library/
if: ${{ matrix.package == 'native_assets_builder' }}

- run: dart pub get -C test_data/user_defines/
if: ${{ matrix.package == 'native_assets_builder' }}

- run: dart pub get -C test_data/no_hook/
if: ${{ matrix.package == 'native_assets_builder' }}

Expand Down
3 changes: 3 additions & 0 deletions pkgs/code_assets/test/data/build_input_macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,8 @@
"out_file": "/Users/dacoharkes/src/dacoharkes/playground/my_package/example/.dart_tool/native_assets_builder/my_package/ca4e7d3d4e7b8912cbd24d9e8a6cecdc/output.json",
"package_name": "my_package",
"package_root": "/Users/dacoharkes/src/dacoharkes/playground/my_package/",
"user_defines": {
"some_key": "some_value"
},
"version": "1.9.0"
}
3 changes: 3 additions & 0 deletions pkgs/code_assets/test/data/link_input_macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,8 @@
"out_file": "/Users/dacoharkes/src/dacoharkes/playground/my_package/example/.dart_tool/native_assets_builder/my_package/ca4e7d3d4e7b8912cbd24d9e8a6cecdc/output.json",
"package_name": "my_package",
"package_root": "/Users/dacoharkes/src/dacoharkes/playground/my_package/",
"user_defines": {
"some_key": "some_value"
},
"version": "1.9.0"
}
3 changes: 3 additions & 0 deletions pkgs/data_assets/test/data/build_input.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@
"out_file": "/Users/dacoharkes/src/dacoharkes/playground/my_package/example/.dart_tool/native_assets_builder/my_package/ca4e7d3d4e7b8912cbd24d9e8a6cecdc/output.json",
"package_name": "my_package",
"package_root": "/Users/dacoharkes/src/dacoharkes/playground/my_package/",
"user_defines": {
"some_key": "some_value"
},
"version": "1.9.0"
}
3 changes: 3 additions & 0 deletions pkgs/data_assets/test/data/link_input.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@
"out_file": "/Users/dacoharkes/src/dacoharkes/playground/my_package/example/.dart_tool/native_assets_builder/my_package/ca4e7d3d4e7b8912cbd24d9e8a6cecdc/output.json",
"package_name": "my_package",
"package_root": "/Users/dacoharkes/src/dacoharkes/playground/my_package/",
"user_defines": {
"some_key": "some_value"
},
"version": "1.9.0"
}
4 changes: 4 additions & 0 deletions pkgs/hooks/doc/schema/shared/shared_definitions.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@
"package_root": {
"$ref": "#/definitions/absolutePath"
},
"user_defines": {
"type": "object",
"additionalProperties": true
},
"version": {
"type": "string"
}
Expand Down
3 changes: 3 additions & 0 deletions pkgs/hooks/test/data/build_input.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@
"out_file": "/Users/dacoharkes/src/dacoharkes/playground/my_package/example/.dart_tool/native_assets_builder/my_package/ca4e7d3d4e7b8912cbd24d9e8a6cecdc/output.json",
"package_name": "my_package",
"package_root": "/Users/dacoharkes/src/dacoharkes/playground/my_package/",
"user_defines": {
"some_key": "some_value"
},
"version": "1.9.0"
}
3 changes: 3 additions & 0 deletions pkgs/hooks/test/data/link_input.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@
"out_file": "/Users/dacoharkes/src/dacoharkes/playground/my_package/example/.dart_tool/native_assets_builder/my_package/ca4e7d3d4e7b8912cbd24d9e8a6cecdc/output.json",
"package_name": "my_package",
"package_root": "/Users/dacoharkes/src/dacoharkes/playground/my_package/",
"user_defines": {
"some_key": "some_value"
},
"version": "1.9.0"
}
1 change: 1 addition & 0 deletions pkgs/hooks/test/schema/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ FieldsReturn _hookFields({
([r'$schema'], expectOptionalFieldMissing),
(['version'], versionMissingExpectation),
if (inputOrOutput == InputOrOutput.input) ...[
(['user_defines'], expectOptionalFieldMissing),
(['out_dir_shared'], expectRequiredFieldMissing),
(['out_dir'], expectRequiredFieldMissing),
(['package_name'], expectRequiredFieldMissing),
Expand Down
78 changes: 78 additions & 0 deletions pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class NativeAssetsBuildRunner {
final Uri dartExecutable;
final Duration singleHookTimeout;
final Map<String, String> hookEnvironment;
final Map<String, Map<String, Object?>?> userDefines;
final PackageLayout packageLayout;

NativeAssetsBuildRunner({
Expand All @@ -55,6 +56,7 @@ class NativeAssetsBuildRunner {
required this.packageLayout,
Duration? singleHookTimeout,
Map<String, String>? hookEnvironment,
this.userDefines = const {},
}) : _fileSystem = fileSystem,
singleHookTimeout = singleHookTimeout ?? const Duration(minutes: 5),
hookEnvironment =
Expand Down Expand Up @@ -131,6 +133,7 @@ class NativeAssetsBuildRunner {
outputFile: buildDirUri.resolve('output.json'),
outputDirectory: outDirUri,
outputDirectoryShared: outDirSharedUri,
userDefines: userDefines[package.name],
);

final input = BuildInput(inputBuilder.json);
Expand Down Expand Up @@ -228,6 +231,7 @@ class NativeAssetsBuildRunner {
outputFile: buildDirUri.resolve('output.json'),
outputDirectory: outDirUri,
outputDirectoryShared: outDirSharedUri,
userDefines: userDefines[package.name],
);
inputBuilder.setupLink(
assets: buildResult.encodedAssetsForLinking[package.name] ?? [],
Expand Down Expand Up @@ -861,6 +865,80 @@ ${compileResult.stdout}
? BuildOutput(hookOutputJson)
: LinkOutput(hookOutputJson);
}

/// Returns a list of errors for [readHooksUserDefinesFromPubspec].
static List<String> validateHooksUserDefinesFromPubspec(
Map<Object?, Object?> pubspec,
) {
final hooks = pubspec['hooks'];
if (hooks == null) return [];
if (hooks is! Map) {
return ["Expected 'hooks' to be a map. Found: '$hooks'"];
}
final userDefines = hooks['user_defines'];
if (userDefines == null) return [];
if (userDefines is! Map) {
return [
"Expected 'hooks.user_defines' to be a map. Found: '$userDefines'",
];
}

final errors = <String>[];
for (final MapEntry(:key, :value) in userDefines.entries) {
if (key is! String) {
errors.add(
"Expected 'hooks.user_defines' to be a map with string keys."
" Found key: '$key'.",
);
}
if (value is! Map) {
errors.add(
"Expected 'hooks.user_defines.$key' to be a map. Found: '$value'",
);
continue;
}
for (final childKey in value.keys) {
if (childKey is! String) {
errors.add(
"Expected 'hooks.user_defines.$key' to be a "
"map with string keys. Found key: '$childKey'.",
);
}
}
}
return errors;
}

/// Reads the user-defines from a pubspec.yaml in the suggested location.
///
/// SDKs do not have to follow this, they might support user-defines in a
/// different way.
///
/// The [pubspec] is expected to be the decoded yaml, a Map.
///
/// Before invoking, check errors with [validateHooksUserDefinesFromPubspec].
static Map<String, Map<String, Object?>> readHooksUserDefinesFromPubspec(
Map<Object?, Object?> pubspec,
) {
assert(validateHooksUserDefinesFromPubspec(pubspec).isEmpty);
final hooks = pubspec['hooks'];
if (hooks is! Map) {
return {};
}
final userDefines = hooks['user_defines'];
if (userDefines is! Map) {
return {};
}
return {
for (final MapEntry(:key, :value) in userDefines.entries)
if (key is String)
key: {
if (value is Map)
for (final MapEntry(:key, :value) in value.entries)
if (key is String) key: value,
},
};
}
}

/// Parses depfile contents.
Expand Down
2 changes: 2 additions & 0 deletions pkgs/native_assets_builder/test/build_runner/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Future<BuildResult?> build(
bool linkingEnabled = false,
required List<BuildAssetType> buildAssetTypes,
Map<String, String>? hookEnvironment,
Map<String, Map<String, Object?>?>? userDefines,
}) async {
final targetOS = target?.os ?? OS.current;
final runPackageName_ =
Expand All @@ -91,6 +92,7 @@ Future<BuildResult?> build(
fileSystem: const LocalFileSystem(),
hookEnvironment: hookEnvironment,
packageLayout: packageLayout,
userDefines: userDefines ?? {},
).build(
extensions: [
if (buildAssetTypes.contains(BuildAssetType.code))
Expand Down
56 changes: 56 additions & 0 deletions pkgs/native_assets_builder/test/test_data/user_defines_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

@OnPlatform({'mac-os': Timeout.factor(2), 'windows': Timeout.factor(10)})
library;

import 'dart:io';

import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';

import '../build_runner/helpers.dart';
import '../helpers.dart';

void main() async {
const name = 'user_defines';

test(
'$name build',
() => inTempDir((tempUri) async {
await copyTestProjects(targetUri: tempUri);
final packageUri = tempUri.resolve('$name/');

await runPubGet(workingDirectory: packageUri, logger: logger);

final pubspec =
loadYamlDocument(
File.fromUri(
packageUri.resolve('pubspec.yaml'),
).readAsStringSync(),
).contents
as YamlMap;
expect(
NativeAssetsBuildRunner.validateHooksUserDefinesFromPubspec(pubspec),
isEmpty,
);
final userDefines =
NativeAssetsBuildRunner.readHooksUserDefinesFromPubspec(pubspec);

final logMessages = <String>[];
final result =
(await build(
packageUri,
logger,
dartExecutable,
capturedLogs: logMessages,
buildAssetTypes: [BuildAssetType.data],
userDefines: userDefines,
))!;

expect(result.encodedAssets.length, 1);
}),
);
}
2 changes: 2 additions & 0 deletions pkgs/native_assets_builder/test_data/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@
- use_all_api/hook/build.dart
- use_all_api/hook/link.dart
- use_all_api/pubspec.yaml
- user_defines/hook/build.dart
- user_defines/pubspec.yaml
- wrong_build_output/hook/build.dart
- wrong_build_output/pubspec.yaml
- wrong_build_output_2/hook/build.dart
Expand Down
27 changes: 27 additions & 0 deletions pkgs/native_assets_builder/test_data/user_defines/hook/build.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:convert';
import 'dart:io';

import 'package:native_assets_cli/data_assets.dart';

void main(List<String> arguments) async {
await build(arguments, (input, output) async {
final value1 = input.userDefines['user_define_key'];
if (value1 != 'user_define_value') {
throw Exception(
'User-define user_define_key does not have the right value.',
);
}
final value2 = input.userDefines['user_define_key2'];
final dataAsset = DataAsset(
file: input.outputDirectoryShared.resolve('my_asset.json'),
name: 'my_asset',
package: input.packageName,
);
File.fromUri(dataAsset.file).writeAsStringSync(jsonEncode(value2));
output.assets.data.add(dataAsset);
});
}
14 changes: 14 additions & 0 deletions pkgs/native_assets_cli/lib/src/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ sealed class HookInput {
String toString() => const JsonEncoder.withIndent(' ').convert(json);

HookConfig get config => HookConfig._(this);

/// The user-defines for this [packageName].
HookInputUserDefines get userDefines => HookInputUserDefines._(this);
}

extension type HookInputUserDefines._(HookInput _input) {
/// The value for the user-define for [key] for this package.
///
/// This can be arbitrary JSON/YAML if provided from the SDK from such source.
/// If it's provided from command-line arguments, it's likely a string.
Object? operator [](String key) => _input._syntax.userDefines?.json[key];
}

sealed class HookInputBuilder {
Expand All @@ -110,13 +121,16 @@ sealed class HookInputBuilder {
required Uri outputDirectory,
required Uri outputDirectoryShared,
required Uri outputFile,
Map<String, Object?>? userDefines,
}) {
_syntax.version = latestVersion.toString();
_syntax.packageRoot = packageRoot;
_syntax.packageName = packageName;
_syntax.outDir = outputDirectory;
_syntax.outDirShared = outputDirectoryShared;
_syntax.outFile = outputFile;
_syntax.userDefines =
userDefines == null ? null : syntax.JsonObject.fromJson(userDefines);
}

/// Constructs a checksum for a [BuildInput].
Expand Down
Loading