@@ -10,15 +10,14 @@ import 'package:logging/logging.dart';
10
10
import 'package:native_assets_cli/native_assets_cli_internal.dart' ;
11
11
import 'package:package_config/package_config.dart' ;
12
12
13
+ import '../file_system_cache/file_system_cache.dart' ;
13
14
import '../locking/locking.dart' ;
14
15
import '../model/build_dry_run_result.dart' ;
15
16
import '../model/build_result.dart' ;
16
17
import '../model/hook_result.dart' ;
17
18
import '../model/link_result.dart' ;
18
19
import '../package_layout/package_layout.dart' ;
19
- import '../utils/file.dart' ;
20
20
import '../utils/run_process.dart' ;
21
- import '../utils/uri.dart' ;
22
21
import 'build_planner.dart' ;
23
22
24
23
typedef DependencyMetadata = Map <String , Metadata >;
@@ -451,6 +450,8 @@ class NativeAssetsBuildRunner {
451
450
return hookResult;
452
451
}
453
452
453
+ // TODO(https://github.com/dart-lang/native/issues/32): Rerun hook if
454
+ // environment variables change.
454
455
Future <HookOutput ?> _runHookForPackageCached (
455
456
Hook hook,
456
457
HookConfig config,
@@ -473,7 +474,7 @@ class NativeAssetsBuildRunner {
473
474
final (
474
475
compileSuccess,
475
476
hookKernelFile,
476
- hookLastSourceChange ,
477
+ hookCacheFile ,
477
478
) = await _compileHookForPackageCached (
478
479
config.packageName,
479
480
config.outputDirectory,
@@ -488,7 +489,13 @@ class NativeAssetsBuildRunner {
488
489
489
490
final buildOutputFile =
490
491
File .fromUri (config.outputDirectory.resolve (hook.outputName));
491
- if (buildOutputFile.existsSync ()) {
492
+ final cacheFile = File .fromUri (
493
+ config.outputDirectory
494
+ .resolve ('../dependencies.file_system_cache.json' ),
495
+ );
496
+ final cache = FileSystemCache (cacheFile: cacheFile);
497
+ final cacheCutoffTime = DateTime .now ();
498
+ if (buildOutputFile.existsSync () && cacheFile.existsSync ()) {
492
499
late final HookOutput output;
493
500
try {
494
501
output = _readHookOutputFromUri (hook, buildOutputFile);
@@ -503,17 +510,13 @@ ${e.message}
503
510
return null ;
504
511
}
505
512
506
- final lastBuilt = output.timestamp.roundDownToSeconds ();
507
- final dependenciesLastChange =
508
- await Dependencies (output.dependencies).lastModified ();
509
- if (lastBuilt.isAfter (dependenciesLastChange) &&
510
- lastBuilt.isAfter (hookLastSourceChange)) {
513
+ await cache.readCacheFile ();
514
+ final changedFile = await cache.findOutdatedFileSystemEntity ();
515
+ if (changedFile == null ) {
511
516
logger.info (
512
517
[
513
518
'Skipping ${hook .name } for ${config .packageName } in $outDir .' ,
514
- 'Last build on $lastBuilt .' ,
515
- 'Last dependencies change on $dependenciesLastChange .' ,
516
- 'Last hook change on $hookLastSourceChange .' ,
519
+ 'Last build on ${output .timestamp }.' ,
517
520
].join (' ' ),
518
521
);
519
522
// All build flags go into [outDir]. Therefore we do not have to
@@ -522,7 +525,7 @@ ${e.message}
522
525
}
523
526
}
524
527
525
- return await _runHookForPackage (
528
+ final result = await _runHookForPackage (
526
529
hook,
527
530
config,
528
531
validator,
@@ -533,6 +536,26 @@ ${e.message}
533
536
hookKernelFile,
534
537
packageLayout,
535
538
);
539
+ if (result == null ) {
540
+ if (await cacheFile.exists ()) {
541
+ await cacheFile.delete ();
542
+ }
543
+ } else {
544
+ cache.reset ();
545
+ final modifiedDuringBuild = await cache.hashFiles (
546
+ [
547
+ ...result.dependencies,
548
+ // Also depend on the hook source code.
549
+ hookCacheFile.uri,
550
+ ],
551
+ validBeforeLastModified: cacheCutoffTime,
552
+ );
553
+ await cache.persist ();
554
+ if (modifiedDuringBuild != null ) {
555
+ logger.severe ('File modified during build. Build must be rerun.' );
556
+ }
557
+ }
558
+ return result;
536
559
},
537
560
);
538
561
}
@@ -644,7 +667,10 @@ ${e.message}
644
667
/// It does not reuse the cached kernel for different configs due to
645
668
/// reentrancy requirements. For more info see:
646
669
/// https://github.com/dart-lang/native/issues/1319
647
- Future <(bool success, File kernelFile, DateTime lastSourceChange)>
670
+ ///
671
+ /// TODO(https://github.com/dart-lang/native/issues/1578): Compile only once
672
+ /// instead of per config. This requires more locking.
673
+ Future <(bool success, File kernelFile, File cacheFile)>
648
674
_compileHookForPackageCached (
649
675
String packageName,
650
676
Uri outputDirectory,
@@ -659,29 +685,18 @@ ${e.message}
659
685
final depFile = File .fromUri (
660
686
outputDirectory.resolve ('../hook.dill.d' ),
661
687
);
688
+ final cacheFile = File .fromUri (
689
+ outputDirectory.resolve ('../hook.file_system_cache.json' ),
690
+ );
691
+ final cache = FileSystemCache (cacheFile: cacheFile);
692
+ final cacheCutoffTime = DateTime .now ();
662
693
final bool mustCompile;
663
- final DateTime sourceLastChange;
664
- if (! await depFile.exists ()) {
694
+ if (! await cacheFile.exists ()) {
665
695
mustCompile = true ;
666
- sourceLastChange = DateTime .now ();
667
696
} else {
668
- // Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
669
- final depFileContents = await depFile.readAsString ();
670
- final dartSourceFiles = depFileContents
671
- .trim ()
672
- .split (' ' )
673
- .skip (1 ) // '<kernel file>:'
674
- .map ((u) => Uri .file (u).fileSystemEntity)
675
- .toList ();
676
- final dartFilesLastChange = await dartSourceFiles.lastModified ();
677
- final packageConfigLastChange =
678
- await packageConfigUri.fileSystemEntity.lastModified ();
679
- sourceLastChange = packageConfigLastChange.isAfter (dartFilesLastChange)
680
- ? packageConfigLastChange
681
- : dartFilesLastChange;
682
- final kernelLastChange = await kernelFile.lastModified ();
683
- mustCompile = sourceLastChange == kernelLastChange ||
684
- sourceLastChange.isAfter (kernelLastChange);
697
+ await cache.readCacheFile ();
698
+ final changedFile = await cache.findOutdatedFileSystemEntity ();
699
+ mustCompile = changedFile != null ;
685
700
}
686
701
final bool success;
687
702
if (! mustCompile) {
@@ -696,8 +711,30 @@ ${e.message}
696
711
kernelFile,
697
712
depFile,
698
713
);
714
+
715
+ if (success) {
716
+ // Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
717
+ final depFileContents = await depFile.readAsString ();
718
+ final dartSources = depFileContents
719
+ .trim ()
720
+ .split (' ' )
721
+ .skip (1 ) // '<kernel file>:'
722
+ .map (Uri .file)
723
+ .toList ();
724
+ cache.reset ();
725
+ final modifiedDuringBuild = await cache.hashFiles (
726
+ dartSources,
727
+ validBeforeLastModified: cacheCutoffTime,
728
+ );
729
+ await cache.persist ();
730
+ if (modifiedDuringBuild != null ) {
731
+ logger.severe ('File modified during build. Build must be rerun.' );
732
+ }
733
+ } else {
734
+ await cacheFile.delete ();
735
+ }
699
736
}
700
- return (success, kernelFile, sourceLastChange );
737
+ return (success, kernelFile, cacheFile );
701
738
}
702
739
703
740
Future <bool > _compileHookForPackage (
@@ -859,12 +896,6 @@ ${compileResult.stdout}
859
896
}
860
897
}
861
898
862
- extension on DateTime {
863
- DateTime roundDownToSeconds () =>
864
- DateTime .fromMillisecondsSinceEpoch (millisecondsSinceEpoch -
865
- millisecondsSinceEpoch % const Duration (seconds: 1 ).inMilliseconds);
866
- }
867
-
868
899
extension on Uri {
869
900
Uri get parent => File (toFilePath ()).parent.uri;
870
901
}
0 commit comments