mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add build script invalidation and snapshotting logic (#28866)
This commit is contained in:
parent
e5b1ed7a7f
commit
dd94499418
@ -12,13 +12,14 @@ import 'package:build_daemon/data/build_status.dart' as build;
|
|||||||
import 'package:build_daemon/client.dart';
|
import 'package:build_daemon/client.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:yaml/yaml.dart';
|
import 'package:yaml/yaml.dart';
|
||||||
|
import 'package:crypto/crypto.dart' show md5;
|
||||||
|
|
||||||
import '../artifacts.dart';
|
import '../artifacts.dart';
|
||||||
|
import '../base/common.dart';
|
||||||
import '../base/file_system.dart';
|
import '../base/file_system.dart';
|
||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process_manager.dart';
|
||||||
import '../cache.dart';
|
|
||||||
import '../codegen.dart';
|
import '../codegen.dart';
|
||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
import '../dart/pub.dart';
|
import '../dart/pub.dart';
|
||||||
@ -53,24 +54,23 @@ class BuildRunner extends CodeGenerator {
|
|||||||
final String sdkRoot = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
|
final String sdkRoot = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
|
||||||
final String engineDartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
|
final String engineDartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
|
||||||
final String packagesPath = flutterProject.packagesFile.absolute.path;
|
final String packagesPath = flutterProject.packagesFile.absolute.path;
|
||||||
final String buildScript = flutterProject
|
final String buildSnapshot = flutterProject
|
||||||
.dartTool
|
.dartTool
|
||||||
.childDirectory('build')
|
.childDirectory('build')
|
||||||
.childDirectory('entrypoint')
|
.childDirectory('entrypoint')
|
||||||
.childFile('build.dart')
|
.childFile('build.dart.snapshot')
|
||||||
.path;
|
.path;
|
||||||
final String scriptPackagesPath = flutterProject
|
final String scriptPackagesPath = flutterProject
|
||||||
.dartTool
|
.dartTool
|
||||||
.childDirectory('flutter_tool')
|
.childDirectory('flutter_tool')
|
||||||
.childFile('.packages')
|
.childFile('.packages')
|
||||||
.path;
|
.path;
|
||||||
final String dartPath = fs.path.join(Cache.flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', 'dart');
|
|
||||||
final Status status = logger.startProgress('running builders...', timeout: null);
|
final Status status = logger.startProgress('running builders...', timeout: null);
|
||||||
try {
|
try {
|
||||||
final Process buildProcess = await processManager.start(<String>[
|
final Process buildProcess = await processManager.start(<String>[
|
||||||
dartPath,
|
engineDartBinaryPath,
|
||||||
'--packages=$scriptPackagesPath',
|
'--packages=$scriptPackagesPath',
|
||||||
buildScript,
|
buildSnapshot,
|
||||||
'build',
|
'build',
|
||||||
'--skip-build-script-check',
|
'--skip-build-script-check',
|
||||||
'--define', 'flutter_build|kernel=disabled=$disableKernelGeneration',
|
'--define', 'flutter_build|kernel=disabled=$disableKernelGeneration',
|
||||||
@ -95,7 +95,6 @@ class BuildRunner extends CodeGenerator {
|
|||||||
.transform(utf8.decoder)
|
.transform(utf8.decoder)
|
||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.listen(printError);
|
.listen(printError);
|
||||||
await buildProcess.exitCode;
|
|
||||||
} finally {
|
} finally {
|
||||||
status.stop();
|
status.stop();
|
||||||
}
|
}
|
||||||
@ -127,32 +126,45 @@ class BuildRunner extends CodeGenerator {
|
|||||||
return CodeGenerationResult(packagesFile, dillFile);
|
return CodeGenerationResult(packagesFile, dillFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> invalidateBuildScript() async {
|
|
||||||
final FlutterProject flutterProject = await FlutterProject.current();
|
|
||||||
final File buildScript = flutterProject.dartTool
|
|
||||||
.absolute
|
|
||||||
.childDirectory('flutter_tool')
|
|
||||||
.childFile('build.dart');
|
|
||||||
if (!buildScript.existsSync()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await buildScript.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> generateBuildScript() async {
|
Future<void> generateBuildScript() async {
|
||||||
final FlutterProject flutterProject = await FlutterProject.current();
|
final FlutterProject flutterProject = await FlutterProject.current();
|
||||||
final String generatedDirectory = fs.path.join(flutterProject.dartTool.path, 'flutter_tool');
|
final Directory entrypointDirectory = fs.directory(fs.path.join(flutterProject.dartTool.path, 'build', 'entrypoint'));
|
||||||
final String resultScriptPath = fs.path.join(flutterProject.dartTool.path, 'build', 'entrypoint', 'build.dart');
|
final Directory generatedDirectory = fs.directory(fs.path.join(flutterProject.dartTool.path, 'flutter_tool'));
|
||||||
if (fs.file(resultScriptPath).existsSync()) {
|
final File buildScript = entrypointDirectory.childFile('build.dart');
|
||||||
return;
|
final File buildSnapshot = entrypointDirectory.childFile('build.dart.snapshot');
|
||||||
|
final File scriptIdFile = entrypointDirectory.childFile('id');
|
||||||
|
final File syntheticPubspec = generatedDirectory.childFile('pubspec.yaml');
|
||||||
|
|
||||||
|
// Check if contents of builders changed. If so, invalidate build script
|
||||||
|
// and regnerate.
|
||||||
|
final YamlMap builders = await flutterProject.builders;
|
||||||
|
final List<int> appliedBuilderDigest = _produceScriptId(builders);
|
||||||
|
if (scriptIdFile.existsSync() && buildSnapshot.existsSync()) {
|
||||||
|
final List<int> previousAppliedBuilderDigest = scriptIdFile.readAsBytesSync();
|
||||||
|
bool digestsAreEqual = false;
|
||||||
|
if (appliedBuilderDigest.length == previousAppliedBuilderDigest.length) {
|
||||||
|
digestsAreEqual = true;
|
||||||
|
for (int i = 0; i < appliedBuilderDigest.length; i++) {
|
||||||
|
if (appliedBuilderDigest[i] != previousAppliedBuilderDigest[i]) {
|
||||||
|
digestsAreEqual = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (digestsAreEqual) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clean-up all existing artifacts.
|
||||||
|
if (flutterProject.dartTool.existsSync()) {
|
||||||
|
flutterProject.dartTool.deleteSync(recursive: true);
|
||||||
}
|
}
|
||||||
final Status status = logger.startProgress('generating build script...', timeout: null);
|
final Status status = logger.startProgress('generating build script...', timeout: null);
|
||||||
try {
|
try {
|
||||||
fs.directory(generatedDirectory).createSync(recursive: true);
|
generatedDirectory.createSync(recursive: true);
|
||||||
|
entrypointDirectory.createSync(recursive: true);
|
||||||
final File syntheticPubspec = fs.file(fs.path.join(generatedDirectory, 'pubspec.yaml'));
|
flutterProject.dartTool.childDirectory('build').childDirectory('generated').createSync(recursive: true);
|
||||||
final StringBuffer stringBuffer = StringBuffer();
|
final StringBuffer stringBuffer = StringBuffer();
|
||||||
|
|
||||||
stringBuffer.writeln('name: flutter_tool');
|
stringBuffer.writeln('name: flutter_tool');
|
||||||
@ -160,24 +172,38 @@ class BuildRunner extends CodeGenerator {
|
|||||||
final YamlMap builders = await flutterProject.builders;
|
final YamlMap builders = await flutterProject.builders;
|
||||||
if (builders != null) {
|
if (builders != null) {
|
||||||
for (String name in builders.keys) {
|
for (String name in builders.keys) {
|
||||||
final YamlNode node = builders[name];
|
final Object node = builders[name];
|
||||||
stringBuffer.writeln(' $name: $node');
|
stringBuffer.writeln(' $name: $node');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stringBuffer.writeln(' build_runner: any');
|
stringBuffer.writeln(' build_runner: any');
|
||||||
stringBuffer.writeln(' flutter_build:');
|
stringBuffer.writeln(' flutter_build:');
|
||||||
stringBuffer.writeln(' sdk: flutter');
|
stringBuffer.writeln(' sdk: flutter');
|
||||||
await syntheticPubspec.writeAsString(stringBuffer.toString());
|
syntheticPubspec.writeAsStringSync(stringBuffer.toString());
|
||||||
|
|
||||||
await pubGet(
|
await pubGet(
|
||||||
context: PubContext.pubGet,
|
context: PubContext.pubGet,
|
||||||
directory: generatedDirectory,
|
directory: generatedDirectory.path,
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
checkLastModified: false,
|
checkLastModified: false,
|
||||||
);
|
);
|
||||||
|
if (!scriptIdFile.existsSync()) {
|
||||||
|
scriptIdFile.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
scriptIdFile.writeAsBytesSync(appliedBuilderDigest);
|
||||||
final PackageGraph packageGraph = PackageGraph.forPath(syntheticPubspec.parent.path);
|
final PackageGraph packageGraph = PackageGraph.forPath(syntheticPubspec.parent.path);
|
||||||
final BuildScriptGenerator buildScriptGenerator = const BuildScriptGeneratorFactory().create(flutterProject, packageGraph);
|
final BuildScriptGenerator buildScriptGenerator = const BuildScriptGeneratorFactory().create(flutterProject, packageGraph);
|
||||||
await buildScriptGenerator.generateBuildScript();
|
await buildScriptGenerator.generateBuildScript();
|
||||||
|
final ProcessResult result = await processManager.run(<String>[
|
||||||
|
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
||||||
|
'--snapshot=${buildSnapshot.path}',
|
||||||
|
'--snapshot-kind=app-jit',
|
||||||
|
'--packages=${fs.path.join(generatedDirectory.path, '.packages')}',
|
||||||
|
buildScript.path,
|
||||||
|
]);
|
||||||
|
if (result.exitCode != 0) {
|
||||||
|
throwToolExit('Error generating build_script snapshot: ${result.stderr}');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
status.stop();
|
status.stop();
|
||||||
}
|
}
|
||||||
@ -200,25 +226,23 @@ class BuildRunner extends CodeGenerator {
|
|||||||
final String sdkRoot = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
|
final String sdkRoot = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
|
||||||
final String engineDartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
|
final String engineDartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
|
||||||
final String packagesPath = flutterProject.packagesFile.absolute.path;
|
final String packagesPath = flutterProject.packagesFile.absolute.path;
|
||||||
final String buildScript = flutterProject
|
final File buildSnapshot = flutterProject
|
||||||
.dartTool
|
.dartTool
|
||||||
.childDirectory('build')
|
.childDirectory('build')
|
||||||
.childDirectory('entrypoint')
|
.childDirectory('entrypoint')
|
||||||
.childFile('build.dart')
|
.childFile('build.dart.snapshot');
|
||||||
.path;
|
|
||||||
final String scriptPackagesPath = flutterProject
|
final String scriptPackagesPath = flutterProject
|
||||||
.dartTool
|
.dartTool
|
||||||
.childDirectory('flutter_tool')
|
.childDirectory('flutter_tool')
|
||||||
.childFile('.packages')
|
.childFile('.packages')
|
||||||
.path;
|
.path;
|
||||||
final String dartPath = fs.path.join(Cache.flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', 'dart');
|
|
||||||
final Status status = logger.startProgress('starting build daemon...', timeout: null);
|
final Status status = logger.startProgress('starting build daemon...', timeout: null);
|
||||||
BuildDaemonClient buildDaemonClient;
|
BuildDaemonClient buildDaemonClient;
|
||||||
try {
|
try {
|
||||||
final List<String> command = <String>[
|
final List<String> command = <String>[
|
||||||
dartPath,
|
engineDartBinaryPath,
|
||||||
'--packages=$scriptPackagesPath',
|
'--packages=$scriptPackagesPath',
|
||||||
buildScript,
|
buildSnapshot.path,
|
||||||
'daemon',
|
'daemon',
|
||||||
'--skip-build-script-check',
|
'--skip-build-script-check',
|
||||||
'--define', 'flutter_build|kernel=disabled=false',
|
'--define', 'flutter_build|kernel=disabled=false',
|
||||||
@ -279,3 +303,14 @@ class _BuildRunnerCodegenDaemon implements CodegenDaemon {
|
|||||||
buildDaemonClient.startBuild();
|
buildDaemonClient.startBuild();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sorts the builders by name and produces a hashcode of the resulting iterable.
|
||||||
|
List<int> _produceScriptId(YamlMap builders) {
|
||||||
|
if (builders == null || builders.isEmpty) {
|
||||||
|
return md5.convert(<int>[]).bytes;
|
||||||
|
}
|
||||||
|
final List<String> orderedBuilders = builders.keys
|
||||||
|
.cast<String>()
|
||||||
|
.toList()..sort();
|
||||||
|
return md5.convert(orderedBuilders.join('').codeUnits).bytes;
|
||||||
|
}
|
||||||
|
@ -81,12 +81,6 @@ abstract class CodeGenerator {
|
|||||||
List<String> extraFrontEndOptions = const <String>[],
|
List<String> extraFrontEndOptions = const <String>[],
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Invalidates a generated build script by deleting it.
|
|
||||||
///
|
|
||||||
/// Must be called any time a pubspec file update triggers a corresponding change
|
|
||||||
/// in .packages.
|
|
||||||
Future<void> invalidateBuildScript();
|
|
||||||
|
|
||||||
// Generates a synthetic package under .dart_tool/flutter_tool which is in turn
|
// Generates a synthetic package under .dart_tool/flutter_tool which is in turn
|
||||||
// used to generate a build script.
|
// used to generate a build script.
|
||||||
Future<void> generateBuildScript();
|
Future<void> generateBuildScript();
|
||||||
@ -113,11 +107,6 @@ class UnsupportedCodeGenerator extends CodeGenerator {
|
|||||||
throw UnsupportedError('build_runner is not currently supported.');
|
throw UnsupportedError('build_runner is not currently supported.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> invalidateBuildScript() {
|
|
||||||
throw UnsupportedError('build_runner is not currently supported.');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CodegenDaemon> daemon({
|
Future<CodegenDaemon> daemon({
|
||||||
String mainPath,
|
String mainPath,
|
||||||
|
Loading…
Reference in New Issue
Block a user