Skip to content

Commit cdb04e3

Browse files
authored
[native_assets_builder] Stop throwing from BuildRunner (#108)
1 parent 10fd63e commit cdb04e3

File tree

21 files changed

+502
-51
lines changed

21 files changed

+502
-51
lines changed

pkgs/native_assets_builder/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
## 0.2.0-wip
22

3-
- **Breaking change** `NativeAssetsBuildRunner`s method now return an object
3+
- **Breaking change** `NativeAssetsBuildRunner`s methods now return an object
44
([#105](https://github.com/dart-lang/native/issues/105)).
5+
- **Breaking change** `NativeAssetsBuildRunner`s methods now return value now
6+
contain a success bool instead of throwing
7+
([#106](https://github.com/dart-lang/native/issues/106)). Error messages are
8+
streamed to the logger.
59
- Use an `out/` sub directory for building native assets
610
([#98](https://github.com/dart-lang/native/issues/98)).
711
- Check asset ids on having having a package uri with the owning package

pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,27 @@ import 'dart:convert';
66
import 'dart:io';
77

88
import 'package:graphs/graphs.dart' as graphs;
9+
import 'package:logging/logging.dart';
910
import 'package:package_config/package_config.dart';
1011

1112
class NativeAssetsBuildPlanner {
1213
final PackageGraph packageGraph;
1314
final List<Package> packagesWithNativeAssets;
1415
final Uri dartExecutable;
16+
final Logger logger;
1517

1618
NativeAssetsBuildPlanner({
1719
required this.packageGraph,
1820
required this.packagesWithNativeAssets,
1921
required this.dartExecutable,
22+
required this.logger,
2023
});
2124

2225
static Future<NativeAssetsBuildPlanner> fromRootPackageRoot({
2326
required Uri rootPackageRoot,
2427
required List<Package> packagesWithNativeAssets,
2528
required Uri dartExecutable,
29+
required Logger logger,
2630
}) async {
2731
final result = await Process.run(
2832
dartExecutable.toFilePath(),
@@ -39,32 +43,35 @@ class NativeAssetsBuildPlanner {
3943
packageGraph: packageGraph,
4044
packagesWithNativeAssets: packagesWithNativeAssets,
4145
dartExecutable: dartExecutable,
46+
logger: logger,
4247
);
4348
}
4449

45-
List<Package> plan() {
50+
(List<Package> packages, bool success) plan() {
4651
final packageMap = {
4752
for (final package in packagesWithNativeAssets) package.name: package
4853
};
4954
final packagesToBuild = packageMap.keys.toSet();
5055
final stronglyConnectedComponents = packageGraph.computeStrongComponents();
5156
final result = <Package>[];
57+
var success = true;
5258
for (final stronglyConnectedComponent in stronglyConnectedComponents) {
5359
final stronglyConnectedComponentWithNativeAssets = [
5460
for (final packageName in stronglyConnectedComponent)
5561
if (packagesToBuild.contains(packageName)) packageName
5662
];
5763
if (stronglyConnectedComponentWithNativeAssets.length > 1) {
58-
throw Exception(
64+
logger.severe(
5965
'Cyclic dependency for native asset builds in the following '
60-
'packages: $stronglyConnectedComponent.',
66+
'packages: $stronglyConnectedComponentWithNativeAssets.',
6167
);
68+
success = false;
6269
} else if (stronglyConnectedComponentWithNativeAssets.length == 1) {
6370
result.add(
6471
packageMap[stronglyConnectedComponentWithNativeAssets.single]!);
6572
}
6673
}
67-
return result;
74+
return (result, success);
6875
}
6976
}
7077

pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart

Lines changed: 127 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,20 @@ class NativeAssetsBuildRunner {
5151
rootPackageRoot: packageLayout.rootPackageRoot,
5252
packagesWithNativeAssets: packagesWithNativeAssets,
5353
dartExecutable: Uri.file(Platform.resolvedExecutable),
54+
logger: logger,
5455
);
55-
final plan = planner.plan();
56+
final (plan, planSuccess) = planner.plan();
57+
if (!planSuccess) {
58+
return _BuildResultImpl(
59+
assets: [],
60+
dependencies: [],
61+
success: false,
62+
);
63+
}
5664
final assets = <Asset>[];
5765
final dependencies = <Uri>[];
5866
final metadata = <String, Metadata>{};
67+
var success = true;
5968
for (final package in plan) {
6069
final dependencyMetadata = _metadataForPackage(
6170
packageGraph: planner.packageGraph,
@@ -73,22 +82,28 @@ class NativeAssetsBuildRunner {
7382
targetIOSSdk: targetIOSSdk,
7483
targetAndroidNdkApi: targetAndroidNdkApi,
7584
);
76-
final (packageAssets, packageDependencies, packageMetadata) =
77-
await _buildPackageCached(
85+
final (
86+
packageAssets,
87+
packageDependencies,
88+
packageMetadata,
89+
packageSuccess,
90+
) = await _buildPackageCached(
7891
config,
7992
packageLayout.packageConfigUri,
8093
workingDirectory,
8194
includeParentEnvironment,
8295
);
8396
assets.addAll(packageAssets);
8497
dependencies.addAll(packageDependencies);
98+
success &= packageSuccess;
8599
if (packageMetadata != null) {
86100
metadata[config.packageName] = packageMetadata;
87101
}
88102
}
89103
return _BuildResultImpl(
90104
assets: assets,
91105
dependencies: dependencies..sort(_uriCompare),
106+
success: success,
92107
);
93108
}
94109

@@ -112,9 +127,17 @@ class NativeAssetsBuildRunner {
112127
rootPackageRoot: packageLayout.rootPackageRoot,
113128
packagesWithNativeAssets: packagesWithNativeAssets,
114129
dartExecutable: Uri.file(Platform.resolvedExecutable),
130+
logger: logger,
115131
);
116-
final plan = planner.plan();
132+
final (plan, planSuccess) = planner.plan();
133+
if (!planSuccess) {
134+
return _DryRunResultImpl(
135+
assets: [],
136+
success: false,
137+
);
138+
}
117139
final assets = <Asset>[];
140+
var success = true;
118141
for (final package in plan) {
119142
final config = await _cliConfigDryRun(
120143
packageName: package.name,
@@ -123,21 +146,23 @@ class NativeAssetsBuildRunner {
123146
linkMode: linkModePreference,
124147
buildParentDir: packageLayout.dartToolNativeAssetsBuilder,
125148
);
126-
final (packageAssets, _, _) = await _buildPackage(
149+
final (packageAssets, _, _, packageSuccess) = await _buildPackage(
127150
config,
128151
packageLayout.packageConfigUri,
129152
workingDirectory,
130153
includeParentEnvironment,
131154
dryRun: true,
132155
);
133156
assets.addAll(packageAssets);
157+
success &= packageSuccess;
134158
}
135159
return _DryRunResultImpl(
136160
assets: assets,
161+
success: success,
137162
);
138163
}
139164

140-
Future<(List<Asset>, List<Uri>, Metadata?)> _buildPackageCached(
165+
Future<_PackageBuildRecord> _buildPackageCached(
141166
BuildConfig config,
142167
Uri packageConfigUri,
143168
Uri workingDirectory,
@@ -163,7 +188,7 @@ class NativeAssetsBuildRunner {
163188
final assets = buildOutput!.assets;
164189
final dependencies = buildOutput.dependencies.dependencies;
165190
final metadata = buildOutput.metadata;
166-
return (assets, dependencies, metadata);
191+
return (assets, dependencies, metadata, true);
167192
}
168193

169194
return await _buildPackage(
@@ -175,7 +200,7 @@ class NativeAssetsBuildRunner {
175200
);
176201
}
177202

178-
Future<(List<Asset>, List<Uri>, Metadata?)> _buildPackage(
203+
Future<_PackageBuildRecord> _buildPackage(
179204
BuildConfig config,
180205
Uri packageConfigUri,
181206
Uri workingDirectory,
@@ -193,25 +218,77 @@ class NativeAssetsBuildRunner {
193218
// Ensure we'll never read outdated build results.
194219
await buildOutputFile.delete();
195220
}
196-
await runProcess(
221+
final arguments = [
222+
'--packages=${packageConfigUri.toFilePath()}',
223+
buildDotDart.toFilePath(),
224+
'--config=${configFile.toFilePath()}',
225+
];
226+
final result = await runProcess(
197227
workingDirectory: workingDirectory,
198228
executable: dartExecutable,
199-
arguments: [
200-
'--packages=${packageConfigUri.toFilePath()}',
201-
buildDotDart.toFilePath(),
202-
'--config=${configFile.toFilePath()}',
203-
],
229+
arguments: arguments,
204230
logger: logger,
205231
includeParentEnvironment: includeParentEnvironment,
206-
expectedExitCode: 0,
207-
throwOnUnexpectedExitCode: true,
208232
);
209-
final buildOutput = await BuildOutput.readFromFile(outDir: outDir);
210-
final assets = buildOutput?.assets ?? [];
211-
validateAssetsPackage(assets, config.packageName);
212-
final dependencies = buildOutput?.dependencies.dependencies ?? [];
213-
final metadata = dryRun ? null : buildOutput?.metadata;
214-
return (assets, dependencies, metadata);
233+
var success = true;
234+
if (result.exitCode != 0) {
235+
final printWorkingDir = workingDirectory != Directory.current.uri;
236+
final commandString = [
237+
if (printWorkingDir) '(cd ${workingDirectory.toFilePath()};',
238+
dartExecutable.toFilePath(),
239+
...arguments.map((a) => a.contains(' ') ? "'$a'" : a),
240+
if (printWorkingDir) ')',
241+
].join(' ');
242+
logger.severe(
243+
'''
244+
Building native assets for package:${config.packageName} failed.
245+
build.dart returned with exit code: ${result.exitCode}.
246+
To reproduce run:
247+
$commandString
248+
stderr:
249+
${result.stderr}
250+
stdout:
251+
${result.stdout}
252+
''',
253+
);
254+
success = false;
255+
}
256+
257+
try {
258+
final buildOutput = await BuildOutput.readFromFile(outDir: outDir);
259+
final assets = buildOutput?.assets ?? [];
260+
success &= validateAssetsPackage(assets, config.packageName);
261+
final dependencies = buildOutput?.dependencies.dependencies ?? [];
262+
final metadata = dryRun ? null : buildOutput?.metadata;
263+
return (assets, dependencies, metadata, success);
264+
} on FormatException catch (e) {
265+
logger.severe('''
266+
Building native assets for package:${config.packageName} failed.
267+
build_output.yaml contained a format error.
268+
${e.message}
269+
''');
270+
success = false;
271+
return (<Asset>[], <Uri>[], Metadata({}), false);
272+
// TODO(https://github.com/dart-lang/native/issues/109): Stop throwing
273+
// type errors in native_assets_cli, release a new version of that package
274+
// and then remove this.
275+
// ignore: avoid_catching_errors
276+
} on TypeError {
277+
logger.severe('''
278+
Building native assets for package:${config.packageName} failed.
279+
build_output.yaml contained a format error.
280+
''');
281+
success = false;
282+
return (<Asset>[], <Uri>[], Metadata({}), false);
283+
} finally {
284+
if (!success) {
285+
final buildOutputFile =
286+
File.fromUri(outDir.resolve(BuildOutput.fileName));
287+
if (await buildOutputFile.exists()) {
288+
await buildOutputFile.delete();
289+
}
290+
}
291+
}
215292
}
216293

217294
static Future<BuildConfig> _cliConfig({
@@ -292,33 +369,53 @@ class NativeAssetsBuildRunner {
292369
};
293370
}
294371

295-
void validateAssetsPackage(List<Asset> assets, String packageName) {
372+
bool validateAssetsPackage(List<Asset> assets, String packageName) {
296373
final invalidAssetIds = assets
297374
.map((a) => a.name)
298375
.where((n) => !n.startsWith('package:$packageName/'))
299376
.toSet()
300377
.toList()
301378
..sort();
302-
if (invalidAssetIds.isNotEmpty) {
303-
throw FormatException(
379+
final success = invalidAssetIds.isEmpty;
380+
if (!success) {
381+
logger.severe(
304382
'`package:$packageName` declares the following assets which do not '
305383
'start with `package:$packageName/`: ${invalidAssetIds.join(', ')}.',
306384
);
307385
}
386+
return success;
308387
}
309388
}
310389

390+
typedef _PackageBuildRecord = (
391+
List<Asset>,
392+
List<Uri> dependencies,
393+
Metadata?,
394+
bool success,
395+
);
396+
311397
/// The result from a [NativeAssetsBuildRunner.dryRun].
312398
abstract interface class DryRunResult {
313399
/// The native assets for all [Target]s for the build or dry run.
314400
List<Asset> get assets;
401+
402+
/// Whether all builds completed without errors.
403+
///
404+
/// All error messages are streamed to [NativeAssetsBuildRunner.logger].
405+
bool get success;
315406
}
316407

317408
final class _DryRunResultImpl implements DryRunResult {
318409
@override
319410
final List<Asset> assets;
320411

321-
_DryRunResultImpl({required this.assets});
412+
@override
413+
final bool success;
414+
415+
_DryRunResultImpl({
416+
required this.assets,
417+
required this.success,
418+
});
322419
}
323420

324421
/// The result from a [NativeAssetsBuildRunner.build].
@@ -339,9 +436,13 @@ final class _BuildResultImpl implements BuildResult {
339436
@override
340437
final List<Uri> dependencies;
341438

439+
@override
440+
final bool success;
441+
342442
_BuildResultImpl({
343443
required this.assets,
344444
required this.dependencies,
445+
required this.success,
345446
});
346447
}
347448

pkgs/native_assets_builder/test/build_runner/build_planner_test.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ void main() async {
4343
packageGraph: graph,
4444
packagesWithNativeAssets: packagesWithNativeAssets,
4545
dartExecutable: Uri.file(Platform.resolvedExecutable),
46+
logger: logger,
4647
);
47-
final buildPlan = planner.plan();
48+
final (buildPlan, _) = planner.plan();
4849
expect(buildPlan.length, 1);
4950
expect(buildPlan.single.name, 'native_add');
5051
});
@@ -61,12 +62,14 @@ void main() async {
6162
await PackageLayout.fromRootPackageRoot(nativeAddUri);
6263
final packagesWithNativeAssets =
6364
await packageLayout.packagesWithNativeAssets;
64-
final buildPlan = (await NativeAssetsBuildPlanner.fromRootPackageRoot(
65+
final nativeAssetsBuildPlanner =
66+
await NativeAssetsBuildPlanner.fromRootPackageRoot(
6567
rootPackageRoot: nativeAddUri,
6668
packagesWithNativeAssets: packagesWithNativeAssets,
6769
dartExecutable: Uri.file(Platform.resolvedExecutable),
68-
))
69-
.plan();
70+
logger: logger,
71+
);
72+
final (buildPlan, _) = nativeAssetsBuildPlanner.plan();
7073
expect(buildPlan.length, 1);
7174
expect(buildPlan.single.name, 'native_add');
7275
});

0 commit comments

Comments
 (0)