Skip to content

Commit

Permalink
[native_assets_builder] Recompile hook kernel if Dart changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dcharkes committed Nov 28, 2024
1 parent e69c74d commit ceb2bec
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 56 deletions.
117 changes: 77 additions & 40 deletions pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -510,10 +510,9 @@ ${e.message}
''');
return null;
}

final outdated =
(await dependenciesHashes.findOutdatedFileSystemEntity()) != null;
if (!outdated) {
final outdatedFile =
await dependenciesHashes.findOutdatedFileSystemEntity();
if (outdatedFile == null) {
logger.info(
[
'Skipping ${hook.name} for ${config.packageName} in $outDir.',
Expand All @@ -524,6 +523,12 @@ ${e.message}
// check here whether the config is equal.
return output;
}
logger.info(
[
'Running ${hook.name} for ${config.packageName} in $outDir.',
'${outdatedFile.toFilePath()} changed.',
].join(' '),
);
}

final result = await _runHookForPackage(
Expand All @@ -542,7 +547,8 @@ ${e.message}
await dependenciesHashFile.delete();
}
} else {
final modifiedDuringBuild = await dependenciesHashes.hashFiles(
final modifiedDuringBuild =
await dependenciesHashes.hashFilesAndDirectories(
[
...result.dependencies,
// Also depend on the hook source code.
Expand Down Expand Up @@ -678,6 +684,10 @@ ${e.message}
Uri workingDirectory,
bool includeParentEnvironment,
) async {
final dartPathFile = File.fromUri(
outputDirectory.resolve('../hook.dill.dart_path.txt'),
);

final kernelFile = File.fromUri(
outputDirectory.resolve('../hook.dill'),
);
Expand All @@ -689,50 +699,77 @@ ${e.message}
);
final dependenciesHashes = DependenciesHashFile(file: dependenciesHashFile);
final lastModifiedCutoffTime = DateTime.now();
final bool mustCompile;
if (!await dependenciesHashFile.exists()) {
var mustCompile = false;
if (!await dependenciesHashFile.exists() || !await dartPathFile.exists()) {
mustCompile = true;
} else {
mustCompile =
(await dependenciesHashes.findOutdatedFileSystemEntity()) != null;
}
final bool success;
if (!mustCompile) {
success = true;
} else {
success = await _compileHookForPackage(
packageName,
scriptUri,
packageConfigUri,
workingDirectory,
includeParentEnvironment,
kernelFile,
depFile,
);
final previousDartExecutable =
Uri.file(await dartPathFile.readAsString());
if (previousDartExecutable != dartExecutable) {
mustCompile = true;
logger.info(
'Recompiling $scriptUri, Dart executable changed.',
);
}

if (success) {
// Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
final depFileContents = await depFile.readAsString();
final dartSources = depFileContents
.trim()
.split(' ')
.skip(1) // '<kernel file>:'
.map(Uri.file)
.toList();
final modifiedDuringBuild = await dependenciesHashes.hashFiles(
dartSources,
validBeforeLastModified: lastModifiedCutoffTime,
final outdatedFile =
await dependenciesHashes.findOutdatedFileSystemEntity();
if (outdatedFile != null) {
mustCompile = true;
logger.info(
'Recompiling $scriptUri, ${outdatedFile.toFilePath()} changed.',
);
if (modifiedDuringBuild != null) {
logger.severe('File modified during build. Build must be rerun.');
}
} else {
await dependenciesHashFile.delete();
}
}

if (!mustCompile) {
return (true, kernelFile, dependenciesHashFile);
}

final success = await _compileHookForPackage(
packageName,
scriptUri,
packageConfigUri,
workingDirectory,
includeParentEnvironment,
kernelFile,
depFile,
);
if (!success) {
await dependenciesHashFile.delete();
return (success, kernelFile, dependenciesHashFile);
}

final dartSources = await _readDepFile(depFile);
final modifiedDuringBuild =
await dependenciesHashes.hashFilesAndDirectories(
[
...dartSources,
// If the Dart executable is replaced in-place, recompile.
dartExecutable,
],
validBeforeLastModified: lastModifiedCutoffTime,
);
await dartPathFile.writeAsString(dartExecutable.toFilePath());
if (modifiedDuringBuild != null) {
logger.severe('File modified during build. Build must be rerun.');
}

return (success, kernelFile, dependenciesHashFile);
}

Future<List<Uri>> _readDepFile(File depFile) async {
// Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart`
final depFileContents = await depFile.readAsString();
final dartSources = depFileContents
.trim()
.split(' ')
.skip(1) // '<kernel file>:'
.map(Uri.file)
.toList();
return dartSources;
}

Future<bool> _compileHookForPackage(
String packageName,
Uri scriptUri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class DependenciesHashFile {
}
final jsonObject =
(json.decode(utf8.decode(await _file.readAsBytes())) as Map)
.cast<String, dynamic>();
.cast<String, Object>();
_hashes = FileSystemHashes.fromJson(jsonObject);
}

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

factory FileSystemHashes.fromJson(Map<String, dynamic> json) {
final version = json[_versionKey] as int;
final rawEntries =
(json[_entitiesKey] as List<dynamic>).cast<Map<String, dynamic>>();
factory FileSystemHashes.fromJson(Map<String, Object> json) {
final rawEntries = (json[_entitiesKey] as List).cast<Object>();
final files = <FilesystemEntityHash>[
for (final Map<String, dynamic> rawEntry in rawEntries)
FilesystemEntityHash._fromJson(rawEntry),
for (final rawEntry in rawEntries)
FilesystemEntityHash._fromJson((rawEntry as Map).cast()),
];
return FileSystemHashes(
version: version,
files: files,
);
}

final int version;
final List<FilesystemEntityHash> files;

static const _versionKey = 'version';
static const _entitiesKey = 'entities';

Map<String, Object> toJson() => <String, Object>{
_versionKey: version,
_entitiesKey: <Object>[
for (final FilesystemEntityHash file in files) file.toJson(),
],
Expand All @@ -177,7 +170,7 @@ class FilesystemEntityHash {
this.hash,
);

factory FilesystemEntityHash._fromJson(Map<String, dynamic> json) =>
factory FilesystemEntityHash._fromJson(Map<String, Object> json) =>
FilesystemEntityHash(
_fileSystemPathToUri(json[_pathKey] as String),
json[_hashKey] as int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void main() async {
await tempFile.writeAsString('hello');
await subFile.writeAsString('world');

await hashes.hashFiles([
await hashes.hashFilesAndDirectories([
tempFile.uri,
tempSubDir.uri,
]);
Expand Down Expand Up @@ -95,7 +95,7 @@ void main() async {

// If a file is modified after the valid timestamp, it should be marked
// as changed.
await hashes.hashFiles(
await hashes.hashFilesAndDirectories(
[
tempFile.uri,
],
Expand Down

0 comments on commit ceb2bec

Please sign in to comment.