blob: 5a5a482c2e77d7698ee125e3432752566c5128d0 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: implementation_imports
import 'dart:async';
import 'dart:io' as io; // ignore: dart_io_import
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:glob/glob.dart';
import 'package:path/path.dart' as path;
import '../base/file_system.dart';
import '../build_info.dart';
import '../convert.dart';
import '../platform_plugins.dart';
import '../plugins.dart';
import '../project.dart';
import '../web/compile.dart';
import 'web_fs.dart';
/// A build_runner specific implementation of the [WebCompilationProxy].
class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
BuildRunnerWebCompilationProxy();
@override
Future<bool> initialize({
Directory projectDirectory,
String testOutputDir,
List<String> testFiles,
BuildMode mode,
String projectName,
bool initializePlatform,
}) async {
// Create the .dart_tool directory if it doesn't exist.
projectDirectory
.childDirectory('.dart_tool')
.createSync();
final FlutterProject flutterProject = FlutterProject.fromDirectory(projectDirectory);
final bool hasWebPlugins = findPlugins(flutterProject)
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
final BuildDaemonClient client = await buildDaemonCreator.startBuildDaemon(
projectDirectory.path,
release: mode == BuildMode.release,
profile: mode == BuildMode.profile,
hasPlugins: hasWebPlugins,
initializePlatform: initializePlatform,
testTargets: WebTestTargetManifest(
testFiles
.map<String>((String absolutePath) {
final String relativePath = path.relative(absolutePath, from: projectDirectory.path);
return '${path.withoutExtension(relativePath)}.*';
})
.toList(),
),
);
client.startBuild();
bool success = true;
await for (BuildResults results in client.buildResults) {
final BuildResult result = results.results.firstWhere((BuildResult result) {
return result.target == 'web';
});
if (result.status == BuildStatus.failed) {
success = false;
break;
}
if (result.status == BuildStatus.succeeded) {
break;
}
}
if (success && testOutputDir != null) {
final Directory rootDirectory = projectDirectory
.childDirectory('.dart_tool')
.childDirectory('build')
.childDirectory('flutter_web');
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;
}
}
/// Handles mapping a single root file scheme to a multi-root scheme.
///
/// This allows one build_runner build to read the output from a previous
/// isolated build.
class MultirootFileBasedAssetReader extends core.FileBasedAssetReader {
MultirootFileBasedAssetReader(
core.PackageGraph packageGraph,
this.generatedDirectory,
) : super(packageGraph);
final Directory generatedDirectory;
@override
Future<bool> canRead(AssetId id) {
if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
return _generatedFile(id).exists();
}
return super.canRead(id);
}
@override
Future<List<int>> readAsBytes(AssetId id) {
if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
return _generatedFile(id).readAsBytes();
}
return super.readAsBytes(id);
}
@override
Future<String> readAsString(AssetId id, {Encoding encoding}) {
if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
return _generatedFile(id).readAsString();
}
return super.readAsString(id, encoding: encoding);
}
@override
Stream<AssetId> findAssets(Glob glob, {String package}) async* {
if (package == null || packageGraph.root.name == package) {
final String generatedRoot = fs.path.join(generatedDirectory.path, packageGraph.root.name);
await for (io.FileSystemEntity entity in glob.list(followLinks: true, root: packageGraph.root.path)) {
if (entity is io.File && _isNotHidden(entity) && !fs.path.isWithin(generatedRoot, entity.path)) {
yield _fileToAssetId(entity, packageGraph.root);
}
}
if (!fs.isDirectorySync(generatedRoot)) {
return;
}
await for (io.FileSystemEntity entity in glob.list(followLinks: true, root: generatedRoot)) {
if (entity is io.File && _isNotHidden(entity)) {
yield _fileToAssetId(entity, packageGraph.root, fs.path.relative(generatedRoot), true);
}
}
return;
}
yield* super.findAssets(glob, package: package);
}
bool _isNotHidden(io.FileSystemEntity entity) {
return !path.basename(entity.path).startsWith('._');
}
bool _missingSource(AssetId id) {
return !fs.file(path.joinAll(<String>[packageGraph.root.path, ...id.pathSegments])).existsSync();
}
File _generatedFile(AssetId id) {
return fs.file(
path.joinAll(<String>[generatedDirectory.path, packageGraph.root.name, ...id.pathSegments])
);
}
/// Creates an [AssetId] for [file], which is a part of [packageNode].
AssetId _fileToAssetId(io.File file, core.PackageNode packageNode, [String root, bool generated = false]) {
final String filePath = path.normalize(file.absolute.path);
String relativePath;
if (generated) {
relativePath = filePath.substring(root.length + 2);
} else {
relativePath = path.relative(filePath, from: packageNode.path);
}
return AssetId(packageNode.name, relativePath);
}
}