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 {
fs.path.split(environment.cacheDir.resolveSymbolicLinksSync()));
break;
case Environment.kFlutterRootDirectory:
// flutter root will not contain a symbolic link.
segments.addAll(
fs.path.split(environment.cacheDir.resolveSymbolicLinksSync()));
fs.path.split(environment.flutterRootDir.absolute.path));
break;
default:
throw InvalidPatternException(pattern);
......
......@@ -10,10 +10,6 @@ import '../../devfs.dart';
import '../build_system.dart';
/// 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 {
const AssetBehavior();
......@@ -24,6 +20,8 @@ class AssetBehavior extends SourceBehavior {
manifestPath: environment.projectDir.childFile('pubspec.yaml').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 Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
for (DevFSFileContent devFsContent in files) {
......@@ -40,56 +38,65 @@ class AssetBehavior extends SourceBehavior {
packagesPath: environment.projectDir.childFile('.packages').path,
);
final List<File> results = <File>[];
for (MapEntry<String, DevFSContent> entry in assetBundle.entries.entries) {
final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', entry.key));
for (String key in assetBundle.entries.keys) {
final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', key));
results.add(file);
}
return results;
}
}
/// Copies the asset files from the [copyAssets] rule into place.
Future<void> copyAssetsInvocation(Map<String, ChangeType> updates, 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();
}
}));
}
/// Copy the assets defined in the flutter manifest into a build directory.
class CopyAssets extends Target {
const CopyAssets();
/// Copy the assets used in the application into a build directory.
const Target copyAssets = Target(
name: 'copy_assets',
inputs: <Source>[
@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()),
],
outputs: <Source>[
];
@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.
],
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 @@
// Use of this source code is governed by a BSD-style license that can be
// 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 'assets.dart';
import '../exceptions.dart';
import 'dart.dart';
/// Create an iOS debug application.
const Target debugIosApplication = Target(
name: 'debug_ios_application',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
dependencies: <Target>[
copyAssets,
kernelSnapshot,
]
);
/// Create an iOS profile application.
const Target profileIosApplication = Target(
name: 'profile_ios_application',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
dependencies: <Target>[
copyAssets,
aotAssemblyProfile,
]
);
/// Create an iOS debug application.
const Target releaseIosApplication = Target(
name: 'release_ios_application',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
dependencies: <Target>[
copyAssets,
aotAssemblyRelease,
]
);
/// Supports compiling a dart kernel file to an assembly file.
///
/// If more than one iOS arch is provided, then this rule will
/// produce a univeral binary.
abstract class AotAssemblyBase extends Target {
const AotAssemblyBase();
@override
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) {
throw MissingDefineException(kBuildMode, 'aot_assembly');
}
if (environment.defines[kTargetPlatform] == null) {
throw MissingDefineException(kTargetPlatform, 'aot_assembly');
}
final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
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');
}
// If we're building for a single architecture (common), then skip the lipo.
if (iosArchs.length == 1) {
final int snapshotExitCode = await snapshotter.build(
platform: targetPlatform,
buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path,
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';
import '../../globals.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.
const Target unpackLinux = Target(
name: 'unpack_linux',
inputs: <Source>[
class UnpackLinux extends Target {
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),
],
outputs: <Source>[
];
@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'),
......@@ -40,7 +29,29 @@ const Target unpackLinux = Target(
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,
);
];
@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';
import '../../base/process_manager.dart';
import '../../globals.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'.
///
......@@ -19,37 +19,20 @@ import 'dart.dart';
/// Removes any previous version of the framework that already exists in the
/// target directory.
// TODO(jonahwilliams): remove shell out.
Future<void> copyFramework(Map<String, ChangeType> updates,
Environment environment) async {
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);
}
class UnpackMacOS extends Target {
const UnpackMacOS();
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}',
);
}
}
@override
String get name => 'unpack_macos';
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/FlutterMacOS.framework';
/// Copies the macOS desktop framework to the copy directory.
const Target unpackMacos = Target(
name: 'unpack_macos',
inputs: <Source>[
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
Source.artifact(Artifact.flutterMacOSFramework),
],
outputs: <Source>[
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('$_kOutputPrefix/FlutterMacOS'),
// Headers
Source.pattern('$_kOutputPrefix/Headers/FLEOpenGLContextHandling.h'),
......@@ -68,33 +51,30 @@ const Target unpackMacos = Target(
Source.pattern('$_kOutputPrefix/Resources/icudtl.dat'),
Source.pattern('$_kOutputPrefix/Resources/info.plist'),
// Ignore Versions folder for now
],
dependencies: <Target>[],
buildAction: copyFramework,
);
];
@override
List<Target> get dependencies => <Target>[];
/// Build a macOS application.
const Target macosApplication = Target(
name: 'debug_macos_application',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
dependencies: <Target>[
unpackMacos,
kernelSnapshot,
copyAssets,
]
);
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
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);
}
/// Build a macOS release application.
const Target macoReleaseApplication = Target(
name: 'release_macos_application',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
dependencies: <Target>[
unpackMacos,
aotElfRelease,
copyAssets,
]
);
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,33 +7,21 @@ import '../../base/file_system.dart';
import '../../globals.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.
const Target unpackWindows = Target(
name: 'unpack_windows',
inputs: <Source>[
class UnpackWindows extends Target {
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),
],
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.exp'),
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.lib'),
......@@ -44,7 +32,30 @@ const Target unpackWindows = Target(
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,
);
];
@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 @@
import '../base/common.dart';
import '../base/context.dart';
import '../build_info.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 '../project.dart';
import '../runner/flutter_command.dart';
......@@ -14,30 +18,23 @@ import '../runner/flutter_command.dart';
/// The [BuildSystem] instance.
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
/// system.
class AssembleCommand extends FlutterCommand {
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(
'define',
abbr: 'd',
......@@ -57,36 +54,19 @@ abstract class AssembleBase extends FlutterCommand {
);
}
/// Returns the provided target platform.
///
/// 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);
}
@override
String get description => 'Assemble and build flutter resources.';
/// Returns the provided build mode.
///
/// 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);
}
@override
String get name => 'assemble';
/// The name of the target we are describing or building.
String get targetName {
/// The target we are building.
Target get target {
if (argResults.rest.isEmpty) {
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.
......@@ -115,19 +95,10 @@ abstract class AssembleBase extends FlutterCommand {
}
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
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'],
));
if (!result.success) {
......@@ -142,67 +113,3 @@ class AssembleRun extends AssembleBase {
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>(
Artifacts: () => CachedArtifacts(),
AssetBundleFactory: () => AssetBundleFactory.defaultInstance,
BotDetector: () => const BotDetector(),
BuildSystem: () => BuildSystem(),
BuildSystem: () => const BuildSystem(),
Cache: () => Cache(),
ChromeLauncher: () => const ChromeLauncher(),
CocoaPods: () => CocoaPods(),
......
......@@ -12,19 +12,9 @@ void main() {
test('Exceptions', () {
final MissingInputException missingInputException = MissingInputException(
<File>[fs.file('foo'), fs.file('bar')], 'example');
final CycleException cycleException = CycleException(const <Target>{
Target(
name: 'foo',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
),
Target(
name: 'bar',
buildAction: null,
inputs: <Source>[],
outputs: <Source>[],
)
final CycleException cycleException = CycleException(<Target>{
TestTarget()..name = 'foo',
TestTarget()..name = 'bar',
});
final InvalidPatternException invalidPatternException = InvalidPatternException(
'ABC'
......@@ -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';
import '../../../src/testbed.dart';
void main() {
group('copy_assets', () {
Testbed testbed;
BuildSystem buildSystem;
Environment environment;
const BuildSystem buildSystem = BuildSystem();
Environment environment;
Testbed testbed;
setUp(() {
testbed = Testbed(setup: () {
environment = Environment(
projectDir: fs.currentDirectory,
);
buildSystem = BuildSystem(<String, Target>{
copyAssets.name: copyAssets,
});
fs.file(fs.path.join('assets', 'foo', 'bar.png'))
..createSync(recursive: true);
fs.file('.packages')
..createSync();
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync('''
setUp(() {
testbed = Testbed(setup: () {
environment = Environment(
projectDir: fs.currentDirectory,
);
fs.file(fs.path.join('packages', 'flutter_tools', 'lib', 'src',
'build_system', 'targets', 'assets.dart'))
..createSync(recursive: true);
fs.file(fs.path.join('assets', 'foo', 'bar.png'))
..createSync(recursive: true);
fs.file('.packages')
..createSync();
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync('''
name: example
flutter:
assets:
- assets/foo/bar.png
''');
});
});
});
test('Copies files to correct asset directory', () => testbed.run(() async {
await buildSystem.build('copy_assets', environment, const BuildSystemConfig());
test('Copies files to correct asset directory', () => testbed.run(() async {
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', 'FontManifest.json')).existsSync(), true);
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'LICENSE')).existsSync(), true);
// 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', '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', 'LICENSE')).existsSync(), true);
// 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);
}));
test('Does not leave stale files in build directory', () => testbed.run(() async {
await buildSystem.build('copy_assets', environment, const BuildSystemConfig());
test('Does not leave stale files in build directory', () => testbed.run(() async {
await buildSystem.build(const CopyAssets(), environment);
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), true);
// Modify manifest to remove asset.
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync('''
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), true);
// Modify manifest to remove asset.
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync('''
name: example
flutter:
''');
await buildSystem.build('copy_assets', environment, const BuildSystemConfig());
await buildSystem.build(const CopyAssets(), environment);
// 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);
}));
});
// 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);
}));
}
......@@ -13,72 +13,72 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart';
void main() {
group('unpack_linux', () {
Testbed testbed;
BuildSystem buildSystem;
Environment environment;
MockPlatform mockPlatform;
Testbed testbed;
const BuildSystem buildSystem = BuildSystem();
Environment environment;
MockPlatform mockPlatform;
setUpAll(() {
Cache.disableLocking();
});
setUpAll(() {
Cache.disableLocking();
Cache.flutterRoot = '';
});
setUp(() {
mockPlatform = MockPlatform();
when(mockPlatform.isWindows).thenReturn(false);
when(mockPlatform.isMacOS).thenReturn(false);
when(mockPlatform.isLinux).thenReturn(true);
testbed = Testbed(setup: () {
Cache.flutterRoot = '';
environment = Environment(
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/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_plugin_registrar.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/cpp_client_wrapper/foo').createSync(recursive: true);
fs.directory('linux').createSync();
}, overrides: <Type, Generator>{
Platform: () => mockPlatform,
});
setUp(() {
mockPlatform = MockPlatform();
when(mockPlatform.isWindows).thenReturn(false);
when(mockPlatform.isMacOS).thenReturn(false);
when(mockPlatform.isLinux).thenReturn(true);
testbed = Testbed(setup: () {
Cache.flutterRoot = '';
environment = Environment(
projectDir: fs.currentDirectory,
);
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_messenger.h').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_plugin_registrar.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/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();
}, overrides: <Type, Generator>{
Platform: () => mockPlatform,
});
});
test('Copies files to correct cache directory', () => testbed.run(() async {
final BuildResult result = await buildSystem.build('unpack_linux', environment, const BuildSystemConfig());
test('Copies files to correct cache directory', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const UnpackLinux(), environment);
expect(result.hasException, false);
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_messenger.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/icudtl.dat').existsSync(), true);
expect(fs.file('linux/flutter/cpp_client_wrapper/foo').existsSync(), true);
}));
expect(result.hasException, false);
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_messenger.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/icudtl.dat').existsSync(), true);
expect(fs.file('linux/flutter/cpp_client_wrapper/foo').existsSync(), true);
}));
test('Does not re-copy files unecessarily', () => testbed.run(() async {
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig());
final DateTime modified = fs.file('linux/flutter/libflutter_linux.so').statSync().modified;
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig());
test('Does not re-copy files unecessarily', () => testbed.run(() async {
await buildSystem.build(const UnpackLinux(), 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('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 {
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig());
fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux.so').writeAsStringSync('asd'); // modify cache.
test('Detects changes in input cache files', () => testbed.run(() async {
await buildSystem.build(const UnpackLinux(), environment);
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 {}
......@@ -14,84 +14,87 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart';
void main() {
group('unpack_windows', () {
Testbed testbed;
BuildSystem buildSystem;
Environment environment;
Platform platform;
Testbed testbed;
const BuildSystem buildSystem = BuildSystem();
Environment environment;
Platform platform;
setUpAll(() {
Cache.disableLocking();
});
setUpAll(() {
Cache.disableLocking();
Cache.flutterRoot = '';
});
setUp(() {
Cache.flutterRoot = '';
platform = MockPlatform();
when(platform.isWindows).thenReturn(true);
when(platform.isMacOS).thenReturn(false);
when(platform.isLinux).thenReturn(false);
when(platform.pathSeparator).thenReturn(r'\');
testbed = Testbed(setup: () {
environment = Environment(
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_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.exp').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_windows.dll.pdb').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_messenger.h').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_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\cpp_client_wrapper\foo').createSync(recursive: true);
fs.directory('windows').createSync();
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
Platform: () => platform,
});
setUp(() {
platform = MockPlatform();
when(platform.isWindows).thenReturn(true);
when(platform.isMacOS).thenReturn(false);
when(platform.isLinux).thenReturn(false);
when(platform.pathSeparator).thenReturn(r'\');
testbed = Testbed(setup: () {
environment = Environment(
projectDir: fs.currentDirectory,
);
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_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_windows.dll.lib').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\lutter_export.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_plugin_registrar.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\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();
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
Platform: () => platform,
});
});
test('Copies files to correct cache directory', () => testbed.run(() async {
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig());
test('Copies files to correct cache directory', () => testbed.run(() async {
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_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.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.pdb').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_plugin_registrar.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\cpp_client_wrapper\foo').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_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.lib').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_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_glfw.h').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);
}));
test('Does not re-copy files unecessarily', () => testbed.run(() async {
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig());
final DateTime modified = fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified;
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig());
test('Does not re-copy files unecessarily', () => testbed.run(() async {
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);
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 {
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig());
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.
test('Detects changes in input cache files', () => testbed.run(() async {
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;
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));
}), skip: true); // TODO(jonahwilliams): track down flakiness.
});
expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, isNot(modified));
}));
}
class MockPlatform extends Mock implements Platform {}
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
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/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
......@@ -15,68 +14,31 @@ import '../../src/common.dart';
import '../../src/testbed.dart';
void main() {
group('Assemble', () {
Testbed testbed;
MockBuildSystem mockBuildSystem;
Testbed testbed;
MockBuildSystem mockBuildSystem;
setUpAll(() {
Cache.disableLocking();
});
setUpAll(() {
Cache.disableLocking();
});
setUp(() {
mockBuildSystem = MockBuildSystem();
testbed = Testbed(overrides: <Type, Generator>{
BuildSystem: () => mockBuildSystem,
});
setUp(() {
mockBuildSystem = MockBuildSystem();
testbed = Testbed(overrides: <Type, Generator>{
BuildSystem: () => mockBuildSystem,
});
});
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 {
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;
test('Can run a build', () => testbed.run(() async {
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
.thenAnswer((Invocation invocation) async {
return BuildResult(true, const <String, ExceptionMeasurement>{}, const <String, PerformanceMeasurement>{});
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', 'unpack_macos']);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText.trim(), 'build succeeded');
}));
});
expect(bufferLogger.statusText.trim(), 'build succeeded');
}));
}
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