Skip to content

Commit

Permalink
[native_assets_cli] Make BuildInput JSON hierarchical (#1872)
Browse files Browse the repository at this point in the history
* [native_assets_cli] Make JSON serialization hierarchical

* [native_assets_cli] Parse hierarchical JSON

* one more deprecated key
  • Loading branch information
dcharkes authored Jan 8, 2025
1 parent 70c0eaf commit 14368a8
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ const _rightContents = '''{
"encodedAssets": [],
"dependencies": [],
"metadata": {},
"version": "1.6.0"
"version": "1.7.0"
}''';
90 changes: 72 additions & 18 deletions pkgs/native_assets_cli/lib/src/code_assets/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,22 @@ class CodeConfig {
factory CodeConfig.fromJson(Map<String, Object?> json) {
final dryRun = json.getOptional<bool>(_dryRunConfigKey) ?? false;

final linkModePreference =
LinkModePreference.fromString(json.string(_linkModePreferenceKey));
final linkModePreference = LinkModePreference.fromString(
json.code?.optionalString(_linkModePreferenceKey) ??
json.string(_linkModePreferenceKey),
);
final targetArchitecture = dryRun
? null
: Architecture.fromString(json.string(_targetArchitectureKey,
validValues: Architecture.values.map((a) => a.name)));
final targetOS = OS.fromString(json.string(_targetOSConfigKey));
final cCompiler = switch (json.optionalMap(_compilerKey)) {
: Architecture.fromString(json.code?.optionalString(
_targetArchitectureKey,
validValues: Architecture.values.map((a) => a.name)) ??
json.string(_targetArchitectureKey,
validValues: Architecture.values.map((a) => a.name)));
final targetOS = OS.fromString(
json.code?.optionalString(_targetOSConfigKey) ??
json.string(_targetOSConfigKey));
final cCompiler = switch (json.code?.optionalMap(_compilerKey) ??
json.optionalMap(_compilerKey)) {
final Map<String, Object?> map => CCompilerConfig.fromJson(map),
null => null
};
Expand Down Expand Up @@ -151,8 +159,12 @@ class IOSConfig {
_targetVersion = targetVersion;

IOSConfig.fromJson(Map<String, Object?> json)
: _targetVersion = json.optionalInt(_targetIOSVersionKey),
_targetSdk = switch (json.optionalString(_targetIOSSdkKey)) {
: _targetVersion =
json.code?.optionalMap(_iosKey)?.optionalInt(_targetVersionKey) ??
json.optionalInt(_targetIOSVersionKeyDeprecated),
_targetSdk = switch (
json.code?.optionalMap(_iosKey)?.optionalString(_targetSdkKey) ??
json.optionalString(_targetIOSSdkKeyDeprecated)) {
null => null,
String e => IOSSdk.fromString(e)
};
Expand All @@ -176,7 +188,10 @@ class AndroidConfig {
}) : _targetNdkApi = targetNdkApi;

AndroidConfig.fromJson(Map<String, Object?> json)
: _targetNdkApi = json.optionalInt(_targetAndroidNdkApiKey);
: _targetNdkApi = json.code
?.optionalMap(_androidKey)
?.optionalInt(_targetNdkApiKey) ??
json.optionalInt(_targetAndroidNdkApiKeyDeprecated);
}

extension AndroidConfigSyntactic on AndroidConfig {
Expand All @@ -195,7 +210,9 @@ class MacOSConfig {
}) : _targetVersion = targetVersion;

MacOSConfig.fromJson(Map<String, Object?> json)
: _targetVersion = json.optionalInt(_targetMacOSVersionKey);
: _targetVersion =
json.code?.optionalMap(_macosKey)?.optionalInt(_targetVersionKey) ??
json.optionalInt(_targetMacOSVersionKeyDeprecated);
}

extension MacOSConfigSyntactic on MacOSConfig {
Expand Down Expand Up @@ -258,22 +275,45 @@ extension CodeAssetBuildInputBuilder on HookConfigBuilder {
}) {
if (targetArchitecture != null) {
json[_targetArchitectureKey] = targetArchitecture.toString();
json.setNested([_configKey, _codeKey, _targetArchitectureKey],
targetArchitecture.toString());
}
json[_targetOSConfigKey] = targetOS.toString();
json.setNested(
[_configKey, _codeKey, _targetOSConfigKey], targetOS.toString());
json[_linkModePreferenceKey] = linkModePreference.toString();
json.setNested([_configKey, _codeKey, _linkModePreferenceKey],
linkModePreference.toString());
if (cCompiler != null) {
json[_compilerKey] = cCompiler.toJson();
json.setNested([_configKey, _codeKey, _compilerKey], cCompiler.toJson());
}

// Note, using ?. instead of !. makes missing data be a semantic error
// rather than a syntactic error to be caught in the validation.
if (targetOS == OS.android) {
json[_targetAndroidNdkApiKey] = android?.targetNdkApi;
json[_targetAndroidNdkApiKeyDeprecated] = android?.targetNdkApi;
json.setNested(
[_configKey, _codeKey, _androidKey, _targetNdkApiKey],
android?.targetNdkApi,
);
} else if (targetOS == OS.iOS) {
json[_targetIOSSdkKey] = iOS?.targetSdk.toString();
json[_targetIOSVersionKey] = iOS?.targetVersion;
json[_targetIOSSdkKeyDeprecated] = iOS?.targetSdk.toString();
json[_targetIOSVersionKeyDeprecated] = iOS?.targetVersion;
json.setNested(
[_configKey, _codeKey, _iosKey, _targetSdkKey],
iOS?.targetSdk.toString(),
);
json.setNested(
[_configKey, _codeKey, _iosKey, _targetVersionKey],
iOS?.targetVersion,
);
} else if (targetOS == OS.macOS) {
json[_targetMacOSVersionKey] = macOS?.targetVersion;
json[_targetMacOSVersionKeyDeprecated] = macOS?.targetVersion;
json.setNested(
[_configKey, _codeKey, _macosKey, _targetVersionKey],
macOS?.targetVersion,
);
}
}
}
Expand All @@ -298,11 +338,25 @@ extension CodeAssetLinkOutput on LinkOutputAssets {

const String _compilerKey = 'c_compiler';
const String _linkModePreferenceKey = 'link_mode_preference';
const String _targetAndroidNdkApiKey = 'target_android_ndk_api';
const String _targetNdkApiKey = 'target_ndk_api';
const String _targetAndroidNdkApiKeyDeprecated = 'target_android_ndk_api';
const String _targetArchitectureKey = 'target_architecture';
const String _targetIOSSdkKey = 'target_ios_sdk';
const String _targetIOSVersionKey = 'target_ios_version';
const String _targetMacOSVersionKey = 'target_macos_version';
const String _targetSdkKey = 'target_sdk';
const String _targetIOSSdkKeyDeprecated = 'target_ios_sdk';
const String _targetVersionKey = 'target_version';
const String _targetIOSVersionKeyDeprecated = 'target_ios_version';
const String _targetMacOSVersionKeyDeprecated = 'target_macos_version';
const String _targetOSConfigKey = 'target_os';

const _dryRunConfigKey = 'dry_run';

const _configKey = 'config';
const _codeKey = 'code';
const _androidKey = 'android';
const _iosKey = 'ios';
const _macosKey = 'macos';

extension on Map<String, Object?> {
Map<String, Object?>? get code =>
optionalMap(_configKey)?.optionalMap(_codeKey);
}
28 changes: 14 additions & 14 deletions pkgs/native_assets_cli/lib/src/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,9 @@ sealed class HookInputBuilder {
/// [BuildInput] being built with this [BuildInputBuilder]. It is therefore
/// assumed the output directory has not been set yet.
String computeChecksum() {
if (json.containsKey(_outDirInputKey) ||
json.containsKey(_outDirSharedInputKey) ||
json.containsKey(_assetsKey)) {
// The bundling tools would first calculate the checksum, create an output
// directory and then call [setupHookInput].
// The output directory should not depend on the assets passed in for
// linking.
throw StateError('The checksum should be generated before setting '
'up the hook configuration');
}
final config = json[_configKey];
final hash = sha256
.convert(const JsonEncoder().fuse(const Utf8Encoder()).convert(json))
.convert(const JsonEncoder().fuse(const Utf8Encoder()).convert(config))
.toString()
// 256 bit hashes lead to 64 hex character strings.
// To avoid overflowing file paths limits, only use 32.
Expand All @@ -137,6 +128,8 @@ const _packageRootInputKey = 'package_root';
const _supportedAssetTypesKey = 'supported_asset_types';
const _buildAssetTypesKey = 'build_asset_types';

const _configKey = 'config';

final class BuildInput extends HookInput {
final Map<String, Metadata> metadata;

Expand Down Expand Up @@ -179,6 +172,7 @@ final class HookConfigBuilder {
}) {
json[_buildAssetTypesKey] = buildAssetTypes;
json[_supportedAssetTypesKey] = buildAssetTypes;
json.setNested([_configKey, _buildAssetTypesKey], buildAssetTypes);
}
}

Expand All @@ -193,6 +187,7 @@ extension BuildConfigBuilderSetup on BuildConfigBuilder {
}) {
json[_dryRunConfigKey] = dryRun;
json[_linkingEnabledKey] = linkingEnabled;
json.setNested([_configKey, _linkingEnabledKey], linkingEnabled);

// TODO: Bump min-SDK constraint to 3.7 and remove once stable.
if (!dryRun) {
Expand Down Expand Up @@ -583,7 +578,7 @@ extension type EncodedAssetLinkOutputBuilder._(LinkOutputBuilder _builder) {
///
/// We'll never bump the major version. Removing old keys from the input and
/// output is done via modifying [latestParsableVersion].
final latestVersion = Version(1, 6, 0);
final latestVersion = Version(1, 7, 0);

/// The parser can deal with inputs and outputs down to this version.
///
Expand All @@ -607,7 +602,10 @@ final class HookConfig {
final List<String> buildAssetTypes;

HookConfig(this.json)
: buildAssetTypes = json.optionalStringList(_buildAssetTypesKey) ??
: buildAssetTypes = json
.optionalMap(_configKey)
?.optionalStringList(_buildAssetTypesKey) ??
json.optionalStringList(_buildAssetTypesKey) ??
json.optionalStringList(_supportedAssetTypesKey) ??
const [];
}
Expand All @@ -627,5 +625,7 @@ final class BuildConfig extends HookConfig {
BuildConfig(super.json)
// ignore: deprecated_member_use_from_same_package
: dryRun = json.getOptional<bool>(_dryRunConfigKey) ?? false,
linkingEnabled = json.get<bool>(_linkingEnabledKey);
linkingEnabled =
json.optionalMap(_configKey)?.optionalBool(_linkingEnabledKey) ??
json.get<bool>(_linkingEnabledKey);
}
18 changes: 17 additions & 1 deletion pkgs/native_assets_cli/lib/src/json_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ extension MapJsonUtils on Map<String, Object?> {
return value;
}

String? optionalString(String key) => getOptional<String>(key);
String? optionalString(String key, {Iterable<String>? validValues}) {
final value = getOptional<String>(key);
if (value == null) return null;
if (validValues != null && !validValues.contains(value)) {
throw FormatException('Json "$key" had value $value but expected one of '
'${validValues.join(',')}');
}
return value;
}

bool? optionalBool(String key) => getOptional<bool>(key);
core.int int(String key) => get<core.int>(key);
Expand Down Expand Up @@ -62,6 +70,14 @@ extension MapJsonUtils on Map<String, Object?> {
'Unexpected value \'$value\' for key \'.$key\' in input file. '
'Expected a $T?.');
}

void setNested(List<String> nestedMapKeys, Object? value) {
var map = this;
for (final key in nestedMapKeys.sublist(0, nestedMapKeys.length - 1)) {
map = (map[key] ??= <String, Object?>{}) as Map<String, Object?>;
}
map[nestedMapKeys.last] = value;
}
}

extension ListJsonUtils on List<Object?> {
Expand Down
8 changes: 8 additions & 0 deletions pkgs/native_assets_cli/test/build_config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ void main() async {
final expectedInputJson = {
'build_asset_types': ['my-asset-type'],
'build_mode': 'release',
'config': {
'build_asset_types': ['my-asset-type'],
'linking_enabled': false,
},
'dependency_metadata': {
'bar': {
'key': 'value',
Expand Down Expand Up @@ -108,6 +112,10 @@ void main() async {

final expectedInputJson = {
'build_asset_types': ['my-asset-type'],
'config': {
'build_asset_types': ['my-asset-type'],
'linking_enabled': true,
},
'dependency_metadata': <String, Object?>{},
'dry_run': true,
'linking_enabled': true,
Expand Down
2 changes: 1 addition & 1 deletion pkgs/native_assets_cli/test/build_output_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void main() {

// The JSON format of the build output.
<String, Object?>{
'version': '1.6.0',
'version': '1.7.0',
'dependencies': ['path0', 'path1', 'path2'],
'metadata': {
'meta-a': 'meta-b',
Expand Down
Loading

0 comments on commit 14368a8

Please sign in to comment.