Skip to content

Commit 3f79866

Browse files
committed
[native_assets_builder] Recompile hook kernel if Dart changes
1 parent e69c74d commit 3f79866

File tree

4 files changed

+113
-68
lines changed

4 files changed

+113
-68
lines changed

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

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -510,20 +510,23 @@ ${e.message}
510510
''');
511511
return null;
512512
}
513-
514-
final outdated =
515-
(await dependenciesHashes.findOutdatedFileSystemEntity()) != null;
516-
if (!outdated) {
513+
final outdatedFile =
514+
await dependenciesHashes.findOutdatedFileSystemEntity();
515+
if (outdatedFile == null) {
517516
logger.info(
518-
[
519-
'Skipping ${hook.name} for ${config.packageName} in $outDir.',
520-
'Last build on ${output.timestamp}.',
521-
].join(' '),
517+
'Skipping ${hook.name} for ${config.packageName}'
518+
' in ${outDir.toFilePath()}.'
519+
' Last build on ${output.timestamp}.',
522520
);
523521
// All build flags go into [outDir]. Therefore we do not have to
524522
// check here whether the config is equal.
525523
return output;
526524
}
525+
logger.info(
526+
'Rerunning ${hook.name} for ${config.packageName}'
527+
' in ${outDir.toFilePath()}.'
528+
' ${outdatedFile.toFilePath()} changed.',
529+
);
527530
}
528531

529532
final result = await _runHookForPackage(
@@ -542,7 +545,8 @@ ${e.message}
542545
await dependenciesHashFile.delete();
543546
}
544547
} else {
545-
final modifiedDuringBuild = await dependenciesHashes.hashFiles(
548+
final modifiedDuringBuild =
549+
await dependenciesHashes.hashFilesAndDirectories(
546550
[
547551
...result.dependencies,
548552
// Also depend on the hook source code.
@@ -678,6 +682,10 @@ ${e.message}
678682
Uri workingDirectory,
679683
bool includeParentEnvironment,
680684
) async {
685+
final dartPathFile = File.fromUri(
686+
outputDirectory.resolve('../hook.dill.dart_path.txt'),
687+
);
688+
681689
final kernelFile = File.fromUri(
682690
outputDirectory.resolve('../hook.dill'),
683691
);
@@ -689,50 +697,78 @@ ${e.message}
689697
);
690698
final dependenciesHashes = DependenciesHashFile(file: dependenciesHashFile);
691699
final lastModifiedCutoffTime = DateTime.now();
692-
final bool mustCompile;
693-
if (!await dependenciesHashFile.exists()) {
700+
var mustCompile = false;
701+
if (!await dependenciesHashFile.exists() || !await dartPathFile.exists()) {
694702
mustCompile = true;
695703
} else {
696-
mustCompile =
697-
(await dependenciesHashes.findOutdatedFileSystemEntity()) != null;
698-
}
699-
final bool success;
700-
if (!mustCompile) {
701-
success = true;
702-
} else {
703-
success = await _compileHookForPackage(
704-
packageName,
705-
scriptUri,
706-
packageConfigUri,
707-
workingDirectory,
708-
includeParentEnvironment,
709-
kernelFile,
710-
depFile,
711-
);
704+
final previousDartExecutable =
705+
Uri.file(await dartPathFile.readAsString());
706+
if (previousDartExecutable != dartExecutable) {
707+
mustCompile = true;
708+
logger.info(
709+
'Recompiling ${scriptUri.toFilePath()}, Dart executable changed.',
710+
);
711+
}
712712

713-
if (success) {
714-
// Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
715-
final depFileContents = await depFile.readAsString();
716-
final dartSources = depFileContents
717-
.trim()
718-
.split(' ')
719-
.skip(1) // '<kernel file>:'
720-
.map(Uri.file)
721-
.toList();
722-
final modifiedDuringBuild = await dependenciesHashes.hashFiles(
723-
dartSources,
724-
validBeforeLastModified: lastModifiedCutoffTime,
713+
final outdatedFile =
714+
await dependenciesHashes.findOutdatedFileSystemEntity();
715+
if (outdatedFile != null) {
716+
mustCompile = true;
717+
logger.info(
718+
'Recompiling ${scriptUri.toFilePath()}, '
719+
'${outdatedFile.toFilePath()} changed.',
725720
);
726-
if (modifiedDuringBuild != null) {
727-
logger.severe('File modified during build. Build must be rerun.');
728-
}
729-
} else {
730-
await dependenciesHashFile.delete();
731721
}
732722
}
723+
724+
if (!mustCompile) {
725+
return (true, kernelFile, dependenciesHashFile);
726+
}
727+
728+
final success = await _compileHookForPackage(
729+
packageName,
730+
scriptUri,
731+
packageConfigUri,
732+
workingDirectory,
733+
includeParentEnvironment,
734+
kernelFile,
735+
depFile,
736+
);
737+
if (!success) {
738+
await dependenciesHashFile.delete();
739+
return (success, kernelFile, dependenciesHashFile);
740+
}
741+
742+
final dartSources = await _readDepFile(depFile);
743+
final modifiedDuringBuild =
744+
await dependenciesHashes.hashFilesAndDirectories(
745+
[
746+
...dartSources,
747+
// If the Dart executable is replaced in-place, recompile.
748+
dartExecutable,
749+
],
750+
validBeforeLastModified: lastModifiedCutoffTime,
751+
);
752+
await dartPathFile.writeAsString(dartExecutable.toFilePath());
753+
if (modifiedDuringBuild != null) {
754+
logger.severe('File modified during build. Build must be rerun.');
755+
}
756+
733757
return (success, kernelFile, dependenciesHashFile);
734758
}
735759

760+
Future<List<Uri>> _readDepFile(File depFile) async {
761+
// Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
762+
final depFileContents = await depFile.readAsString();
763+
final dartSources = depFileContents
764+
.trim()
765+
.split(' ')
766+
.skip(1) // '<kernel file>:'
767+
.map(Uri.file)
768+
.toList();
769+
return dartSources;
770+
}
771+
736772
Future<bool> _compileHookForPackage(
737773
String packageName,
738774
Uri scriptUri,

pkgs/native_assets_builder/lib/src/dependencies_hash_file/dependencies_hash_file.dart

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class DependenciesHashFile {
2626
}
2727
final jsonObject =
2828
(json.decode(utf8.decode(await _file.readAsBytes())) as Map)
29-
.cast<String, dynamic>();
29+
.cast<String, Object>();
3030
_hashes = FileSystemHashes.fromJson(jsonObject);
3131
}
3232

@@ -38,7 +38,7 @@ class DependenciesHashFile {
3838
/// If [validBeforeLastModified] is provided, any entities that were modified
3939
/// after [validBeforeLastModified] will get a dummy hash so that they will
4040
/// show up as outdated. If any such entity exists, its uri will be returned.
41-
Future<Uri?> hashFiles(
41+
Future<Uri?> hashFilesAndDirectories(
4242
List<Uri> fileSystemEntities, {
4343
DateTime? validBeforeLastModified,
4444
}) async {
@@ -134,32 +134,25 @@ class DependenciesHashFile {
134134
/// [Directory] hashes are a hash of the names of the direct children.
135135
class FileSystemHashes {
136136
FileSystemHashes({
137-
this.version = 1,
138137
List<FilesystemEntityHash>? files,
139138
}) : files = files ?? [];
140139

141-
factory FileSystemHashes.fromJson(Map<String, dynamic> json) {
142-
final version = json[_versionKey] as int;
143-
final rawEntries =
144-
(json[_entitiesKey] as List<dynamic>).cast<Map<String, dynamic>>();
140+
factory FileSystemHashes.fromJson(Map<String, Object> json) {
141+
final rawEntries = (json[_entitiesKey] as List).cast<Object>();
145142
final files = <FilesystemEntityHash>[
146-
for (final Map<String, dynamic> rawEntry in rawEntries)
147-
FilesystemEntityHash._fromJson(rawEntry),
143+
for (final rawEntry in rawEntries)
144+
FilesystemEntityHash._fromJson((rawEntry as Map).cast()),
148145
];
149146
return FileSystemHashes(
150-
version: version,
151147
files: files,
152148
);
153149
}
154150

155-
final int version;
156151
final List<FilesystemEntityHash> files;
157152

158-
static const _versionKey = 'version';
159153
static const _entitiesKey = 'entities';
160154

161155
Map<String, Object> toJson() => <String, Object>{
162-
_versionKey: version,
163156
_entitiesKey: <Object>[
164157
for (final FilesystemEntityHash file in files) file.toJson(),
165158
],
@@ -177,7 +170,7 @@ class FilesystemEntityHash {
177170
this.hash,
178171
);
179172

180-
factory FilesystemEntityHash._fromJson(Map<String, dynamic> json) =>
173+
factory FilesystemEntityHash._fromJson(Map<String, Object> json) =>
181174
FilesystemEntityHash(
182175
_fileSystemPathToUri(json[_pathKey] as String),
183176
json[_hashKey] as int,

pkgs/native_assets_builder/test/build_runner/build_runner_caching_test.dart

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,14 @@ void main() async {
8787
await copyTestProjects(targetUri: tempUri);
8888
final packageUri = tempUri.resolve('native_add/');
8989

90+
final logMessages = <String>[];
91+
final logger = createCapturingLogger(logMessages);
92+
9093
await runPubGet(
9194
workingDirectory: packageUri,
9295
logger: logger,
9396
);
97+
logMessages.clear();
9498

9599
{
96100
final result = (await build(
@@ -105,6 +109,7 @@ void main() async {
105109
await expectSymbols(
106110
asset: CodeAsset.fromEncoded(result.encodedAssets.single),
107111
symbols: ['add']);
112+
logMessages.clear();
108113
}
109114

110115
await copyTestProjects(
@@ -122,6 +127,18 @@ void main() async {
122127
buildValidator: validateCodeAssetBuildOutput,
123128
applicationAssetValidator: validateCodeAssetInApplication,
124129
))!;
130+
131+
final cUri = packageUri.resolve('src/').resolve('native_add.c');
132+
expect(
133+
logMessages.join('\n'),
134+
stringContainsInOrder(
135+
[
136+
'Rerunning build for native_add in',
137+
'${cUri.toFilePath()} changed.'
138+
],
139+
),
140+
);
141+
125142
await expectSymbols(
126143
asset: CodeAsset.fromEncoded(result.encodedAssets.single),
127144
symbols: ['add', 'subtract'],
@@ -181,14 +198,13 @@ void main() async {
181198
buildValidator: validateCodeAssetBuildOutput,
182199
applicationAssetValidator: validateCodeAssetInApplication,
183200
))!;
184-
{
185-
final compiledHook = logMessages
186-
.where((m) =>
187-
m.contains('dart compile kernel') ||
188-
m.contains('dart.exe compile kernel'))
189-
.isNotEmpty;
190-
expect(compiledHook, isTrue);
191-
}
201+
202+
final hookUri = packageUri.resolve('hook/').resolve('build.dart');
203+
expect(
204+
logMessages.join('\n'),
205+
contains('Recompiling ${hookUri.toFilePath()}'),
206+
);
207+
192208
logMessages.clear();
193209
await expectSymbols(
194210
asset: CodeAsset.fromEncoded(result.encodedAssets.single),

pkgs/native_assets_builder/test/dependencies_hash_file/dependencies_hash_file_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void main() async {
4343
await tempFile.writeAsString('hello');
4444
await subFile.writeAsString('world');
4545

46-
await hashes.hashFiles([
46+
await hashes.hashFilesAndDirectories([
4747
tempFile.uri,
4848
tempSubDir.uri,
4949
]);
@@ -95,7 +95,7 @@ void main() async {
9595

9696
// If a file is modified after the valid timestamp, it should be marked
9797
// as changed.
98-
await hashes.hashFiles(
98+
await hashes.hashFilesAndDirectories(
9999
[
100100
tempFile.uri,
101101
],

0 commit comments

Comments
 (0)