@@ -157,7 +157,7 @@ class NativeAssetsBuildRunner {
157
157
'Build configuration for ${package .name } contains errors' , errors);
158
158
}
159
159
160
- final hookOutput = await _runHookForPackageCached (
160
+ final result = await _runHookForPackageCached (
161
161
Hook .build,
162
162
config,
163
163
(config, output) =>
@@ -167,8 +167,9 @@ class NativeAssetsBuildRunner {
167
167
null ,
168
168
packageLayout,
169
169
);
170
- if (hookOutput == null ) return null ;
171
- hookResult = hookResult.copyAdd (hookOutput);
170
+ if (result == null ) return null ;
171
+ final (hookOutput, hookDeps) = result;
172
+ hookResult = hookResult.copyAdd (hookOutput, hookDeps);
172
173
globalMetadata[package.name] = (hookOutput as BuildOutput ).metadata;
173
174
}
174
175
@@ -259,7 +260,7 @@ class NativeAssetsBuildRunner {
259
260
'Link configuration for ${package .name } contains errors' , errors);
260
261
}
261
262
262
- final hookOutput = await _runHookForPackageCached (
263
+ final result = await _runHookForPackageCached (
263
264
Hook .link,
264
265
config,
265
266
(config, output) =>
@@ -269,8 +270,9 @@ class NativeAssetsBuildRunner {
269
270
resourceIdentifiers,
270
271
packageLayout,
271
272
);
272
- if (hookOutput == null ) return null ;
273
- hookResult = hookResult.copyAdd (hookOutput);
273
+ if (result == null ) return null ;
274
+ final (hookOutput, hookDeps) = result;
275
+ hookResult = hookResult.copyAdd (hookOutput, hookDeps);
274
276
}
275
277
276
278
final errors = await applicationAssetValidator (hookResult.encodedAssets);
@@ -403,18 +405,15 @@ class NativeAssetsBuildRunner {
403
405
404
406
final config = BuildConfig (configBuilder.json);
405
407
final packageConfigUri = packageLayout.packageConfigUri;
406
- final (
407
- compileSuccess,
408
- hookKernelFile,
409
- _,
410
- ) = await _compileHookForPackageCached (
408
+ final hookCompileResult = await _compileHookForPackageCached (
411
409
config.packageName,
412
410
config.outputDirectory,
413
411
config.packageRoot.resolve ('hook/${hook .scriptName }' ),
414
412
packageConfigUri,
415
413
workingDirectory,
416
414
);
417
- if (! compileSuccess) return null ;
415
+ if (hookCompileResult == null ) return null ;
416
+ final (hookKernelFile, _) = hookCompileResult;
418
417
419
418
// TODO(https://github.com/dart-lang/native/issues/1321): Should dry runs be cached?
420
419
final buildOutput = await runUnderDirectoriesLock (
@@ -437,12 +436,12 @@ class NativeAssetsBuildRunner {
437
436
),
438
437
);
439
438
if (buildOutput == null ) return null ;
440
- hookResult = hookResult.copyAdd (buildOutput);
439
+ hookResult = hookResult.copyAdd (buildOutput, [ /*dry run is not cached*/ ] );
441
440
}
442
441
return hookResult;
443
442
}
444
443
445
- Future <HookOutput ?> _runHookForPackageCached (
444
+ Future <( HookOutput , List < Uri >) ?> _runHookForPackageCached (
446
445
Hook hook,
447
446
HookConfig config,
448
447
_HookValidator validator,
@@ -461,20 +460,17 @@ class NativeAssetsBuildRunner {
461
460
timeout: singleHookTimeout,
462
461
logger: logger,
463
462
() async {
464
- final (
465
- compileSuccess,
466
- hookKernelFile,
467
- hookHashesFile,
468
- ) = await _compileHookForPackageCached (
463
+ final hookCompileResult = await _compileHookForPackageCached (
469
464
config.packageName,
470
465
config.outputDirectory,
471
466
config.packageRoot.resolve ('hook/${hook .scriptName }' ),
472
467
packageConfigUri,
473
468
workingDirectory,
474
469
);
475
- if (! compileSuccess ) {
470
+ if (hookCompileResult == null ) {
476
471
return null ;
477
472
}
473
+ final (hookKernelFile, hookHashes) = hookCompileResult;
478
474
479
475
final buildOutputFile =
480
476
File .fromUri (config.outputDirectory.resolve (hook.outputName));
@@ -510,7 +506,7 @@ ${e.message}
510
506
);
511
507
// All build flags go into [outDir]. Therefore we do not have to
512
508
// check here whether the config is equal.
513
- return output;
509
+ return ( output, hookHashes.fileSystemEntities) ;
514
510
}
515
511
logger.info (
516
512
'Rerunning ${hook .name } for ${config .packageName }'
@@ -533,12 +529,13 @@ ${e.message}
533
529
if (await dependenciesHashFile.exists ()) {
534
530
await dependenciesHashFile.delete ();
535
531
}
532
+ return null ;
536
533
} else {
537
534
final modifiedDuringBuild = await dependenciesHashes.hashDependencies (
538
535
[
539
536
...result.dependencies,
540
537
// Also depend on the hook source code.
541
- hookHashesFile .uri,
538
+ hookHashes.file .uri,
542
539
],
543
540
lastModifiedCutoffTime,
544
541
environment,
@@ -547,7 +544,7 @@ ${e.message}
547
544
logger.severe ('File modified during build. Build must be rerun.' );
548
545
}
549
546
}
550
- return result;
547
+ return ( result, hookHashes.fileSystemEntities) ;
551
548
},
552
549
);
553
550
}
@@ -685,7 +682,7 @@ ${e.message}
685
682
///
686
683
/// TODO(https://github.com/dart-lang/native/issues/1578): Compile only once
687
684
/// instead of per config. This requires more locking.
688
- Future <(bool success, File kernelFile, File cacheFile)>
685
+ Future <(File kernelFile, DependenciesHashFile cacheFile)? >
689
686
_compileHookForPackageCached (
690
687
String packageName,
691
688
Uri outputDirectory,
@@ -721,7 +718,7 @@ ${e.message}
721
718
}
722
719
723
720
if (! mustCompile) {
724
- return (true , kernelFile, dependenciesHashFile );
721
+ return (kernelFile, dependenciesHashes );
725
722
}
726
723
727
724
final success = await _compileHookForPackage (
@@ -733,8 +730,7 @@ ${e.message}
733
730
depFile,
734
731
);
735
732
if (! success) {
736
- await dependenciesHashFile.delete ();
737
- return (success, kernelFile, dependenciesHashFile);
733
+ return null ;
738
734
}
739
735
740
736
final dartSources = await _readDepFile (depFile);
@@ -751,19 +747,7 @@ ${e.message}
751
747
logger.severe ('File modified during build. Build must be rerun.' );
752
748
}
753
749
754
- return (success, kernelFile, dependenciesHashFile);
755
- }
756
-
757
- Future <List <Uri >> _readDepFile (File depFile) async {
758
- // Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
759
- final depFileContents = await depFile.readAsString ();
760
- final dartSources = depFileContents
761
- .trim ()
762
- .split (' ' )
763
- .skip (1 ) // '<kernel file>:'
764
- .map (Uri .file)
765
- .toList ();
766
- return dartSources;
750
+ return (kernelFile, dependenciesHashes);
767
751
}
768
752
769
753
Future <bool > _compileHookForPackage (
@@ -811,6 +795,12 @@ ${compileResult.stdout}
811
795
''' ,
812
796
);
813
797
success = false ;
798
+ if (await depFile.exists ()) {
799
+ await depFile.delete ();
800
+ }
801
+ if (await kernelFile.exists ()) {
802
+ await kernelFile.delete ();
803
+ }
814
804
}
815
805
return success;
816
806
}
@@ -927,3 +917,57 @@ ${compileResult.stdout}
927
917
extension on Uri {
928
918
Uri get parent => File (toFilePath ()).parent.uri;
929
919
}
920
+
921
+ /// Parses depfile contents.
922
+ ///
923
+ /// Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
924
+ ///
925
+ /// However, the spaces in paths are escaped with backslashes, and the
926
+ /// backslashes are escaped with backslashes:
927
+ ///
928
+ /// ```dart
929
+ /// String _escapePath(String path) {
930
+ /// return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ ');
931
+ /// }
932
+ /// ```
933
+ List <String > parseDepFileInputs (String contents) {
934
+ final output = contents.substring (0 , contents.indexOf (': ' ));
935
+ contents = contents.substring (output.length + ': ' .length).trim ();
936
+ final pathsEscaped = _splitOnNonEscapedSpaces (contents);
937
+ return pathsEscaped.map (_unescapeDepsPath).toList ();
938
+ }
939
+
940
+ String _unescapeDepsPath (String path) =>
941
+ path.replaceAll (r'\ ' , ' ' ).replaceAll (r'\\' , r'\' );
942
+
943
+ List <String > _splitOnNonEscapedSpaces (String contents) {
944
+ var index = 0 ;
945
+ final result = < String > [];
946
+ while (index < contents.length) {
947
+ final start = index;
948
+ while (index < contents.length) {
949
+ final u = contents.codeUnitAt (index);
950
+ if (u == ' ' .codeUnitAt (0 )) {
951
+ break ;
952
+ }
953
+ if (u == r'\' .codeUnitAt (0 )) {
954
+ index++ ;
955
+ if (index == contents.length) {
956
+ throw const FormatException ('malformed, ending with backslash' );
957
+ }
958
+ final v = contents.codeUnitAt (index);
959
+ assert (v == ' ' .codeUnitAt (0 ) || v == r'\' .codeUnitAt (0 ));
960
+ }
961
+ index++ ;
962
+ }
963
+ result.add (contents.substring (start, index));
964
+ index++ ;
965
+ }
966
+ return result;
967
+ }
968
+
969
+ Future <List <Uri >> _readDepFile (File depFile) async {
970
+ final depFileContents = await depFile.readAsString ();
971
+ final dartSources = parseDepFileInputs (depFileContents);
972
+ return dartSources.map (Uri .file).toList ();
973
+ }
0 commit comments