in build_web_compilers/lib/src/dev_compiler_builder.dart [154:315]
Future<void> _createDevCompilerModule(
Module module,
BuildStep buildStep,
bool useIncrementalCompiler,
bool generateFullDill,
bool emitDebugSymbols,
bool trackUnusedInputs,
String dartSdk,
String sdkKernelPath,
String librariesPath,
Map<String, String> environment,
bool soundNullSafety,
{bool debugMode = true}) async {
var transitiveDeps = await buildStep.trackStage('CollectTransitiveDeps',
() => module.computeTransitiveDependencies(buildStep));
var transitiveKernelDeps = [
for (var dep in transitiveDeps)
dep.primarySource.changeExtension(ddcKernelExtension(soundNullSafety)),
];
var scratchSpace = await buildStep.fetchResource(scratchSpaceResource);
var allAssetIds = <AssetId>{...module.sources, ...transitiveKernelDeps};
await buildStep.trackStage(
'EnsureAssets', () => scratchSpace.ensureAssets(allAssetIds, buildStep));
var jsId =
module.primarySource.changeExtension(jsModuleExtension(soundNullSafety));
var jsOutputFile = scratchSpace.fileFor(jsId);
var sdkSummary = p.url.join(dartSdk, sdkKernelPath);
// Maps the inputs paths we provide to the ddc worker to asset ids, if
// `trackUnusedInputs` is `true`.
Map<String, AssetId>? kernelInputPathToId;
// If `trackUnusedInputs` is `true`, this is the file we will use to
// communicate the used inputs with the ddc worker.
File? usedInputsFile;
if (trackUnusedInputs) {
usedInputsFile = await File(p.join(
(await Directory.systemTemp.createTemp('ddk_builder_')).path,
'used_inputs.txt'))
.create();
kernelInputPathToId = {};
}
var request = WorkRequest()
..arguments.addAll([
'--dart-sdk-summary=$sdkSummary',
'--modules=amd',
'--no-summarize',
if (generateFullDill) '--experimental-output-compiled-kernel',
if (emitDebugSymbols) '--emit-debug-symbols',
'-o',
jsOutputFile.path,
debugMode ? '--source-map' : '--no-source-map',
for (var dep in transitiveDeps) _summaryArg(dep, soundNullSafety),
'--packages=$multiRootScheme:///.dart_tool/package_config.json',
'--module-name=${ddcModuleName(jsId, soundNullSafety)}',
'--multi-root-scheme=$multiRootScheme',
'--multi-root=.',
'--track-widget-creation',
'--inline-source-map',
'--libraries-file=${p.toUri(librariesPath)}',
'--experimental-emit-debug-metadata',
if (useIncrementalCompiler) ...[
'--reuse-compiler-result',
'--use-incremental-compiler',
],
if (usedInputsFile != null)
'--used-inputs-file=${usedInputsFile.uri.toFilePath()}',
for (var source in module.sources) _sourceArg(source),
for (var define in environment.entries) '-D${define.key}=${define.value}',
for (var experiment in enabledExperiments)
'--enable-experiment=$experiment',
'--${soundNullSafety ? '' : 'no-'}sound-null-safety',
])
..inputs.add(Input()
..path = sdkSummary
..digest = [0])
..inputs.addAll(await Future.wait(transitiveKernelDeps.map((dep) async {
var file = scratchSpace.fileFor(dep);
if (kernelInputPathToId != null) {
kernelInputPathToId[file.uri.toString()] = dep;
}
return Input()
..path = file.path
..digest = (await buildStep.digest(dep)).bytes;
})));
try {
var driverResource = dartdevkDriverResource;
var driver = await buildStep.fetchResource(driverResource);
var response = await driver.doWork(request,
trackWork: (response) =>
buildStep.trackStage('Compile', () => response, isExternal: true));
// TODO(jakemac53): Fix the ddc worker mode so it always sends back a bad
// status code if something failed. Today we just make sure there is an output
// JS file to verify it was successful.
var message = response.output
.replaceAll('${scratchSpace.tempDir.path}/', '')
.replaceAll('$multiRootScheme:///', '');
if (response.exitCode != EXIT_CODE_OK ||
!jsOutputFile.existsSync() ||
message.contains('Error:')) {
throw DartDevcCompilationException(jsId, message);
}
if (message.isNotEmpty) {
log.info('\n$message');
}
// Copy the output back using the buildStep.
await scratchSpace.copyOutput(jsId, buildStep);
if (generateFullDill) {
var currentFullKernelId = module.primarySource
.changeExtension(fullKernelExtension(soundNullSafety));
await scratchSpace.copyOutput(currentFullKernelId, buildStep);
}
if (debugMode) {
// We need to modify the sources in the sourcemap to remove the custom
// `multiRootScheme` that we use.
var sourceMapId = module.primarySource
.changeExtension(jsSourceMapExtension(soundNullSafety));
var file = scratchSpace.fileFor(sourceMapId);
var content = await file.readAsString();
var json = jsonDecode(content);
json['sources'] = fixSourceMapSources((json['sources'] as List).cast());
await buildStep.writeAsString(sourceMapId, jsonEncode(json));
// Copy the metadata output, modifying its contents to remove the temp
// directory from paths
var metadataId = module.primarySource
.changeExtension(metadataExtension(soundNullSafety));
file = scratchSpace.fileFor(metadataId);
content = await file.readAsString();
json = jsonDecode(content);
_fixMetadataSources(
json as Map<String, dynamic>, scratchSpace.tempDir.uri);
await buildStep.writeAsString(metadataId, jsonEncode(json));
// Copy the symbols output, modifying its contents to remove the temp
// directory from paths
if (emitDebugSymbols) {
var symbolsId = module.primarySource
.changeExtension(symbolsExtension(soundNullSafety));
await scratchSpace.copyOutput(symbolsId, buildStep);
}
}
// Note that we only want to do this on success, we can't trust the unused
// inputs if there is a failure.
if (usedInputsFile != null) {
await reportUnusedKernelInputs(usedInputsFile, transitiveKernelDeps,
kernelInputPathToId!, buildStep);
}
} finally {
await usedInputsFile?.parent.delete(recursive: true);
}
}