From d340ad1dcc4674aefb8644f2da9242067696b7e3 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Fri, 4 Nov 2022 15:58:46 -0500 Subject: [PATCH 01/22] fix(mason): hook execution after pub cache clean --- packages/mason/lib/src/generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mason/lib/src/generator.dart b/packages/mason/lib/src/generator.dart index 74c6cf0ac..d27d6e063 100644 --- a/packages/mason/lib/src/generator.dart +++ b/packages/mason/lib/src/generator.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io' show Directory, File, FileMode, Process; +import 'dart:io' show Directory, File, FileMode, Process, ProcessResult; import 'dart:isolate'; import 'package:checked_yaml/checked_yaml.dart'; From d9ee07695fb803b1691ec09a14c2aad921acc7f5 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Mon, 7 Nov 2022 17:12:40 -0600 Subject: [PATCH 02/22] test coverage --- packages/mason/test/src/hooks_test.dart | 31 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/mason/test/src/hooks_test.dart b/packages/mason/test/src/hooks_test.dart index ba3e05f78..9d23f10d3 100644 --- a/packages/mason/test/src/hooks_test.dart +++ b/packages/mason/test/src/hooks_test.dart @@ -24,10 +24,10 @@ void main() { }); test( - 'throws HookDependencyInstallFailure ' - 'when pubspec is malformed', () async { + 'throws HookRunException ' + 'when unable to resolve a type', () async { final brick = Brick.path( - path.join('test', 'fixtures', 'malformed_pubspec'), + path.join('test', 'fixtures', 'spawn_exception'), ); final generator = await MasonGenerator.fromBrick(brick); @@ -35,7 +35,30 @@ void main() { await generator.hooks.preGen(); fail('should throw'); } catch (error) { - expect(error, isA()); + expect(error, isA()); + } + }); + + test( + 'throws HookRunException ' + 'when unable to resolve a type (back-to-back)', () async { + final brick = Brick.path( + path.join('test', 'fixtures', 'spawn_exception'), + ); + final generator = await MasonGenerator.fromBrick(brick); + + try { + await generator.hooks.preGen(); + fail('should throw'); + } catch (error) { + expect(error, isA()); + } + + try { + await generator.hooks.preGen(); + fail('should throw'); + } catch (error) { + expect(error, isA()); } }); From 65e1f2a82f7d21af8b9662247999e670a4c8315c Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Mon, 7 Nov 2022 23:01:14 -0600 Subject: [PATCH 03/22] chore: remove unused import --- packages/mason/lib/src/generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mason/lib/src/generator.dart b/packages/mason/lib/src/generator.dart index d27d6e063..74c6cf0ac 100644 --- a/packages/mason/lib/src/generator.dart +++ b/packages/mason/lib/src/generator.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io' show Directory, File, FileMode, Process, ProcessResult; +import 'dart:io' show Directory, File, FileMode, Process; import 'dart:isolate'; import 'package:checked_yaml/checked_yaml.dart'; From 5785a44b40db3b2b347ab063c5c11262cd41f9c2 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Mon, 7 Nov 2022 23:02:52 -0600 Subject: [PATCH 04/22] revert test removal --- packages/mason/test/src/hooks_test.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/mason/test/src/hooks_test.dart b/packages/mason/test/src/hooks_test.dart index 9d23f10d3..7a3bf8f80 100644 --- a/packages/mason/test/src/hooks_test.dart +++ b/packages/mason/test/src/hooks_test.dart @@ -23,6 +23,22 @@ void main() { } }); + test( + 'throws HookDependencyInstallFailure ' + 'when pubspec is malformed', () async { + final brick = Brick.path( + path.join('test', 'fixtures', 'malformed_pubspec'), + ); + final generator = await MasonGenerator.fromBrick(brick); + + try { + await generator.hooks.preGen(); + fail('should throw'); + } catch (error) { + expect(error, isA()); + } + }); + test( 'throws HookRunException ' 'when unable to resolve a type', () async { From 629bdd402f3d9030f87990f4925083162935dfb1 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 00:14:52 -0600 Subject: [PATCH 05/22] perf(mason): compile hooks --- packages/mason/lib/src/hooks.dart | 84 ++++++++++++++----- packages/mason_cli/lib/src/commands/make.dart | 2 + 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index f55122bec..c8dd085a7 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -202,6 +202,7 @@ class GeneratorHooks { Map vars = const {}, String? workingDirectory, void Function(Map vars)? onVarsChanged, + Logger? logger, }) async { final preGenHook = this.preGenHook; if (preGenHook != null && pubspec != null) { @@ -210,6 +211,7 @@ class GeneratorHooks { vars: vars, workingDirectory: workingDirectory, onVarsChanged: onVarsChanged, + logger: logger, ); } } @@ -220,6 +222,7 @@ class GeneratorHooks { Map vars = const {}, String? workingDirectory, void Function(Map vars)? onVarsChanged, + Logger? logger, }) async { final postGenHook = this.postGenHook; if (postGenHook != null && pubspec != null) { @@ -228,6 +231,7 @@ class GeneratorHooks { vars: vars, workingDirectory: workingDirectory, onVarsChanged: onVarsChanged, + logger: logger, ); } } @@ -239,6 +243,7 @@ class GeneratorHooks { Map vars = const {}, void Function(Map vars)? onVarsChanged, String? workingDirectory, + Logger? logger, }) async { final pubspec = this.pubspec; final subscriptions = []; @@ -274,13 +279,15 @@ class GeneratorHooks { } var dependenciesInstalled = false; - Directory? hookCacheDir; + final hookHash = sha1.convert(hook.content).toString(); + final directoryHash = + pubspec != null ? sha1.convert(pubspec).toString() : hookHash; + final hookCacheDir = Directory( + p.join(Directory.systemTemp.path, '.mason', directoryHash), + ); + Uri? packageConfigUri; if (pubspec != null) { - final directoryHash = sha1.convert(pubspec).toString(); - hookCacheDir = Directory( - p.join(Directory.systemTemp.path, '.mason', directoryHash), - ); final packageConfigFile = File( p.join(hookCacheDir.path, '.dart_tool', 'package_config.json'), ); @@ -297,15 +304,50 @@ class GeneratorHooks { packageConfigUri = packageConfigFile.uri; } - Uri? uri; - try { - uri = _getHookUri(hook.content); - // ignore: avoid_catching_errors - } on ArgumentError { - throw HookInvalidCharactersException(hook.path); - } + final hookBuildDir = Directory( + p.join(hookCacheDir.path, 'build', p.basenameWithoutExtension(hook.path)), + ); + final snapshot = File(p.join(hookBuildDir.path, '.$hookHash.jit')); + Uri? uri = Uri.file(snapshot.path); + + if (!snapshot.existsSync()) { + try { + await hookBuildDir.delete(recursive: true); + } catch (_) {} + + try { + uri = _getHookUri(hook.content); + // ignore: avoid_catching_errors + } on ArgumentError { + throw HookInvalidCharactersException(hook.path); + } + + if (uri == null) throw HookMissingRunException(hook.path); - if (uri == null) throw HookMissingRunException(hook.path); + File(p.join(hookBuildDir.path, '.$hookHash.dart')) + ..createSync(recursive: true) + ..writeAsBytesSync(uri.data!.contentAsBytes()); + + final progress = logger?.progress('Compiling ${p.basename(hook.path)}'); + final result = await Process.run( + 'dart', + [ + 'compile', + 'jit-snapshot', + p.join(hookBuildDir.path, '.$hookHash.dart') + ], + workingDirectory: hookCacheDir.path, + runInShell: true, + ); + + if (result.exitCode != ExitCode.success.code) { + progress?.fail(result.stderr.toString()); + // TODO(felangel): create `HookCompileException` + throw Exception('HookCompileException'); + } + + progress?.complete('Compiled ${p.basename(hook.path)}'); + } Future spawnIsolate(Uri uri) { return Isolate.spawnUri( @@ -331,9 +373,7 @@ class GeneratorHooks { throw HookRunException(hook.path, content.trim()); } - final shouldRetry = !dependenciesInstalled && hookCacheDir != null; - - if (!shouldRetry) throwHookRunException(error); + if (!!dependenciesInstalled) throwHookRunException(error); // Failure to spawn the isolate could be due to changes in the pub cache. // We attempt to reinstall hook dependencies. @@ -441,15 +481,15 @@ import 'dart:isolate'; $content -void main(List args, SendPort port) { - run(_HookContext._(port, vars: json.decode(args.first))); +void main(List args, [SendPort? port]) { + run(_HookContext._(port, vars: args.isEmpty ? {} : json.decode(args.first))); } class _HookContext implements HookContext { _HookContext._(this._port, {Map? vars}) : _vars = _Vars(_port, vars: vars); - final SendPort _port; + final SendPort? _port; _Vars _vars; @override @@ -461,7 +501,7 @@ class _HookContext implements HookContext { @override set vars(Map value) { _vars = _Vars(_port, vars: value); - _port.send(json.encode(_vars)); + _port?.send(json.encode(_vars)); } } @@ -471,7 +511,7 @@ class _Vars with MapMixin { Map? vars, }) : _vars = vars ?? const {}; - final SendPort _port; + final SendPort? _port; final Map _vars; @override @@ -499,6 +539,6 @@ class _Vars with MapMixin { return result; } - void _updateVars() => _port.send(json.encode(_vars)); + void _updateVars() => _port?.send(json.encode(_vars)); } '''; diff --git a/packages/mason_cli/lib/src/commands/make.dart b/packages/mason_cli/lib/src/commands/make.dart index 9f8f957aa..f21482664 100644 --- a/packages/mason_cli/lib/src/commands/make.dart +++ b/packages/mason_cli/lib/src/commands/make.dart @@ -171,6 +171,7 @@ class _MakeCommand extends MasonCommand { vars: vars, workingDirectory: outputDir, onVarsChanged: (vars) => updatedVars = vars, + logger: logger, ); } @@ -189,6 +190,7 @@ class _MakeCommand extends MasonCommand { await generator.hooks.postGen( vars: updatedVars ?? vars, workingDirectory: outputDir, + logger: logger, ); } From bf72b00258f7d345a618301e0d0e082a83f1b6fc Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 22:01:29 -0600 Subject: [PATCH 06/22] simplify --- packages/mason/lib/src/hooks.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index c8dd085a7..5e533dc1c 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -280,10 +280,8 @@ class GeneratorHooks { var dependenciesInstalled = false; final hookHash = sha1.convert(hook.content).toString(); - final directoryHash = - pubspec != null ? sha1.convert(pubspec).toString() : hookHash; final hookCacheDir = Directory( - p.join(Directory.systemTemp.path, '.mason', directoryHash), + p.join(Directory.systemTemp.path, '.mason', hookHash), ); Uri? packageConfigUri; From 8a4a41ac6d655d59d4e7c13c0afa1c1c32cd8bcb Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 22:05:21 -0600 Subject: [PATCH 07/22] revert test changes --- packages/mason/test/src/hooks_test.dart | 39 ------------------------- 1 file changed, 39 deletions(-) diff --git a/packages/mason/test/src/hooks_test.dart b/packages/mason/test/src/hooks_test.dart index 7a3bf8f80..ba3e05f78 100644 --- a/packages/mason/test/src/hooks_test.dart +++ b/packages/mason/test/src/hooks_test.dart @@ -78,45 +78,6 @@ void main() { } }); - test( - 'throws HookRunException ' - 'when unable to resolve a type', () async { - final brick = Brick.path( - path.join('test', 'fixtures', 'spawn_exception'), - ); - final generator = await MasonGenerator.fromBrick(brick); - - try { - await generator.hooks.preGen(); - fail('should throw'); - } catch (error) { - expect(error, isA()); - } - }); - - test( - 'throws HookRunException ' - 'when unable to resolve a type (back-to-back)', () async { - final brick = Brick.path( - path.join('test', 'fixtures', 'spawn_exception'), - ); - final generator = await MasonGenerator.fromBrick(brick); - - try { - await generator.hooks.preGen(); - fail('should throw'); - } catch (error) { - expect(error, isA()); - } - - try { - await generator.hooks.preGen(); - fail('should throw'); - } catch (error) { - expect(error, isA()); - } - }); - test( 'throws HookMissingRunException ' 'when hook does not contain a valid run method', () async { From 9a0f1e5fa6d3050a5b287987c03ff2eab843b34d Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 22:09:26 -0600 Subject: [PATCH 08/22] refactor to use HookCompileException --- packages/mason/lib/src/hooks.dart | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index 5e533dc1c..2f1e7068d 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -45,8 +45,21 @@ Ensure the hook contains a 'run' method: ); } +/// {@template hook_compile_exception} +/// Thrown when an error occurs when trying to compile a hook. +/// {@endtemplate} +class HookCompileException extends MasonException { + /// {@macro hook_compile_exception} + HookCompileException(String path, String error) + : super( + ''' +Unable to compile hook: $path. +Error: $error''', + ); +} + /// {@template hook_run_exception} -/// Thrown when an error occurs when trying to run hook. +/// Thrown when an error occurs when trying to run a hook. /// {@endtemplate} class HookRunException extends MasonException { /// {@macro hook_run_exception} @@ -339,9 +352,9 @@ class GeneratorHooks { ); if (result.exitCode != ExitCode.success.code) { - progress?.fail(result.stderr.toString()); - // TODO(felangel): create `HookCompileException` - throw Exception('HookCompileException'); + final error = result.stderr.toString(); + progress?.fail(error); + throw HookCompileException(hook.path, error); } progress?.complete('Compiled ${p.basename(hook.path)}'); From f05880f616aeaded32fab6d04d75c3c47d227b2d Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 23:29:04 -0600 Subject: [PATCH 09/22] refactor --- packages/mason/lib/src/hooks.dart | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index 2f1e7068d..f6cfb1eba 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -292,9 +292,15 @@ class GeneratorHooks { } var dependenciesInstalled = false; - final hookHash = sha1.convert(hook.content).toString(); final hookCacheDir = Directory( - p.join(Directory.systemTemp.path, '.mason', hookHash), + p.join( + Directory(p.join(Directory.systemTemp.path, '.mason')).path, + sha1 + .convert( + pubspec ?? utf8.encode(File(hook.path).parent.absolute.path), + ) + .toString(), + ), ); Uri? packageConfigUri; @@ -318,6 +324,7 @@ class GeneratorHooks { final hookBuildDir = Directory( p.join(hookCacheDir.path, 'build', p.basenameWithoutExtension(hook.path)), ); + final hookHash = sha1.convert(hook.content).toString(); final snapshot = File(p.join(hookBuildDir.path, '.$hookHash.jit')); Uri? uri = Uri.file(snapshot.path); @@ -345,13 +352,13 @@ class GeneratorHooks { [ 'compile', 'jit-snapshot', - p.join(hookBuildDir.path, '.$hookHash.dart') + p.join(hookBuildDir.path, '.$hookHash.dart'), ], workingDirectory: hookCacheDir.path, runInShell: true, ); - if (result.exitCode != ExitCode.success.code) { + if (result.exitCode != ExitCode.success.code && !snapshot.existsSync()) { final error = result.stderr.toString(); progress?.fail(error); throw HookCompileException(hook.path, error); @@ -384,7 +391,9 @@ class GeneratorHooks { throw HookRunException(hook.path, content.trim()); } - if (!!dependenciesInstalled) throwHookRunException(error); + final shouldRetry = !dependenciesInstalled && pubspec != null; + + if (!shouldRetry) throwHookRunException(error); // Failure to spawn the isolate could be due to changes in the pub cache. // We attempt to reinstall hook dependencies. @@ -492,15 +501,15 @@ import 'dart:isolate'; $content -void main(List args, [SendPort? port]) { - run(_HookContext._(port, vars: args.isEmpty ? {} : json.decode(args.first))); +void main(List args, SendPort port) { + run(_HookContext._(port, vars: json.decode(args.first))); } class _HookContext implements HookContext { _HookContext._(this._port, {Map? vars}) : _vars = _Vars(_port, vars: vars); - final SendPort? _port; + final SendPort _port; _Vars _vars; @override @@ -512,7 +521,7 @@ class _HookContext implements HookContext { @override set vars(Map value) { _vars = _Vars(_port, vars: value); - _port?.send(json.encode(_vars)); + _port.send(json.encode(_vars)); } } @@ -522,7 +531,7 @@ class _Vars with MapMixin { Map? vars, }) : _vars = vars ?? const {}; - final SendPort? _port; + final SendPort _port; final Map _vars; @override @@ -550,6 +559,6 @@ class _Vars with MapMixin { return result; } - void _updateVars() => _port?.send(json.encode(_vars)); + void _updateVars() => _port.send(json.encode(_vars)); } '''; From bbc9f92ff85cf8568d5ededa7e7131ce1a9341e8 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 23:50:51 -0600 Subject: [PATCH 10/22] retry --- packages/mason/lib/src/hooks.dart | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index f6cfb1eba..892356a6f 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -328,9 +328,9 @@ class GeneratorHooks { final snapshot = File(p.join(hookBuildDir.path, '.$hookHash.jit')); Uri? uri = Uri.file(snapshot.path); - if (!snapshot.existsSync()) { + Future _compileSnapshot() async { try { - await hookBuildDir.delete(recursive: true); + await snapshot.delete(recursive: true); } catch (_) {} try { @@ -344,7 +344,7 @@ class GeneratorHooks { File(p.join(hookBuildDir.path, '.$hookHash.dart')) ..createSync(recursive: true) - ..writeAsBytesSync(uri.data!.contentAsBytes()); + ..writeAsBytesSync(uri!.data!.contentAsBytes()); final progress = logger?.progress('Compiling ${p.basename(hook.path)}'); final result = await Process.run( @@ -367,6 +367,8 @@ class GeneratorHooks { progress?.complete('Compiled ${p.basename(hook.path)}'); } + if (!snapshot.existsSync()) await _compileSnapshot(); + Future spawnIsolate(Uri uri) { return Isolate.spawnUri( uri, @@ -381,7 +383,7 @@ class GeneratorHooks { Isolate? isolate; try { if (workingDirectory != null) Directory.current = workingDirectory; - isolate = await spawnIsolate(uri); + isolate = await spawnIsolate(uri!); } on IsolateSpawnException catch (error) { Never throwHookRunException(IsolateSpawnException error) { Directory.current = cwd; @@ -391,18 +393,26 @@ class GeneratorHooks { throw HookRunException(hook.path, content.trim()); } - final shouldRetry = !dependenciesInstalled && pubspec != null; + final incompatibleSnapshot = error.message.contains( + 'Snapshot not compatible with the current VM configuration', + ); + final missingDependencies = !dependenciesInstalled && pubspec != null; + final shouldRetry = incompatibleSnapshot || missingDependencies; if (!shouldRetry) throwHookRunException(error); - // Failure to spawn the isolate could be due to changes in the pub cache. - // We attempt to reinstall hook dependencies. - await _dartPubGet(workingDirectory: hookCacheDir.path); + incompatibleSnapshot + // Failure to spawn the isolate could be due to vm incompatibilities. + // We attempt to recompile the hook. + ? await _compileSnapshot() + // Failure to spawn the isolate could be due + // to changes in the pub cache. + // We attempt to reinstall hook dependencies. + : await _dartPubGet(workingDirectory: hookCacheDir.path); - // Retry spawning the isolate if the hook dependencies - // have been successfully reinstalled. + // Retry spawning the isolate. try { - isolate = await spawnIsolate(uri); + isolate = await spawnIsolate(uri!); } on IsolateSpawnException catch (error) { throwHookRunException(error); } From b601d9ad7b91a6f5656271faf5e778413f6daf79 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 23:55:34 -0600 Subject: [PATCH 11/22] cleanup --- packages/mason/lib/src/hooks.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index 892356a6f..c29a09774 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -330,7 +330,7 @@ class GeneratorHooks { Future _compileSnapshot() async { try { - await snapshot.delete(recursive: true); + await hookBuildDir.delete(recursive: true); } catch (_) {} try { From b2808c78edcc15e9f6e5e3f9b4a00095106d79e5 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 8 Nov 2022 23:59:22 -0600 Subject: [PATCH 12/22] tests --- packages/mason/test/src/hooks_test.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mason/test/src/hooks_test.dart b/packages/mason/test/src/hooks_test.dart index ba3e05f78..77183b5da 100644 --- a/packages/mason/test/src/hooks_test.dart +++ b/packages/mason/test/src/hooks_test.dart @@ -40,23 +40,23 @@ void main() { }); test( - 'throws HookRunException ' + 'throws HookCompileException ' 'when unable to resolve a type', () async { final brick = Brick.path( path.join('test', 'fixtures', 'spawn_exception'), ); final generator = await MasonGenerator.fromBrick(brick); - try { + try { await generator.hooks.preGen(); fail('should throw'); } catch (error) { - expect(error, isA()); + expect(error, isA()); } }); test( - 'throws HookRunException ' + 'throws HookCompileException ' 'when unable to resolve a type (back-to-back)', () async { final brick = Brick.path( path.join('test', 'fixtures', 'spawn_exception'), @@ -67,14 +67,14 @@ void main() { await generator.hooks.preGen(); fail('should throw'); } catch (error) { - expect(error, isA()); + expect(error, isA()); } try { await generator.hooks.preGen(); fail('should throw'); } catch (error) { - expect(error, isA()); + expect(error, isA()); } }); @@ -94,7 +94,7 @@ void main() { } }); - test('throws HookRunException when hook cannot be run', () async { + test('throws HookCompileException when hook cannot be run', () async { final brick = Brick.path( path.join('test', 'fixtures', 'run_exception'), ); @@ -104,7 +104,7 @@ void main() { await generator.hooks.preGen(); fail('should throw'); } catch (error) { - expect(error, isA()); + expect(error, isA()); } }); From 14cf8695e5f418f45c9e53471845c737b003ca0d Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Wed, 9 Nov 2022 00:00:18 -0600 Subject: [PATCH 13/22] format --- packages/mason/test/src/hooks_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mason/test/src/hooks_test.dart b/packages/mason/test/src/hooks_test.dart index 77183b5da..e1e0a4b8e 100644 --- a/packages/mason/test/src/hooks_test.dart +++ b/packages/mason/test/src/hooks_test.dart @@ -47,7 +47,7 @@ void main() { ); final generator = await MasonGenerator.fromBrick(brick); - try { + try { await generator.hooks.preGen(); fail('should throw'); } catch (error) { From ff21fede1cca5045d3f9c544b1401473854448b3 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Wed, 9 Nov 2022 23:52:40 -0600 Subject: [PATCH 14/22] compile on installation --- packages/mason/lib/src/generator.dart | 20 ++ packages/mason/lib/src/hooks.dart | 201 ++++++++++--------- packages/mason_cli/lib/src/commands/add.dart | 12 ++ 3 files changed, 143 insertions(+), 90 deletions(-) diff --git a/packages/mason/lib/src/generator.dart b/packages/mason/lib/src/generator.dart index 74c6cf0ac..072a423c4 100644 --- a/packages/mason/lib/src/generator.dart +++ b/packages/mason/lib/src/generator.dart @@ -638,3 +638,23 @@ extension on String { } } } + +extension on HookFile { + String get hash => sha1.convert(content).toString(); + + Directory get cacheDirectory { + return Directory( + p.join( + Directory(p.join(Directory.systemTemp.path, '.mason')).path, + sha1.convert(utf8.encode(File(path).parent.absolute.path)).toString(), + ), + ); + } + + File get snapshot { + final hookBuildDir = Directory( + p.join(cacheDirectory.path, 'build', p.basenameWithoutExtension(path)), + ); + return File(p.join(hookBuildDir.path, '.$hash.jit')); + } +} diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index c29a09774..f7940d08a 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -249,6 +249,106 @@ class GeneratorHooks { } } + /// Compiles all hooks. + /// Steps: + /// 1. Install dependencies for hooks (`dart pub get`) + /// 2. Compile jit snapshots (`dart compile jit-snapshot`) + Future compile({Logger? logger}) async { + await _installDependencies(); + + if (preGenHook != null) { + await _compile(hook: preGenHook!, logger: logger); + } + + if (postGenHook != null) { + await _compile(hook: postGenHook!, logger: logger); + } + } + + Future _dartPubGet({required String workingDirectory}) async { + final result = await Process.run( + 'dart', + ['pub', 'get'], + workingDirectory: workingDirectory, + runInShell: true, + ); + if (result.exitCode != ExitCode.success.code) { + throw HookDependencyInstallFailure( + workingDirectory, + '${result.stderr}', + ); + } + } + + Future _installDependencies() async { + final hook = preGenHook ?? postGenHook; + if (hook == null) return true; + + final hookCacheDir = hook.cacheDirectory; + final pubspec = this.pubspec; + + if (pubspec != null) { + final packageConfigFile = File( + p.join(hookCacheDir.path, '.dart_tool', 'package_config.json'), + ); + + if (!packageConfigFile.existsSync()) { + await hookCacheDir.create(recursive: true); + await File( + p.join(hookCacheDir.path, 'pubspec.yaml'), + ).writeAsBytes(pubspec); + await _dartPubGet(workingDirectory: hookCacheDir.path); + return true; + } + } + return false; + } + + Future _compile({required HookFile hook, Logger? logger}) async { + final hookCacheDir = hook.cacheDirectory; + final hookBuildDir = Directory( + p.join(hookCacheDir.path, 'build', p.basenameWithoutExtension(hook.path)), + ); + final hookHash = hook.hash; + final snapshot = hook.snapshot; + + if (snapshot.existsSync()) return; + + try { + await hookBuildDir.delete(recursive: true); + } catch (_) {} + + Uri? uri; + try { + uri = _getHookUri(hook.content); + // ignore: avoid_catching_errors + } on ArgumentError { + throw HookInvalidCharactersException(hook.path); + } + + if (uri == null) throw HookMissingRunException(hook.path); + + final hookFile = File(p.join(hookBuildDir.path, '.$hookHash.dart')) + ..createSync(recursive: true) + ..writeAsBytesSync(uri.data!.contentAsBytes()); + + final progress = logger?.progress('Compiling ${p.basename(hook.path)}'); + final result = await Process.run( + 'dart', + ['compile', 'jit-snapshot', hookFile.path], + workingDirectory: hookCacheDir.path, + runInShell: true, + ); + + if (result.exitCode != ExitCode.success.code && !snapshot.existsSync()) { + final error = result.stderr.toString(); + progress?.fail(error); + throw HookCompileException(hook.path, error); + } + + progress?.complete('Compiled ${p.basename(hook.path)}'); + } + /// Runs the provided [hook] with the specified [vars]. /// An optional [workingDirectory] can also be specified. Future _runHook({ @@ -279,96 +379,17 @@ class GeneratorHooks { ); } - Future _dartPubGet({required String workingDirectory}) async { - final result = await Process.run( - 'dart', - ['pub', 'get'], - workingDirectory: workingDirectory, - runInShell: true, - ); - if (result.exitCode != ExitCode.success.code) { - throw HookDependencyInstallFailure(hook.path, '${result.stderr}'); - } - } - - var dependenciesInstalled = false; - final hookCacheDir = Directory( - p.join( - Directory(p.join(Directory.systemTemp.path, '.mason')).path, - sha1 - .convert( - pubspec ?? utf8.encode(File(hook.path).parent.absolute.path), - ) - .toString(), - ), - ); - - Uri? packageConfigUri; - if (pubspec != null) { - final packageConfigFile = File( - p.join(hookCacheDir.path, '.dart_tool', 'package_config.json'), - ); - - if (!packageConfigFile.existsSync()) { - await hookCacheDir.create(recursive: true); - await File( - p.join(hookCacheDir.path, 'pubspec.yaml'), - ).writeAsBytes(pubspec); - await _dartPubGet(workingDirectory: hookCacheDir.path); - dependenciesInstalled = true; - } - - packageConfigUri = packageConfigFile.uri; - } - - final hookBuildDir = Directory( - p.join(hookCacheDir.path, 'build', p.basenameWithoutExtension(hook.path)), - ); - final hookHash = sha1.convert(hook.content).toString(); - final snapshot = File(p.join(hookBuildDir.path, '.$hookHash.jit')); - Uri? uri = Uri.file(snapshot.path); - - Future _compileSnapshot() async { - try { - await hookBuildDir.delete(recursive: true); - } catch (_) {} - - try { - uri = _getHookUri(hook.content); - // ignore: avoid_catching_errors - } on ArgumentError { - throw HookInvalidCharactersException(hook.path); - } - - if (uri == null) throw HookMissingRunException(hook.path); - - File(p.join(hookBuildDir.path, '.$hookHash.dart')) - ..createSync(recursive: true) - ..writeAsBytesSync(uri!.data!.contentAsBytes()); - - final progress = logger?.progress('Compiling ${p.basename(hook.path)}'); - final result = await Process.run( - 'dart', - [ - 'compile', - 'jit-snapshot', - p.join(hookBuildDir.path, '.$hookHash.dart'), - ], - workingDirectory: hookCacheDir.path, - runInShell: true, - ); + final dependenciesInstalled = await _installDependencies(); + final hookCacheDir = hook.cacheDirectory; + final packageConfigUri = File( + p.join(hookCacheDir.path, '.dart_tool', 'package_config.json'), + ).uri; + final snapshot = hook.snapshot; - if (result.exitCode != ExitCode.success.code && !snapshot.existsSync()) { - final error = result.stderr.toString(); - progress?.fail(error); - throw HookCompileException(hook.path, error); - } - - progress?.complete('Compiled ${p.basename(hook.path)}'); + if (!snapshot.existsSync()) { + await _compile(hook: hook, logger: logger); } - if (!snapshot.existsSync()) await _compileSnapshot(); - Future spawnIsolate(Uri uri) { return Isolate.spawnUri( uri, @@ -383,7 +404,7 @@ class GeneratorHooks { Isolate? isolate; try { if (workingDirectory != null) Directory.current = workingDirectory; - isolate = await spawnIsolate(uri!); + isolate = await spawnIsolate(snapshot.uri); } on IsolateSpawnException catch (error) { Never throwHookRunException(IsolateSpawnException error) { Directory.current = cwd; @@ -404,7 +425,7 @@ class GeneratorHooks { incompatibleSnapshot // Failure to spawn the isolate could be due to vm incompatibilities. // We attempt to recompile the hook. - ? await _compileSnapshot() + ? await _compile(hook: hook, logger: logger) // Failure to spawn the isolate could be due // to changes in the pub cache. // We attempt to reinstall hook dependencies. @@ -412,7 +433,7 @@ class GeneratorHooks { // Retry spawning the isolate. try { - isolate = await spawnIsolate(uri!); + isolate = await spawnIsolate(snapshot.uri); } on IsolateSpawnException catch (error) { throwHookRunException(error); } diff --git a/packages/mason_cli/lib/src/commands/add.dart b/packages/mason_cli/lib/src/commands/add.dart index d789c8eb4..cd22971b6 100644 --- a/packages/mason_cli/lib/src/commands/add.dart +++ b/packages/mason_cli/lib/src/commands/add.dart @@ -65,6 +65,18 @@ class AddCommand extends MasonCommand with InstallBrickMixin { final cachedBrick = await addBrick(brick, global: isGlobal); final file = File(p.join(cachedBrick.path, BrickYaml.file)); + final generator = await MasonGenerator.fromBrick( + Brick.path(cachedBrick.path), + ); + + final compileProgress = logger.progress('Compiling hooks'); + try { + await generator.hooks.compile(); + compileProgress.complete(); + } catch (_) { + compileProgress.fail(); + rethrow; + } final brickYaml = checkedYamlDecode( file.readAsStringSync(), From 4466cdb8fb5e8f7566b0870496b461ccd4d1e3f7 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Thu, 10 Nov 2022 23:28:58 -0600 Subject: [PATCH 15/22] use kernel module --- packages/mason/lib/src/generator.dart | 9 +- packages/mason/lib/src/hooks.dart | 86 ++++--------------- .../test/fixtures/basic/hooks/post_gen.dart | 3 + packages/mason/test/src/hooks_test.dart | 58 +++++++++++++ 4 files changed, 82 insertions(+), 74 deletions(-) create mode 100644 packages/mason/test/fixtures/basic/hooks/post_gen.dart diff --git a/packages/mason/lib/src/generator.dart b/packages/mason/lib/src/generator.dart index 072a423c4..7346a65b6 100644 --- a/packages/mason/lib/src/generator.dart +++ b/packages/mason/lib/src/generator.dart @@ -651,10 +651,13 @@ extension on HookFile { ); } - File get snapshot { - final hookBuildDir = Directory( + Directory get buildDirectory { + return Directory( p.join(cacheDirectory.path, 'build', p.basenameWithoutExtension(path)), ); - return File(p.join(hookBuildDir.path, '.$hash.jit')); + } + + File get module { + return File(p.join(buildDirectory.path, '.$hash.dill')); } } diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index f7940d08a..8ed018953 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -58,19 +58,6 @@ Error: $error''', ); } -/// {@template hook_run_exception} -/// Thrown when an error occurs when trying to run a hook. -/// {@endtemplate} -class HookRunException extends MasonException { - /// {@macro hook_run_exception} - HookRunException(String path, String error) - : super( - ''' -Unable to execute hook: $path. -Error: $error''', - ); -} - /// {@template hook_execution_exception} /// Thrown when an error occurs during hook execution. /// {@endtemplate} @@ -249,18 +236,17 @@ class GeneratorHooks { } } - /// Compiles all hooks. - /// Steps: - /// 1. Install dependencies for hooks (`dart pub get`) - /// 2. Compile jit snapshots (`dart compile jit-snapshot`) + /// Compile all hooks into modules for faster execution. + /// Hooks are compiled lazily by default but calling [compile] + /// can be used to compile hooks ahead of time. Future compile({Logger? logger}) async { await _installDependencies(); - if (preGenHook != null) { + if (preGenHook != null && !preGenHook!.module.existsSync()) { await _compile(hook: preGenHook!, logger: logger); } - if (postGenHook != null) { + if (postGenHook != null && !postGenHook!.module.existsSync()) { await _compile(hook: postGenHook!, logger: logger); } } @@ -280,9 +266,9 @@ class GeneratorHooks { } } - Future _installDependencies() async { + Future _installDependencies() async { final hook = preGenHook ?? postGenHook; - if (hook == null) return true; + if (hook == null) return; final hookCacheDir = hook.cacheDirectory; final pubspec = this.pubspec; @@ -298,21 +284,14 @@ class GeneratorHooks { p.join(hookCacheDir.path, 'pubspec.yaml'), ).writeAsBytes(pubspec); await _dartPubGet(workingDirectory: hookCacheDir.path); - return true; } } - return false; } Future _compile({required HookFile hook, Logger? logger}) async { final hookCacheDir = hook.cacheDirectory; - final hookBuildDir = Directory( - p.join(hookCacheDir.path, 'build', p.basenameWithoutExtension(hook.path)), - ); + final hookBuildDir = hook.buildDirectory; final hookHash = hook.hash; - final snapshot = hook.snapshot; - - if (snapshot.existsSync()) return; try { await hookBuildDir.delete(recursive: true); @@ -335,12 +314,12 @@ class GeneratorHooks { final progress = logger?.progress('Compiling ${p.basename(hook.path)}'); final result = await Process.run( 'dart', - ['compile', 'jit-snapshot', hookFile.path], + ['compile', 'kernel', hookFile.path], workingDirectory: hookCacheDir.path, runInShell: true, ); - if (result.exitCode != ExitCode.success.code && !snapshot.existsSync()) { + if (result.exitCode != ExitCode.success.code) { final error = result.stderr.toString(); progress?.fail(error); throw HookCompileException(hook.path, error); @@ -358,7 +337,6 @@ class GeneratorHooks { String? workingDirectory, Logger? logger, }) async { - final pubspec = this.pubspec; final subscriptions = []; final messagePort = ReceivePort(); final errorPort = ReceivePort(); @@ -379,14 +357,14 @@ class GeneratorHooks { ); } - final dependenciesInstalled = await _installDependencies(); + await _installDependencies(); final hookCacheDir = hook.cacheDirectory; final packageConfigUri = File( p.join(hookCacheDir.path, '.dart_tool', 'package_config.json'), ).uri; - final snapshot = hook.snapshot; + final module = hook.module; - if (!snapshot.existsSync()) { + if (!module.existsSync()) { await _compile(hook: hook, logger: logger); } @@ -401,43 +379,9 @@ class GeneratorHooks { } final cwd = Directory.current; - Isolate? isolate; - try { - if (workingDirectory != null) Directory.current = workingDirectory; - isolate = await spawnIsolate(snapshot.uri); - } on IsolateSpawnException catch (error) { - Never throwHookRunException(IsolateSpawnException error) { - Directory.current = cwd; - final msg = error.message; - final content = - msg.contains('Error: ') ? msg.split('Error: ').last : msg; - throw HookRunException(hook.path, content.trim()); - } - - final incompatibleSnapshot = error.message.contains( - 'Snapshot not compatible with the current VM configuration', - ); - final missingDependencies = !dependenciesInstalled && pubspec != null; - final shouldRetry = incompatibleSnapshot || missingDependencies; + if (workingDirectory != null) Directory.current = workingDirectory; - if (!shouldRetry) throwHookRunException(error); - - incompatibleSnapshot - // Failure to spawn the isolate could be due to vm incompatibilities. - // We attempt to recompile the hook. - ? await _compile(hook: hook, logger: logger) - // Failure to spawn the isolate could be due - // to changes in the pub cache. - // We attempt to reinstall hook dependencies. - : await _dartPubGet(workingDirectory: hookCacheDir.path); - - // Retry spawning the isolate. - try { - isolate = await spawnIsolate(snapshot.uri); - } on IsolateSpawnException catch (error) { - throwHookRunException(error); - } - } + final isolate = await spawnIsolate(module.uri); isolate ..addErrorListener(errorPort.sendPort) diff --git a/packages/mason/test/fixtures/basic/hooks/post_gen.dart b/packages/mason/test/fixtures/basic/hooks/post_gen.dart new file mode 100644 index 000000000..b506987b3 --- /dev/null +++ b/packages/mason/test/fixtures/basic/hooks/post_gen.dart @@ -0,0 +1,3 @@ +import 'package:mason/mason.dart'; + +void run(HookContext context) {} diff --git a/packages/mason/test/src/hooks_test.dart b/packages/mason/test/src/hooks_test.dart index e1e0a4b8e..031d35ab3 100644 --- a/packages/mason/test/src/hooks_test.dart +++ b/packages/mason/test/src/hooks_test.dart @@ -2,11 +2,24 @@ import 'dart:io'; import 'package:mason/mason.dart'; import 'package:mason/src/generator.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as path; import 'package:test/test.dart'; +class _MockLogger extends Mock implements Logger {} + +class _MockProgress extends Mock implements Progress {} + void main() { group('Hooks', () { + setUp(() async { + try { + await Directory( + path.join(Directory.systemTemp.path, '.mason'), + ).delete(recursive: true); + } catch (_) {} + }); + test( 'throws HookInvalidCharactersException ' 'when containining non-ascii characters', () async { @@ -138,6 +151,51 @@ void main() { } }); + test('installs dependencies and compiles hooks only once', () async { + final brick = Brick.path(path.join('test', 'fixtures', 'basic')); + final generator = await MasonGenerator.fromBrick(brick); + final logger = _MockLogger(); + final progress = _MockProgress(); + when(() => logger.progress(any())).thenReturn(progress); + + await expectLater(generator.hooks.compile(logger: logger), completes); + + verify(() => logger.progress('Compiling pre_gen.dart')).called(1); + verify(() => progress.complete('Compiled pre_gen.dart')).called(1); + verify(() => logger.progress('Compiling post_gen.dart')).called(1); + verify(() => progress.complete('Compiled post_gen.dart')).called(1); + + await expectLater(generator.hooks.compile(logger: logger), completes); + + verifyNever(() => logger.progress('Compiling pre_gen.dart')); + verifyNever(() => progress.complete('Compiled pre_gen.dart')); + verifyNever(() => logger.progress('Compiling post_gen.dart')); + verifyNever(() => progress.complete('Compiled post_gen.dart')); + }); + + test('compile reports compilation errors', () async { + final brick = Brick.path( + path.join('test', 'fixtures', 'run_exception'), + ); + final generator = await MasonGenerator.fromBrick(brick); + final logger = _MockLogger(); + final progress = _MockProgress(); + when(() => logger.progress(any())).thenReturn(progress); + + try { + await generator.hooks.compile(logger: logger); + fail('should throw'); + } catch (error) { + expect(error, isA()); + } + verify(() => logger.progress('Compiling pre_gen.dart')).called(1); + verify( + () => progress.fail( + any(that: contains("Error: Expected '{' before this.")), + ), + ).called(1); + }); + test('recovers from cleared pub cache', () async { final brick = Brick.path(path.join('test', 'fixtures', 'basic')); final generator = await MasonGenerator.fromBrick(brick); From 73882d2b2aff85ebcfa3732a909992c326c31d53 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Thu, 10 Nov 2022 23:44:31 -0600 Subject: [PATCH 16/22] coverage --- .github/workflows/mason_cli.yaml | 2 +- .../test/bricks/compilation_error/__brick__/.gitkeep | 0 .../test/bricks/compilation_error/brick.yaml | 3 +++ .../test/bricks/compilation_error/hooks/pre_gen.dart | 2 ++ .../test/bricks/compilation_error/hooks/pubspec.yaml | 10 ++++++++++ packages/mason_cli/test/commands/add_test.dart | 11 +++++++++++ 6 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 packages/mason_cli/test/bricks/compilation_error/__brick__/.gitkeep create mode 100644 packages/mason_cli/test/bricks/compilation_error/brick.yaml create mode 100644 packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart create mode 100644 packages/mason_cli/test/bricks/compilation_error/hooks/pubspec.yaml diff --git a/.github/workflows/mason_cli.yaml b/.github/workflows/mason_cli.yaml index 940c1410c..6e29386bf 100644 --- a/.github/workflows/mason_cli.yaml +++ b/.github/workflows/mason_cli.yaml @@ -93,4 +93,4 @@ jobs: dart pub global activate pana - name: Verify Pub Score - run: ../../tool/verify_pub_score.sh + run: ../../tool/verify_pub_score.sh 100 diff --git a/packages/mason_cli/test/bricks/compilation_error/__brick__/.gitkeep b/packages/mason_cli/test/bricks/compilation_error/__brick__/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/mason_cli/test/bricks/compilation_error/brick.yaml b/packages/mason_cli/test/bricks/compilation_error/brick.yaml new file mode 100644 index 000000000..5377ea885 --- /dev/null +++ b/packages/mason_cli/test/bricks/compilation_error/brick.yaml @@ -0,0 +1,3 @@ +name: compilation_error +description: A test brick +version: 0.1.0+1 diff --git a/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart b/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart new file mode 100644 index 000000000..172390673 --- /dev/null +++ b/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart @@ -0,0 +1,2 @@ +// ignore_for_file: missing_function_body +import 'package:mason/mason.dart';void run(HookContext context) \ No newline at end of file diff --git a/packages/mason_cli/test/bricks/compilation_error/hooks/pubspec.yaml b/packages/mason_cli/test/bricks/compilation_error/hooks/pubspec.yaml new file mode 100644 index 000000000..9f5d90d8a --- /dev/null +++ b/packages/mason_cli/test/bricks/compilation_error/hooks/pubspec.yaml @@ -0,0 +1,10 @@ +name: compilation_error_hooks + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + mason: + git: + url: https://github.com/felangel/mason + path: packages/mason diff --git a/packages/mason_cli/test/commands/add_test.dart b/packages/mason_cli/test/commands/add_test.dart index d01a2a6f1..d9d1220bc 100644 --- a/packages/mason_cli/test/commands/add_test.dart +++ b/packages/mason_cli/test/commands/add_test.dart @@ -78,6 +78,17 @@ void main() { verify(() => logger.err('oops')).called(1); }); + test('exits with code 70 on hook compilation exception', () async { + final progress = MockProgress(); + when(() => logger.progress(any())).thenReturn(progress); + final brickPath = path.join('..', '..', 'bricks', 'compilation_error'); + final result = await commandRunner.run( + ['add', 'compilation_error', '--path', brickPath], + ); + expect(result, equals(ExitCode.usage.code)); + verify(progress.fail).called(1); + }); + group('local', () { test('exits with code 64 when brick is not provided', () async { final result = await commandRunner.run(['add']); From 40aa45cc76350cbf7d5e52622aafc4bc0b09918e Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Thu, 10 Nov 2022 23:45:59 -0600 Subject: [PATCH 17/22] format --- .../mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart b/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart index 172390673..5becf0f1d 100644 --- a/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart +++ b/packages/mason_cli/test/bricks/compilation_error/hooks/pre_gen.dart @@ -1,2 +1,2 @@ // ignore_for_file: missing_function_body -import 'package:mason/mason.dart';void run(HookContext context) \ No newline at end of file +import 'package:mason/mason.dart';void run(HookContext context) From 55452af89f758434f089b3fba3d92b9bccb4ea3d Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Fri, 11 Nov 2022 18:17:19 -0600 Subject: [PATCH 18/22] rename hash to fileHash --- packages/mason/lib/src/generator.dart | 4 ++-- packages/mason/lib/src/hooks.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mason/lib/src/generator.dart b/packages/mason/lib/src/generator.dart index 7346a65b6..0690cc07d 100644 --- a/packages/mason/lib/src/generator.dart +++ b/packages/mason/lib/src/generator.dart @@ -640,7 +640,7 @@ extension on String { } extension on HookFile { - String get hash => sha1.convert(content).toString(); + String get fileHash => sha1.convert(content).toString(); Directory get cacheDirectory { return Directory( @@ -658,6 +658,6 @@ extension on HookFile { } File get module { - return File(p.join(buildDirectory.path, '.$hash.dill')); + return File(p.join(buildDirectory.path, '.$fileHash.dill')); } } diff --git a/packages/mason/lib/src/hooks.dart b/packages/mason/lib/src/hooks.dart index 8ed018953..753909847 100644 --- a/packages/mason/lib/src/hooks.dart +++ b/packages/mason/lib/src/hooks.dart @@ -291,7 +291,7 @@ class GeneratorHooks { Future _compile({required HookFile hook, Logger? logger}) async { final hookCacheDir = hook.cacheDirectory; final hookBuildDir = hook.buildDirectory; - final hookHash = hook.hash; + final hookHash = hook.fileHash; try { await hookBuildDir.delete(recursive: true); From 948f87adb3447bc64965fa030ecb481f2ca40383 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Sat, 12 Nov 2022 13:18:13 -0600 Subject: [PATCH 19/22] compile on installation --- packages/mason_cli/lib/src/install_brick.dart | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/packages/mason_cli/lib/src/install_brick.dart b/packages/mason_cli/lib/src/install_brick.dart index 7d5b12dcc..8765989fd 100644 --- a/packages/mason_cli/lib/src/install_brick.dart +++ b/packages/mason_cli/lib/src/install_brick.dart @@ -48,40 +48,51 @@ mixin InstallBrickMixin on MasonCommand { if (bricksJson == null) throw const MasonYamlNotFoundException(); final lockJson = global ? globalMasonLockJson : localMasonLockJson; final resolvedBricks = {}; - final getBricksProgress = logger.progress( - upgrade ? 'Upgrading bricks' : 'Getting bricks', - ); + final message = upgrade ? 'Upgrading bricks' : 'Getting bricks'; + final getBricksProgress = logger.progress(message); try { bricksJson.clear(); final masonYaml = global ? globalMasonYaml : localMasonYaml; if (masonYaml.bricks.entries.isNotEmpty) { - await Future.forEach>( - masonYaml.bricks.entries, - (entry) async { - final location = resolveBrickLocation( - location: entry.value, - lockedLocation: upgrade ? null : lockJson.bricks[entry.key], - ); - final masonYamlFile = - global ? globalMasonYamlFile : localMasonYamlFile; - final normalizedLocation = location.path != null - ? BrickLocation( - path: canonicalize( - p.join(masonYamlFile.parent.path, location.path), - ), - ) - : location; - final cachedBrick = await bricksJson.add( - Brick(name: entry.key, location: normalizedLocation), - ); - resolvedBricks.addAll( - {entry.key: cachedBrick.brick.location}, - ); - }, + await Future.wait( + masonYaml.bricks.entries.map( + (entry) { + return () async { + getBricksProgress.update('Resolving ${entry.key}'); + final location = resolveBrickLocation( + location: entry.value, + lockedLocation: upgrade ? null : lockJson.bricks[entry.key], + ); + final masonYamlFile = + global ? globalMasonYamlFile : localMasonYamlFile; + final normalizedLocation = location.path != null + ? BrickLocation( + path: canonicalize( + p.join(masonYamlFile.parent.path, location.path), + ), + ) + : location; + getBricksProgress.update('Installing ${entry.key}'); + final cachedBrick = await bricksJson.add( + Brick(name: entry.key, location: normalizedLocation), + ); + resolvedBricks.addAll( + { + entry.key: cachedBrick.brick.location + }, + ); + getBricksProgress.update('Compiling ${entry.key}'); + final generator = await MasonGenerator.fromBrick( + Brick.path(cachedBrick.path), + ); + await generator.hooks.compile(); + }(); + }, + ), ); } } finally { - getBricksProgress.complete(); + getBricksProgress.complete(message); await bricksJson.flush(); final masonLockJsonFile = global ? globalMasonLockJsonFile : localMasonLockJsonFile; From e8f8b14913a435a87e8ddf26d7184b81d229401a Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Sat, 12 Nov 2022 13:43:03 -0600 Subject: [PATCH 20/22] fix test --- .../mason_cli/test/commands/upgrade_test.dart | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/mason_cli/test/commands/upgrade_test.dart b/packages/mason_cli/test/commands/upgrade_test.dart index 4b4751dd2..dbc44e16a 100644 --- a/packages/mason_cli/test/commands/upgrade_test.dart +++ b/packages/mason_cli/test/commands/upgrade_test.dart @@ -92,14 +92,11 @@ bricks: ); final getResult = await commandRunner.run(['get']); expect(getResult, equals(ExitCode.success.code)); - expect( - File( - path.join(Directory.current.path, MasonLockJson.file), - ).readAsStringSync(), - equals( - '{"bricks":{"greeting":"0.1.0+1","simple":{"path":"$simplePath"}}}', - ), - ); + final initialContents = File( + path.join(Directory.current.path, MasonLockJson.file), + ).readAsStringSync(); + expect(initialContents, contains('"greeting":"0.1.0+1"')); + expect(initialContents, contains('"simple":{"path":"$simplePath"}')); File(path.join(Directory.current.path, 'mason.yaml')).writeAsStringSync( ''' bricks: @@ -116,14 +113,11 @@ bricks: final upgradeResult = await commandRunner.run(['upgrade']); Directory.current = workspace; expect(upgradeResult, equals(ExitCode.success.code)); - expect( - File( - path.join(Directory.current.path, MasonLockJson.file), - ).readAsStringSync(), - equals( - '{"bricks":{"greeting":"0.1.0+2","simple":{"path":"$simplePath"}}}', - ), - ); + final updatedContents = File( + path.join(Directory.current.path, MasonLockJson.file), + ).readAsStringSync(); + expect(updatedContents, contains('"greeting":"0.1.0+2"')); + expect(updatedContents, contains('"simple":{"path":"$simplePath"}')); }); }); From 3aa591b345d2f5e8777b07dd03b2869a22859cd4 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Sat, 12 Nov 2022 14:01:48 -0600 Subject: [PATCH 21/22] improve logging --- packages/mason_cli/lib/src/commands/add.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mason_cli/lib/src/commands/add.dart b/packages/mason_cli/lib/src/commands/add.dart index cd22971b6..138ada158 100644 --- a/packages/mason_cli/lib/src/commands/add.dart +++ b/packages/mason_cli/lib/src/commands/add.dart @@ -69,7 +69,7 @@ class AddCommand extends MasonCommand with InstallBrickMixin { Brick.path(cachedBrick.path), ); - final compileProgress = logger.progress('Compiling hooks'); + final compileProgress = logger.progress('Compiling ${brick.name}'); try { await generator.hooks.compile(); compileProgress.complete(); From e5ea2457ce2d66a1f0d135964a62cd9f52412d14 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Sat, 12 Nov 2022 14:39:23 -0600 Subject: [PATCH 22/22] test fixes --- packages/mason_cli/lib/src/install_brick.dart | 10 ++++++- .../mason_cli/test/commands/upgrade_test.dart | 28 +++++++++++-------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/mason_cli/lib/src/install_brick.dart b/packages/mason_cli/lib/src/install_brick.dart index 8765989fd..ede304831 100644 --- a/packages/mason_cli/lib/src/install_brick.dart +++ b/packages/mason_cli/lib/src/install_brick.dart @@ -97,7 +97,7 @@ mixin InstallBrickMixin on MasonCommand { final masonLockJsonFile = global ? globalMasonLockJsonFile : localMasonLockJsonFile; await masonLockJsonFile.writeAsString( - json.encode(MasonLockJson(bricks: resolvedBricks)), + json.encode(MasonLockJson(bricks: resolvedBricks.sorted())), ); } } @@ -132,3 +132,11 @@ extension on GitPath { return url == other.url && path == other.path; } } + +extension on Map { + Map sorted() { + return Map.fromEntries( + entries.toList()..sort((a, b) => a.key.compareTo(b.key)), + ); + } +} diff --git a/packages/mason_cli/test/commands/upgrade_test.dart b/packages/mason_cli/test/commands/upgrade_test.dart index dbc44e16a..ddb9c90d4 100644 --- a/packages/mason_cli/test/commands/upgrade_test.dart +++ b/packages/mason_cli/test/commands/upgrade_test.dart @@ -77,7 +77,7 @@ bricks: ); }); - test('updates lockfile from nested directtory', () async { + test('updates lockfile from nested directory', () async { final bricksPath = path.join('..', '..', '..', '..', '..', 'bricks'); final simplePath = canonicalize( path.join(Directory.current.path, bricksPath, 'simple'), @@ -92,11 +92,14 @@ bricks: ); final getResult = await commandRunner.run(['get']); expect(getResult, equals(ExitCode.success.code)); - final initialContents = File( - path.join(Directory.current.path, MasonLockJson.file), - ).readAsStringSync(); - expect(initialContents, contains('"greeting":"0.1.0+1"')); - expect(initialContents, contains('"simple":{"path":"$simplePath"}')); + expect( + File( + path.join(Directory.current.path, MasonLockJson.file), + ).readAsStringSync(), + equals( + '{"bricks":{"greeting":"0.1.0+1","simple":{"path":"$simplePath"}}}', + ), + ); File(path.join(Directory.current.path, 'mason.yaml')).writeAsStringSync( ''' bricks: @@ -113,11 +116,14 @@ bricks: final upgradeResult = await commandRunner.run(['upgrade']); Directory.current = workspace; expect(upgradeResult, equals(ExitCode.success.code)); - final updatedContents = File( - path.join(Directory.current.path, MasonLockJson.file), - ).readAsStringSync(); - expect(updatedContents, contains('"greeting":"0.1.0+2"')); - expect(updatedContents, contains('"simple":{"path":"$simplePath"}')); + expect( + File( + path.join(Directory.current.path, MasonLockJson.file), + ).readAsStringSync(), + equals( + '{"bricks":{"greeting":"0.1.0+2","simple":{"path":"$simplePath"}}}', + ), + ); }); });