mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Rearrange flutter assemble implementation (#36240)
This commit is contained in:
parent
c469b1fea9
commit
188093c912
@ -18,30 +18,9 @@ import '../globals.dart';
|
|||||||
import 'exceptions.dart';
|
import 'exceptions.dart';
|
||||||
import 'file_hash_store.dart';
|
import 'file_hash_store.dart';
|
||||||
import 'source.dart';
|
import 'source.dart';
|
||||||
import 'targets/assets.dart';
|
|
||||||
import 'targets/dart.dart';
|
|
||||||
import 'targets/ios.dart';
|
|
||||||
import 'targets/linux.dart';
|
|
||||||
import 'targets/macos.dart';
|
|
||||||
import 'targets/windows.dart';
|
|
||||||
|
|
||||||
export 'source.dart';
|
export 'source.dart';
|
||||||
|
|
||||||
/// The function signature of a build target which can be invoked to perform
|
|
||||||
/// the underlying task.
|
|
||||||
typedef BuildAction = FutureOr<void> Function(
|
|
||||||
Map<String, ChangeType> inputs, Environment environment);
|
|
||||||
|
|
||||||
/// A description of the update to each input file.
|
|
||||||
enum ChangeType {
|
|
||||||
/// The file was added.
|
|
||||||
Added,
|
|
||||||
/// The file was deleted.
|
|
||||||
Removed,
|
|
||||||
/// The file was modified.
|
|
||||||
Modified,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration for the build system itself.
|
/// Configuration for the build system itself.
|
||||||
class BuildSystemConfig {
|
class BuildSystemConfig {
|
||||||
/// Create a new [BuildSystemConfig].
|
/// Create a new [BuildSystemConfig].
|
||||||
@ -102,7 +81,7 @@ class BuildSystemConfig {
|
|||||||
/// Example: aot_elf has a dependency on the dill and packages file
|
/// Example: aot_elf has a dependency on the dill and packages file
|
||||||
/// produced by the kernel_snapshot step.
|
/// produced by the kernel_snapshot step.
|
||||||
///
|
///
|
||||||
/// ### Targest should declare all outputs produced
|
/// ### Targets should declare all outputs produced
|
||||||
///
|
///
|
||||||
/// If a target produces an output it should be listed, even if it is not
|
/// If a target produces an output it should be listed, even if it is not
|
||||||
/// intended to be consumed by another target.
|
/// intended to be consumed by another target.
|
||||||
@ -116,43 +95,38 @@ class BuildSystemConfig {
|
|||||||
/// exercise the rule, ensuring that the existing input and output verification
|
/// exercise the rule, ensuring that the existing input and output verification
|
||||||
/// logic can run, as well as verifying it correctly handles provided defines
|
/// logic can run, as well as verifying it correctly handles provided defines
|
||||||
/// and meets any additional contracts present in the target.
|
/// and meets any additional contracts present in the target.
|
||||||
class Target {
|
abstract class Target {
|
||||||
const Target({
|
const Target();
|
||||||
@required this.name,
|
|
||||||
@required this.inputs,
|
|
||||||
@required this.outputs,
|
|
||||||
@required this.buildAction,
|
|
||||||
this.dependencies = const <Target>[],
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The user-readable name of the target.
|
/// The user-readable name of the target.
|
||||||
///
|
///
|
||||||
/// This information is surfaced in the assemble commands and used as an
|
/// This information is surfaced in the assemble commands and used as an
|
||||||
/// argument to build a particular target.
|
/// argument to build a particular target.
|
||||||
final String name;
|
String get name;
|
||||||
|
|
||||||
/// The dependencies of this target.
|
/// The dependencies of this target.
|
||||||
final List<Target> dependencies;
|
List<Target> get dependencies;
|
||||||
|
|
||||||
/// The input [Source]s which are diffed to determine if a target should run.
|
/// The input [Source]s which are diffed to determine if a target should run.
|
||||||
final List<Source> inputs;
|
List<Source> get inputs;
|
||||||
|
|
||||||
/// The output [Source]s which we attempt to verify are correctly produced.
|
/// The output [Source]s which we attempt to verify are correctly produced.
|
||||||
final List<Source> outputs;
|
List<Source> get outputs;
|
||||||
|
|
||||||
/// The action which performs this build step.
|
/// The action which performs this build step.
|
||||||
final BuildAction buildAction;
|
Future<void> build(List<File> inputFiles, Environment environment);
|
||||||
|
|
||||||
/// Collect hashes for all inputs to determine if any have changed.
|
/// Collect hashes for all inputs to determine if any have changed.
|
||||||
Future<Map<String, ChangeType>> computeChanges(
|
///
|
||||||
|
/// Returns whether this target can be skipped.
|
||||||
|
Future<bool> computeChanges(
|
||||||
List<File> inputs,
|
List<File> inputs,
|
||||||
Environment environment,
|
Environment environment,
|
||||||
FileHashStore fileHashStore,
|
FileHashStore fileHashStore,
|
||||||
) async {
|
) async {
|
||||||
final Map<String, ChangeType> updates = <String, ChangeType>{};
|
|
||||||
final File stamp = _findStampFile(environment);
|
final File stamp = _findStampFile(environment);
|
||||||
final Set<String> previousInputs = <String>{};
|
final Set<String> previousInputs = <String>{};
|
||||||
final List<String> previousOutputs = <String>[];
|
final List<String> previousOutputs = <String>[];
|
||||||
|
bool canSkip = true;
|
||||||
|
|
||||||
// If the stamp file doesn't exist, we haven't run this step before and
|
// If the stamp file doesn't exist, we haven't run this step before and
|
||||||
// all inputs were added.
|
// all inputs were added.
|
||||||
@ -161,6 +135,8 @@ class Target {
|
|||||||
// Something went wrong writing the stamp file.
|
// Something went wrong writing the stamp file.
|
||||||
if (content == null || content.isEmpty) {
|
if (content == null || content.isEmpty) {
|
||||||
stamp.deleteSync();
|
stamp.deleteSync();
|
||||||
|
// Malformed stamp file, not safe to skip.
|
||||||
|
canSkip = false;
|
||||||
} else {
|
} else {
|
||||||
final Map<String, Object> values = json.decode(content);
|
final Map<String, Object> values = json.decode(content);
|
||||||
final List<Object> inputs = values['inputs'];
|
final List<Object> inputs = values['inputs'];
|
||||||
@ -168,12 +144,13 @@ class Target {
|
|||||||
inputs.cast<String>().forEach(previousInputs.add);
|
inputs.cast<String>().forEach(previousInputs.add);
|
||||||
outputs.cast<String>().forEach(previousOutputs.add);
|
outputs.cast<String>().forEach(previousOutputs.add);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No stamp file, not safe to skip.
|
||||||
|
canSkip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each input type, first determine if we've already computed the hash
|
// For each input, first determine if we've already computed the hash
|
||||||
// for it. If not and it is a directory we skip hashing and instead use a
|
// for it. Then collect it to be sent off for hashing as a group.
|
||||||
// timestamp. If it is a file we collect it to be sent off for hashing as
|
|
||||||
// a group.
|
|
||||||
final List<File> sourcesToHash = <File>[];
|
final List<File> sourcesToHash = <File>[];
|
||||||
final List<File> missingInputs = <File>[];
|
final List<File> missingInputs = <File>[];
|
||||||
for (File file in inputs) {
|
for (File file in inputs) {
|
||||||
@ -187,19 +164,19 @@ class Target {
|
|||||||
if (fileHashStore.currentHashes.containsKey(absolutePath)) {
|
if (fileHashStore.currentHashes.containsKey(absolutePath)) {
|
||||||
final String currentHash = fileHashStore.currentHashes[absolutePath];
|
final String currentHash = fileHashStore.currentHashes[absolutePath];
|
||||||
if (currentHash != previousHash) {
|
if (currentHash != previousHash) {
|
||||||
updates[absolutePath] = previousInputs.contains(absolutePath)
|
canSkip = false;
|
||||||
? ChangeType.Modified
|
|
||||||
: ChangeType.Added;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sourcesToHash.add(file);
|
sourcesToHash.add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if any outputs were deleted or modified from the previous run.
|
|
||||||
|
// For each outut, first determine if we've already computed the hash
|
||||||
|
// for it. Then collect it to be sent off for hashing as a group.
|
||||||
for (String previousOutput in previousOutputs) {
|
for (String previousOutput in previousOutputs) {
|
||||||
final File file = fs.file(previousOutput);
|
final File file = fs.file(previousOutput);
|
||||||
if (!file.existsSync()) {
|
if (!file.existsSync()) {
|
||||||
updates[previousOutput] = ChangeType.Removed;
|
canSkip = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final String absolutePath = file.resolveSymbolicLinksSync();
|
final String absolutePath = file.resolveSymbolicLinksSync();
|
||||||
@ -207,15 +184,14 @@ class Target {
|
|||||||
if (fileHashStore.currentHashes.containsKey(absolutePath)) {
|
if (fileHashStore.currentHashes.containsKey(absolutePath)) {
|
||||||
final String currentHash = fileHashStore.currentHashes[absolutePath];
|
final String currentHash = fileHashStore.currentHashes[absolutePath];
|
||||||
if (currentHash != previousHash) {
|
if (currentHash != previousHash) {
|
||||||
updates[absolutePath] = previousInputs.contains(absolutePath)
|
canSkip = false;
|
||||||
? ChangeType.Modified
|
|
||||||
: ChangeType.Added;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sourcesToHash.add(file);
|
sourcesToHash.add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we depend on a file that doesnt exist on disk, kill the build.
|
||||||
if (missingInputs.isNotEmpty) {
|
if (missingInputs.isNotEmpty) {
|
||||||
throw MissingInputException(missingInputs, name);
|
throw MissingInputException(missingInputs, name);
|
||||||
}
|
}
|
||||||
@ -224,24 +200,11 @@ class Target {
|
|||||||
// update the result.
|
// update the result.
|
||||||
if (sourcesToHash.isNotEmpty) {
|
if (sourcesToHash.isNotEmpty) {
|
||||||
final List<File> dirty = await fileHashStore.hashFiles(sourcesToHash);
|
final List<File> dirty = await fileHashStore.hashFiles(sourcesToHash);
|
||||||
for (File file in dirty) {
|
if (dirty.isNotEmpty) {
|
||||||
final String absolutePath = file.resolveSymbolicLinksSync();
|
canSkip = false;
|
||||||
updates[absolutePath] = previousInputs.contains(absolutePath)
|
|
||||||
? ChangeType.Modified
|
|
||||||
: ChangeType.Added;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return canSkip;
|
||||||
// Find which, if any, inputs have been deleted.
|
|
||||||
final Set<String> currentInputPaths = Set<String>.from(
|
|
||||||
inputs.map<String>((File entity) => entity.resolveSymbolicLinksSync())
|
|
||||||
);
|
|
||||||
for (String previousInput in previousInputs) {
|
|
||||||
if (!currentInputPaths.contains(previousInput)) {
|
|
||||||
updates[previousInput] = ChangeType.Removed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return updates;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke to remove the stamp file if the [buildAction] threw an exception;
|
/// Invoke to remove the stamp file if the [buildAction] threw an exception;
|
||||||
@ -413,6 +376,7 @@ class Environment {
|
|||||||
rootBuildDir: rootBuildDir,
|
rootBuildDir: rootBuildDir,
|
||||||
cacheDir: Cache.instance.getRoot(),
|
cacheDir: Cache.instance.getRoot(),
|
||||||
defines: defines,
|
defines: defines,
|
||||||
|
flutterRootDir: fs.directory(Cache.flutterRoot),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,6 +386,7 @@ class Environment {
|
|||||||
@required this.rootBuildDir,
|
@required this.rootBuildDir,
|
||||||
@required this.cacheDir,
|
@required this.cacheDir,
|
||||||
@required this.defines,
|
@required this.defines,
|
||||||
|
@required this.flutterRootDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The [Source] value which is substituted with the path to [projectDir].
|
/// The [Source] value which is substituted with the path to [projectDir].
|
||||||
@ -454,6 +419,11 @@ class Environment {
|
|||||||
/// the flutter tool.
|
/// the flutter tool.
|
||||||
final Directory cacheDir;
|
final Directory cacheDir;
|
||||||
|
|
||||||
|
/// The `FLUTTER_ROOT` environment variable.
|
||||||
|
///
|
||||||
|
/// Defaults to to the value of [Cache.flutterRoot].
|
||||||
|
final Directory flutterRootDir;
|
||||||
|
|
||||||
/// Additional configuration passed to the build targets.
|
/// Additional configuration passed to the build targets.
|
||||||
///
|
///
|
||||||
/// Setting values here forces a unique build directory to be chosen
|
/// Setting values here forces a unique build directory to be chosen
|
||||||
@ -477,36 +447,14 @@ class BuildResult {
|
|||||||
|
|
||||||
/// The build system is responsible for invoking and ordering [Target]s.
|
/// The build system is responsible for invoking and ordering [Target]s.
|
||||||
class BuildSystem {
|
class BuildSystem {
|
||||||
BuildSystem([Map<String, Target> targets])
|
const BuildSystem();
|
||||||
: targets = targets ?? _defaultTargets;
|
|
||||||
|
|
||||||
/// All currently registered targets.
|
/// Build `target` and all of its dependencies.
|
||||||
static final Map<String, Target> _defaultTargets = <String, Target>{
|
|
||||||
unpackMacos.name: unpackMacos,
|
|
||||||
macosApplication.name: macosApplication,
|
|
||||||
macoReleaseApplication.name: macoReleaseApplication,
|
|
||||||
unpackLinux.name: unpackLinux,
|
|
||||||
unpackWindows.name: unpackWindows,
|
|
||||||
copyAssets.name: copyAssets,
|
|
||||||
kernelSnapshot.name: kernelSnapshot,
|
|
||||||
aotElfProfile.name: aotElfProfile,
|
|
||||||
aotElfRelease.name: aotElfRelease,
|
|
||||||
aotAssemblyProfile.name: aotAssemblyProfile,
|
|
||||||
aotAssemblyRelease.name: aotAssemblyRelease,
|
|
||||||
releaseIosApplication.name: releaseIosApplication,
|
|
||||||
profileIosApplication.name: profileIosApplication,
|
|
||||||
debugIosApplication.name: debugIosApplication,
|
|
||||||
};
|
|
||||||
|
|
||||||
final Map<String, Target> targets;
|
|
||||||
|
|
||||||
/// Build the target `name` and all of its dependencies.
|
|
||||||
Future<BuildResult> build(
|
Future<BuildResult> build(
|
||||||
String name,
|
Target target,
|
||||||
Environment environment,
|
Environment environment,
|
||||||
BuildSystemConfig buildSystemConfig,
|
{ BuildSystemConfig buildSystemConfig = const BuildSystemConfig() }
|
||||||
) async {
|
) async {
|
||||||
final Target target = _getNamedTarget(name);
|
|
||||||
environment.buildDir.createSync(recursive: true);
|
environment.buildDir.createSync(recursive: true);
|
||||||
|
|
||||||
// Load file hash store from previous builds.
|
// Load file hash store from previous builds.
|
||||||
@ -530,35 +478,6 @@ class BuildSystem {
|
|||||||
buildInstance.stepTimings,
|
buildInstance.stepTimings,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describe the target `name` and all of its dependencies.
|
|
||||||
List<Map<String, Object>> describe(
|
|
||||||
String name,
|
|
||||||
Environment environment,
|
|
||||||
) {
|
|
||||||
final Target target = _getNamedTarget(name);
|
|
||||||
environment.buildDir.createSync(recursive: true);
|
|
||||||
checkCycles(target);
|
|
||||||
// Cheat a bit and re-use the same map.
|
|
||||||
Map<String, Map<String, Object>> fold(Map<String, Map<String, Object>> accumulation, Target current) {
|
|
||||||
accumulation[current.name] = current.toJson(environment);
|
|
||||||
return accumulation;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, Map<String, Object>> result =
|
|
||||||
<String, Map<String, Object>>{};
|
|
||||||
final Map<String, Map<String, Object>> targets = target.fold(result, fold);
|
|
||||||
return targets.values.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the corresponding target or throws.
|
|
||||||
Target _getNamedTarget(String name) {
|
|
||||||
final Target target = targets[name];
|
|
||||||
if (target == null) {
|
|
||||||
throw Exception('No registered target:$name.');
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An active instance of a build.
|
/// An active instance of a build.
|
||||||
@ -594,14 +513,13 @@ class _BuildInstance {
|
|||||||
bool skipped = false;
|
bool skipped = false;
|
||||||
try {
|
try {
|
||||||
final List<File> inputs = target.resolveInputs(environment);
|
final List<File> inputs = target.resolveInputs(environment);
|
||||||
final Map<String, ChangeType> updates = await target.computeChanges(inputs, environment, fileCache);
|
final bool canSkip = await target.computeChanges(inputs, environment, fileCache);
|
||||||
if (updates.isEmpty) {
|
if (canSkip) {
|
||||||
skipped = true;
|
skipped = true;
|
||||||
printStatus('Skipping target: ${target.name}');
|
printStatus('Skipping target: ${target.name}');
|
||||||
} else {
|
} else {
|
||||||
printStatus('${target.name}: Starting');
|
printStatus('${target.name}: Starting');
|
||||||
// build actions may be null.
|
await target.build(inputs, environment);
|
||||||
await target?.buildAction(updates, environment);
|
|
||||||
printStatus('${target.name}: Complete');
|
printStatus('${target.name}: Complete');
|
||||||
|
|
||||||
final List<File> outputs = target.resolveOutputs(environment);
|
final List<File> outputs = target.resolveOutputs(environment);
|
||||||
@ -610,7 +528,6 @@ class _BuildInstance {
|
|||||||
target._writeStamp(inputs, outputs, environment);
|
target._writeStamp(inputs, outputs, environment);
|
||||||
}
|
}
|
||||||
} catch (exception, stackTrace) {
|
} catch (exception, stackTrace) {
|
||||||
// TODO(jonahwilliams): test
|
|
||||||
target.clearStamp(environment);
|
target.clearStamp(environment);
|
||||||
passed = false;
|
passed = false;
|
||||||
skipped = false;
|
skipped = false;
|
||||||
|
@ -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';
|
|||||||
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 {
|
|||||||
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
|
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
|
||||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||||
);
|
);
|
||||||
|
// Filter the file type to remove the files that are generated by this
|
||||||
|
// command as inputs.
|
||||||
final List<File> results = <File>[];
|
final List<File> results = <File>[];
|
||||||
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
|
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
|
||||||
for (DevFSFileContent devFsContent in files) {
|
for (DevFSFileContent devFsContent in files) {
|
||||||
@ -40,56 +38,65 @@ class AssetBehavior extends SourceBehavior {
|
|||||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||||
);
|
);
|
||||||
final List<File> results = <File>[];
|
final List<File> results = <File>[];
|
||||||
for (MapEntry<String, DevFSContent> entry in assetBundle.entries.entries) {
|
for (String key in assetBundle.entries.keys) {
|
||||||
final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', entry.key));
|
final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', key));
|
||||||
results.add(file);
|
results.add(file);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies the asset files from the [copyAssets] rule into place.
|
/// Copy the assets defined in the flutter manifest into a build directory.
|
||||||
Future<void> copyAssetsInvocation(Map<String, ChangeType> updates, Environment environment) async {
|
class CopyAssets extends Target {
|
||||||
final Directory output = environment
|
const CopyAssets();
|
||||||
.buildDir
|
|
||||||
.childDirectory('flutter_assets');
|
|
||||||
if (output.existsSync()) {
|
|
||||||
output.deleteSync(recursive: true);
|
|
||||||
}
|
|
||||||
output.createSync(recursive: true);
|
|
||||||
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
|
|
||||||
await assetBundle.build(
|
|
||||||
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
|
|
||||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
|
||||||
);
|
|
||||||
// Limit number of open files to avoid running out of file descriptors.
|
|
||||||
final Pool pool = Pool(64);
|
|
||||||
await Future.wait<void>(
|
|
||||||
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
|
|
||||||
final PoolResource resource = await pool.request();
|
|
||||||
try {
|
|
||||||
final File file = fs.file(fs.path.join(output.path, entry.key));
|
|
||||||
file.parent.createSync(recursive: true);
|
|
||||||
await file.writeAsBytes(await entry.value.contentsAsBytes());
|
|
||||||
} finally {
|
|
||||||
resource.release();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copy the assets used in the application into a build directory.
|
@override
|
||||||
const Target copyAssets = Target(
|
String get name => 'copy_assets';
|
||||||
name: 'copy_assets',
|
|
||||||
inputs: <Source>[
|
@override
|
||||||
|
List<Target> get dependencies => const <Target>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/assets.dart'),
|
||||||
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
|
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
|
||||||
Source.behavior(AssetBehavior()),
|
Source.behavior(AssetBehavior()),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'),
|
Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'),
|
||||||
Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'),
|
Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'),
|
||||||
Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'),
|
Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'),
|
||||||
Source.behavior(AssetBehavior()), // <- everything in this subdirectory.
|
Source.behavior(AssetBehavior()), // <- everything in this subdirectory.
|
||||||
],
|
];
|
||||||
dependencies: <Target>[],
|
|
||||||
buildAction: copyAssetsInvocation,
|
@override
|
||||||
);
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
final Directory output = environment
|
||||||
|
.buildDir
|
||||||
|
.childDirectory('flutter_assets');
|
||||||
|
if (output.existsSync()) {
|
||||||
|
output.deleteSync(recursive: true);
|
||||||
|
}
|
||||||
|
output.createSync(recursive: true);
|
||||||
|
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
|
||||||
|
await assetBundle.build(
|
||||||
|
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
|
||||||
|
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||||
|
);
|
||||||
|
// Limit number of open files to avoid running out of file descriptors.
|
||||||
|
final Pool pool = Pool(64);
|
||||||
|
await Future.wait<void>(
|
||||||
|
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
|
||||||
|
final PoolResource resource = await pool.request();
|
||||||
|
try {
|
||||||
|
final File file = fs.file(fs.path.join(output.path, entry.key));
|
||||||
|
file.parent.createSync(recursive: true);
|
||||||
|
await file.writeAsBytes(await entry.value.contentsAsBytes());
|
||||||
|
} finally {
|
||||||
|
resource.release();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
import '../../artifacts.dart';
|
import '../../artifacts.dart';
|
||||||
import '../../base/build.dart';
|
import '../../base/build.dart';
|
||||||
import '../../base/file_system.dart';
|
import '../../base/file_system.dart';
|
||||||
import '../../base/io.dart';
|
|
||||||
import '../../base/platform.dart';
|
import '../../base/platform.dart';
|
||||||
import '../../base/process_manager.dart';
|
|
||||||
import '../../build_info.dart';
|
import '../../build_info.dart';
|
||||||
import '../../compile.dart';
|
import '../../compile.dart';
|
||||||
import '../../dart/package_map.dart';
|
import '../../dart/package_map.dart';
|
||||||
@ -36,63 +34,6 @@ const String kBitcodeFlag = 'EnableBitcode';
|
|||||||
/// The other supported value is armv7, the 32-bit iOS architecture.
|
/// The other supported value is armv7, the 32-bit iOS architecture.
|
||||||
const String kIosArchs = 'IosArchs';
|
const String kIosArchs = 'IosArchs';
|
||||||
|
|
||||||
/// Supports compiling dart source to kernel with a subset of flags.
|
|
||||||
///
|
|
||||||
/// This is a non-incremental compile so the specific [updates] are ignored.
|
|
||||||
Future<void> compileKernel(Map<String, ChangeType> updates, Environment environment) async {
|
|
||||||
final KernelCompiler compiler = await kernelCompilerFactory.create(
|
|
||||||
FlutterProject.fromDirectory(environment.projectDir),
|
|
||||||
);
|
|
||||||
if (environment.defines[kBuildMode] == null) {
|
|
||||||
throw MissingDefineException(kBuildMode, 'kernel_snapshot');
|
|
||||||
}
|
|
||||||
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
|
||||||
final String targetFile = environment.defines[kTargetFile] ?? fs.path.join('lib', 'main.dart');
|
|
||||||
|
|
||||||
final CompilerOutput output = await compiler.compile(
|
|
||||||
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
|
|
||||||
aot: buildMode != BuildMode.debug,
|
|
||||||
trackWidgetCreation: false,
|
|
||||||
targetModel: TargetModel.flutter,
|
|
||||||
targetProductVm: buildMode == BuildMode.release,
|
|
||||||
outputFilePath: environment
|
|
||||||
.buildDir
|
|
||||||
.childFile('main.app.dill')
|
|
||||||
.path,
|
|
||||||
depFilePath: null,
|
|
||||||
mainPath: targetFile,
|
|
||||||
);
|
|
||||||
if (output.errorCount != 0) {
|
|
||||||
throw Exception('Errors during snapshot creation: $output');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Supports compiling a dart kernel file to an ELF binary.
|
|
||||||
Future<void> compileAotElf(Map<String, ChangeType> updates, Environment environment) async {
|
|
||||||
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false);
|
|
||||||
final String outputPath = environment.buildDir.path;
|
|
||||||
if (environment.defines[kBuildMode] == null) {
|
|
||||||
throw MissingDefineException(kBuildMode, 'aot_elf');
|
|
||||||
}
|
|
||||||
if (environment.defines[kTargetPlatform] == null) {
|
|
||||||
throw MissingDefineException(kTargetPlatform, 'aot_elf');
|
|
||||||
}
|
|
||||||
|
|
||||||
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
|
||||||
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
|
|
||||||
final int snapshotExitCode = await snapshotter.build(
|
|
||||||
platform: targetPlatform,
|
|
||||||
buildMode: buildMode,
|
|
||||||
mainPath: environment.buildDir.childFile('main.app.dill').path,
|
|
||||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
|
||||||
outputPath: outputPath,
|
|
||||||
bitcode: false,
|
|
||||||
);
|
|
||||||
if (snapshotExitCode != 0) {
|
|
||||||
throw Exception('AOT snapshotter exited with code $snapshotExitCode');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the locations of all dart files within the project.
|
/// Finds the locations of all dart files within the project.
|
||||||
///
|
///
|
||||||
/// This does not attempt to determine if a file is used or imported, so it
|
/// This does not attempt to determine if a file is used or imported, so it
|
||||||
@ -111,96 +52,99 @@ List<File> listDartSources(Environment environment) {
|
|||||||
return dartFiles;
|
return dartFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
|
||||||
Future<void> compileAotAssembly(Map<String, ChangeType> updates, 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 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');
|
|
||||||
}
|
|
||||||
final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
|
|
||||||
|
|
||||||
// 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('main.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('main.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 a snapshot of the dart code used in the program.
|
/// Generate a snapshot of the dart code used in the program.
|
||||||
const Target kernelSnapshot = Target(
|
class KernelSnapshot extends Target {
|
||||||
name: 'kernel_snapshot',
|
const KernelSnapshot();
|
||||||
inputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
String get name => 'kernel_snapshot';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/dart.dart'),
|
||||||
Source.function(listDartSources), // <- every dart file under {PROJECT_DIR}/lib and in .packages
|
Source.function(listDartSources), // <- every dart file under {PROJECT_DIR}/lib and in .packages
|
||||||
Source.artifact(Artifact.platformKernelDill),
|
Source.artifact(Artifact.platformKernelDill),
|
||||||
Source.artifact(Artifact.engineDartBinary),
|
Source.artifact(Artifact.engineDartBinary),
|
||||||
Source.artifact(Artifact.frontendServerSnapshotForEngineDartSdk),
|
Source.artifact(Artifact.frontendServerSnapshotForEngineDartSdk),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/main.app.dill'),
|
@override
|
||||||
],
|
List<Source> get outputs => const <Source>[
|
||||||
dependencies: <Target>[],
|
Source.pattern('{BUILD_DIR}/app.dill'),
|
||||||
buildAction: compileKernel,
|
];
|
||||||
);
|
|
||||||
|
@override
|
||||||
|
List<Target> get dependencies => <Target>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
final KernelCompiler compiler = await kernelCompilerFactory.create(
|
||||||
|
FlutterProject.fromDirectory(environment.projectDir),
|
||||||
|
);
|
||||||
|
if (environment.defines[kBuildMode] == null) {
|
||||||
|
throw MissingDefineException(kBuildMode, 'kernel_snapshot');
|
||||||
|
}
|
||||||
|
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
||||||
|
final String targetFile = environment.defines[kTargetFile] ?? fs.path.join('lib', 'main.dart');
|
||||||
|
|
||||||
|
final CompilerOutput output = await compiler.compile(
|
||||||
|
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
|
||||||
|
aot: buildMode != BuildMode.debug,
|
||||||
|
trackWidgetCreation: false,
|
||||||
|
targetModel: TargetModel.flutter,
|
||||||
|
targetProductVm: buildMode == BuildMode.release,
|
||||||
|
outputFilePath: environment.buildDir.childFile('app.dill').path,
|
||||||
|
depFilePath: null,
|
||||||
|
mainPath: targetFile,
|
||||||
|
);
|
||||||
|
if (output.errorCount != 0) {
|
||||||
|
throw Exception('Errors during snapshot creation: $output');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Supports compiling a dart kernel file to an ELF binary.
|
||||||
|
abstract class AotElfBase extends Target {
|
||||||
|
const AotElfBase();
|
||||||
|
|
||||||
|
@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_elf');
|
||||||
|
}
|
||||||
|
if (environment.defines[kTargetPlatform] == null) {
|
||||||
|
throw MissingDefineException(kTargetPlatform, 'aot_elf');
|
||||||
|
}
|
||||||
|
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
||||||
|
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
|
||||||
|
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,
|
||||||
|
bitcode: false,
|
||||||
|
);
|
||||||
|
if (snapshotExitCode != 0) {
|
||||||
|
throw Exception('AOT snapshotter exited with code $snapshotExitCode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate an ELF binary from a dart kernel file in profile mode.
|
/// Generate an ELF binary from a dart kernel file in profile mode.
|
||||||
const Target aotElfProfile = Target(
|
class AotElfProfile extends AotElfBase {
|
||||||
name: 'aot_elf_profile',
|
const AotElfProfile();
|
||||||
inputs: <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/main.app.dill'),
|
@override
|
||||||
|
String get name => 'aot_elf_profile';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/dart.dart'),
|
||||||
|
Source.pattern('{BUILD_DIR}/app.dill'),
|
||||||
Source.pattern('{PROJECT_DIR}/.packages'),
|
Source.pattern('{PROJECT_DIR}/.packages'),
|
||||||
Source.artifact(Artifact.engineDartBinary),
|
Source.artifact(Artifact.engineDartBinary),
|
||||||
Source.artifact(Artifact.skyEnginePath),
|
Source.artifact(Artifact.skyEnginePath),
|
||||||
@ -208,21 +152,30 @@ const Target aotElfProfile = Target(
|
|||||||
platform: TargetPlatform.android_arm,
|
platform: TargetPlatform.android_arm,
|
||||||
mode: BuildMode.profile,
|
mode: BuildMode.profile,
|
||||||
),
|
),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
Source.pattern('{BUILD_DIR}/app.so'),
|
Source.pattern('{BUILD_DIR}/app.so'),
|
||||||
],
|
];
|
||||||
dependencies: <Target>[
|
|
||||||
kernelSnapshot,
|
@override
|
||||||
],
|
List<Target> get dependencies => const <Target>[
|
||||||
buildAction: compileAotElf,
|
KernelSnapshot(),
|
||||||
);
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate an ELF binary from a dart kernel file in release mode.
|
/// Generate an ELF binary from a dart kernel file in release mode.
|
||||||
const Target aotElfRelease= Target(
|
class AotElfRelease extends AotElfBase {
|
||||||
name: 'aot_elf_release',
|
const AotElfRelease();
|
||||||
inputs: <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/main.app.dill'),
|
@override
|
||||||
|
String get name => 'aot_elf_release';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/dart.dart'),
|
||||||
|
Source.pattern('{BUILD_DIR}/app.dill'),
|
||||||
Source.pattern('{PROJECT_DIR}/.packages'),
|
Source.pattern('{PROJECT_DIR}/.packages'),
|
||||||
Source.artifact(Artifact.engineDartBinary),
|
Source.artifact(Artifact.engineDartBinary),
|
||||||
Source.artifact(Artifact.skyEnginePath),
|
Source.artifact(Artifact.skyEnginePath),
|
||||||
@ -230,62 +183,15 @@ const Target aotElfRelease= Target(
|
|||||||
platform: TargetPlatform.android_arm,
|
platform: TargetPlatform.android_arm,
|
||||||
mode: BuildMode.release,
|
mode: BuildMode.release,
|
||||||
),
|
),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
Source.pattern('{BUILD_DIR}/app.so'),
|
Source.pattern('{BUILD_DIR}/app.so'),
|
||||||
],
|
];
|
||||||
dependencies: <Target>[
|
|
||||||
kernelSnapshot,
|
|
||||||
],
|
|
||||||
buildAction: compileAotElf,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Generate an assembly target from a dart kernel file in profile mode.
|
@override
|
||||||
const Target aotAssemblyProfile = Target(
|
List<Target> get dependencies => const <Target>[
|
||||||
name: 'aot_assembly_profile',
|
KernelSnapshot(),
|
||||||
inputs: <Source>[
|
];
|
||||||
Source.pattern('{BUILD_DIR}/main.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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
outputs: <Source>[
|
|
||||||
// TODO(jonahwilliams): are these used or just a side effect?
|
|
||||||
// Source.pattern('{BUILD_DIR}/snapshot_assembly.S'),
|
|
||||||
// Source.pattern('{BUILD_DIR}/snapshot_assembly.o'),
|
|
||||||
Source.pattern('{BUILD_DIR}/App.framework/App'),
|
|
||||||
],
|
|
||||||
dependencies: <Target>[
|
|
||||||
kernelSnapshot,
|
|
||||||
],
|
|
||||||
buildAction: compileAotAssembly,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Generate an assembly target from a dart kernel file in release mode.
|
|
||||||
const Target aotAssemblyRelease = Target(
|
|
||||||
name: 'aot_assembly_release',
|
|
||||||
inputs: <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/main.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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
outputs: <Source>[
|
|
||||||
// TODO(jonahwilliams): are these used or just a side effect?
|
|
||||||
// Source.pattern('{BUILD_DIR}/snapshot_assembly.S'),
|
|
||||||
// Source.pattern('{BUILD_DIR}/snapshot_assembly.o'),
|
|
||||||
Source.pattern('{BUILD_DIR}/App.framework/App'),
|
|
||||||
],
|
|
||||||
dependencies: <Target>[
|
|
||||||
kernelSnapshot,
|
|
||||||
],
|
|
||||||
buildAction: compileAotAssembly,
|
|
||||||
);
|
|
||||||
|
@ -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,
|
|
||||||
kernelSnapshot,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Create an iOS profile application.
|
@override
|
||||||
const Target profileIosApplication = Target(
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
name: 'profile_ios_application',
|
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false);
|
||||||
buildAction: null,
|
final String outputPath = environment.buildDir.path;
|
||||||
inputs: <Source>[],
|
if (environment.defines[kBuildMode] == null) {
|
||||||
outputs: <Source>[],
|
throw MissingDefineException(kBuildMode, 'aot_assembly');
|
||||||
dependencies: <Target>[
|
}
|
||||||
copyAssets,
|
if (environment.defines[kTargetPlatform] == null) {
|
||||||
aotAssemblyProfile,
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
/// Create an iOS debug application.
|
// If we're building for a single architecture (common), then skip the lipo.
|
||||||
const Target releaseIosApplication = Target(
|
if (iosArchs.length == 1) {
|
||||||
name: 'release_ios_application',
|
final int snapshotExitCode = await snapshotter.build(
|
||||||
buildAction: null,
|
platform: targetPlatform,
|
||||||
inputs: <Source>[],
|
buildMode: buildMode,
|
||||||
outputs: <Source>[],
|
mainPath: environment.buildDir.childFile('app.dill').path,
|
||||||
dependencies: <Target>[
|
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||||
copyAssets,
|
outputPath: outputPath,
|
||||||
aotAssemblyRelease,
|
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 '../../globals.dart';
|
||||||
import '../build_system.dart';
|
import '../build_system.dart';
|
||||||
|
|
||||||
// Copies all of the input files to the correct copy dir.
|
|
||||||
Future<void> copyLinuxAssets(Map<String, ChangeType> updates,
|
|
||||||
Environment environment) async {
|
|
||||||
final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
|
|
||||||
for (String input in updates.keys) {
|
|
||||||
final String outputPath = fs.path.join(
|
|
||||||
environment.projectDir.path,
|
|
||||||
'linux',
|
|
||||||
'flutter',
|
|
||||||
fs.path.relative(input, from: basePath),
|
|
||||||
);
|
|
||||||
final File destinationFile = fs.file(outputPath);
|
|
||||||
if (!destinationFile.parent.existsSync()) {
|
|
||||||
destinationFile.parent.createSync(recursive: true);
|
|
||||||
}
|
|
||||||
fs.file(input).copySync(destinationFile.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies the Linux desktop embedding files to the copy directory.
|
/// Copies the Linux desktop embedding files to the copy directory.
|
||||||
const Target unpackLinux = Target(
|
class UnpackLinux extends Target {
|
||||||
name: 'unpack_linux',
|
const UnpackLinux();
|
||||||
inputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
String get name => 'unpack_linux';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'),
|
||||||
Source.artifact(Artifact.linuxDesktopPath),
|
Source.artifact(Artifact.linuxDesktopPath),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
Source.pattern('{PROJECT_DIR}/linux/flutter/libflutter_linux.so'),
|
Source.pattern('{PROJECT_DIR}/linux/flutter/libflutter_linux.so'),
|
||||||
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_export.h'),
|
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_export.h'),
|
||||||
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_messenger.h'),
|
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_messenger.h'),
|
||||||
@ -40,7 +29,29 @@ const Target unpackLinux = Target(
|
|||||||
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_glfw.h'),
|
Source.pattern('{PROJECT_DIR}/linux/flutter/flutter_glfw.h'),
|
||||||
Source.pattern('{PROJECT_DIR}/linux/flutter/icudtl.dat'),
|
Source.pattern('{PROJECT_DIR}/linux/flutter/icudtl.dat'),
|
||||||
Source.pattern('{PROJECT_DIR}/linux/flutter/cpp_client_wrapper/*'),
|
Source.pattern('{PROJECT_DIR}/linux/flutter/cpp_client_wrapper/*'),
|
||||||
],
|
];
|
||||||
dependencies: <Target>[],
|
|
||||||
buildAction: copyLinuxAssets,
|
@override
|
||||||
);
|
List<Target> get dependencies => <Target>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
|
||||||
|
for (File input in inputFiles) {
|
||||||
|
if (fs.path.basename(input.path) == 'linux.dart') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String outputPath = fs.path.join(
|
||||||
|
environment.projectDir.path,
|
||||||
|
'linux',
|
||||||
|
'flutter',
|
||||||
|
fs.path.relative(input.path, from: basePath),
|
||||||
|
);
|
||||||
|
final File destinationFile = fs.file(outputPath);
|
||||||
|
if (!destinationFile.parent.existsSync()) {
|
||||||
|
destinationFile.parent.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
fs.file(input).copySync(destinationFile.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,8 +8,8 @@ import '../../base/io.dart';
|
|||||||
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';
|
|||||||
/// Removes any previous version of the framework that already exists in the
|
/// Removes any previous version of the framework that already exists in the
|
||||||
/// target directory.
|
/// target directory.
|
||||||
// TODO(jonahwilliams): remove shell out.
|
// TODO(jonahwilliams): remove shell out.
|
||||||
Future<void> copyFramework(Map<String, ChangeType> updates,
|
class UnpackMacOS extends Target {
|
||||||
Environment environment) async {
|
const UnpackMacOS();
|
||||||
final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework);
|
|
||||||
final Directory targetDirectory = environment
|
|
||||||
.projectDir
|
|
||||||
.childDirectory('macos')
|
|
||||||
.childDirectory('Flutter')
|
|
||||||
.childDirectory('FlutterMacOS.framework');
|
|
||||||
if (targetDirectory.existsSync()) {
|
|
||||||
targetDirectory.deleteSync(recursive: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ProcessResult result = processManager
|
@override
|
||||||
.runSync(<String>['cp', '-R', basePath, targetDirectory.path]);
|
String get name => 'unpack_macos';
|
||||||
if (result.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Failed to copy framework (exit ${result.exitCode}:\n'
|
|
||||||
'${result.stdout}\n---\n${result.stderr}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/FlutterMacOS.framework';
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
/// Copies the macOS desktop framework to the copy directory.
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
|
||||||
const Target unpackMacos = Target(
|
|
||||||
name: 'unpack_macos',
|
|
||||||
inputs: <Source>[
|
|
||||||
Source.artifact(Artifact.flutterMacOSFramework),
|
Source.artifact(Artifact.flutterMacOSFramework),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
Source.pattern('$_kOutputPrefix/FlutterMacOS'),
|
Source.pattern('$_kOutputPrefix/FlutterMacOS'),
|
||||||
// Headers
|
// Headers
|
||||||
Source.pattern('$_kOutputPrefix/Headers/FLEOpenGLContextHandling.h'),
|
Source.pattern('$_kOutputPrefix/Headers/FLEOpenGLContextHandling.h'),
|
||||||
@ -68,33 +51,30 @@ const Target unpackMacos = Target(
|
|||||||
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,33 +7,21 @@ import '../../base/file_system.dart';
|
|||||||
import '../../globals.dart';
|
import '../../globals.dart';
|
||||||
import '../build_system.dart';
|
import '../build_system.dart';
|
||||||
|
|
||||||
/// Copies all of the input files to the correct copy dir.
|
|
||||||
Future<void> copyWindowsAssets(Map<String, ChangeType> updates,
|
|
||||||
Environment environment) async {
|
|
||||||
// This path needs to match the prefix in the rule below.
|
|
||||||
final String basePath = artifacts.getArtifactPath(Artifact.windowsDesktopPath);
|
|
||||||
for (String input in updates.keys) {
|
|
||||||
final String outputPath = fs.path.join(
|
|
||||||
environment.projectDir.path,
|
|
||||||
'windows',
|
|
||||||
'flutter',
|
|
||||||
fs.path.relative(input, from: basePath),
|
|
||||||
);
|
|
||||||
final File destinationFile = fs.file(outputPath);
|
|
||||||
if (!destinationFile.parent.existsSync()) {
|
|
||||||
destinationFile.parent.createSync(recursive: true);
|
|
||||||
}
|
|
||||||
fs.file(input).copySync(destinationFile.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies the Windows desktop embedding files to the copy directory.
|
/// Copies the Windows desktop embedding files to the copy directory.
|
||||||
const Target unpackWindows = Target(
|
class UnpackWindows extends Target {
|
||||||
name: 'unpack_windows',
|
const UnpackWindows();
|
||||||
inputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
String get name => 'unpack_windows';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/windows.dart'),
|
||||||
Source.artifact(Artifact.windowsDesktopPath),
|
Source.artifact(Artifact.windowsDesktopPath),
|
||||||
],
|
];
|
||||||
outputs: <Source>[
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll'),
|
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll'),
|
||||||
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.exp'),
|
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.exp'),
|
||||||
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.lib'),
|
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_windows.dll.lib'),
|
||||||
@ -44,7 +32,30 @@ const Target unpackWindows = Target(
|
|||||||
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_glfw.h'),
|
Source.pattern('{PROJECT_DIR}/windows/flutter/flutter_glfw.h'),
|
||||||
Source.pattern('{PROJECT_DIR}/windows/flutter/icudtl.dat'),
|
Source.pattern('{PROJECT_DIR}/windows/flutter/icudtl.dat'),
|
||||||
Source.pattern('{PROJECT_DIR}/windows/flutter/cpp_client_wrapper/*'),
|
Source.pattern('{PROJECT_DIR}/windows/flutter/cpp_client_wrapper/*'),
|
||||||
],
|
];
|
||||||
dependencies: <Target>[],
|
|
||||||
buildAction: copyWindowsAssets,
|
@override
|
||||||
);
|
List<Target> get dependencies => const <Target>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
// This path needs to match the prefix in the rule below.
|
||||||
|
final String basePath = artifacts.getArtifactPath(Artifact.windowsDesktopPath);
|
||||||
|
for (File input in inputFiles) {
|
||||||
|
if (fs.path.basename(input.path) == 'windows.dart') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String outputPath = fs.path.join(
|
||||||
|
environment.projectDir.path,
|
||||||
|
'windows',
|
||||||
|
'flutter',
|
||||||
|
fs.path.relative(input.path, from: basePath),
|
||||||
|
);
|
||||||
|
final File destinationFile = fs.file(outputPath);
|
||||||
|
if (!destinationFile.parent.existsSync()) {
|
||||||
|
destinationFile.parent.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
fs.file(input).copySync(destinationFile.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,9 +4,13 @@
|
|||||||
|
|
||||||
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';
|
|||||||
/// 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 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 {
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
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>(
|
|||||||
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(),
|
||||||
|
@ -2,14 +2,10 @@
|
|||||||
// 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 'package:flutter_tools/src/artifacts.dart';
|
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.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/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/file_hash_store.dart';
|
|
||||||
import 'package:flutter_tools/src/build_system/filecache.pb.dart' as pb;
|
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/convert.dart';
|
import 'package:flutter_tools/src/convert.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
@ -23,515 +19,217 @@ void main() {
|
|||||||
Cache.disableLocking();
|
Cache.disableLocking();
|
||||||
});
|
});
|
||||||
|
|
||||||
group(Target, () {
|
const BuildSystem buildSystem = BuildSystem();
|
||||||
Testbed testbed;
|
Testbed testbed;
|
||||||
MockPlatform mockPlatform;
|
MockPlatform mockPlatform;
|
||||||
Environment environment;
|
Environment environment;
|
||||||
Target fooTarget;
|
Target fooTarget;
|
||||||
Target barTarget;
|
Target barTarget;
|
||||||
Target fizzTarget;
|
Target fizzTarget;
|
||||||
BuildSystem buildSystem;
|
Target sharedTarget;
|
||||||
int fooInvocations;
|
int fooInvocations;
|
||||||
int barInvocations;
|
int barInvocations;
|
||||||
|
int shared;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
fooInvocations = 0;
|
fooInvocations = 0;
|
||||||
barInvocations = 0;
|
barInvocations = 0;
|
||||||
mockPlatform = MockPlatform();
|
shared = 0;
|
||||||
// Keep file paths the same.
|
mockPlatform = MockPlatform();
|
||||||
when(mockPlatform.isWindows).thenReturn(false);
|
// Keep file paths the same.
|
||||||
testbed = Testbed(
|
when(mockPlatform.isWindows).thenReturn(false);
|
||||||
setup: () {
|
|
||||||
environment = Environment(
|
|
||||||
projectDir: fs.currentDirectory,
|
|
||||||
);
|
|
||||||
fs.file('foo.dart').createSync(recursive: true);
|
|
||||||
fs.file('pubspec.yaml').createSync();
|
|
||||||
fooTarget = Target(
|
|
||||||
name: 'foo',
|
|
||||||
inputs: const <Source>[
|
|
||||||
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
|
||||||
],
|
|
||||||
outputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/out'),
|
|
||||||
],
|
|
||||||
dependencies: <Target>[],
|
|
||||||
buildAction: (Map<String, ChangeType> updates, Environment environment) {
|
|
||||||
environment
|
|
||||||
.buildDir
|
|
||||||
.childFile('out')
|
|
||||||
..createSync(recursive: true)
|
|
||||||
..writeAsStringSync('hey');
|
|
||||||
fooInvocations++;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
barTarget = Target(
|
|
||||||
name: 'bar',
|
|
||||||
inputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/out'),
|
|
||||||
],
|
|
||||||
outputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/bar'),
|
|
||||||
],
|
|
||||||
dependencies: <Target>[fooTarget],
|
|
||||||
buildAction: (Map<String, ChangeType> updates, Environment environment) {
|
|
||||||
environment.buildDir
|
|
||||||
.childFile('bar')
|
|
||||||
..createSync(recursive: true)
|
|
||||||
..writeAsStringSync('there');
|
|
||||||
barInvocations++;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
fizzTarget = Target(
|
|
||||||
name: 'fizz',
|
|
||||||
inputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/out'),
|
|
||||||
],
|
|
||||||
outputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/fizz'),
|
|
||||||
],
|
|
||||||
dependencies: <Target>[fooTarget],
|
|
||||||
buildAction: (Map<String, ChangeType> updates, Environment environment) {
|
|
||||||
throw Exception('something bad happens');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
buildSystem = BuildSystem(<String, Target>{
|
|
||||||
fooTarget.name: fooTarget,
|
|
||||||
barTarget.name: barTarget,
|
|
||||||
fizzTarget.name: fizzTarget,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
overrides: <Type, Generator>{
|
|
||||||
Platform: () => mockPlatform,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('can describe build rules', () => testbed.run(() {
|
/// Create various testing targets.
|
||||||
expect(buildSystem.describe('foo', environment), <Object>[
|
fooTarget = TestTarget((List<File> inputFiles, Environment environment) async {
|
||||||
<String, Object>{
|
environment
|
||||||
'name': 'foo',
|
.buildDir
|
||||||
'dependencies': <String>[],
|
.childFile('out')
|
||||||
'inputs': <String>['/foo.dart'],
|
..createSync(recursive: true)
|
||||||
'outputs': <String>[fs.path.join(environment.buildDir.path, 'out')],
|
..writeAsStringSync('hey');
|
||||||
'stamp': fs.path.join(environment.buildDir.path, 'foo.stamp'),
|
fooInvocations++;
|
||||||
}
|
})
|
||||||
]);
|
..name = 'foo'
|
||||||
}));
|
..inputs = const <Source>[
|
||||||
|
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
||||||
test('Throws exception if asked to build non-existent target', () => testbed.run(() {
|
]
|
||||||
expect(buildSystem.build('not_real', environment, const BuildSystemConfig()), throwsA(isInstanceOf<Exception>()));
|
..outputs = const <Source>[
|
||||||
}));
|
Source.pattern('{BUILD_DIR}/out'),
|
||||||
|
]
|
||||||
test('Throws exception if asked to build with missing inputs', () => testbed.run(() async {
|
..dependencies = <Target>[];
|
||||||
// Delete required input file.
|
barTarget = TestTarget((List<File> inputFiles, Environment environment) async {
|
||||||
fs.file('foo.dart').deleteSync();
|
environment.buildDir
|
||||||
final BuildResult buildResult = await buildSystem.build('foo', environment, const BuildSystemConfig());
|
.childFile('bar')
|
||||||
|
..createSync(recursive: true)
|
||||||
expect(buildResult.hasException, true);
|
..writeAsStringSync('there');
|
||||||
expect(buildResult.exceptions.values.single.exception, isInstanceOf<MissingInputException>());
|
barInvocations++;
|
||||||
}));
|
})
|
||||||
|
..name = 'bar'
|
||||||
test('Throws exception if it does not produce a specified output', () => testbed.run(() async {
|
..inputs = const <Source>[
|
||||||
final Target badTarget = Target
|
Source.pattern('{BUILD_DIR}/out'),
|
||||||
(buildAction: (Map<String, ChangeType> inputs, Environment environment) {},
|
]
|
||||||
inputs: const <Source>[
|
..outputs = const <Source>[
|
||||||
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
Source.pattern('{BUILD_DIR}/bar'),
|
||||||
],
|
]
|
||||||
outputs: const <Source>[
|
..dependencies = <Target>[];
|
||||||
Source.pattern('{BUILD_DIR}/out')
|
fizzTarget = TestTarget((List<File> inputFiles, Environment environment) async {
|
||||||
],
|
throw Exception('something bad happens');
|
||||||
name: 'bad'
|
})
|
||||||
);
|
..name = 'fizz'
|
||||||
buildSystem = BuildSystem(<String, Target>{
|
..inputs = const <Source>[
|
||||||
badTarget.name: badTarget,
|
Source.pattern('{BUILD_DIR}/out'),
|
||||||
});
|
]
|
||||||
final BuildResult result = await buildSystem.build('bad', environment, const BuildSystemConfig());
|
..outputs = const <Source>[
|
||||||
|
Source.pattern('{BUILD_DIR}/fizz'),
|
||||||
expect(result.hasException, true);
|
]
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<MissingOutputException>());
|
..dependencies = <Target>[fooTarget];
|
||||||
}));
|
sharedTarget = TestTarget((List<File> inputFiles, Environment environment) async {
|
||||||
|
shared += 1;
|
||||||
test('Saves a stamp file with inputs and outputs', () => testbed.run(() async {
|
})
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
..name = 'shared'
|
||||||
|
..inputs = const <Source>[
|
||||||
final File stampFile = fs.file(fs.path.join(environment.buildDir.path, 'foo.stamp'));
|
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
||||||
expect(stampFile.existsSync(), true);
|
];
|
||||||
|
testbed = Testbed(
|
||||||
final Map<String, Object> stampContents = json.decode(stampFile.readAsStringSync());
|
setup: () {
|
||||||
expect(stampContents['inputs'], <Object>['/foo.dart']);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('Does not re-invoke build if stamp is valid', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(fooInvocations, 1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('Re-invoke build if input is modified', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
fs.file('foo.dart').writeAsStringSync('new contents');
|
|
||||||
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
expect(fooInvocations, 2);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('does not re-invoke build if input timestamp changes', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
fs.file('foo.dart').writeAsStringSync('');
|
|
||||||
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
expect(fooInvocations, 1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('does not re-invoke build if output timestamp changes', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
environment.buildDir.childFile('out').writeAsStringSync('hey');
|
|
||||||
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
expect(fooInvocations, 1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
test('Re-invoke build if output is modified', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
environment.buildDir.childFile('out').writeAsStringSync('Something different');
|
|
||||||
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
expect(fooInvocations, 2);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('Runs dependencies of targets', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('bar', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(fs.file(fs.path.join(environment.buildDir.path, 'bar')).existsSync(), true);
|
|
||||||
expect(fooInvocations, 1);
|
|
||||||
expect(barInvocations, 1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('handles a throwing build action', () => testbed.run(() async {
|
|
||||||
final BuildResult result = await buildSystem.build('fizz', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.hasException, true);
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('Can describe itself with JSON output', () => testbed.run(() {
|
|
||||||
environment.buildDir.createSync(recursive: true);
|
|
||||||
expect(fooTarget.toJson(environment), <String, dynamic>{
|
|
||||||
'inputs': <Object>[
|
|
||||||
'/foo.dart'
|
|
||||||
],
|
|
||||||
'outputs': <Object>[
|
|
||||||
fs.path.join(environment.buildDir.path, 'out'),
|
|
||||||
],
|
|
||||||
'dependencies': <Object>[],
|
|
||||||
'name': 'foo',
|
|
||||||
'stamp': fs.path.join(environment.buildDir.path, 'foo.stamp'),
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('Compute update recognizes added files', () => testbed.run(() async {
|
|
||||||
fs.directory('build').createSync();
|
|
||||||
final FileHashStore fileCache = FileHashStore(environment);
|
|
||||||
fileCache.initialize();
|
|
||||||
final List<File> inputs = fooTarget.resolveInputs(environment);
|
|
||||||
final Map<String, ChangeType> changes = await fooTarget.computeChanges(inputs, environment, fileCache);
|
|
||||||
fileCache.persist();
|
|
||||||
|
|
||||||
expect(changes, <String, ChangeType>{
|
|
||||||
'/foo.dart': ChangeType.Added
|
|
||||||
});
|
|
||||||
|
|
||||||
await buildSystem.build('foo', environment, const BuildSystemConfig());
|
|
||||||
final Map<String, ChangeType> secondChanges = await fooTarget.computeChanges(inputs, environment, fileCache);
|
|
||||||
|
|
||||||
expect(secondChanges, <String, ChangeType>{});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
group('FileCache', () {
|
|
||||||
Testbed testbed;
|
|
||||||
Environment environment;
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
testbed = Testbed(setup: () {
|
|
||||||
fs.directory('build').createSync();
|
|
||||||
environment = Environment(
|
environment = Environment(
|
||||||
projectDir: fs.currentDirectory,
|
projectDir: fs.currentDirectory,
|
||||||
);
|
);
|
||||||
});
|
fs.file('foo.dart')
|
||||||
});
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('');
|
||||||
test('Initializes file cache', () => testbed.run(() {
|
fs.file('pubspec.yaml').createSync();
|
||||||
final FileHashStore fileCache = FileHashStore(environment);
|
},
|
||||||
fileCache.initialize();
|
overrides: <Type, Generator>{
|
||||||
fileCache.persist();
|
Platform: () => mockPlatform,
|
||||||
|
}
|
||||||
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());
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Target', () {
|
test('Throws exception if asked to build with missing inputs', () => testbed.run(() async {
|
||||||
Testbed testbed;
|
// Delete required input file.
|
||||||
MockPlatform mockPlatform;
|
fs.file('foo.dart').deleteSync();
|
||||||
Environment environment;
|
final BuildResult buildResult = await buildSystem.build(fooTarget, environment);
|
||||||
Target sharedTarget;
|
|
||||||
BuildSystem buildSystem;
|
|
||||||
int shared;
|
|
||||||
|
|
||||||
setUp(() {
|
expect(buildResult.hasException, true);
|
||||||
shared = 0;
|
expect(buildResult.exceptions.values.single.exception, isInstanceOf<MissingInputException>());
|
||||||
Cache.flutterRoot = '';
|
}));
|
||||||
mockPlatform = MockPlatform();
|
|
||||||
// Keep file paths the same.
|
test('Throws exception if it does not produce a specified output', () => testbed.run(() async {
|
||||||
when(mockPlatform.isWindows).thenReturn(false);
|
final Target badTarget = TestTarget((List<File> inputFiles, Environment environment) async {})
|
||||||
when(mockPlatform.isLinux).thenReturn(true);
|
..inputs = const <Source>[
|
||||||
when(mockPlatform.isMacOS).thenReturn(false);
|
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
||||||
testbed = Testbed(
|
]
|
||||||
setup: () {
|
..outputs = const <Source>[
|
||||||
environment = Environment(
|
Source.pattern('{BUILD_DIR}/out')
|
||||||
projectDir: fs.currentDirectory,
|
];
|
||||||
);
|
final BuildResult result = await buildSystem.build(badTarget, environment);
|
||||||
fs.file('foo.dart').createSync(recursive: true);
|
|
||||||
fs.file('pubspec.yaml').createSync();
|
expect(result.hasException, true);
|
||||||
sharedTarget = Target(
|
expect(result.exceptions.values.single.exception, isInstanceOf<MissingOutputException>());
|
||||||
name: 'shared',
|
}));
|
||||||
inputs: const <Source>[
|
|
||||||
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
test('Saves a stamp file with inputs and outputs', () => testbed.run(() async {
|
||||||
],
|
await buildSystem.build(fooTarget, environment);
|
||||||
outputs: const <Source>[],
|
|
||||||
dependencies: <Target>[],
|
final File stampFile = fs.file(fs.path.join(environment.buildDir.path, 'foo.stamp'));
|
||||||
buildAction: (Map<String, ChangeType> updates, Environment environment) {
|
expect(stampFile.existsSync(), true);
|
||||||
shared += 1;
|
|
||||||
}
|
final Map<String, Object> stampContents = json.decode(stampFile.readAsStringSync());
|
||||||
);
|
expect(stampContents['inputs'], <Object>['/foo.dart']);
|
||||||
final Target fooTarget = Target(
|
}));
|
||||||
name: 'foo',
|
|
||||||
inputs: const <Source>[
|
test('Does not re-invoke build if stamp is valid', () => testbed.run(() async {
|
||||||
Source.pattern('{PROJECT_DIR}/foo.dart'),
|
await buildSystem.build(fooTarget, environment);
|
||||||
],
|
await buildSystem.build(fooTarget, environment);
|
||||||
outputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/out'),
|
expect(fooInvocations, 1);
|
||||||
],
|
}));
|
||||||
dependencies: <Target>[sharedTarget],
|
|
||||||
buildAction: (Map<String, ChangeType> updates, Environment environment) {
|
test('Re-invoke build if input is modified', () => testbed.run(() async {
|
||||||
environment
|
await buildSystem.build(fooTarget, environment);
|
||||||
.buildDir
|
|
||||||
.childFile('out')
|
fs.file('foo.dart').writeAsStringSync('new contents');
|
||||||
..createSync(recursive: true)
|
|
||||||
..writeAsStringSync('hey');
|
await buildSystem.build(fooTarget, environment);
|
||||||
}
|
expect(fooInvocations, 2);
|
||||||
);
|
}));
|
||||||
final Target barTarget = Target(
|
|
||||||
name: 'bar',
|
test('does not re-invoke build if input timestamp changes', () => testbed.run(() async {
|
||||||
inputs: const <Source>[
|
await buildSystem.build(fooTarget, environment);
|
||||||
Source.pattern('{BUILD_DIR}/out'),
|
|
||||||
],
|
fs.file('foo.dart').writeAsStringSync('');
|
||||||
outputs: const <Source>[
|
|
||||||
Source.pattern('{BUILD_DIR}/bar'),
|
await buildSystem.build(fooTarget, environment);
|
||||||
],
|
expect(fooInvocations, 1);
|
||||||
dependencies: <Target>[fooTarget, sharedTarget],
|
}));
|
||||||
buildAction: (Map<String, ChangeType> updates, Environment environment) {
|
|
||||||
environment
|
test('does not re-invoke build if output timestamp changes', () => testbed.run(() async {
|
||||||
.buildDir
|
await buildSystem.build(fooTarget, environment);
|
||||||
.childFile('bar')
|
|
||||||
..createSync(recursive: true)
|
environment.buildDir.childFile('out').writeAsStringSync('hey');
|
||||||
..writeAsStringSync('there');
|
|
||||||
}
|
await buildSystem.build(fooTarget, environment);
|
||||||
);
|
expect(fooInvocations, 1);
|
||||||
buildSystem = BuildSystem(<String, Target>{
|
}));
|
||||||
fooTarget.name: fooTarget,
|
|
||||||
barTarget.name: barTarget,
|
|
||||||
sharedTarget.name: sharedTarget,
|
test('Re-invoke build if output is modified', () => testbed.run(() async {
|
||||||
});
|
await buildSystem.build(fooTarget, environment);
|
||||||
},
|
|
||||||
overrides: <Type, Generator>{
|
environment.buildDir.childFile('out').writeAsStringSync('Something different');
|
||||||
Platform: () => mockPlatform,
|
|
||||||
}
|
await buildSystem.build(fooTarget, environment);
|
||||||
);
|
expect(fooInvocations, 2);
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('Runs dependencies of targets', () => testbed.run(() async {
|
||||||
|
barTarget.dependencies.add(fooTarget);
|
||||||
|
|
||||||
|
await buildSystem.build(barTarget, environment);
|
||||||
|
|
||||||
|
expect(fs.file(fs.path.join(environment.buildDir.path, 'bar')).existsSync(), true);
|
||||||
|
expect(fooInvocations, 1);
|
||||||
|
expect(barInvocations, 1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('Only invokes shared dependencies once', () => testbed.run(() async {
|
||||||
|
fooTarget.dependencies.add(sharedTarget);
|
||||||
|
barTarget.dependencies.add(sharedTarget);
|
||||||
|
barTarget.dependencies.add(fooTarget);
|
||||||
|
|
||||||
|
await buildSystem.build(barTarget, environment);
|
||||||
|
|
||||||
|
expect(shared, 1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
test('handles a throwing build action', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem.build(fizzTarget, environment);
|
||||||
|
|
||||||
|
expect(result.hasException, true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('Can describe itself with JSON output', () => testbed.run(() {
|
||||||
|
environment.buildDir.createSync(recursive: true);
|
||||||
|
expect(fooTarget.toJson(environment), <String, dynamic>{
|
||||||
|
'inputs': <Object>[
|
||||||
|
'/foo.dart'
|
||||||
|
],
|
||||||
|
'outputs': <Object>[
|
||||||
|
fs.path.join(environment.buildDir.path, 'out'),
|
||||||
|
],
|
||||||
|
'dependencies': <Object>[],
|
||||||
|
'name': 'foo',
|
||||||
|
'stamp': fs.path.join(environment.buildDir.path, 'foo.stamp'),
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
test('Only invokes shared target once', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('bar', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(shared, 1);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
group('Source', () {
|
|
||||||
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 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>()));
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
test('Can find dependency cycles', () {
|
test('Can find dependency cycles', () {
|
||||||
final Target barTarget = Target(
|
final Target barTarget = TestTarget()..name = 'bar';
|
||||||
name: 'bar',
|
final Target fooTarget = TestTarget()..name = 'foo';
|
||||||
inputs: <Source>[],
|
|
||||||
outputs: <Source>[],
|
|
||||||
buildAction: null,
|
|
||||||
dependencies: nonconst(<Target>[])
|
|
||||||
);
|
|
||||||
final Target fooTarget = Target(
|
|
||||||
name: 'foo',
|
|
||||||
inputs: <Source>[],
|
|
||||||
outputs: <Source>[],
|
|
||||||
buildAction: null,
|
|
||||||
dependencies: nonconst(<Target>[])
|
|
||||||
);
|
|
||||||
barTarget.dependencies.add(fooTarget);
|
barTarget.dependencies.add(fooTarget);
|
||||||
fooTarget.dependencies.add(barTarget);
|
fooTarget.dependencies.add(barTarget);
|
||||||
|
|
||||||
expect(() => checkCycles(barTarget), throwsA(isInstanceOf<CycleException>()));
|
expect(() => checkCycles(barTarget), throwsA(isInstanceOf<CycleException>()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -541,14 +239,23 @@ class MockPlatform extends Mock implements Platform {}
|
|||||||
// Work-around for silly lint check.
|
// Work-around for silly lint check.
|
||||||
T nonconst<T>(T input) => input;
|
T nonconst<T>(T input) => input;
|
||||||
|
|
||||||
class TestBehavior extends SourceBehavior {
|
class TestTarget extends Target {
|
||||||
@override
|
TestTarget([this._build]);
|
||||||
List<File> inputs(Environment environment) {
|
|
||||||
return null;
|
final Future<void> Function(List<File> inputFiles, Environment environment) _build;
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<File> outputs(Environment environment) {
|
Future<void> build(List<File> inputFiles, Environment environment) => _build(inputFiles, environment);
|
||||||
return null;
|
|
||||||
}
|
@override
|
||||||
|
List<Target> dependencies = <Target>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> inputs = <Source>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String name = 'test';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> outputs = <Source>[];
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>[];
|
||||||
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
// 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());
|
||||||
|
}));
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
// 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';
|
import '../../../src/testbed.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('copy_assets', () {
|
const BuildSystem buildSystem = BuildSystem();
|
||||||
Testbed testbed;
|
Environment environment;
|
||||||
BuildSystem buildSystem;
|
Testbed testbed;
|
||||||
Environment environment;
|
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
testbed = Testbed(setup: () {
|
testbed = Testbed(setup: () {
|
||||||
environment = Environment(
|
environment = Environment(
|
||||||
projectDir: fs.currentDirectory,
|
projectDir: fs.currentDirectory,
|
||||||
);
|
);
|
||||||
buildSystem = BuildSystem(<String, Target>{
|
fs.file(fs.path.join('packages', 'flutter_tools', 'lib', 'src',
|
||||||
copyAssets.name: copyAssets,
|
'build_system', 'targets', 'assets.dart'))
|
||||||
});
|
..createSync(recursive: true);
|
||||||
fs.file(fs.path.join('assets', 'foo', 'bar.png'))
|
fs.file(fs.path.join('assets', 'foo', 'bar.png'))
|
||||||
..createSync(recursive: true);
|
..createSync(recursive: true);
|
||||||
fs.file('.packages')
|
fs.file('.packages')
|
||||||
..createSync();
|
..createSync();
|
||||||
fs.file('pubspec.yaml')
|
|
||||||
..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());
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
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')
|
fs.file('pubspec.yaml')
|
||||||
..createSync()
|
..createSync()
|
||||||
..writeAsStringSync('''
|
..writeAsStringSync('''
|
||||||
name: example
|
name: example
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/foo/bar.png
|
||||||
''');
|
''');
|
||||||
await buildSystem.build('copy_assets', environment, const BuildSystemConfig());
|
});
|
||||||
|
|
||||||
// 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);
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}));
|
||||||
|
|
||||||
|
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('''
|
||||||
|
name: example
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
''');
|
||||||
|
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);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
@ -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,221 +23,234 @@ 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;
|
MockXcode mockXcode;
|
||||||
MockXcode mockXcode;
|
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
Cache.disableLocking();
|
Cache.disableLocking();
|
||||||
});
|
});
|
||||||
|
|
||||||
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,
|
||||||
defines: <String, String>{
|
defines: <String, String>{
|
||||||
kBuildMode: getNameForBuildMode(BuildMode.profile),
|
kBuildMode: getNameForBuildMode(BuildMode.profile),
|
||||||
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
|
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
|
||||||
}
|
|
||||||
);
|
|
||||||
iosEnvironment = Environment(
|
|
||||||
projectDir: fs.currentDirectory,
|
|
||||||
defines: <String, String>{
|
|
||||||
kBuildMode: getNameForBuildMode(BuildMode.profile),
|
|
||||||
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
buildSystem = BuildSystem();
|
|
||||||
HostPlatform hostPlatform;
|
|
||||||
if (platform.isWindows) {
|
|
||||||
hostPlatform = HostPlatform.windows_x64;
|
|
||||||
} else if (platform.isLinux) {
|
|
||||||
hostPlatform = HostPlatform.linux_x64;
|
|
||||||
} else if (platform.isMacOS) {
|
|
||||||
hostPlatform = HostPlatform.darwin_x64;
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
}
|
||||||
final String skyEngineLine = platform.isWindows
|
);
|
||||||
? r'sky_engine:file:///C:/bin/cache/pkg/sky_engine/lib/'
|
iosEnvironment = Environment(
|
||||||
: 'sky_engine:file:///bin/cache/pkg/sky_engine/lib/';
|
projectDir: fs.currentDirectory,
|
||||||
fs.file('.packages')
|
defines: <String, String>{
|
||||||
..createSync()
|
kBuildMode: getNameForBuildMode(BuildMode.profile),
|
||||||
..writeAsStringSync('''
|
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
HostPlatform hostPlatform;
|
||||||
|
if (platform.isWindows) {
|
||||||
|
hostPlatform = HostPlatform.windows_x64;
|
||||||
|
} else if (platform.isLinux) {
|
||||||
|
hostPlatform = HostPlatform.linux_x64;
|
||||||
|
} else if (platform.isMacOS) {
|
||||||
|
hostPlatform = HostPlatform.darwin_x64;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
final String skyEngineLine = platform.isWindows
|
||||||
|
? r'sky_engine:file:///C:/bin/cache/pkg/sky_engine/lib/'
|
||||||
|
: 'sky_engine:file:///bin/cache/pkg/sky_engine/lib/';
|
||||||
|
fs.file('.packages')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync('''
|
||||||
# Generated
|
# Generated
|
||||||
$skyEngineLine
|
$skyEngineLine
|
||||||
flutter_tools:lib/''');
|
flutter_tools:lib/''');
|
||||||
final String engineArtifacts = fs.path.join('bin', 'cache',
|
final String engineArtifacts = fs.path.join('bin', 'cache',
|
||||||
'artifacts', 'engine');
|
'artifacts', 'engine');
|
||||||
final List<String> paths = <String>[
|
final List<String> paths = <String>[
|
||||||
fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'lib', 'ui',
|
fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'lib', 'ui',
|
||||||
'ui.dart'),
|
'ui.dart'),
|
||||||
fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'sdk_ext',
|
fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'sdk_ext',
|
||||||
'vmservice_io.dart'),
|
'vmservice_io.dart'),
|
||||||
fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
|
fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
|
||||||
fs.path.join(engineArtifacts, getNameForHostPlatform(hostPlatform),
|
fs.path.join(engineArtifacts, getNameForHostPlatform(hostPlatform),
|
||||||
'frontend_server.dart.snapshot'),
|
'frontend_server.dart.snapshot'),
|
||||||
fs.path.join(engineArtifacts, 'android-arm-profile',
|
fs.path.join(engineArtifacts, 'android-arm-profile',
|
||||||
getNameForHostPlatform(hostPlatform), 'gen_snapshot'),
|
getNameForHostPlatform(hostPlatform), 'gen_snapshot'),
|
||||||
fs.path.join(engineArtifacts, 'ios-profile', 'gen_snapshot'),
|
fs.path.join(engineArtifacts, 'ios-profile', 'gen_snapshot'),
|
||||||
fs.path.join(engineArtifacts, 'common', 'flutter_patched_sdk',
|
fs.path.join(engineArtifacts, 'common', 'flutter_patched_sdk',
|
||||||
'platform_strong.dill'),
|
'platform_strong.dill'),
|
||||||
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'),
|
||||||
for (String path in paths) {
|
fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'ios.dart'),
|
||||||
fs.file(path).createSync(recursive: true);
|
];
|
||||||
}
|
for (String path in paths) {
|
||||||
}, overrides: <Type, Generator>{
|
fs.file(path).createSync(recursive: true);
|
||||||
KernelCompilerFactory: () => FakeKernelCompilerFactory(),
|
}
|
||||||
GenSnapshot: () => FakeGenSnapshot(),
|
}, overrides: <Type, Generator>{
|
||||||
});
|
KernelCompilerFactory: () => FakeKernelCompilerFactory(),
|
||||||
|
GenSnapshot: () => FakeGenSnapshot(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('kernel_snapshot Produces correct output directory', () => testbed.run(() async {
|
||||||
|
await buildSystem.build(const KernelSnapshot(), androidEnvironment);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
final BuildResult result = await buildSystem.build(const KernelSnapshot(),
|
||||||
|
androidEnvironment..defines.remove(kBuildMode));
|
||||||
|
|
||||||
|
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('aot_elf_profile Produces correct output directory', () => testbed.run(() async {
|
||||||
|
await buildSystem.build(const AotElfProfile(), androidEnvironment);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('aot_elf_profile throws error if missing build mode', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem.build(const AotElfProfile(),
|
||||||
|
androidEnvironment..defines.remove(kBuildMode));
|
||||||
|
|
||||||
|
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
test('aot_elf_profile throws error if missing target platform', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem.build(const AotElfProfile(),
|
||||||
|
androidEnvironment..defines.remove(kTargetPlatform));
|
||||||
|
|
||||||
|
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
test('aot_assembly_profile throws error if missing build mode', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(),
|
||||||
|
iosEnvironment..defines.remove(kBuildMode));
|
||||||
|
|
||||||
|
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('aot_assembly_profile throws error if missing target platform', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(),
|
||||||
|
iosEnvironment..defines.remove(kTargetPlatform));
|
||||||
|
|
||||||
|
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('aot_assembly_profile throws error if built for non-iOS platform', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem
|
||||||
|
.build(const AotAssemblyProfile(), androidEnvironment);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
iosEnvironment.defines[kIosArchs] = 'arm64';
|
||||||
|
iosEnvironment.defines[kBitcodeFlag] = 'true';
|
||||||
|
|
||||||
|
final FakeProcessResult fakeProcessResult = FakeProcessResult(
|
||||||
|
stdout: '',
|
||||||
|
stderr: '',
|
||||||
|
);
|
||||||
|
final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
|
||||||
|
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
||||||
|
fs.file(fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
|
||||||
|
.createSync(recursive: true);
|
||||||
|
return fakeProcessResult;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('kernel_snapshot Produces correct output directory', () => testbed.run(() async {
|
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
||||||
await buildSystem.build('kernel_snapshot', androidEnvironment, const BuildSystemConfig());
|
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
||||||
|
when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
||||||
|
|
||||||
expect(fs.file(fs.path.join(androidEnvironment.buildDir.path,'main.app.dill')).existsSync(), true);
|
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
|
||||||
}));
|
|
||||||
|
|
||||||
test('kernel_snapshot throws error if missing build mode', () => testbed.run(() async {
|
expect(result.success, true);
|
||||||
final BuildResult result = await buildSystem.build('kernel_snapshot',
|
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
|
||||||
androidEnvironment..defines.remove(kBuildMode), const BuildSystemConfig());
|
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(1);
|
||||||
|
verify(mockXcode.dsymutil(any)).called(1);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
Xcode: () => mockXcode,
|
||||||
|
}));
|
||||||
|
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (mutli arch)', () => testbed.run(() async {
|
||||||
}));
|
iosEnvironment.defines[kIosArchs] = 'armv7,arm64';
|
||||||
|
iosEnvironment.defines[kBitcodeFlag] = 'true';
|
||||||
|
|
||||||
test('aot_elf_profile Produces correct output directory', () => testbed.run(() async {
|
final FakeProcessResult fakeProcessResult = FakeProcessResult(
|
||||||
await buildSystem.build('aot_elf_profile', androidEnvironment, const BuildSystemConfig());
|
stdout: '',
|
||||||
|
stderr: '',
|
||||||
|
);
|
||||||
|
final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
|
||||||
|
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
||||||
|
fs.file(fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
|
||||||
|
.createSync(recursive: true);
|
||||||
|
return fakeProcessResult;
|
||||||
|
});
|
||||||
|
|
||||||
expect(fs.file(fs.path.join(androidEnvironment.buildDir.path, 'main.app.dill')).existsSync(), true);
|
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
||||||
expect(fs.file(fs.path.join(androidEnvironment.buildDir.path, 'app.so')).existsSync(), true);
|
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
||||||
}));
|
when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
||||||
|
|
||||||
test('aot_elf_profile throws error if missing build mode', () => testbed.run(() async {
|
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
|
||||||
final BuildResult result = await buildSystem.build('aot_elf_profile',
|
|
||||||
androidEnvironment..defines.remove(kBuildMode), const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
expect(result.success, true);
|
||||||
}));
|
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2);
|
||||||
|
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2);
|
||||||
|
verify(mockXcode.dsymutil(any)).called(2);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
Xcode: () => mockXcode,
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
|
||||||
test('aot_elf_profile throws error if missing target platform', () => testbed.run(() async {
|
iosEnvironment.defines[kIosArchs] = 'armv7,arm64';
|
||||||
final BuildResult result = await buildSystem.build('aot_elf_profile',
|
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
||||||
androidEnvironment..defines.remove(kTargetPlatform), const BuildSystemConfig());
|
fs.file(fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
|
||||||
|
.createSync(recursive: true);
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
return FakeProcessResult(
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
test('aot_assembly_profile throws error if missing build mode', () => testbed.run(() async {
|
|
||||||
final BuildResult result = await buildSystem.build('aot_assembly_profile',
|
|
||||||
iosEnvironment..defines.remove(kBuildMode), const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('aot_assembly_profile throws error if missing target platform', () => testbed.run(() async {
|
|
||||||
final BuildResult result = await buildSystem.build('aot_assembly_profile',
|
|
||||||
iosEnvironment..defines.remove(kTargetPlatform), const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<MissingDefineException>());
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('aot_assembly_profile throws error if built for non-iOS platform', () => testbed.run(() async {
|
|
||||||
final BuildResult result = await buildSystem.build('aot_assembly_profile',
|
|
||||||
androidEnvironment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.exceptions.values.single.exception, isInstanceOf<Exception>());
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async {
|
|
||||||
iosEnvironment.defines[kIosArchs] = 'arm64';
|
|
||||||
iosEnvironment.defines[kBitcodeFlag] = 'true';
|
|
||||||
|
|
||||||
final FakeProcessResult fakeProcessResult = FakeProcessResult(
|
|
||||||
stdout: '',
|
stdout: '',
|
||||||
stderr: '',
|
stderr: '',
|
||||||
);
|
);
|
||||||
final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
|
});
|
||||||
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
|
||||||
fs.file(fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
|
|
||||||
.createSync(recursive: true);
|
|
||||||
return fakeProcessResult;
|
|
||||||
});
|
|
||||||
|
|
||||||
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
expect(result.success, true);
|
||||||
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
}, overrides: <Type, Generator>{
|
||||||
when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
ProcessManager: () => mockProcessManager,
|
||||||
|
}));
|
||||||
final BuildResult result = await buildSystem.build('aot_assembly_profile',
|
|
||||||
iosEnvironment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.success, true);
|
|
||||||
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
|
|
||||||
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(1);
|
|
||||||
verify(mockXcode.dsymutil(any)).called(1);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
ProcessManager: () => mockProcessManager,
|
|
||||||
Xcode: () => mockXcode,
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (mutli arch)', () => testbed.run(() async {
|
|
||||||
iosEnvironment.defines[kIosArchs] = 'armv7,arm64';
|
|
||||||
iosEnvironment.defines[kBitcodeFlag] = 'true';
|
|
||||||
|
|
||||||
final FakeProcessResult fakeProcessResult = FakeProcessResult(
|
|
||||||
stdout: '',
|
|
||||||
stderr: '',
|
|
||||||
);
|
|
||||||
final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
|
|
||||||
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
|
||||||
fs.file(fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
|
|
||||||
.createSync(recursive: true);
|
|
||||||
return fakeProcessResult;
|
|
||||||
});
|
|
||||||
|
|
||||||
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
|
||||||
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
|
||||||
when(mockXcode.dsymutil(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
|
|
||||||
|
|
||||||
final BuildResult result = await buildSystem.build('aot_assembly_profile',
|
|
||||||
iosEnvironment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.success, true);
|
|
||||||
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2);
|
|
||||||
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2);
|
|
||||||
verify(mockXcode.dsymutil(any)).called(2);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
ProcessManager: () => mockProcessManager,
|
|
||||||
Xcode: () => mockXcode,
|
|
||||||
}));
|
|
||||||
|
|
||||||
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('aot_assembly_profile',
|
|
||||||
iosEnvironment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(result.success, true);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
ProcessManager: () => mockProcessManager,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockProcessManager extends Mock implements ProcessManager {}
|
class MockProcessManager extends Mock implements ProcessManager {}
|
||||||
|
@ -13,72 +13,72 @@ import '../../../src/common.dart';
|
|||||||
import '../../../src/testbed.dart';
|
import '../../../src/testbed.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('unpack_linux', () {
|
Testbed testbed;
|
||||||
Testbed testbed;
|
const BuildSystem buildSystem = BuildSystem();
|
||||||
BuildSystem buildSystem;
|
Environment environment;
|
||||||
Environment environment;
|
MockPlatform mockPlatform;
|
||||||
MockPlatform mockPlatform;
|
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
Cache.disableLocking();
|
Cache.disableLocking();
|
||||||
});
|
Cache.flutterRoot = '';
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Copies files to correct cache directory', () => testbed.run(() async {
|
|
||||||
final BuildResult result = await buildSystem.build('unpack_linux', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
expect(fs.file('linux/flutter/libflutter_linux.so').statSync().modified, equals(modified));
|
|
||||||
}));
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
await buildSystem.build('unpack_linux', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(fs.file('linux/flutter/libflutter_linux.so').readAsStringSync(), 'asd');
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
}));
|
||||||
|
|
||||||
|
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(theDistantPast));
|
||||||
|
}));
|
||||||
|
|
||||||
|
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(const UnpackLinux(), environment);
|
||||||
|
|
||||||
|
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';
|
|||||||
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,86 +16,87 @@ import '../../../src/common.dart';
|
|||||||
import '../../../src/testbed.dart';
|
import '../../../src/testbed.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('unpack_macos', () {
|
Testbed testbed;
|
||||||
Testbed testbed;
|
const BuildSystem buildSystem = BuildSystem();
|
||||||
BuildSystem buildSystem;
|
Environment environment;
|
||||||
Environment environment;
|
MockPlatform mockPlatform;
|
||||||
MockPlatform mockPlatform;
|
|
||||||
|
|
||||||
setUp(() {
|
setUpAll(() {
|
||||||
mockPlatform = MockPlatform();
|
Cache.disableLocking();
|
||||||
when(mockPlatform.isWindows).thenReturn(false);
|
Cache.flutterRoot = '';
|
||||||
when(mockPlatform.isMacOS).thenReturn(true);
|
|
||||||
when(mockPlatform.isLinux).thenReturn(false);
|
|
||||||
testbed = Testbed(setup: () {
|
|
||||||
environment = Environment(
|
|
||||||
projectDir: fs.currentDirectory,
|
|
||||||
);
|
|
||||||
buildSystem = BuildSystem(<String, Target>{
|
|
||||||
unpackMacos.name: unpackMacos,
|
|
||||||
});
|
|
||||||
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/Headers/FLEOpenGLContextHandling.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEReshapeListener.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEView.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEViewController.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterChannels.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterCodecs.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterMacOS.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h'),
|
|
||||||
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h'),
|
|
||||||
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/info.plist'),
|
|
||||||
];
|
|
||||||
for (File input in inputs) {
|
|
||||||
input.createSync(recursive: true);
|
|
||||||
}
|
|
||||||
when(processManager.runSync(any)).thenAnswer((Invocation invocation) {
|
|
||||||
final List<String> arguments = invocation.positionalArguments.first;
|
|
||||||
final Directory source = fs.directory(arguments[arguments.length - 2]);
|
|
||||||
final Directory target = fs.directory(arguments.last)
|
|
||||||
..createSync(recursive: true);
|
|
||||||
for (FileSystemEntity entity in source.listSync(recursive: true)) {
|
|
||||||
if (entity is File) {
|
|
||||||
final String relative = fs.path.relative(entity.path, from: source.path);
|
|
||||||
final String destination = fs.path.join(target.path, relative);
|
|
||||||
if (!fs.file(destination).parent.existsSync()) {
|
|
||||||
fs.file(destination).parent.createSync();
|
|
||||||
}
|
|
||||||
entity.copySync(destination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FakeProcessResult()..exitCode = 0;
|
|
||||||
});
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
ProcessManager: () => MockProcessManager(),
|
|
||||||
Platform: () => mockPlatform,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Copies files to correct cache directory', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('unpack_macos', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
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/Headers/FLEOpenGLContextHandling.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEReshapeListener.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEView.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEViewController.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterChannels.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterCodecs.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterMacOS.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h').existsSync(), true);
|
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Modules/module.modulemap').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);
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockPlatform = MockPlatform();
|
||||||
|
when(mockPlatform.isWindows).thenReturn(false);
|
||||||
|
when(mockPlatform.isMacOS).thenReturn(true);
|
||||||
|
when(mockPlatform.isLinux).thenReturn(false);
|
||||||
|
testbed = Testbed(setup: () {
|
||||||
|
environment = Environment(
|
||||||
|
projectDir: fs.currentDirectory,
|
||||||
|
);
|
||||||
|
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/Headers/FLEOpenGLContextHandling.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEReshapeListener.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEView.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEViewController.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterChannels.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterCodecs.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterMacOS.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h'),
|
||||||
|
fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h'),
|
||||||
|
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/info.plist'),
|
||||||
|
fs.file('packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
|
||||||
|
];
|
||||||
|
for (File input in inputs) {
|
||||||
|
input.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
when(processManager.run(any)).thenAnswer((Invocation invocation) async {
|
||||||
|
final List<String> arguments = invocation.positionalArguments.first;
|
||||||
|
final Directory source = fs.directory(arguments[arguments.length - 2]);
|
||||||
|
final Directory target = fs.directory(arguments.last)
|
||||||
|
..createSync(recursive: true);
|
||||||
|
for (FileSystemEntity entity in source.listSync(recursive: true)) {
|
||||||
|
if (entity is File) {
|
||||||
|
final String relative = fs.path.relative(entity.path, from: source.path);
|
||||||
|
final String destination = fs.path.join(target.path, relative);
|
||||||
|
if (!fs.file(destination).parent.existsSync()) {
|
||||||
|
fs.file(destination).parent.createSync();
|
||||||
|
}
|
||||||
|
entity.copySync(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FakeProcessResult()..exitCode = 0;
|
||||||
|
});
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => MockProcessManager(),
|
||||||
|
Platform: () => mockPlatform,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Copies files to correct cache directory', () => testbed.run(() async {
|
||||||
|
await buildSystem.build(const UnpackMacOS(), environment);
|
||||||
|
|
||||||
|
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/Headers/FLEOpenGLContextHandling.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEReshapeListener.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEView.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEViewController.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterChannels.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterCodecs.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterMacOS.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h').existsSync(), true);
|
||||||
|
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Modules/module.modulemap').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);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockPlatform extends Mock implements Platform {}
|
class MockPlatform extends Mock implements Platform {}
|
||||||
|
@ -14,84 +14,87 @@ import '../../../src/common.dart';
|
|||||||
import '../../../src/testbed.dart';
|
import '../../../src/testbed.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('unpack_windows', () {
|
Testbed testbed;
|
||||||
Testbed testbed;
|
const BuildSystem buildSystem = BuildSystem();
|
||||||
BuildSystem buildSystem;
|
Environment environment;
|
||||||
Environment environment;
|
Platform platform;
|
||||||
Platform platform;
|
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
Cache.disableLocking();
|
Cache.disableLocking();
|
||||||
});
|
Cache.flutterRoot = '';
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Copies files to correct cache directory', () => testbed.run(() async {
|
|
||||||
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, equals(modified));
|
|
||||||
}));
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
await buildSystem.build('unpack_windows', environment, const BuildSystemConfig());
|
|
||||||
|
|
||||||
expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, isNot(modified));
|
|
||||||
}), skip: true); // TODO(jonahwilliams): track down flakiness.
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
}));
|
||||||
|
|
||||||
|
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(theDistantPast));
|
||||||
|
}));
|
||||||
|
|
||||||
|
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(const UnpackWindows(), environment);
|
||||||
|
|
||||||
|
expect(fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, isNot(modified));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockPlatform extends Mock implements Platform {}
|
class MockPlatform extends Mock implements Platform {}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
@ -15,68 +14,31 @@ import '../../src/common.dart';
|
|||||||
import '../../src/testbed.dart';
|
import '../../src/testbed.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('Assemble', () {
|
Testbed testbed;
|
||||||
Testbed testbed;
|
MockBuildSystem mockBuildSystem;
|
||||||
MockBuildSystem mockBuildSystem;
|
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
Cache.disableLocking();
|
Cache.disableLocking();
|
||||||
});
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
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;
|
|
||||||
|
|
||||||
expect(bufferLogger.statusText.trim(), 'build succeeded');
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockBuildSystem = MockBuildSystem();
|
||||||
|
testbed = Testbed(overrides: <Type, Generator>{
|
||||||
|
BuildSystem: () => mockBuildSystem,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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');
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockBuildSystem extends Mock implements BuildSystem {}
|
class MockBuildSystem extends Mock implements BuildSystem {}
|
||||||
|
Loading…
Reference in New Issue
Block a user