Skip to content

Commit

Permalink
[native_assets_cli] Add HookInput.outputFile (#1882)
Browse files Browse the repository at this point in the history
Pass in a path to where the `output.json` should be written.

This enables the output file to not be written into the output directory but next to it. This is cleaner.

I don't remember where we discussed this, but some references to adding the output json can be found in the discussions here:

* #1738
  • Loading branch information
dcharkes authored Jan 13, 2025
1 parent ba55784 commit 3832e4b
Show file tree
Hide file tree
Showing 40 changed files with 154 additions and 41 deletions.
47 changes: 37 additions & 10 deletions pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class NativeAssetsBuildRunner {
inputBuilder.setupShared(
packageName: package.name,
packageRoot: packageLayout.packageRoot(package.name),
outputFile: buildDirUri.resolve('output.json'),
outputDirectory: outDirUri,
outputDirectoryShared: outDirSharedUri,
);
Expand Down Expand Up @@ -244,6 +245,7 @@ class NativeAssetsBuildRunner {
inputBuilder.setupShared(
packageName: package.name,
packageRoot: packageLayout.packageRoot(package.name),
outputFile: buildDirUri.resolve('output.json'),
outputDirectory: outDirUri,
outputDirectoryShared: outDirSharedUri,
);
Expand Down Expand Up @@ -350,21 +352,27 @@ class NativeAssetsBuildRunner {
}
final (hookKernelFile, hookHashes) = hookCompileResult;

final buildOutputFile =
_fileSystem.file(input.outputDirectory.resolve(hook.outputName));
final buildOutputFile = _fileSystem.file(input.outputFile);
final buildOutputFileDeprecated = _fileSystem
// ignore: deprecated_member_use
.file(input.outputDirectory.resolve(hook.outputNameDeprecated));

final dependenciesHashFile = input.outputDirectory
.resolve('../dependencies.dependencies_hash_file.json');
final dependenciesHashes =
DependenciesHashFile(_fileSystem, fileUri: dependenciesHashFile);
final lastModifiedCutoffTime = DateTime.now();
if (buildOutputFile.existsSync() && await dependenciesHashes.exists()) {
if ((buildOutputFile.existsSync() ||
buildOutputFileDeprecated.existsSync()) &&
await dependenciesHashes.exists()) {
late final HookOutput output;
try {
output = _readHookOutputFromUri(hook, buildOutputFile);
output = _readHookOutputFromUri(
hook, buildOutputFile, buildOutputFileDeprecated);
} on FormatException catch (e) {
logger.severe('''
Building assets for package:${input.packageName} failed.
${hook.outputName} contained a format error.
${input.outputFile.toFilePath()} contained a format error.
Contents: ${buildOutputFile.readAsStringSync()}.
${e.message}
Expand Down Expand Up @@ -458,12 +466,20 @@ ${e.message}
const JsonEncoder.withIndent(' ').convert(input.json);
logger.info('input.json contents: $inputFileContents');
await _fileSystem.file(inputFile).writeAsString(inputFileContents);
final hookOutputUri = input.outputDirectory.resolve(hook.outputName);
final hookOutputUri = input.outputFile;
final hookOutputFile = _fileSystem.file(hookOutputUri);
if (await hookOutputFile.exists()) {
// Ensure we'll never read outdated build results.
await hookOutputFile.delete();
}
final hookOutputUriDeprecated =
// ignore: deprecated_member_use
input.outputDirectory.resolve(hook.outputNameDeprecated);
final hookOutputFileDeprecated = _fileSystem.file(hookOutputUriDeprecated);
if (await hookOutputFileDeprecated.exists()) {
// Ensure we'll never read outdated build results.
await hookOutputFileDeprecated.delete();
}

final arguments = [
'--packages=${packageConfigUri.toFilePath()}',
Expand Down Expand Up @@ -508,7 +524,11 @@ ${e.message}
return null;
}

final output = _readHookOutputFromUri(hook, hookOutputFile);
final output = _readHookOutputFromUri(
hook,
hookOutputFile,
hookOutputFileDeprecated,
);
final errors = await _validate(input, output, packageLayout, validator);
if (errors.isNotEmpty) {
_printErrors(
Expand All @@ -521,7 +541,7 @@ ${e.message}
} on FormatException catch (e) {
logger.severe('''
Building assets for package:${input.packageName} failed.
${hook.outputName} contained a format error.
${input.outputFile.toFilePath()} contained a format error.
Contents: ${hookOutputFile.readAsStringSync()}.
${e.message}
Expand Down Expand Up @@ -780,10 +800,17 @@ ${compileResult.stdout}
return (buildPlan, packageGraph);
}

HookOutput _readHookOutputFromUri(Hook hook, File hookOutputFile) {
HookOutput _readHookOutputFromUri(
Hook hook,
File hookOutputFile,
// TODO(dcharkes): Remove when hooks with 1.7.0 are no longer supported.
File hookOutputFileDeprecated,
) {
final decode = const Utf8Decoder().fuse(const JsonDecoder()).convert;
final file =
hookOutputFile.existsSync() ? hookOutputFile : hookOutputFileDeprecated;
final hookOutputJson =
decode(hookOutputFile.readAsBytesSync()) as Map<String, Object?>;
decode(file.readAsBytesSync()) as Map<String, Object?>;
return hook == Hook.build
? BuildOutput(hookOutputJson)
: LinkOutput(hookOutputJson);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void main() async {
} else {
expect(
fullLog,
contains('build_output.json contained a format error.'),
contains('output.json contained a format error.'),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ void main() async {
test(
'native_dynamic_linking build',
() => inTempDir((tempUri) async {
final buildOutputUri = tempUri.resolve('build_output.json');
final outputDirectory = tempUri.resolve('out/');
await Directory.fromUri(outputDirectory).create();
final outputDirectoryShared = tempUri.resolve('out_shared/');
Expand All @@ -35,6 +36,7 @@ void main() async {
..setupShared(
packageName: name,
packageRoot: testPackageUri,
outputFile: buildOutputUri,
outputDirectory: outputDirectory,
outputDirectoryShared: outputDirectoryShared,
)
Expand Down Expand Up @@ -69,7 +71,6 @@ void main() async {
}
expect(processResult.exitCode, 0);

final buildOutputUri = outputDirectory.resolve('build_output.json');
final buildOutput = BuildOutput(
json.decode(await File.fromUri(buildOutputUri).readAsString())
as Map<String, Object?>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void main() async {
logger: logger,
);

final buildOutputUri = tempUri.resolve('build_output.json');
final outputDirectory = tempUri.resolve('out/');
await Directory.fromUri(outputDirectory).create();
final outputDirectoryShared = tempUri.resolve('out_shared/');
Expand All @@ -50,6 +51,7 @@ void main() async {
..setupShared(
packageName: packageName,
packageRoot: packageUri,
outputFile: buildOutputUri,
outputDirectory: outputDirectory,
outputDirectoryShared: outputDirectoryShared,
)
Expand Down Expand Up @@ -87,7 +89,6 @@ void main() async {
expect(processResult.exitCode, 0);
stdout = processResult.stdout as String;

final buildOutputUri = outputDirectory.resolve('build_output.json');
output = BuildOutput(
json.decode(await File.fromUri(buildOutputUri).readAsString())
as Map<String, Object?>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ void main(List<String> args) async {
final inputPath = getInputArgument(args);
final buildInput = BuildInput(
json.decode(File(inputPath).readAsStringSync()) as Map<String, Object?>);
await File.fromUri(buildInput.outputDirectory.resolve('build_output.json'))
.writeAsString(_wrongContents);
await File.fromUri(buildInput.outputFile).writeAsString(_wrongContents);
}

const _wrongContents = '''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ void main(List<String> args) async {
final inputPath = getInputArgument(args);
final buildInput = BuildInput(
json.decode(File(inputPath).readAsStringSync()) as Map<String, Object?>);
await File.fromUri(buildInput.outputDirectory.resolve('build_output.json'))
.writeAsString(_wrongContents);
await File.fromUri(buildInput.outputFile).writeAsString(_wrongContents);
}

const _wrongContents = '''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ void main(List<String> args) async {
final inputPath = getInputArgument(args);
final buildInput = BuildInput(
json.decode(File(inputPath).readAsStringSync()) as Map<String, Object?>);
await File.fromUri(buildInput.outputDirectory.resolve('build_output.json'))
.writeAsString(_rightContents);
await File.fromUri(buildInput.outputFile).writeAsString(_rightContents);
exit(1);
}

Expand All @@ -22,5 +21,5 @@ const _rightContents = '''{
"encodedAssets": [],
"dependencies": [],
"metadata": {},
"version": "1.7.0"
"version": "1.8.0"
}''';
1 change: 1 addition & 0 deletions pkgs/native_assets_cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
shouldn't change on subsequent invocations of the same flutter or dart command
for the same target. The `outputDirectory` is the same if the config is the
same.
- **Breaking change** The `output.json` is now part of `BuildInput`.

## 0.10.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,15 @@ BuildInput createBuildInput(
packageRoot.resolve('.dart_tool/download_asset/$targetName/');
final outputDirectoryShared =
packageRoot.resolve('.dart_tool/download_asset/shared/');
final outputFile =
packageRoot.resolve('.dart_tool/download_asset/output.json');

final os = OS.fromString(osString);
final inputBuilder = BuildInputBuilder()
..setupShared(
packageRoot: packageRoot,
packageName: 'download_asset',
outputFile: outputFile,
outputDirectory: outputDirectory,
outputDirectoryShared: outputDirectoryShared)
..config.setupShared(
Expand Down
3 changes: 1 addition & 2 deletions pkgs/native_assets_cli/lib/src/api/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ Future<void> build(
if (errors.isEmpty) {
final jsonOutput =
const JsonEncoder().fuse(const Utf8Encoder()).convert(output.json);
await File.fromUri(input.outputDirectory.resolve('build_output.json'))
.writeAsBytes(jsonOutput);
await File.fromUri(input.outputFile).writeAsBytes(jsonOutput);
} else {
final message = [
'The output contained unsupported output:',
Expand Down
3 changes: 1 addition & 2 deletions pkgs/native_assets_cli/lib/src/api/link.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ Future<void> link(
if (errors.isEmpty) {
final jsonOutput =
const JsonEncoder().fuse(const Utf8Encoder()).convert(output.json);
await File.fromUri(input.outputDirectory.resolve('link_output.json'))
.writeAsBytes(jsonOutput);
await File.fromUri(input.outputFile).writeAsBytes(jsonOutput);
} else {
final message = [
'The output contained unsupported output:',
Expand Down
22 changes: 19 additions & 3 deletions pkgs/native_assets_cli/lib/src/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ sealed class HookInput {
/// each other.
final Uri outputDirectoryShared;

/// The file to write the [HookOutput] to at the end of a hook invocation.
Uri get outputFile;

/// The name of the package the assets are built for.
final String packageName;

Expand Down Expand Up @@ -91,9 +94,11 @@ sealed class HookInputBuilder {
required String packageName,
required Uri outputDirectory,
required Uri outputDirectoryShared,
required Uri outputFile,
}) {
json[_packageNameInputKey] = packageName;
json[_packageRootInputKey] = packageRoot.toFilePath();
json[_outputFileKey] = outputFile.toFilePath();
json[_outDirInputKey] = outputDirectory.toFilePath();
json[_outDirSharedInputKey] = outputDirectoryShared.toFilePath();
}
Expand Down Expand Up @@ -121,6 +126,7 @@ sealed class HookInputBuilder {
// TODO: Bump min-SDK constraint to 3.7 and remove once stable.
const _buildModeInputKeyDeprecated = 'build_mode';
const _metadataConfigKey = 'metadata';
const _outputFileKey = 'out_file';
const _outDirInputKey = 'out_dir';
const _outDirSharedInputKey = 'out_dir_shared';
const _packageNameInputKey = 'package_name';
Expand All @@ -133,8 +139,13 @@ const _configKey = 'config';
final class BuildInput extends HookInput {
final Map<String, Metadata> metadata;

@override
final Uri outputFile;

BuildInput(super.json)
: metadata = {
: outputFile = json.optionalPath(_outputFileKey) ??
json.path(_outDirInputKey).resolve('build_output.json'),
metadata = {
for (final entry
in (json.optionalMap(_dependencyMetadataKey) ?? {}).entries)
entry.key: Metadata.fromJson(as<Map<String, Object?>>(entry.value)),
Expand Down Expand Up @@ -204,8 +215,13 @@ final class LinkInput extends HookInput {

final Uri? recordedUsagesFile;

@override
final Uri outputFile;

LinkInput(super.json)
: _encodedAssets =
: outputFile = json.optionalPath(_outputFileKey) ??
json.path(_outDirInputKey).resolve('link_output.json'),
_encodedAssets =
_parseAssets(json.getOptional<List<Object?>>(_assetsKey)),
recordedUsagesFile = json.optionalPath(_recordedUsagesFileInputKey);

Expand Down Expand Up @@ -578,7 +594,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, 7, 0);
final latestVersion = Version(1, 8, 0);

/// The parser can deal with inputs and outputs down to this version.
///
Expand Down
14 changes: 14 additions & 0 deletions pkgs/native_assets_cli/lib/src/model/build_config_CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## 1.8.0

- Add `BuildInput.outputFile` to specify the outfile. This means the out file
can be outside the `outputDirectory` and avoid potential conflicts.
Compatibility with older hooks: If the file doesn't exist, try the previous
location.
Compatibility with older SDKs: Default the location to where it was.

## 1.7.0

- Complete rewrite of JSON
Compatibility with older hooks: also emit old structure.
Compatibility with older SDKs: keep parsing old structure.

## 1.6.0

- `BuildConfig.supportedAssetTypes` renamed to `BuildConfig.buildAssetTypes`.
Expand Down
3 changes: 2 additions & 1 deletion pkgs/native_assets_cli/lib/src/model/hook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ enum Hook {

String get scriptName => '$_scriptName.dart';

String get outputName => '${_scriptName}_output.json';
@Deprecated('Use HookInput.outputFile instead.')
String get outputNameDeprecated => '${_scriptName}_output.json';
}
8 changes: 4 additions & 4 deletions pkgs/native_assets_cli/lib/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import 'dart:io';
import 'package:yaml/yaml.dart';

import 'native_assets_cli_builder.dart';
import 'native_assets_cli_internal.dart' show Hook;
import 'src/validation.dart';

export 'native_assets_cli_builder.dart';
Expand Down Expand Up @@ -41,6 +40,7 @@ Future<void> testBuildHook({
Directory(await tempDir.resolveSymbolicLinks()).uri.normalizePath();
final outputDirectory = tempUri.resolve('output/');
final outputDirectoryShared = tempUri.resolve('output_shared/');
final outputFile = tempUri.resolve('output.json');

await Directory.fromUri(outputDirectory).create();
await Directory.fromUri(outputDirectoryShared).create();
Expand All @@ -50,6 +50,7 @@ Future<void> testBuildHook({
..setupShared(
packageRoot: Directory.current.uri,
packageName: _readPackageNameFromPubspec(),
outputFile: outputFile,
outputDirectory: outputDirectory,
outputDirectoryShared: outputDirectoryShared,
)
Expand All @@ -61,11 +62,10 @@ Future<void> testBuildHook({

final input = BuildInput(inputBuilder.json);

final inputUri = tempUri.resolve(Hook.build.outputName);
final inputUri = tempUri.resolve('input.json');
_writeJsonTo(inputUri, input.json);
await mainMethod(['--config=${inputUri.toFilePath()}']);
final output = BuildOutput(
_readJsonFrom(input.outputDirectory.resolve(Hook.build.outputName)));
final output = BuildOutput(_readJsonFrom(input.outputFile));

// Test conformance of protocol invariants.
final validationErrors = await validateBuildOutput(input, output);
Expand Down
Loading

0 comments on commit 3832e4b

Please sign in to comment.