Unverified Commit 188093c9 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Rearrange flutter assemble implementation (#36240)

parent c469b1fe
...@@ -65,8 +65,9 @@ class SourceVisitor { ...@@ -65,8 +65,9 @@ class SourceVisitor {
fs.path.split(environment.cacheDir.resolveSymbolicLinksSync())); fs.path.split(environment.cacheDir.resolveSymbolicLinksSync()));
break; break;
case Environment.kFlutterRootDirectory: case Environment.kFlutterRootDirectory:
// flutter root will not contain a symbolic link.
segments.addAll( segments.addAll(
fs.path.split(environment.cacheDir.resolveSymbolicLinksSync())); fs.path.split(environment.flutterRootDir.absolute.path));
break; break;
default: default:
throw InvalidPatternException(pattern); throw InvalidPatternException(pattern);
......
...@@ -10,10 +10,6 @@ import '../../devfs.dart'; ...@@ -10,10 +10,6 @@ import '../../devfs.dart';
import '../build_system.dart'; import '../build_system.dart';
/// The copying logic for flutter assets. /// The copying logic for flutter assets.
// TODO(jonahwilliams): combine the asset bundle logic with this rule so that
// we can compute the key for deleted assets. This is required to remove assets
// from build directories that are no longer part of the manifest and to unify
// the update/diff logic.
class AssetBehavior extends SourceBehavior { class AssetBehavior extends SourceBehavior {
const AssetBehavior(); const AssetBehavior();
...@@ -24,6 +20,8 @@ class AssetBehavior extends SourceBehavior { ...@@ -24,6 +20,8 @@ class AssetBehavior extends SourceBehavior {
manifestPath: environment.projectDir.childFile('pubspec.yaml').path, manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path, packagesPath: environment.projectDir.childFile('.packages').path,
); );
// Filter the file type to remove the files that are generated by this
// command as inputs.
final List<File> results = <File>[]; final List<File> results = <File>[];
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>(); final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
for (DevFSFileContent devFsContent in files) { for (DevFSFileContent devFsContent in files) {
...@@ -40,56 +38,65 @@ class AssetBehavior extends SourceBehavior { ...@@ -40,56 +38,65 @@ class AssetBehavior extends SourceBehavior {
packagesPath: environment.projectDir.childFile('.packages').path, packagesPath: environment.projectDir.childFile('.packages').path,
); );
final List<File> results = <File>[]; final List<File> results = <File>[];
for (MapEntry<String, DevFSContent> entry in assetBundle.entries.entries) { for (String key in assetBundle.entries.keys) {
final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', entry.key)); final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', key));
results.add(file); results.add(file);
} }
return results; return results;
} }
} }
/// Copies the asset files from the [copyAssets] rule into place. /// Copy the assets defined in the flutter manifest into a build directory.
Future<void> copyAssetsInvocation(Map<String, ChangeType> updates, Environment environment) async { class CopyAssets extends Target {
final Directory output = environment const CopyAssets();
.buildDir
.childDirectory('flutter_assets');
if (output.existsSync()) {
output.deleteSync(recursive: true);
}
output.createSync(recursive: true);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
await assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
// Limit number of open files to avoid running out of file descriptors.
final Pool pool = Pool(64);
await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final PoolResource resource = await pool.request();
try {
final File file = fs.file(fs.path.join(output.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
} finally {
resource.release();
}
}));
}
/// Copy the assets used in the application into a build directory. @override
const Target copyAssets = Target( String get name => 'copy_assets';
name: 'copy_assets',
inputs: <Source>[ @override
List<Target> get dependencies => const <Target>[];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/assets.dart'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'), Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.behavior(AssetBehavior()), Source.behavior(AssetBehavior()),
], ];
outputs: <Source>[
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'), Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'), Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'), Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'),
Source.behavior(AssetBehavior()), // <- everything in this subdirectory. Source.behavior(AssetBehavior()), // <- everything in this subdirectory.
], ];
dependencies: <Target>[],
buildAction: copyAssetsInvocation, @override
); Future<void> build(List<File> inputFiles, Environment environment) async {
final Directory output = environment
.buildDir
.childDirectory('flutter_assets');
if (output.existsSync()) {
output.deleteSync(recursive: true);
}
output.createSync(recursive: true);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
await assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
// Limit number of open files to avoid running out of file descriptors.
final Pool pool = Pool(64);
await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final PoolResource resource = await pool.request();
try {
final File file = fs.file(fs.path.join(output.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
} finally {
resource.release();
}
}));
}
}
...@@ -2,42 +2,149 @@ ...@@ -2,42 +2,149 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import '../../artifacts.dart';
import '../../base/build.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../base/process_manager.dart';
import '../../build_info.dart';
import '../build_system.dart'; import '../build_system.dart';
import 'assets.dart'; import '../exceptions.dart';
import 'dart.dart'; import 'dart.dart';
/// Create an iOS debug application. /// Supports compiling a dart kernel file to an assembly file.
const Target debugIosApplication = Target( ///
name: 'debug_ios_application', /// If more than one iOS arch is provided, then this rule will
buildAction: null, /// produce a univeral binary.
inputs: <Source>[], abstract class AotAssemblyBase extends Target {
outputs: <Source>[], const AotAssemblyBase();
dependencies: <Target>[
copyAssets, @override
kernelSnapshot, Future<void> build(List<File> inputFiles, Environment environment) async {
] final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false);
); final String outputPath = environment.buildDir.path;
if (environment.defines[kBuildMode] == null) {
/// Create an iOS profile application. throw MissingDefineException(kBuildMode, 'aot_assembly');
const Target profileIosApplication = Target( }
name: 'profile_ios_application', if (environment.defines[kTargetPlatform] == null) {
buildAction: null, throw MissingDefineException(kTargetPlatform, 'aot_assembly');
inputs: <Source>[], }
outputs: <Source>[], final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
dependencies: <Target>[ final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
copyAssets, final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
aotAssemblyProfile, final List<IOSArch> iosArchs = environment.defines[kIosArchs]?.split(',')?.map(getIOSArchForName)?.toList()
] ?? <IOSArch>[IOSArch.arm64];
); if (targetPlatform != TargetPlatform.ios) {
throw Exception('aot_assembly is only supported for iOS applications');
/// Create an iOS debug application. }
const Target releaseIosApplication = Target(
name: 'release_ios_application', // If we're building for a single architecture (common), then skip the lipo.
buildAction: null, if (iosArchs.length == 1) {
inputs: <Source>[], final int snapshotExitCode = await snapshotter.build(
outputs: <Source>[], platform: targetPlatform,
dependencies: <Target>[ buildMode: buildMode,
copyAssets, mainPath: environment.buildDir.childFile('app.dill').path,
aotAssemblyRelease, packagesPath: environment.projectDir.childFile('.packages').path,
] outputPath: outputPath,
); iosArch: iosArchs.single,
bitcode: bitcode,
);
if (snapshotExitCode != 0) {
throw Exception('AOT snapshotter exited with code $snapshotExitCode');
}
} else {
// If we're building multiple iOS archs the binaries need to be lipo'd
// together.
final List<Future<int>> pending = <Future<int>>[];
for (IOSArch iosArch in iosArchs) {
pending.add(snapshotter.build(
platform: targetPlatform,
buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path,
packagesPath: environment.projectDir.childFile('.packages').path,
outputPath: fs.path.join(outputPath, getNameForIOSArch(iosArch)),
iosArch: iosArch,
bitcode: bitcode,
));
}
final List<int> results = await Future.wait(pending);
if (results.any((int result) => result != 0)) {
throw Exception('AOT snapshotter exited with code ${results.join()}');
}
final ProcessResult result = await processManager.run(<String>[
'lipo',
...iosArchs.map((IOSArch iosArch) =>
fs.path.join(outputPath, getNameForIOSArch(iosArch), 'App.framework', 'App')),
'-create',
'-output',
fs.path.join(outputPath, 'App.framework', 'App'),
]);
if (result.exitCode != 0) {
throw Exception('lipo exited with code ${result.exitCode}');
}
}
}
}
/// Generate an assembly target from a dart kernel file in release mode.
class AotAssemblyRelease extends AotAssemblyBase {
const AotAssemblyRelease();
@override
String get name => 'aot_assembly_release';
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart'),
Source.pattern('{BUILD_DIR}/app.dill'),
Source.pattern('{PROJECT_DIR}/.packages'),
Source.artifact(Artifact.engineDartBinary),
Source.artifact(Artifact.skyEnginePath),
Source.artifact(Artifact.genSnapshot,
platform: TargetPlatform.ios,
mode: BuildMode.release,
),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/App.framework/App'),
];
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
];
}
/// Generate an assembly target from a dart kernel file in profile mode.
class AotAssemblyProfile extends AotAssemblyBase {
const AotAssemblyProfile();
@override
String get name => 'aot_assembly_profile';
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart'),
Source.pattern('{BUILD_DIR}/app.dill'),
Source.pattern('{PROJECT_DIR}/.packages'),
Source.artifact(Artifact.engineDartBinary),
Source.artifact(Artifact.skyEnginePath),
Source.artifact(Artifact.genSnapshot,
platform: TargetPlatform.ios,
mode: BuildMode.profile,
),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/App.framework/App'),
];
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
];
}
...@@ -7,32 +7,21 @@ import '../../base/file_system.dart'; ...@@ -7,32 +7,21 @@ import '../../base/file_system.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../build_system.dart'; import '../build_system.dart';
// Copies all of the input files to the correct copy dir.
Future<void> copyLinuxAssets(Map<String, ChangeType> updates,
Environment environment) async {
final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
for (String input in updates.keys) {
final String outputPath = fs.path.join(
environment.projectDir.path,
'linux',
'flutter',
fs.path.relative(input, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
}
fs.file(input).copySync(destinationFile.path);
}
}
/// Copies the Linux desktop embedding files to the copy directory. /// Copies the Linux desktop embedding files to the copy directory.
const Target unpackLinux = Target( class UnpackLinux extends Target {
name: 'unpack_linux', const UnpackLinux();
inputs: <Source>[
@override
String get name => 'unpack_linux';
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'),
Source.artifact(Artifact.linuxDesktopPath), Source.artifact(Artifact.linuxDesktopPath),
], ];
outputs: <Source>[
@override
List<Source> get outputs => const <Source>[
Source.pattern('{PROJECT_DIR}/linux/flutter/libflutter_linux.so'), Source.pattern('{PROJECT_DIR}/linux/flutter/libflutter_linux.so'),
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_export.h'), Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_export.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_messenger.h'), Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_messenger.h'),
...@@ -40,7 +29,29 @@ const Target unpackLinux = Target( ...@@ -40,7 +29,29 @@ const Target unpackLinux = Target(
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_glfw.h'), Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/icudtl.dat'), Source.pattern('{PROJECT_DIR}/linux/flutter/icudtl.dat'),
Source.pattern('{PROJECT_DIR}/linux/flutter/cpp_client_wrapper/*'), Source.pattern('{PROJECT_DIR}/linux/flutter/cpp_client_wrapper/*'),
], ];
dependencies: <Target>[],
buildAction: copyLinuxAssets, @override
); List<Target> get dependencies => <Target>[];
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
for (File input in inputFiles) {
if (fs.path.basename(input.path) == 'linux.dart') {
continue;
}
final String outputPath = fs.path.join(
environment.projectDir.path,
'linux',
'flutter',
fs.path.relative(input.path, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
}
fs.file(input).copySync(destinationFile.path);
}
}
}
...@@ -8,8 +8,8 @@ import '../../base/io.dart'; ...@@ -8,8 +8,8 @@ import '../../base/io.dart';
import '../../base/process_manager.dart'; import '../../base/process_manager.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../build_system.dart'; import '../build_system.dart';
import 'assets.dart';
import 'dart.dart'; const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/FlutterMacOS.framework';
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'. /// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
/// ///
...@@ -19,37 +19,20 @@ import 'dart.dart'; ...@@ -19,37 +19,20 @@ import 'dart.dart';
/// Removes any previous version of the framework that already exists in the /// Removes any previous version of the framework that already exists in the
/// target directory. /// target directory.
// TODO(jonahwilliams): remove shell out. // TODO(jonahwilliams): remove shell out.
Future<void> copyFramework(Map<String, ChangeType> updates, class UnpackMacOS extends Target {
Environment environment) async { const UnpackMacOS();
final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework);
final Directory targetDirectory = environment
.projectDir
.childDirectory('macos')
.childDirectory('Flutter')
.childDirectory('FlutterMacOS.framework');
if (targetDirectory.existsSync()) {
targetDirectory.deleteSync(recursive: true);
}
final ProcessResult result = processManager @override
.runSync(<String>['cp', '-R', basePath, targetDirectory.path]); String get name => 'unpack_macos';
if (result.exitCode != 0) {
throw Exception(
'Failed to copy framework (exit ${result.exitCode}:\n'
'${result.stdout}\n---\n${result.stderr}',
);
}
}
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/FlutterMacOS.framework'; @override
List<Source> get inputs => const <Source>[
/// Copies the macOS desktop framework to the copy directory. Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
const Target unpackMacos = Target(
name: 'unpack_macos',
inputs: <Source>[
Source.artifact(Artifact.flutterMacOSFramework), Source.artifact(Artifact.flutterMacOSFramework),
], ];
outputs: <Source>[
@override
List<Source> get outputs => const <Source>[
Source.pattern('$_kOutputPrefix/FlutterMacOS'), Source.pattern('$_kOutputPrefix/FlutterMacOS'),
// Headers // Headers
Source.pattern('$_kOutputPrefix/Headers/FLEOpenGLContextHandling.h'), Source.pattern('$_kOutputPrefix/Headers/FLEOpenGLContextHandling.h'),
...@@ -68,33 +51,30 @@ const Target unpackMacos = Target( ...@@ -68,33 +51,30 @@ const Target unpackMacos = Target(
Source.pattern('$_kOutputPrefix/Resources/icudtl.dat'), Source.pattern('$_kOutputPrefix/Resources/icudtl.dat'),
Source.pattern('$_kOutputPrefix/Resources/info.plist'), Source.pattern('$_kOutputPrefix/Resources/info.plist'),
// Ignore Versions folder for now // Ignore Versions folder for now
], ];
dependencies: <Target>[],
buildAction: copyFramework, @override
); List<Target> get dependencies => <Target>[];
/// Build a macOS application. @override
const Target macosApplication = Target( Future<void> build(List<File> inputFiles, Environment environment) async {
name: 'debug_macos_application', final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework);
buildAction: null, final Directory targetDirectory = environment
inputs: <Source>[], .projectDir
outputs: <Source>[], .childDirectory('macos')
dependencies: <Target>[ .childDirectory('Flutter')
unpackMacos, .childDirectory('FlutterMacOS.framework');
kernelSnapshot, if (targetDirectory.existsSync()) {
copyAssets, targetDirectory.deleteSync(recursive: true);
] }
);
/// Build a macOS release application. final ProcessResult result = await processManager
const Target macoReleaseApplication = Target( .run(<String>['cp', '-R', basePath, targetDirectory.path]);
name: 'release_macos_application', if (result.exitCode != 0) {
buildAction: null, throw Exception(
inputs: <Source>[], 'Failed to copy framework (exit ${result.exitCode}:\n'
outputs: <Source>[], '${result.stdout}\n---\n${result.stderr}',
dependencies: <Target>[ );
unpackMacos, }
aotElfRelease, }
copyAssets, }
]
);
...@@ -7,33 +7,21 @@ import '../../base/file_system.dart'; ...@@ -7,33 +7,21 @@ import '../../base/file_system.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../build_system.dart'; import '../build_system.dart';
/// Copies all of the input files to the correct copy dir.
Future<void> copyWindowsAssets(Map<String, ChangeType> updates,
Environment environment) async {
// This path needs to match the prefix in the rule below.
final String basePath = artifacts.getArtifactPath(Artifact.windowsDesktopPath);
for (String input in updates.keys) {
final String outputPath = fs.path.join(
environment.projectDir.path,
'windows',
'flutter',
fs.path.relative(input, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
}
fs.file(input).copySync(destinationFile.path);
}
}
/// Copies the Windows desktop embedding files to the copy directory. /// Copies the Windows desktop embedding files to the copy directory.
const Target unpackWindows = Target( class UnpackWindows extends Target {
name: 'unpack_windows', const UnpackWindows();
inputs: <Source>[
@override
String get name => 'unpack_windows';
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/windows.dart'),
Source.artifact(Artifact.windowsDesktopPath), Source.artifact(Artifact.windowsDesktopPath),
], ];
outputs: <Source>[
@override
List<Source> get outputs => const <Source>[
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll'), Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.exp'), Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.exp'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.lib'), Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.lib'),
...@@ -44,7 +32,30 @@ const Target unpackWindows = Target( ...@@ -44,7 +32,30 @@ const Target unpackWindows = Target(
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_glfw.h'), Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/icudtl.dat'), Source.pattern('{PROJECT_DIR}/windows/flutter/icudtl.dat'),
Source.pattern('{PROJECT_DIR}/windows/flutter/cpp_client_wrapper/*'), Source.pattern('{PROJECT_DIR}/windows/flutter/cpp_client_wrapper/*'),
], ];
dependencies: <Target>[],
buildAction: copyWindowsAssets, @override
); List<Target> get dependencies => const <Target>[];
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
// This path needs to match the prefix in the rule below.
final String basePath = artifacts.getArtifactPath(Artifact.windowsDesktopPath);
for (File input in inputFiles) {
if (fs.path.basename(input.path) == 'windows.dart') {
continue;
}
final String outputPath = fs.path.join(
environment.projectDir.path,
'windows',
'flutter',
fs.path.relative(input.path, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
}
fs.file(input).copySync(destinationFile.path);
}
}
}
...@@ -4,9 +4,13 @@ ...@@ -4,9 +4,13 @@
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../build_info.dart';
import '../build_system/build_system.dart'; import '../build_system/build_system.dart';
import '../convert.dart'; import '../build_system/targets/assets.dart';
import '../build_system/targets/dart.dart';
import '../build_system/targets/ios.dart';
import '../build_system/targets/linux.dart';
import '../build_system/targets/macos.dart';
import '../build_system/targets/windows.dart';
import '../globals.dart'; import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
...@@ -14,30 +18,23 @@ import '../runner/flutter_command.dart'; ...@@ -14,30 +18,23 @@ import '../runner/flutter_command.dart';
/// The [BuildSystem] instance. /// The [BuildSystem] instance.
BuildSystem get buildSystem => context.get<BuildSystem>(); BuildSystem get buildSystem => context.get<BuildSystem>();
/// All currently implemented targets.
const List<Target> _kDefaultTargets = <Target>[
UnpackMacOS(),
UnpackLinux(),
UnpackWindows(),
CopyAssets(),
KernelSnapshot(),
AotElfProfile(),
AotElfRelease(),
AotAssemblyProfile(),
AotAssemblyRelease(),
];
/// Assemble provides a low level API to interact with the flutter tool build /// Assemble provides a low level API to interact with the flutter tool build
/// system. /// system.
class AssembleCommand extends FlutterCommand { class AssembleCommand extends FlutterCommand {
AssembleCommand() { AssembleCommand() {
addSubcommand(AssembleRun());
addSubcommand(AssembleDescribe());
addSubcommand(AssembleListInputs());
addSubcommand(AssembleBuildDirectory());
}
@override
String get description => 'Assemble and build flutter resources.';
@override
String get name => 'assemble';
@override
Future<FlutterCommandResult> runCommand() {
return null;
}
}
abstract class AssembleBase extends FlutterCommand {
AssembleBase() {
argParser.addMultiOption( argParser.addMultiOption(
'define', 'define',
abbr: 'd', abbr: 'd',
...@@ -57,36 +54,19 @@ abstract class AssembleBase extends FlutterCommand { ...@@ -57,36 +54,19 @@ abstract class AssembleBase extends FlutterCommand {
); );
} }
/// Returns the provided target platform. @override
/// String get description => 'Assemble and build flutter resources.';
/// Throws a [ToolExit] if none is provided. This intentionally has no
/// default.
TargetPlatform get targetPlatform {
final String value = argResults['target-platform'] ?? 'darwin-x64';
if (value == null) {
throwToolExit('--target-platform is required for flutter assemble.');
}
return getTargetPlatformForName(value);
}
/// Returns the provided build mode. @override
/// String get name => 'assemble';
/// Throws a [ToolExit] if none is provided. This intentionally has no
/// default.
BuildMode get buildMode {
final String value = argResults['build-mode'] ?? 'debug';
if (value == null) {
throwToolExit('--build-mode is required for flutter assemble.');
}
return getBuildModeForName(value);
}
/// The name of the target we are describing or building. /// The target we are building.
String get targetName { Target get target {
if (argResults.rest.isEmpty) { if (argResults.rest.isEmpty) {
throwToolExit('missing target name for flutter assemble.'); throwToolExit('missing target name for flutter assemble.');
} }
return argResults.rest.first; final String name = argResults.rest.first;
return _kDefaultTargets.firstWhere((Target target) => target.name == name);
} }
/// The environmental configuration for a build invocation. /// The environmental configuration for a build invocation.
...@@ -115,19 +95,10 @@ abstract class AssembleBase extends FlutterCommand { ...@@ -115,19 +95,10 @@ abstract class AssembleBase extends FlutterCommand {
} }
return results; return results;
} }
}
/// Execute a build starting from a target action.
class AssembleRun extends AssembleBase {
@override
String get description => 'Execute the stages for a specified target.';
@override
String get name => 'run';
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final BuildResult result = await buildSystem.build(targetName, environment, BuildSystemConfig( final BuildResult result = await buildSystem.build(target, environment, buildSystemConfig: BuildSystemConfig(
resourcePoolSize: argResults['resource-pool-size'], resourcePoolSize: argResults['resource-pool-size'],
)); ));
if (!result.success) { if (!result.success) {
...@@ -142,67 +113,3 @@ class AssembleRun extends AssembleBase { ...@@ -142,67 +113,3 @@ class AssembleRun extends AssembleBase {
return null; return null;
} }
} }
/// Fully describe a target and its dependencies.
class AssembleDescribe extends AssembleBase {
@override
String get description => 'List the stages for a specified target.';
@override
String get name => 'describe';
@override
Future<FlutterCommandResult> runCommand() {
try {
printStatus(
json.encode(buildSystem.describe(targetName, environment))
);
} on Exception catch (err, stackTrace) {
printTrace(stackTrace.toString());
throwToolExit(err.toString());
}
return null;
}
}
/// List input files for a target.
class AssembleListInputs extends AssembleBase {
@override
String get description => 'List the inputs for a particular target.';
@override
String get name => 'inputs';
@override
Future<FlutterCommandResult> runCommand() {
try {
final List<Map<String, Object>> results = buildSystem.describe(targetName, environment);
for (Map<String, Object> result in results) {
if (result['name'] == targetName) {
final List<String> inputs = result['inputs'];
inputs.forEach(printStatus);
}
}
} on Exception catch (err, stackTrace) {
printTrace(stackTrace.toString());
throwToolExit(err.toString());
}
return null;
}
}
/// Return the build directory for a configuiration.
class AssembleBuildDirectory extends AssembleBase {
@override
String get description => 'List the inputs for a particular target.';
@override
String get name => 'build-dir';
@override
Future<FlutterCommandResult> runCommand() {
printStatus(environment.buildDir.path);
return null;
}
}
...@@ -69,7 +69,7 @@ Future<T> runInContext<T>( ...@@ -69,7 +69,7 @@ Future<T> runInContext<T>(
Artifacts: () => CachedArtifacts(), Artifacts: () => CachedArtifacts(),
AssetBundleFactory: () => AssetBundleFactory.defaultInstance, AssetBundleFactory: () => AssetBundleFactory.defaultInstance,
BotDetector: () => const BotDetector(), BotDetector: () => const BotDetector(),
BuildSystem: () => BuildSystem(), BuildSystem: () => const BuildSystem(),
Cache: () => Cache(), Cache: () => Cache(),
ChromeLauncher: () => const ChromeLauncher(), ChromeLauncher: () => const ChromeLauncher(),
CocoaPods: () => CocoaPods(), CocoaPods: () => CocoaPods(),
......
...@@ -12,19 +12,9 @@ void main() { ...@@ -12,19 +12,9 @@ void main() {
test('Exceptions', () { test('Exceptions', () {
final MissingInputException missingInputException = MissingInputException( final MissingInputException missingInputException = MissingInputException(
<File>[fs.file('foo'), fs.file('bar')], 'example'); <File>[fs.file('foo'), fs.file('bar')], 'example');
final CycleException cycleException = CycleException(const <Target>{ final CycleException cycleException = CycleException(<Target>{
Target( TestTarget()..name = 'foo',
name: 'foo', TestTarget()..name = 'bar',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
),
Target(
name: 'bar',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
)
}); });
final InvalidPatternException invalidPatternException = InvalidPatternException( final InvalidPatternException invalidPatternException = InvalidPatternException(
'ABC' 'ABC'
...@@ -70,3 +60,24 @@ void main() { ...@@ -70,3 +60,24 @@ void main() {
); );
}); });
} }
class TestTarget extends Target {
TestTarget([this._build]);
final Future<void> Function(List<File> inputFiles, Environment environment) _build;
@override
Future<void> build(List<File> inputFiles, Environment environment) => _build(inputFiles, environment);
@override
List<Target> dependencies = <Target>[];
@override
List<Source> inputs = <Source>[];
@override
String name = 'test';
@override
List<Source> outputs = <Source>[];
}
// 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.
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/file_hash_store.dart';
import 'package:flutter_tools/src/build_system/filecache.pb.dart' as pb;
import '../../src/common.dart';
import '../../src/testbed.dart';
void main() {
Testbed testbed;
Environment environment;
setUp(() {
testbed = Testbed(setup: () {
fs.directory('build').createSync();
environment = Environment(
projectDir: fs.currentDirectory,
);
});
});
test('Initializes file cache', () => testbed.run(() {
final FileHashStore fileCache = FileHashStore(environment);
fileCache.initialize();
fileCache.persist();
expect(fs.file(fs.path.join('build', '.filecache')).existsSync(), true);
final List<int> buffer = fs.file(fs.path.join('build', '.filecache')).readAsBytesSync();
final pb.FileStorage fileStorage = pb.FileStorage.fromBuffer(buffer);
expect(fileStorage.files, isEmpty);
expect(fileStorage.version, 1);
}));
test('saves and restores to file cache', () => testbed.run(() {
final File file = fs.file('foo.dart')
..createSync()
..writeAsStringSync('hello');
final FileHashStore fileCache = FileHashStore(environment);
fileCache.initialize();
fileCache.hashFiles(<File>[file]);
fileCache.persist();
final String currentHash = fileCache.currentHashes[file.resolveSymbolicLinksSync()];
final List<int> buffer = fs.file(fs.path.join('build', '.filecache')).readAsBytesSync();
pb.FileStorage fileStorage = pb.FileStorage.fromBuffer(buffer);
expect(fileStorage.files.single.hash, currentHash);
expect(fileStorage.files.single.path, file.resolveSymbolicLinksSync());
final FileHashStore newFileCache = FileHashStore(environment);
newFileCache.initialize();
expect(newFileCache.currentHashes, isEmpty);
expect(newFileCache.previousHashes[fs.path.absolute('foo.dart')], currentHash);
newFileCache.persist();
// Still persisted correctly.
fileStorage = pb.FileStorage.fromBuffer(buffer);
expect(fileStorage.files.single.hash, currentHash);
expect(fileStorage.files.single.path, file.resolveSymbolicLinksSync());
}));
}
// 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.
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/exceptions.dart';
import 'package:flutter_tools/src/build_system/source.dart';
import 'package:flutter_tools/src/cache.dart';
import '../../src/common.dart';
import '../../src/testbed.dart';
void main() {
Testbed testbed;
SourceVisitor visitor;
Environment environment;
setUp(() {
testbed = Testbed(setup: () {
fs.directory('cache').createSync();
environment = Environment(
projectDir: fs.currentDirectory,
buildDir: fs.directory('build'),
);
visitor = SourceVisitor(environment);
environment.buildDir.createSync(recursive: true);
});
});
test('configures implicit vs explict correctly', () => testbed.run(() {
expect(const Source.pattern('{PROJECT_DIR}/foo').implicit, false);
expect(const Source.pattern('{PROJECT_DIR}/*foo').implicit, true);
expect(Source.function((Environment environment) => <File>[]).implicit, true);
expect(Source.behavior(TestBehavior()).implicit, true);
}));
test('can substitute {PROJECT_DIR}/foo', () => testbed.run(() {
fs.file('foo').createSync();
const Source fooSource = Source.pattern('{PROJECT_DIR}/foo');
fooSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute('foo'));
}));
test('can substitute {BUILD_DIR}/bar', () => testbed.run(() {
final String path = fs.path.join(environment.buildDir.path, 'bar');
fs.file(path).createSync();
const Source barSource = Source.pattern('{BUILD_DIR}/bar');
barSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute(path));
}));
test('can substitute {FLUTTER_ROOT}/foo', () => testbed.run(() {
final String path = fs.path.join(environment.flutterRootDir.path, 'foo');
fs.file(path).createSync();
const Source barSource = Source.pattern('{FLUTTER_ROOT}/foo');
barSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute(path));
}));
test('can substitute Artifact', () => testbed.run(() {
final String path = fs.path.join(
Cache.instance.getArtifactDirectory('engine').path,
'windows-x64',
'foo',
);
fs.file(path).createSync(recursive: true);
const Source fizzSource = Source.artifact(Artifact.windowsDesktopPath, platform: TargetPlatform.windows_x64);
fizzSource.accept(visitor);
expect(visitor.sources.single.resolveSymbolicLinksSync(), fs.path.absolute(path));
}));
test('can substitute {PROJECT_DIR}/*.fizz', () => testbed.run(() {
const Source fizzSource = Source.pattern('{PROJECT_DIR}/*.fizz');
fizzSource.accept(visitor);
expect(visitor.sources, isEmpty);
fs.file('foo.fizz').createSync();
fs.file('foofizz').createSync();
fizzSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute('foo.fizz'));
}));
test('can substitute {PROJECT_DIR}/fizz.*', () => testbed.run(() {
const Source fizzSource = Source.pattern('{PROJECT_DIR}/fizz.*');
fizzSource.accept(visitor);
expect(visitor.sources, isEmpty);
fs.file('fizz.foo').createSync();
fs.file('fizz').createSync();
fizzSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute('fizz.foo'));
}));
test('can substitute {PROJECT_DIR}/a*bc', () => testbed.run(() {
const Source fizzSource = Source.pattern('{PROJECT_DIR}/bc*bc');
fizzSource.accept(visitor);
expect(visitor.sources, isEmpty);
fs.file('bcbc').createSync();
fs.file('bc').createSync();
fizzSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute('bcbc'));
}));
test('crashes on bad substitute of two **', () => testbed.run(() {
const Source fizzSource = Source.pattern('{PROJECT_DIR}/*.*bar');
fs.file('abcd.bar').createSync();
expect(() => fizzSource.accept(visitor), throwsA(isInstanceOf<InvalidPatternException>()));
}));
test('can\'t substitute foo', () => testbed.run(() {
const Source invalidBase = Source.pattern('foo');
expect(() => invalidBase.accept(visitor), throwsA(isInstanceOf<InvalidPatternException>()));
}));
}
class TestBehavior extends SourceBehavior {
@override
List<File> inputs(Environment environment) {
return null;
}
@override
List<File> outputs(Environment environment) {
return null;
}
}
...@@ -10,61 +10,59 @@ import '../../../src/common.dart'; ...@@ -10,61 +10,59 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('copy_assets', () { const BuildSystem buildSystem = BuildSystem();
Testbed testbed; Environment environment;
BuildSystem buildSystem; Testbed testbed;
Environment environment;
setUp(() { setUp(() {
testbed = Testbed(setup: () { testbed = Testbed(setup: () {
environment = Environment( environment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
); );
buildSystem = BuildSystem(<String, Target>{ fs.file(fs.path.join('packages', 'flutter_tools', 'lib', 'src',
copyAssets.name: copyAssets, 'build_system', 'targets', 'assets.dart'))
}); ..createSync(recursive: true);
fs.file(fs.path.join('assets', 'foo', 'bar.png')) fs.file(fs.path.join('assets', 'foo', 'bar.png'))
..createSync(recursive: true); ..createSync(recursive: true);
fs.file('.packages') fs.file('.packages')
..createSync(); ..createSync();
fs.file('pubspec.yaml') fs.file('pubspec.yaml')
..createSync() ..createSync()
..writeAsStringSync(''' ..writeAsStringSync('''
name: example name: example
flutter: flutter:
assets: assets:
- assets/foo/bar.png - assets/foo/bar.png
'''); ''');
});
}); });
});
test('Copies files to correct asset directory', () => testbed.run(() async { test('Copies files to correct asset directory', () => testbed.run(() async {
await buildSystem.build('copy_assets', environment, const BuildSystemConfig()); await buildSystem.build(const CopyAssets(), environment);
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'AssetManifest.json')).existsSync(), true); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'AssetManifest.json')).existsSync(), true);
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'FontManifest.json')).existsSync(), true); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'FontManifest.json')).existsSync(), true);
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'LICENSE')).existsSync(), true); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'LICENSE')).existsSync(), true);
// See https://github.com/flutter/flutter/issues/35293 // See https://github.com/flutter/flutter/issues/35293
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), true); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), true);
})); }));
test('Does not leave stale files in build directory', () => testbed.run(() async { test('Does not leave stale files in build directory', () => testbed.run(() async {
await buildSystem.build('copy_assets', environment, const BuildSystemConfig()); await buildSystem.build(const CopyAssets(), environment);
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), true); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), true);
// Modify manifest to remove asset. // Modify manifest to remove asset.
fs.file('pubspec.yaml') fs.file('pubspec.yaml')
..createSync() ..createSync()
..writeAsStringSync(''' ..writeAsStringSync('''
name: example name: example
flutter: flutter:
'''); ''');
await buildSystem.build('copy_assets', environment, const BuildSystemConfig()); await buildSystem.build(const CopyAssets(), environment);
// See https://github.com/flutter/flutter/issues/35293 // See https://github.com/flutter/flutter/issues/35293
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), false); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), false);
})); }));
});
} }
...@@ -13,72 +13,72 @@ import '../../../src/common.dart'; ...@@ -13,72 +13,72 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('unpack_linux', () { Testbed testbed;
Testbed testbed; const BuildSystem buildSystem = BuildSystem();
BuildSystem buildSystem; Environment environment;
Environment environment; MockPlatform mockPlatform;
MockPlatform mockPlatform;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
}); Cache.flutterRoot = '';
});
setUp(() { setUp(() {
mockPlatform = MockPlatform(); mockPlatform = MockPlatform();
when(mockPlatform.isWindows).thenReturn(false); when(mockPlatform.isWindows).thenReturn(false);
when(mockPlatform.isMacOS).thenReturn(false); when(mockPlatform.isMacOS).thenReturn(false);
when(mockPlatform.isLinux).thenReturn(true); when(mockPlatform.isLinux).thenReturn(true);
testbed = Testbed(setup: () { testbed = Testbed(setup: () {
Cache.flutterRoot = ''; Cache.flutterRoot = '';
environment = Environment( environment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
); );
buildSystem = BuildSystem(<String, Target>{ fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').createSync(recursive: true);
unpackLinux.name: unpackLinux, fs.file('bin/cache/artifacts/engine/linux-x64/flutter_export.h').createSync();
}); fs.file('bin/cache/artifacts/engine/linux-x64/flutter_messenger.h').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').createSync(recursive: true); fs.file('bin/cache/artifacts/engine/linux-x64/flutter_plugin_registrar.h').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_export.h').createSync(); fs.file('bin/cache/artifacts/engine/linux-x64/flutter_glfw.h').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_messenger.h').createSync(); fs.file('bin/cache/artifacts/engine/linux-x64/icudtl.dat').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_plugin_registrar.h').createSync(); fs.file('bin/cache/artifacts/engine/linux-x64/cpp_client_wrapper/foo').createSync(recursive: true);
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_glfw.h').createSync(); fs.file('packages/flutter_tools/lib/src/build_system/targets/linux.dart').createSync(recursive: true);
fs.file('bin/cache/artifacts/engine/linux-x64/icudtl.dat').createSync(); fs.directory('linux').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/cpp_client_wrapper/foo').createSync(recursive: true); }, overrides: <Type, Generator>{
fs.directory('linux').createSync(); Platform: () => mockPlatform,
}, overrides: <Type, Generator>{
Platform: () => mockPlatform,
});
}); });
});
test('Copies files to correct cache directory', () => testbed.run(() async { test('Copies files to correct cache directory', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('unpack_linux', environment, const BuildSystemConfig()); final BuildResult result = await buildSystem.build(const UnpackLinux(), environment);
expect(result.hasException, false); expect(result.hasException, false);
expect(fs.file('linux/flutter/libflutter_linux.so').existsSync(), true); expect(fs.file('linux/flutter/libflutter_linux.so').existsSync(), true);
expect(fs.file('linux/flutter/flutter_export.h').existsSync(), true); expect(fs.file('linux/flutter/flutter_export.h').existsSync(), true);
expect(fs.file('linux/flutter/flutter_messenger.h').existsSync(), true); expect(fs.file('linux/flutter/flutter_messenger.h').existsSync(), true);
expect(fs.file('linux/flutter/flutter_plugin_registrar.h').existsSync(), true); expect(fs.file('linux/flutter/flutter_plugin_registrar.h').existsSync(), true);
expect(fs.file('linux/flutter/flutter_glfw.h').existsSync(), true); expect(fs.file('linux/flutter/flutter_glfw.h').existsSync(), true);
expect(fs.file('linux/flutter/icudtl.dat').existsSync(), true); expect(fs.file('linux/flutter/icudtl.dat').existsSync(), true);
expect(fs.file('linux/flutter/cpp_client_wrapper/foo').existsSync(), true); expect(fs.file('linux/flutter/cpp_client_wrapper/foo').existsSync(), true);
})); }));
test('Does not re-copy files unecessarily', () => testbed.run(() async { test('Does not re-copy files unecessarily', () => testbed.run(() async {
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackLinux(), environment);
final DateTime modified = fs.file('linux/flutter/libflutter_linux.so').statSync().modified; // Set a date in the far distant past to deal with the limited resolution
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig()); // of the windows filesystem.
final DateTime theDistantPast = DateTime(1991, 8, 23);
fs.file('linux/flutter/libflutter_linux.so').setLastModifiedSync(theDistantPast);
await buildSystem.build(const UnpackLinux(), environment);
expect(fs.file('linux/flutter/libflutter_linux.so').statSync().modified, equals(modified)); expect(fs.file('linux/flutter/libflutter_linux.so').statSync().modified, equals(theDistantPast));
})); }));
test('Detects changes in input cache files', () => testbed.run(() async { test('Detects changes in input cache files', () => testbed.run(() async {
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackLinux(), environment);
fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').writeAsStringSync('asd'); // modify cache. fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').writeAsStringSync('asd'); // modify cache.
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackLinux(), environment);
expect(fs.file('linux/flutter/libflutter_linux.so').readAsStringSync(), 'asd'); expect(fs.file('linux/flutter/libflutter_linux.so').readAsStringSync(), 'asd');
})); }));
});
} }
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
...@@ -14,84 +14,87 @@ import '../../../src/common.dart'; ...@@ -14,84 +14,87 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('unpack_windows', () { Testbed testbed;
Testbed testbed; const BuildSystem buildSystem = BuildSystem();
BuildSystem buildSystem; Environment environment;
Environment environment; Platform platform;
Platform platform;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
}); Cache.flutterRoot = '';
});
setUp(() { setUp(() {
Cache.flutterRoot = ''; platform = MockPlatform();
platform = MockPlatform(); when(platform.isWindows).thenReturn(true);
when(platform.isWindows).thenReturn(true); when(platform.isMacOS).thenReturn(false);
when(platform.isMacOS).thenReturn(false); when(platform.isLinux).thenReturn(false);
when(platform.isLinux).thenReturn(false); when(platform.pathSeparator).thenReturn(r'\');
when(platform.pathSeparator).thenReturn(r'\'); testbed = Testbed(setup: () {
testbed = Testbed(setup: () { environment = Environment(
environment = Environment( projectDir: fs.currentDirectory,
projectDir: fs.currentDirectory, );
); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').createSync(recursive: true);
buildSystem = BuildSystem(<String, Target>{ fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h').createSync();
unpackWindows.name: unpackWindows, fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll').createSync();
}); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.exp').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').createSync(recursive: true); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.lib').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.pdb').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\lutter_export.h').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.exp').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.lib').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_plugin_registrar.h').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.pdb').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_glfw.h').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\lutter_export.h').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\icudtl.dat').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\cpp_client_wrapper\foo').createSync(recursive: true);
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_plugin_registrar.h').createSync(); fs.file(r'C:\packages\flutter_tools\lib\src\build_system\targets\windows.dart').createSync(recursive: true);
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_glfw.h').createSync(); fs.directory('windows').createSync();
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\icudtl.dat').createSync(); }, overrides: <Type, Generator>{
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\cpp_client_wrapper\foo').createSync(recursive: true); FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
fs.directory('windows').createSync(); Platform: () => platform,
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
Platform: () => platform,
});
}); });
});
test('Copies files to correct cache directory', () => testbed.run(() async { test('Copies files to correct cache directory', () => testbed.run(() async {
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackWindows(), environment);
expect(fs.file(r'C:\windows\flutter\flutter_export.h').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_export.h').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_messenger.h').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_messenger.h').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_windows.dll').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_windows.dll').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_windows.dll.exp').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_windows.dll.exp').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_windows.dll.lib').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_windows.dll.lib').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_windows.dll.pdb').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_windows.dll.pdb').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_export.h').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_export.h').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_messenger.h').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_messenger.h').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_plugin_registrar.h').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_plugin_registrar.h').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\flutter_glfw.h').existsSync(), true); expect(fs.file(r'C:\windows\flutter\flutter_glfw.h').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\icudtl.dat').existsSync(), true); expect(fs.file(r'C:\windows\flutter\icudtl.dat').existsSync(), true);
expect(fs.file(r'C:\windows\flutter\cpp_client_wrapper\foo').existsSync(), true); expect(fs.file(r'C:\windows\flutter\cpp_client_wrapper\foo').existsSync(), true);
})); }));
test('Does not re-copy files unecessarily', () => testbed.run(() async { test('Does not re-copy files unecessarily', () => testbed.run(() async {
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackWindows(), environment);
final DateTime modified = fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified; // Set a date in the far distant past to deal with the limited resolution
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig()); // of the windows filesystem.
final DateTime theDistantPast = DateTime(1991, 8, 23);
fs.file(r'C:\windows\flutter\flutter_export.h').setLastModifiedSync(theDistantPast);
await buildSystem.build(const UnpackWindows(), environment);
expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, equals(modified)); expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, equals(theDistantPast));
})); }));
test('Detects changes in input cache files', () => testbed.run(() async { test('Detects changes in input cache files', () => testbed.run(() async {
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackWindows(), environment);
final DateTime modified = fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified; // Set a date in the far distant past to deal with the limited resolution
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').writeAsStringSync('asd'); // modify cache. // of the windows filesystem.
final DateTime theDistantPast = DateTime(1991, 8, 23);
fs.file(r'C:\windows\flutter\flutter_export.h').setLastModifiedSync(theDistantPast);
final DateTime modified = fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified;
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').writeAsStringSync('asd'); // modify cache.
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackWindows(), environment);
expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, isNot(modified)); expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, isNot(modified));
}), skip: true); // TODO(jonahwilliams): track down flakiness. }));
});
} }
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
...@@ -15,68 +14,31 @@ import '../../src/common.dart'; ...@@ -15,68 +14,31 @@ import '../../src/common.dart';
import '../../src/testbed.dart'; import '../../src/testbed.dart';
void main() { void main() {
group('Assemble', () { Testbed testbed;
Testbed testbed; MockBuildSystem mockBuildSystem;
MockBuildSystem mockBuildSystem;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
}); });
setUp(() { setUp(() {
mockBuildSystem = MockBuildSystem(); mockBuildSystem = MockBuildSystem();
testbed = Testbed(overrides: <Type, Generator>{ testbed = Testbed(overrides: <Type, Generator>{
BuildSystem: () => mockBuildSystem, BuildSystem: () => mockBuildSystem,
});
}); });
});
test('Can list the output directory relative to project root', () => testbed.run(() async { test('Can run a build', () => testbed.run(() async {
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand()); when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
await commandRunner.run(<String>['assemble', '--flutter-root=.', 'build-dir', '-dBuildMode=debug']); .thenAnswer((Invocation invocation) async {
final BufferLogger bufferLogger = logger; return BuildResult(true, const <String, ExceptionMeasurement>{}, const <String, PerformanceMeasurement>{});
final Environment environment = Environment( });
defines: <String, String>{ final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
'BuildMode': 'debug' await commandRunner.run(<String>['assemble', 'unpack_macos']);
}, projectDir: fs.currentDirectory, final BufferLogger bufferLogger = logger;
buildDir: fs.directory(fs.path.join('.dart_tool', 'flutter_build')).absolute,
);
expect(bufferLogger.statusText.trim(), environment.buildDir.path);
}));
test('Can describe a target', () => testbed.run(() async {
when(mockBuildSystem.describe('foobar', any)).thenReturn(<Map<String, Object>>[
<String, Object>{'fizz': 'bar'},
]);
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', '--flutter-root=.', 'describe', 'foobar']);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText.trim(), '[{"fizz":"bar"}]');
}));
test('Can describe a target\'s inputs', () => testbed.run(() async {
when(mockBuildSystem.describe('foobar', any)).thenReturn(<Map<String, Object>>[
<String, Object>{'name': 'foobar', 'inputs': <String>['bar', 'baz']},
]);
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', '--flutter-root=.', 'inputs', 'foobar']);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText.trim(), 'bar\nbaz');
}));
test('Can run a build', () => testbed.run(() async {
when(mockBuildSystem.build('foobar', any, any)).thenAnswer((Invocation invocation) async {
return BuildResult(true, const <String, ExceptionMeasurement>{}, const <String, PerformanceMeasurement>{});
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', 'run', 'foobar']);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText.trim(), 'build succeeded'); expect(bufferLogger.statusText.trim(), 'build succeeded');
})); }));
});
} }
class MockBuildSystem extends Mock implements BuildSystem {} class MockBuildSystem extends Mock implements BuildSystem {}
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