-
Notifications
You must be signed in to change notification settings - Fork 67
[native_assets_builder] Report dart sources as dependencies of hooks #1780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,7 +157,7 @@ class NativeAssetsBuildRunner { | |
'Build configuration for ${package.name} contains errors', errors); | ||
} | ||
|
||
final hookOutput = await _runHookForPackageCached( | ||
final result = await _runHookForPackageCached( | ||
Hook.build, | ||
config, | ||
(config, output) => | ||
|
@@ -167,8 +167,9 @@ class NativeAssetsBuildRunner { | |
null, | ||
packageLayout, | ||
); | ||
if (hookOutput == null) return null; | ||
hookResult = hookResult.copyAdd(hookOutput); | ||
if (result == null) return null; | ||
final (hookOutput, hookDeps) = result; | ||
hookResult = hookResult.copyAdd(hookOutput, hookDeps); | ||
globalMetadata[package.name] = (hookOutput as BuildOutput).metadata; | ||
} | ||
|
||
|
@@ -259,7 +260,7 @@ class NativeAssetsBuildRunner { | |
'Link configuration for ${package.name} contains errors', errors); | ||
} | ||
|
||
final hookOutput = await _runHookForPackageCached( | ||
final result = await _runHookForPackageCached( | ||
Hook.link, | ||
config, | ||
(config, output) => | ||
|
@@ -269,8 +270,9 @@ class NativeAssetsBuildRunner { | |
resourceIdentifiers, | ||
packageLayout, | ||
); | ||
if (hookOutput == null) return null; | ||
hookResult = hookResult.copyAdd(hookOutput); | ||
if (result == null) return null; | ||
final (hookOutput, hookDeps) = result; | ||
hookResult = hookResult.copyAdd(hookOutput, hookDeps); | ||
} | ||
|
||
final errors = await applicationAssetValidator(hookResult.encodedAssets); | ||
|
@@ -403,18 +405,15 @@ class NativeAssetsBuildRunner { | |
|
||
final config = BuildConfig(configBuilder.json); | ||
final packageConfigUri = packageLayout.packageConfigUri; | ||
final ( | ||
compileSuccess, | ||
hookKernelFile, | ||
_, | ||
) = await _compileHookForPackageCached( | ||
final hookCompileResult = await _compileHookForPackageCached( | ||
config.packageName, | ||
config.outputDirectory, | ||
config.packageRoot.resolve('hook/${hook.scriptName}'), | ||
packageConfigUri, | ||
workingDirectory, | ||
); | ||
if (!compileSuccess) return null; | ||
if (hookCompileResult == null) return null; | ||
final (hookKernelFile, _) = hookCompileResult; | ||
|
||
// TODO(https://github.com/dart-lang/native/issues/1321): Should dry runs be cached? | ||
final buildOutput = await runUnderDirectoriesLock( | ||
|
@@ -437,12 +436,12 @@ class NativeAssetsBuildRunner { | |
), | ||
); | ||
if (buildOutput == null) return null; | ||
hookResult = hookResult.copyAdd(buildOutput); | ||
hookResult = hookResult.copyAdd(buildOutput, [/*dry run is not cached*/]); | ||
} | ||
return hookResult; | ||
} | ||
|
||
Future<HookOutput?> _runHookForPackageCached( | ||
Future<(HookOutput, List<Uri>)?> _runHookForPackageCached( | ||
Hook hook, | ||
HookConfig config, | ||
_HookValidator validator, | ||
|
@@ -461,20 +460,17 @@ class NativeAssetsBuildRunner { | |
timeout: singleHookTimeout, | ||
logger: logger, | ||
() async { | ||
final ( | ||
compileSuccess, | ||
hookKernelFile, | ||
hookHashesFile, | ||
) = await _compileHookForPackageCached( | ||
final hookCompileResult = await _compileHookForPackageCached( | ||
config.packageName, | ||
config.outputDirectory, | ||
config.packageRoot.resolve('hook/${hook.scriptName}'), | ||
packageConfigUri, | ||
workingDirectory, | ||
); | ||
if (!compileSuccess) { | ||
if (hookCompileResult == null) { | ||
return null; | ||
} | ||
final (hookKernelFile, hookHashes) = hookCompileResult; | ||
|
||
final buildOutputFile = | ||
File.fromUri(config.outputDirectory.resolve(hook.outputName)); | ||
|
@@ -510,7 +506,7 @@ ${e.message} | |
); | ||
// All build flags go into [outDir]. Therefore we do not have to | ||
// check here whether the config is equal. | ||
return output; | ||
return (output, hookHashes.fileSystemEntities); | ||
} | ||
logger.info( | ||
'Rerunning ${hook.name} for ${config.packageName}' | ||
|
@@ -533,12 +529,13 @@ ${e.message} | |
if (await dependenciesHashFile.exists()) { | ||
await dependenciesHashFile.delete(); | ||
} | ||
return null; | ||
} else { | ||
final modifiedDuringBuild = await dependenciesHashes.hashDependencies( | ||
[ | ||
...result.dependencies, | ||
// Also depend on the hook source code. | ||
hookHashesFile.uri, | ||
hookHashes.file.uri, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's assume we did one build. Now I modify Next time we run a build, the system sees that hook sources changed and it will re-compile the hook, giving us a new dill file. Now, since we only changed comments in the So the next step is running the hook. Though we can skip running the hook if none of the hook-reported deps changed and the Though this code looks like we're going to re-run the hook, because we add the (One could argue this redundant run is ok because it's very uncommon, but from conceptual point we should only re-run things when needed) |
||
], | ||
lastModifiedCutoffTime, | ||
environment, | ||
|
@@ -547,7 +544,7 @@ ${e.message} | |
logger.severe('File modified during build. Build must be rerun.'); | ||
} | ||
} | ||
return result; | ||
return (result, hookHashes.fileSystemEntities); | ||
}, | ||
); | ||
} | ||
|
@@ -685,7 +682,7 @@ ${e.message} | |
/// | ||
/// TODO(https://github.com/dart-lang/native/issues/1578): Compile only once | ||
/// instead of per config. This requires more locking. | ||
Future<(bool success, File kernelFile, File cacheFile)> | ||
Future<(File kernelFile, DependenciesHashFile cacheFile)?> | ||
_compileHookForPackageCached( | ||
String packageName, | ||
Uri outputDirectory, | ||
|
@@ -721,7 +718,7 @@ ${e.message} | |
} | ||
|
||
if (!mustCompile) { | ||
return (true, kernelFile, dependenciesHashFile); | ||
return (kernelFile, dependenciesHashes); | ||
} | ||
|
||
final success = await _compileHookForPackage( | ||
|
@@ -733,8 +730,7 @@ ${e.message} | |
depFile, | ||
); | ||
if (!success) { | ||
await dependenciesHashFile.delete(); | ||
return (success, kernelFile, dependenciesHashFile); | ||
return null; | ||
} | ||
|
||
final dartSources = await _readDepFile(depFile); | ||
|
@@ -751,19 +747,7 @@ ${e.message} | |
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; | ||
return (kernelFile, dependenciesHashes); | ||
} | ||
|
||
Future<bool> _compileHookForPackage( | ||
|
@@ -811,6 +795,12 @@ ${compileResult.stdout} | |
''', | ||
); | ||
success = false; | ||
if (await depFile.exists()) { | ||
await depFile.delete(); | ||
} | ||
if (await kernelFile.exists()) { | ||
await kernelFile.delete(); | ||
} | ||
} | ||
return success; | ||
} | ||
|
@@ -927,3 +917,57 @@ ${compileResult.stdout} | |
extension on Uri { | ||
Uri get parent => File(toFilePath()).parent.uri; | ||
} | ||
|
||
/// Parses depfile contents. | ||
/// | ||
/// Format: `path/to/my.dill: path/to/my.dart, path/to/more.dart` | ||
/// | ||
/// However, the spaces in paths are escaped with backslashes, and the | ||
/// backslashes are escaped with backslashes: | ||
/// | ||
/// ```dart | ||
/// String _escapePath(String path) { | ||
/// return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ '); | ||
/// } | ||
/// ``` | ||
List<String> parseDepFileInputs(String contents) { | ||
final output = contents.substring(0, contents.indexOf(': ')); | ||
contents = contents.substring(output.length + ': '.length).trim(); | ||
final pathsEscaped = _splitOnNonEscapedSpaces(contents); | ||
return pathsEscaped.map(_unescapeDepsPath).toList(); | ||
} | ||
|
||
String _unescapeDepsPath(String path) => | ||
path.replaceAll(r'\ ', ' ').replaceAll(r'\\', r'\'); | ||
|
||
List<String> _splitOnNonEscapedSpaces(String contents) { | ||
var index = 0; | ||
final result = <String>[]; | ||
while (index < contents.length) { | ||
final start = index; | ||
while (index < contents.length) { | ||
final u = contents.codeUnitAt(index); | ||
if (u == ' '.codeUnitAt(0)) { | ||
break; | ||
} | ||
if (u == r'\'.codeUnitAt(0)) { | ||
index++; | ||
if (index == contents.length) { | ||
throw const FormatException('malformed, ending with backslash'); | ||
} | ||
final v = contents.codeUnitAt(index); | ||
assert(v == ' '.codeUnitAt(0) || v == r'\'.codeUnitAt(0)); | ||
} | ||
index++; | ||
} | ||
result.add(contents.substring(start, index)); | ||
index++; | ||
} | ||
return result; | ||
} | ||
|
||
Future<List<Uri>> _readDepFile(File depFile) async { | ||
final depFileContents = await depFile.readAsString(); | ||
final dartSources = parseDepFileInputs(depFileContents); | ||
return dartSources.map(Uri.file).toList(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't you say dry run was finally purged from existence?
Why is this code still here?