Unverified Commit 477ae6b8 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

cleanup use of build runner internals (#40045)

parent eb7eb3ce
...@@ -7,205 +7,74 @@ import 'dart:async'; ...@@ -7,205 +7,74 @@ import 'dart:async';
import 'dart:io' as io; // ignore: dart_io_import import 'dart:io' as io; // ignore: dart_io_import
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:build_daemon/client.dart';
import 'package:build_daemon/data/build_status.dart';
import 'package:build_runner_core/build_runner_core.dart' as core; import 'package:build_runner_core/build_runner_core.dart' as core;
import 'package:build_runner_core/src/asset_graph/graph.dart';
import 'package:build_runner_core/src/asset_graph/node.dart';
import 'package:build_runner_core/src/generate/build_impl.dart';
import 'package:build_runner_core/src/generate/options.dart';
import 'package:glob/glob.dart'; import 'package:glob/glob.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:watcher/watcher.dart';
import '../artifacts.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../compile.dart';
import '../convert.dart'; import '../convert.dart';
import '../dart/package_map.dart';
import '../globals.dart';
import '../web/compile.dart'; import '../web/compile.dart';
import 'build_script.dart'; import 'web_fs.dart';
/// A build_runner specific implementation of the [WebCompilationProxy]. /// A build_runner specific implementation of the [WebCompilationProxy].
class BuildRunnerWebCompilationProxy extends WebCompilationProxy { class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
BuildRunnerWebCompilationProxy(); BuildRunnerWebCompilationProxy();
core.PackageGraph _packageGraph;
BuildImpl _builder;
PackageUriMapper _packageUriMapper;
@override @override
Future<bool> initialize({ Future<bool> initialize({
Directory projectDirectory, Directory projectDirectory,
String testOutputDir, String testOutputDir,
BuildMode mode, BuildMode mode,
String projectName
}) async { }) async {
// Create the .dart_tool directory if it doesn't exist. // Create the .dart_tool directory if it doesn't exist.
projectDirectory.childDirectory('.dart_tool').createSync(); projectDirectory
final Directory generatedDirectory = projectDirectory
.childDirectory('.dart_tool') .childDirectory('.dart_tool')
.childDirectory('build') .createSync();
.childDirectory('generated'); final BuildDaemonClient client = await buildDaemonCreator.startBuildDaemon(
// Override the generated output directory so this does not conflict with
// other build_runner output.
core.overrideGeneratedOutputDirectory('flutter_web');
_packageUriMapper = PackageUriMapper(
path.absolute('lib/main.dart'), PackageMap.globalPackagesPath, null, null);
_packageGraph = core.PackageGraph.forPath(projectDirectory.path);
final core.BuildEnvironment buildEnvironment = core.OverrideableEnvironment(
core.IOEnvironment(_packageGraph), onLog: (LogRecord record) {
if (record.level == Level.SEVERE || record.level == Level.SHOUT) {
printError(record.message);
} else {
printTrace(record.message);
}
}, reader: MultirootFileBasedAssetReader(_packageGraph, generatedDirectory));
final LogSubscription logSubscription = LogSubscription(
buildEnvironment,
verbose: false,
logLevel: Level.FINE,
);
final BuildOptions buildOptions = await BuildOptions.create(
logSubscription,
packageGraph: _packageGraph,
skipBuildScriptCheck: true,
trackPerformance: false,
deleteFilesByDefault: true,
enableLowResourcesMode: platform.environment['FLUTTER_LOW_RESOURCE_MODE']?.toLowerCase() == 'true',
);
final Set<core.BuildDirectory> buildDirs = <core.BuildDirectory>{
if (testOutputDir != null)
core.BuildDirectory(
'test',
outputLocation: core.OutputLocation(
testOutputDir,
useSymlinks: !platform.isWindows,
),
),
};
core.BuildResult result;
try {
result = await _runBuilder(
buildEnvironment,
buildOptions,
mode,
buildDirs,
);
return result.status == core.BuildStatus.success;
} on core.BuildConfigChangedException {
await _cleanAssets(projectDirectory);
result = await _runBuilder(
buildEnvironment,
buildOptions,
mode,
buildDirs,
);
return result.status == core.BuildStatus.success;
} on core.BuildScriptChangedException {
await _cleanAssets(projectDirectory);
result = await _runBuilder(
buildEnvironment,
buildOptions,
mode,
buildDirs,
);
return result.status == core.BuildStatus.success;
}
}
@override
Future<bool> invalidate({@required List<Uri> inputs}) async {
final Status status =
logger.startProgress('Recompiling sources...', timeout: null);
final Map<AssetId, ChangeType> updates = <AssetId, ChangeType>{};
for (Uri input in inputs) {
final AssetId assetId = AssetId.resolve(_packageUriMapper.map(input.toFilePath()).toString());
updates[assetId] = ChangeType.MODIFY;
}
core.BuildResult result;
try {
result = await _builder.run(updates);
} finally {
status.cancel();
}
return result.status == core.BuildStatus.success;
}
Future<core.BuildResult> _runBuilder(core.BuildEnvironment buildEnvironment, BuildOptions buildOptions, BuildMode buildMode, Set<core.BuildDirectory> buildDirs) async {
_builder = await BuildImpl.create(
buildOptions,
buildEnvironment,
builders,
<String, Map<String, dynamic>>{
'flutter_tools:ddc': <String, dynamic>{
'flutterWebSdk': artifacts.getArtifactPath(Artifact.flutterWebSdk),
},
'flutter_tools:entrypoint': <String, dynamic>{
'release': buildMode == BuildMode.release,
'flutterWebSdk': artifacts.getArtifactPath(Artifact.flutterWebSdk),
'profile': buildMode == BuildMode.profile,
},
'flutter_tools:test_entrypoint': <String, dynamic>{
'release': buildMode == BuildMode.release,
'profile': buildMode == BuildMode.profile,
},
},
isReleaseBuild: false,
);
return _builder.run(
const <AssetId, ChangeType>{},
buildDirs: buildDirs,
);
}
Future<void> _cleanAssets(Directory projectDirectory) async {
final File assetGraphFile = fs.file(core.assetGraphPath);
AssetGraph assetGraph;
try {
assetGraph = AssetGraph.deserialize(await assetGraphFile.readAsBytes());
} catch (_) {
printTrace('Failed to clean up asset graph.');
}
final core.PackageGraph packageGraph = core.PackageGraph.forThisPackage();
await _cleanUpSourceOutputs(assetGraph, packageGraph);
final Directory cacheDirectory = fs.directory(fs.path.join(
projectDirectory.path, projectDirectory.path,
'.dart_tool', release: mode == BuildMode.release,
'build', profile: mode == BuildMode.profile,
'flutter_web', hasPlugins: false,
)); includeTests: true,
if (assetGraphFile.existsSync()) { );
assetGraphFile.deleteSync(); client.startBuild();
} bool success = true;
if (cacheDirectory.existsSync()) { await for (BuildResults results in client.buildResults) {
cacheDirectory.deleteSync(recursive: true); final BuildResult result = results.results.firstWhere((BuildResult result) {
} return result.target == 'web';
} });
if (result.status == BuildStatus.failed) {
Future<void> _cleanUpSourceOutputs(AssetGraph assetGraph, core.PackageGraph packageGraph) async { success = false;
final core.FileBasedAssetWriter writer = core.FileBasedAssetWriter(packageGraph); break;
if (assetGraph?.outputs == null) { }
return; if (result.status == BuildStatus.succeeded) {
} break;
for (AssetId id in assetGraph.outputs) {
if (id.package != packageGraph.root.name) {
continue;
} }
final GeneratedAssetNode node = assetGraph.get(id); }
if (node.wasOutput) { if (success && testOutputDir != null) {
// Note that this does a file.exists check in the root package and final Directory rootDirectory = projectDirectory
// only tries to delete the file if it exists. This way we only .childDirectory('.dart_tool')
// actually delete to_source outputs, without reading in the build .childDirectory('build')
// actions. .childDirectory('flutter_web');
await writer.delete(id);
final Iterable<Directory> childDirectories = rootDirectory
.listSync()
.whereType<Directory>();
for (Directory childDirectory in childDirectories) {
final String path = fs.path.join(testOutputDir, 'packages',
fs.path.basename(childDirectory.path));
copyDirectorySync(childDirectory.childDirectory('lib'), fs.directory(path));
} }
final Directory outputDirectory = rootDirectory
.childDirectory(projectName)
.childDirectory('test');
copyDirectorySync(outputDirectory, fs.directory(fs.path.join(testOutputDir)));
} }
return success;
} }
} }
......
...@@ -336,7 +336,12 @@ class BuildDaemonCreator { ...@@ -336,7 +336,12 @@ class BuildDaemonCreator {
static const String _ignoredLine3 = 'have your dependencies specified fully in your pubspec.yaml'; static const String _ignoredLine3 = 'have your dependencies specified fully in your pubspec.yaml';
/// Start a build daemon and register the web targets. /// Start a build daemon and register the web targets.
Future<BuildDaemonClient> startBuildDaemon(String workingDirectory, {bool release = false, bool profile = false, bool hasPlugins = false}) async { Future<BuildDaemonClient> startBuildDaemon(String workingDirectory, {
bool release = false,
bool profile = false,
bool hasPlugins = false,
bool includeTests = false,
}) async {
try { try {
final BuildDaemonClient client = await _connectClient( final BuildDaemonClient client = await _connectClient(
workingDirectory, workingDirectory,
...@@ -344,7 +349,7 @@ class BuildDaemonCreator { ...@@ -344,7 +349,7 @@ class BuildDaemonCreator {
profile: profile, profile: profile,
hasPlugins: hasPlugins, hasPlugins: hasPlugins,
); );
_registerBuildTargets(client); _registerBuildTargets(client, includeTests);
return client; return client;
} on OptionsSkew { } on OptionsSkew {
throwToolExit( throwToolExit(
...@@ -357,6 +362,7 @@ class BuildDaemonCreator { ...@@ -357,6 +362,7 @@ class BuildDaemonCreator {
void _registerBuildTargets( void _registerBuildTargets(
BuildDaemonClient client, BuildDaemonClient client,
bool includeTests,
) { ) {
final OutputLocation outputLocation = OutputLocation((OutputLocationBuilder b) => b final OutputLocation outputLocation = OutputLocation((OutputLocationBuilder b) => b
..output = '' ..output = ''
...@@ -365,6 +371,11 @@ class BuildDaemonCreator { ...@@ -365,6 +371,11 @@ class BuildDaemonCreator {
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) => b client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) => b
..target = 'web' ..target = 'web'
..outputLocation = outputLocation?.toBuilder())); ..outputLocation = outputLocation?.toBuilder()));
if (includeTests) {
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) => b
..target = 'test'
..outputLocation = outputLocation?.toBuilder()));
}
} }
Future<BuildDaemonClient> _connectClient( Future<BuildDaemonClient> _connectClient(
......
...@@ -69,6 +69,7 @@ Future<int> runTests( ...@@ -69,6 +69,7 @@ Future<int> runTests(
final bool result = await webCompilationProxy.initialize( final bool result = await webCompilationProxy.initialize(
projectDirectory: flutterProject.directory, projectDirectory: flutterProject.directory,
testOutputDir: tempBuildDir, testOutputDir: tempBuildDir,
projectName: flutterProject.manifest.appName,
); );
if (!result) { if (!result) {
throwToolExit('Failed to compile tests'); throwToolExit('Failed to compile tests');
......
...@@ -31,6 +31,7 @@ Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo bu ...@@ -31,6 +31,7 @@ Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo bu
result = await webCompilationProxy.initialize( result = await webCompilationProxy.initialize(
projectDirectory: FlutterProject.current().directory, projectDirectory: FlutterProject.current().directory,
mode: buildInfo.mode, mode: buildInfo.mode,
projectName: flutterProject.manifest.appName,
); );
if (result) { if (result) {
// Places assets adjacent to the web stuff. // Places assets adjacent to the web stuff.
...@@ -80,14 +81,10 @@ class WebCompilationProxy { ...@@ -80,14 +81,10 @@ class WebCompilationProxy {
/// the entrypoints for dart2js to later take over. /// the entrypoints for dart2js to later take over.
Future<bool> initialize({ Future<bool> initialize({
@required Directory projectDirectory, @required Directory projectDirectory,
@required String projectName,
String testOutputDir, String testOutputDir,
BuildMode mode, BuildMode mode,
}) async { }) async {
throw UnimplementedError(); throw UnimplementedError();
} }
/// Invalidate the source files in `inputs` and recompile them to JavaScript.
Future<void> invalidate({@required List<Uri> inputs}) async {
throw UnimplementedError();
}
} }
...@@ -41,6 +41,7 @@ void main() { ...@@ -41,6 +41,7 @@ void main() {
fs.file(fs.path.join('web', 'index.html')).createSync(recursive: true); fs.file(fs.path.join('web', 'index.html')).createSync(recursive: true);
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true); fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
when(mockWebCompilationProxy.initialize( when(mockWebCompilationProxy.initialize(
projectName: anyNamed('projectName'),
projectDirectory: anyNamed('projectDirectory'), projectDirectory: anyNamed('projectDirectory'),
mode: anyNamed('mode') mode: anyNamed('mode')
)).thenAnswer((Invocation invocation) { )).thenAnswer((Invocation invocation) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment