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,16 +38,41 @@ class AssetBehavior extends SourceBehavior { ...@@ -40,16 +38,41 @@ 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 {
const CopyAssets();
@override
String get name => 'copy_assets';
@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.behavior(AssetBehavior()),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'),
Source.behavior(AssetBehavior()), // <- everything in this subdirectory.
];
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
final Directory output = environment final Directory output = environment
.buildDir .buildDir
.childDirectory('flutter_assets'); .childDirectory('flutter_assets');
...@@ -75,21 +98,5 @@ Future<void> copyAssetsInvocation(Map<String, ChangeType> updates, Environment e ...@@ -75,21 +98,5 @@ Future<void> copyAssetsInvocation(Map<String, ChangeType> updates, Environment e
resource.release(); resource.release();
} }
})); }));
}
} }
/// Copy the assets used in the application into a build directory.
const Target copyAssets = Target(
name: 'copy_assets',
inputs: <Source>[
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.behavior(AssetBehavior()),
],
outputs: <Source>[
Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'),
Source.behavior(AssetBehavior()), // <- everything in this subdirectory.
],
dependencies: <Target>[],
buildAction: copyAssetsInvocation,
);
...@@ -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,16 +7,45 @@ import '../../base/file_system.dart'; ...@@ -7,16 +7,45 @@ 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. /// Copies the Linux desktop embedding files to the copy directory.
Future<void> copyLinuxAssets(Map<String, ChangeType> updates, class UnpackLinux extends Target {
Environment environment) async { const UnpackLinux();
@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),
];
@override
List<Source> get outputs => const <Source>[
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_messenger.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_plugin_registrar.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/icudtl.dat'),
Source.pattern('{PROJECT_DIR}/linux/flutter/cpp_client_wrapper/*'),
];
@override
List<Target> get dependencies => <Target>[];
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath); final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
for (String input in updates.keys) { for (File input in inputFiles) {
if (fs.path.basename(input.path) == 'linux.dart') {
continue;
}
final String outputPath = fs.path.join( final String outputPath = fs.path.join(
environment.projectDir.path, environment.projectDir.path,
'linux', 'linux',
'flutter', 'flutter',
fs.path.relative(input, from: basePath), fs.path.relative(input.path, from: basePath),
); );
final File destinationFile = fs.file(outputPath); final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) { if (!destinationFile.parent.existsSync()) {
...@@ -24,23 +53,5 @@ Future<void> copyLinuxAssets(Map<String, ChangeType> updates, ...@@ -24,23 +53,5 @@ Future<void> copyLinuxAssets(Map<String, ChangeType> updates,
} }
fs.file(input).copySync(destinationFile.path); fs.file(input).copySync(destinationFile.path);
} }
}
} }
/// Copies the Linux desktop embedding files to the copy directory.
const Target unpackLinux = Target(
name: 'unpack_linux',
inputs: <Source>[
Source.artifact(Artifact.linuxDesktopPath),
],
outputs: <Source>[
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_messenger.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_plugin_registrar.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/icudtl.dat'),
Source.pattern('{PROJECT_DIR}/linux/flutter/cpp_client_wrapper/*'),
],
dependencies: <Target>[],
buildAction: copyLinuxAssets,
);
...@@ -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
.runSync(<String>['cp', '-R', basePath, targetDirectory.path]);
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
String get name => 'unpack_macos';
/// Copies the macOS desktop framework to the copy directory. @override
const Target unpackMacos = Target( List<Source> get inputs => const <Source>[
name: 'unpack_macos', Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
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,
);
/// Build a macOS application. @override
const Target macosApplication = Target( List<Target> get dependencies => <Target>[];
name: 'debug_macos_application',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
dependencies: <Target>[
unpackMacos,
kernelSnapshot,
copyAssets,
]
);
/// Build a macOS release application. @override
const Target macoReleaseApplication = Target( Future<void> build(List<File> inputFiles, Environment environment) async {
name: 'release_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');
aotElfRelease, if (targetDirectory.existsSync()) {
copyAssets, targetDirectory.deleteSync(recursive: true);
] }
);
final ProcessResult result = await processManager
.run(<String>['cp', '-R', basePath, targetDirectory.path]);
if (result.exitCode != 0) {
throw Exception(
'Failed to copy framework (exit ${result.exitCode}:\n'
'${result.stdout}\n---\n${result.stderr}',
);
}
}
}
...@@ -7,17 +7,49 @@ import '../../base/file_system.dart'; ...@@ -7,17 +7,49 @@ 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. /// Copies the Windows desktop embedding files to the copy directory.
Future<void> copyWindowsAssets(Map<String, ChangeType> updates, class UnpackWindows extends Target {
Environment environment) async { const UnpackWindows();
@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),
];
@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.exp'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.lib'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.pdb'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_export.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_messenger.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_plugin_registrar.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/icudtl.dat'),
Source.pattern('{PROJECT_DIR}/windows/flutter/cpp_client_wrapper/*'),
];
@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. // This path needs to match the prefix in the rule below.
final String basePath = artifacts.getArtifactPath(Artifact.windowsDesktopPath); final String basePath = artifacts.getArtifactPath(Artifact.windowsDesktopPath);
for (String input in updates.keys) { for (File input in inputFiles) {
if (fs.path.basename(input.path) == 'windows.dart') {
continue;
}
final String outputPath = fs.path.join( final String outputPath = fs.path.join(
environment.projectDir.path, environment.projectDir.path,
'windows', 'windows',
'flutter', 'flutter',
fs.path.relative(input, from: basePath), fs.path.relative(input.path, from: basePath),
); );
final File destinationFile = fs.file(outputPath); final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) { if (!destinationFile.parent.existsSync()) {
...@@ -25,26 +57,5 @@ Future<void> copyWindowsAssets(Map<String, ChangeType> updates, ...@@ -25,26 +57,5 @@ Future<void> copyWindowsAssets(Map<String, ChangeType> updates,
} }
fs.file(input).copySync(destinationFile.path); fs.file(input).copySync(destinationFile.path);
} }
}
} }
/// Copies the Windows desktop embedding files to the copy directory.
const Target unpackWindows = Target(
name: 'unpack_windows',
inputs: <Source>[
Source.artifact(Artifact.windowsDesktopPath),
],
outputs: <Source>[
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.lib'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.pdb'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_export.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_messenger.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_plugin_registrar.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/windows/flutter/icudtl.dat'),
Source.pattern('{PROJECT_DIR}/windows/flutter/cpp_client_wrapper/*'),
],
dependencies: <Target>[],
buildAction: copyWindowsAssets,
);
...@@ -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,19 +10,18 @@ import '../../../src/common.dart'; ...@@ -10,19 +10,18 @@ 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;
BuildSystem buildSystem;
Environment environment; Environment environment;
Testbed testbed;
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')
...@@ -40,7 +39,7 @@ flutter: ...@@ -40,7 +39,7 @@ flutter:
}); });
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);
...@@ -50,7 +49,7 @@ flutter: ...@@ -50,7 +49,7 @@ flutter:
})); }));
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.
...@@ -61,10 +60,9 @@ name: example ...@@ -61,10 +60,9 @@ 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);
})); }));
});
} }
...@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/build_info.dart'; ...@@ -10,6 +10,7 @@ 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/build_system.dart';
import 'package:flutter_tools/src/build_system/exceptions.dart'; import 'package:flutter_tools/src/build_system/exceptions.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart'; import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/ios.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/macos/xcode.dart'; import 'package:flutter_tools/src/macos/xcode.dart';
...@@ -22,9 +23,8 @@ import '../../../src/mocks.dart'; ...@@ -22,9 +23,8 @@ import '../../../src/mocks.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('dart rules', () { const BuildSystem buildSystem = BuildSystem();
Testbed testbed; Testbed testbed;
BuildSystem buildSystem;
Environment androidEnvironment; Environment androidEnvironment;
Environment iosEnvironment; Environment iosEnvironment;
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
...@@ -35,8 +35,8 @@ void main() { ...@@ -35,8 +35,8 @@ void main() {
}); });
setUp(() { setUp(() {
mockProcessManager = MockProcessManager();
mockXcode = MockXcode(); mockXcode = MockXcode();
mockProcessManager = MockProcessManager();
testbed = Testbed(setup: () { testbed = Testbed(setup: () {
androidEnvironment = Environment( androidEnvironment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
...@@ -52,7 +52,6 @@ void main() { ...@@ -52,7 +52,6 @@ void main() {
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
} }
); );
buildSystem = BuildSystem();
HostPlatform hostPlatform; HostPlatform hostPlatform;
if (platform.isWindows) { if (platform.isWindows) {
hostPlatform = HostPlatform.windows_x64; hostPlatform = HostPlatform.windows_x64;
...@@ -90,6 +89,8 @@ flutter_tools:lib/'''); ...@@ -90,6 +89,8 @@ flutter_tools:lib/''');
fs.path.join('lib', 'foo.dart'), fs.path.join('lib', 'foo.dart'),
fs.path.join('lib', 'bar.dart'), fs.path.join('lib', 'bar.dart'),
fs.path.join('lib', 'fizz'), fs.path.join('lib', 'fizz'),
fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'dart.dart'),
fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'ios.dart'),
]; ];
for (String path in paths) { for (String path in paths) {
fs.file(path).createSync(recursive: true); fs.file(path).createSync(recursive: true);
...@@ -101,62 +102,79 @@ flutter_tools:lib/'''); ...@@ -101,62 +102,79 @@ flutter_tools:lib/''');
}); });
test('kernel_snapshot Produces correct output directory', () => testbed.run(() async { test('kernel_snapshot Produces correct output directory', () => testbed.run(() async {
await buildSystem.build('kernel_snapshot', androidEnvironment, const BuildSystemConfig()); await buildSystem.build(const KernelSnapshot(), androidEnvironment);
expect(fs.file(fs.path.join(androidEnvironment.buildDir.path,'main.app.dill')).existsSync(), true); expect(fs.file(fs.path.join(androidEnvironment.buildDir.path,'app.dill')).existsSync(), true);
})); }));
test('kernel_snapshot throws error if missing build mode', () => testbed.run(() async { test('kernel_snapshot throws error if missing build mode', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('kernel_snapshot', final BuildResult result = await buildSystem.build(const KernelSnapshot(),
androidEnvironment..defines.remove(kBuildMode), const BuildSystemConfig()); androidEnvironment..defines.remove(kBuildMode));
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>()); expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
})); }));
test('aot_elf_profile Produces correct output directory', () => testbed.run(() async { test('aot_elf_profile Produces correct output directory', () => testbed.run(() async {
await buildSystem.build('aot_elf_profile', androidEnvironment, const BuildSystemConfig()); await buildSystem.build(const AotElfProfile(), androidEnvironment);
expect(fs.file(fs.path.join(androidEnvironment.buildDir.path, 'main.app.dill')).existsSync(), true); expect(fs.file(fs.path.join(androidEnvironment.buildDir.path, 'app.dill')).existsSync(), true);
expect(fs.file(fs.path.join(androidEnvironment.buildDir.path, 'app.so')).existsSync(), true); expect(fs.file(fs.path.join(androidEnvironment.buildDir.path, 'app.so')).existsSync(), true);
})); }));
test('aot_elf_profile throws error if missing build mode', () => testbed.run(() async { test('aot_elf_profile throws error if missing build mode', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('aot_elf_profile', final BuildResult result = await buildSystem.build(const AotElfProfile(),
androidEnvironment..defines.remove(kBuildMode), const BuildSystemConfig()); androidEnvironment..defines.remove(kBuildMode));
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>()); expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
})); }));
test('aot_elf_profile throws error if missing target platform', () => testbed.run(() async { test('aot_elf_profile throws error if missing target platform', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('aot_elf_profile', final BuildResult result = await buildSystem.build(const AotElfProfile(),
androidEnvironment..defines.remove(kTargetPlatform), const BuildSystemConfig()); androidEnvironment..defines.remove(kTargetPlatform));
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>()); expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
})); }));
test('aot_assembly_profile throws error if missing build mode', () => testbed.run(() async { test('aot_assembly_profile throws error if missing build mode', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('aot_assembly_profile', final BuildResult result = await buildSystem.build(const AotAssemblyProfile(),
iosEnvironment..defines.remove(kBuildMode), const BuildSystemConfig()); iosEnvironment..defines.remove(kBuildMode));
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>()); expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
})); }));
test('aot_assembly_profile throws error if missing target platform', () => testbed.run(() async { test('aot_assembly_profile throws error if missing target platform', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('aot_assembly_profile', final BuildResult result = await buildSystem.build(const AotAssemblyProfile(),
iosEnvironment..defines.remove(kTargetPlatform), const BuildSystemConfig()); iosEnvironment..defines.remove(kTargetPlatform));
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>()); expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
})); }));
test('aot_assembly_profile throws error if built for non-iOS platform', () => testbed.run(() async { test('aot_assembly_profile throws error if built for non-iOS platform', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('aot_assembly_profile', final BuildResult result = await buildSystem
androidEnvironment, const BuildSystemConfig()); .build(const AotAssemblyProfile(), androidEnvironment);
expect(result.exceptions.values.single.exception, isInstanceOf<Exception>()); expect(result.exceptions.values.single.exception, isInstanceOf<Exception>());
})); }));
test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] ='armv7,arm64';
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
fs.file(fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
return FakeProcessResult(
stdout: '',
stderr: '',
);
});
final BuildResult result = await buildSystem
.build(const AotAssemblyProfile(), iosEnvironment);
expect(result.success, true);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
}));
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async { test('aot_assembly_profile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] = 'arm64'; iosEnvironment.defines[kIosArchs] = 'arm64';
iosEnvironment.defines[kBitcodeFlag] = 'true'; iosEnvironment.defines[kBitcodeFlag] = 'true';
...@@ -176,8 +194,7 @@ flutter_tools:lib/'''); ...@@ -176,8 +194,7 @@ flutter_tools:lib/''');
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
final BuildResult result = await buildSystem.build('aot_assembly_profile', final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
iosEnvironment, const BuildSystemConfig());
expect(result.success, true); expect(result.success, true);
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1); verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
...@@ -207,8 +224,7 @@ flutter_tools:lib/'''); ...@@ -207,8 +224,7 @@ flutter_tools:lib/''');
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
final BuildResult result = await buildSystem.build('aot_assembly_profile', final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
iosEnvironment, const BuildSystemConfig());
expect(result.success, true); expect(result.success, true);
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2); verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2);
...@@ -229,14 +245,12 @@ flutter_tools:lib/'''); ...@@ -229,14 +245,12 @@ flutter_tools:lib/''');
stderr: '', stderr: '',
); );
}); });
final BuildResult result = await buildSystem.build('aot_assembly_profile', final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
iosEnvironment, const BuildSystemConfig());
expect(result.success, true); expect(result.success, true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
})); }));
});
} }
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
......
...@@ -13,14 +13,14 @@ import '../../../src/common.dart'; ...@@ -13,14 +13,14 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('unpack_linux', () {
Testbed testbed; Testbed testbed;
BuildSystem buildSystem; const BuildSystem buildSystem = BuildSystem();
Environment environment; Environment environment;
MockPlatform mockPlatform; MockPlatform mockPlatform;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
Cache.flutterRoot = '';
}); });
setUp(() { setUp(() {
...@@ -33,9 +33,6 @@ void main() { ...@@ -33,9 +33,6 @@ void main() {
environment = Environment( environment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
); );
buildSystem = BuildSystem(<String, Target>{
unpackLinux.name: unpackLinux,
});
fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').createSync(recursive: true); fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').createSync(recursive: true);
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_export.h').createSync(); 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/flutter_messenger.h').createSync();
...@@ -43,6 +40,7 @@ void main() { ...@@ -43,6 +40,7 @@ void main() {
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_glfw.h').createSync(); fs.file('bin/cache/artifacts/engine/linux-x64/flutter_glfw.h').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/icudtl.dat').createSync(); fs.file('bin/cache/artifacts/engine/linux-x64/icudtl.dat').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/cpp_client_wrapper/foo').createSync(recursive: true); fs.file('bin/cache/artifacts/engine/linux-x64/cpp_client_wrapper/foo').createSync(recursive: true);
fs.file('packages/flutter_tools/lib/src/build_system/targets/linux.dart').createSync(recursive: true);
fs.directory('linux').createSync(); fs.directory('linux').createSync();
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => mockPlatform, Platform: () => mockPlatform,
...@@ -50,7 +48,7 @@ void main() { ...@@ -50,7 +48,7 @@ void main() {
}); });
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);
...@@ -63,22 +61,24 @@ void main() { ...@@ -63,22 +61,24 @@ void main() {
})); }));
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 {}
...@@ -8,6 +8,7 @@ import 'package:flutter_tools/src/base/platform.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process_manager.dart'; import 'package:flutter_tools/src/base/process_manager.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/build_system/targets/macos.dart'; import 'package:flutter_tools/src/build_system/targets/macos.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -15,12 +16,16 @@ import '../../../src/common.dart'; ...@@ -15,12 +16,16 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('unpack_macos', () {
Testbed testbed; Testbed testbed;
BuildSystem buildSystem; const BuildSystem buildSystem = BuildSystem();
Environment environment; Environment environment;
MockPlatform mockPlatform; MockPlatform mockPlatform;
setUpAll(() {
Cache.disableLocking();
Cache.flutterRoot = '';
});
setUp(() { setUp(() {
mockPlatform = MockPlatform(); mockPlatform = MockPlatform();
when(mockPlatform.isWindows).thenReturn(false); when(mockPlatform.isWindows).thenReturn(false);
...@@ -30,9 +35,6 @@ void main() { ...@@ -30,9 +35,6 @@ void main() {
environment = Environment( environment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
); );
buildSystem = BuildSystem(<String, Target>{
unpackMacos.name: unpackMacos,
});
final List<File> inputs = <File>[ final List<File> inputs = <File>[
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/FlutterMacOS'), fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/FlutterMacOS'),
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEOpenGLContextHandling.h'), fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEOpenGLContextHandling.h'),
...@@ -48,11 +50,12 @@ void main() { ...@@ -48,11 +50,12 @@ void main() {
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Modules/module.modulemap'), fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Modules/module.modulemap'),
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Resources/icudtl.dat'), fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Resources/icudtl.dat'),
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Resources/info.plist'), fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Resources/info.plist'),
fs.file('packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
]; ];
for (File input in inputs) { for (File input in inputs) {
input.createSync(recursive: true); input.createSync(recursive: true);
} }
when(processManager.runSync(any)).thenAnswer((Invocation invocation) { when(processManager.run(any)).thenAnswer((Invocation invocation) async {
final List<String> arguments = invocation.positionalArguments.first; final List<String> arguments = invocation.positionalArguments.first;
final Directory source = fs.directory(arguments[arguments.length - 2]); final Directory source = fs.directory(arguments[arguments.length - 2]);
final Directory target = fs.directory(arguments.last) final Directory target = fs.directory(arguments.last)
...@@ -76,7 +79,7 @@ void main() { ...@@ -76,7 +79,7 @@ void main() {
}); });
test('Copies files to correct cache directory', () => testbed.run(() async { test('Copies files to correct cache directory', () => testbed.run(() async {
await buildSystem.build('unpack_macos', environment, const BuildSystemConfig()); await buildSystem.build(const UnpackMacOS(), environment);
expect(fs.directory('macos/Flutter/FlutterMacOS.framework').existsSync(), true); expect(fs.directory('macos/Flutter/FlutterMacOS.framework').existsSync(), true);
expect(fs.file('macos/Flutter/FlutterMacOS.framework/FlutterMacOS').existsSync(), true); expect(fs.file('macos/Flutter/FlutterMacOS.framework/FlutterMacOS').existsSync(), true);
...@@ -94,7 +97,6 @@ void main() { ...@@ -94,7 +97,6 @@ void main() {
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Resources/icudtl.dat').existsSync(), true); expect(fs.file('macos/Flutter/FlutterMacOS.framework/Resources/icudtl.dat').existsSync(), true);
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Resources/info.plist').existsSync(), true); expect(fs.file('macos/Flutter/FlutterMacOS.framework/Resources/info.plist').existsSync(), true);
})); }));
});
} }
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
......
...@@ -14,18 +14,17 @@ import '../../../src/common.dart'; ...@@ -14,18 +14,17 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
void main() { void main() {
group('unpack_windows', () {
Testbed testbed; Testbed testbed;
BuildSystem buildSystem; const 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);
...@@ -35,9 +34,6 @@ void main() { ...@@ -35,9 +34,6 @@ void main() {
environment = Environment( environment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
); );
buildSystem = BuildSystem(<String, Target>{
unpackWindows.name: unpackWindows,
});
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_export.h').createSync(recursive: true);
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_messenger.h').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\flutter_windows.dll').createSync();
...@@ -50,6 +46,7 @@ void main() { ...@@ -50,6 +46,7 @@ void main() {
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_glfw.h').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\icudtl.dat').createSync(); fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\icudtl.dat').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\cpp_client_wrapper\foo').createSync(recursive: true);
fs.file(r'C:\packages\flutter_tools\lib\src\build_system\targets\windows.dart').createSync(recursive: true);
fs.directory('windows').createSync(); fs.directory('windows').createSync();
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
...@@ -58,7 +55,7 @@ void main() { ...@@ -58,7 +55,7 @@ void main() {
}); });
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);
...@@ -75,23 +72,29 @@ void main() { ...@@ -75,23 +72,29 @@ void main() {
})); }));
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);
// Set a date in the far distant past to deal with the limited resolution
// 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; 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. 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,7 +14,6 @@ import '../../src/common.dart'; ...@@ -15,7 +14,6 @@ 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;
...@@ -30,53 +28,17 @@ void main() { ...@@ -30,53 +28,17 @@ void main() {
}); });
}); });
test('Can list the output directory relative to project root', () => testbed.run(() async {
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', '--flutter-root=.', 'build-dir', '-dBuildMode=debug']);
final BufferLogger bufferLogger = logger;
final Environment environment = Environment(
defines: <String, String>{
'BuildMode': 'debug'
}, projectDir: fs.currentDirectory,
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 { test('Can run a build', () => testbed.run(() async {
when(mockBuildSystem.build('foobar', any, any)).thenAnswer((Invocation invocation) async { when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
.thenAnswer((Invocation invocation) async {
return BuildResult(true, const <String, ExceptionMeasurement>{}, const <String, PerformanceMeasurement>{}); return BuildResult(true, const <String, ExceptionMeasurement>{}, const <String, PerformanceMeasurement>{});
}); });
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand()); final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', 'run', 'foobar']); await commandRunner.run(<String>['assemble', 'unpack_macos']);
final BufferLogger bufferLogger = logger; 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