Unverified Commit 01feddbe authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Support for macOS release mode (1 of 3) (#37425)

parent 57f1508b
......@@ -33,18 +33,6 @@ if [[ -n "$FLUTTER_TARGET" ]]; then
target_path="${FLUTTER_TARGET}"
fi
# Set the track widget creation flag.
track_widget_creation_flag=""
if [[ -n "$TRACK_WIDGET_CREATION" ]]; then
track_widget_creation_flag="--track-widget-creation"
fi
# Copy the framework and handle local engine builds.
framework_name="FlutterMacOS.framework"
ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral"
framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/darwin-x64"
flutter_framework="${framework_path}/${framework_name}"
if [[ -n "$FLUTTER_ENGINE" ]]; then
flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
fi
......@@ -63,22 +51,29 @@ if [[ -n "$LOCAL_ENGINE" ]]; then
exit -1
fi
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/${framework_name}"
fi
RunCommand mkdir -p -- "$ephemeral_dir"
RunCommand rm -rf -- "${ephemeral_dir}/${framework_name}"
RunCommand cp -Rp -- "${flutter_framework}" "${ephemeral_dir}"
# Set the build mode
build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
# The path where the input/output xcfilelists are stored. These are used by xcode
# to conditionally skip this script phase if neither have changed.
ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral"
build_inputs_path="${ephemeral_dir}/FlutterInputs.xcfilelist"
build_outputs_path="${ephemeral_dir}/FlutterOutputs.xcfilelist"
# TODO(jonahwilliams): connect AOT rules once engine artifacts are published.
# The build mode is currently hard-coded to debug only. Since this does not yet
# support AOT, we need to ensure that we compile the kernel file in debug so that
# the VM can load it.
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
${verbose_flag} \
build bundle \
--target-platform=darwin-x64 \
--target="${target_path}" \
--${build_mode} \
${track_widget_creation_flag} \
${flutter_engine_flag} \
${local_engine_flag}
${local_engine_flag} \
assemble \
-dTargetPlatform=darwin-x64 \
-dTargetFile="${target_path}" \
-dBuildMode=debug \
--build-inputs="${build_inputs_path}" \
--build-outputs="${build_outputs_path}" \
debug_bundle_flutter_assets
......@@ -485,16 +485,18 @@ class BuildSystem {
// timestamps to track files, this leads to unecessary rebuilds if they
// are included. Once all the places that write these files have been
// tracked down and moved into assemble, these checks should be removable.
// We also remove files under .dart_tool, since these are intermediaries
// and don't need to be tracked by external systems.
{
buildInstance.inputFiles.removeWhere((String path, File file) {
return path.contains('pubspec.yaml') ||
path.contains('.flutter-plugins') ||
path.contains('xcconfig');
return path.contains('.flutter-plugins') ||
path.contains('xcconfig') ||
path.contains('.dart_tool');
});
buildInstance.outputFiles.removeWhere((String path, File file) {
return path.contains('pubspec.yaml') ||
path.contains('.flutter-plugins') ||
path.contains('xcconfig');
return path.contains('.flutter-plugins') ||
path.contains('xcconfig') ||
path.contains('.dart_tool');
});
}
return BuildResult(
......@@ -509,6 +511,7 @@ class BuildSystem {
}
}
/// An active instance of a build.
class _BuildInstance {
_BuildInstance(this.environment, this.fileCache, this.buildSystemConfig)
......
......@@ -64,6 +64,7 @@ class KernelSnapshot extends Target {
@override
List<Source> get inputs => const <Source>[
Source.pattern('{PROJECT_DIR}/.packages'),
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.artifact(Artifact.platformKernelDill),
......
......@@ -2,21 +2,65 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:pool/pool.dart';
import '../../artifacts.dart';
import '../../asset.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../base/process.dart';
import '../../base/process_manager.dart';
import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart';
import '../../macos/cocoapods.dart';
import '../../macos/xcode.dart';
import '../../project.dart';
import '../build_system.dart';
import '../exceptions.dart';
import 'assets.dart';
import 'dart.dart';
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework';
/// The copying logic for flutter assets in macOS.
// TODO(jonahwilliams): remove once build planning lands.
class MacOSAssetBehavior extends SourceBehavior {
const MacOSAssetBehavior();
@override
List<File> inputs(Environment environment) {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
// Filter the file type to remove the files that are generated by this
// command as inputs.
final List<File> results = <File>[];
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
for (DevFSFileContent devFsContent in files) {
results.add(fs.file(devFsContent.file.path));
}
return results;
}
@override
List<File> outputs(Environment environment) {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final String prefix = fs.path.join(flutterProject.macos.ephemeralDirectory.path,
'App.framework', 'flutter_assets');
final List<File> results = <File>[];
for (String key in assetBundle.entries.keys) {
final File file = fs.file(fs.path.join(prefix, key));
results.add(file);
}
return results;
}
}
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
///
/// The shelling out is done to avoid complications with preserving special
......@@ -41,18 +85,21 @@ class UnpackMacOS extends Target {
List<Source> get outputs => const <Source>[
Source.pattern('$_kOutputPrefix/FlutterMacOS'),
// Headers
Source.pattern('$_kOutputPrefix/Headers/FLEViewController.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterDartProject.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterEngine.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterViewController.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterBinaryMessenger.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterChannels.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterCodecs.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterMacOS.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterMacros.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterPluginMacOS.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterPluginRegistrarMacOS.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterMacOS.h'),
// Modules
Source.pattern('$_kOutputPrefix/Modules/module.modulemap'),
// Resources
Source.pattern('$_kOutputPrefix/Resources/icudtl.dat'),
Source.pattern('$_kOutputPrefix/Resources/info.plist'),
Source.pattern('$_kOutputPrefix/Resources/Info.plist'),
// Ignore Versions folder for now
];
......@@ -62,11 +109,9 @@ class UnpackMacOS extends Target {
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework);
final Directory targetDirectory = environment
.projectDir
.childDirectory('macos')
.childDirectory('Flutter')
.childDirectory('ephemeral')
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory targetDirectory = flutterProject.macos
.ephemeralDirectory
.childDirectory('FlutterMacOS.framework');
if (targetDirectory.existsSync()) {
targetDirectory.deleteSync(recursive: true);
......@@ -83,111 +128,151 @@ class UnpackMacOS extends Target {
}
}
/// Tell cocoapods to re-fetch dependencies.
class DebugMacOSPodInstall extends Target {
const DebugMacOSPodInstall();
/// Create an App.framework for debug macOS targets.
///
/// This framework needs to exist for the Xcode project to link/bundle,
/// but it isn't actually executed. To generate something valid, we compile a trivial
/// constant.
class DebugMacOSFramework extends Target {
const DebugMacOSFramework();
@override
String get name => 'debug_macos_framework';
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final File outputFile = fs.file(fs.path.join(
flutterProject.macos.ephemeralDirectory.path, 'App.framework', 'App'));
outputFile.createSync(recursive: true);
final File debugApp = environment.buildDir.childFile('debug_app.cc')
..writeAsStringSync(r'''
static const int Moo = 88;
''');
final RunResult result = await xcode.clang(<String>[
'-x',
'c',
debugApp.path,
'-arch', 'x86_64',
'-dynamiclib',
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
'-install_name', '@rpath/App.framework/App',
'-o', 'macos/Flutter/ephemeral/App.framework/App',
]);
if (result.exitCode != 0) {
throw Exception('Failed to compile debug App.framework');
}
}
@override
String get name => 'debug_macos_pod_install';
List<Target> get dependencies => const <Target>[];
@override
List<Source> get inputs => const <Source>[
Source.artifact(Artifact.flutterMacOSPodspec,
platform: TargetPlatform.darwin_x64,
mode: BuildMode.debug
),
Source.pattern('{PROJECT_DIR}/macos/Podfile', optional: true),
Source.pattern('{PROJECT_DIR}/macos/Runner.xcodeproj/project.pbxproj'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/Flutter-Generated.xcconfig'),
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
];
@override
List<Source> get outputs => const <Source>[
// TODO(jonahwilliams): introduce configuration/planning phase to build.
// No outputs because Cocoapods is fully responsible for tracking. plus there
// is no concept of an optional output. Instead we will need a build config
// phase to conditionally add this rule so that it can be written properly.
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/App'),
];
}
/// Bundle the flutter assets, app.dill, and precompiled runtimes into the App.framework.
class DebugBundleFlutterAssets extends Target {
const DebugBundleFlutterAssets();
@override
List<Target> get dependencies => const <Target>[
UnpackMacOS(),
FlutterPlugins(),
];
String get name => 'debug_bundle_flutter_assets';
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'debug_macos_pod_install');
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory outputDirectory = flutterProject.macos
.ephemeralDirectory.childDirectory('App.framework');
if (!outputDirectory.existsSync()) {
throw Exception('App.framework must exist to bundle assets.');
}
// If there is no podfile do not perform any pods actions.
if (!environment.projectDir.childDirectory('macos')
.childFile('Podfile').existsSync()) {
return;
// Copy assets into asset directory.
final Directory assetDirectory = outputDirectory.childDirectory('flutter_assets');
// We're not smart enough to only remove assets that are removed. If
// anything changes blow away the whole directory.
if (assetDirectory.existsSync()) {
assetDirectory.deleteSync(recursive: true);
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final FlutterProject project = FlutterProject.fromDirectory(environment.projectDir);
final String enginePath = artifacts.getArtifactPath(Artifact.flutterMacOSPodspec,
mode: buildMode, platform: TargetPlatform.darwin_x64);
await cocoaPods.processPods(
xcodeProject: project.macos,
engineDir: enginePath,
isSwift: true,
dependenciesChanged: true,
assetDirectory.createSync();
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
final int result = await assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
}
}
/// Build all of the artifacts for a debug macOS application.
class DebugMacOSApplication extends Target {
const DebugMacOSApplication();
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
final File sourceFile = environment.buildDir.childFile('app.dill');
final File destinationFile = environment.buildDir
.childDirectory('flutter_assets')
.childFile('kernel_blob.bin');
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
if (result != 0) {
throw Exception('Failed to create asset bundle: $result');
}
// Limit number of open files to avoid running out of file descriptors.
try {
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(assetDirectory.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
} finally {
resource.release();
}
}));
} catch (err, st){
throw Exception('Failed to copy assets: $st');
}
// Copy dill file.
try {
final File sourceFile = environment.buildDir.childFile('app.dill');
sourceFile.copySync(assetDirectory.childFile('kernel_blob.bin').path);
} catch (err) {
throw Exception('Failed to copy app.dill: $err');
}
// Copy precompiled runtimes.
try {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData,
platform: TargetPlatform.darwin_x64, mode: BuildMode.debug);
final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData,
platform: TargetPlatform.darwin_x64, mode: BuildMode.debug);
fs.file(vmSnapshotData).copySync(
assetDirectory.childFile('vm_snapshot_data').path);
fs.file(isolateSnapshotData).copySync(
assetDirectory.childFile('isolate_snapshot_data').path);
} catch (err) {
throw Exception('Failed to copy precompiled runtimes: $err');
}
sourceFile.copySync(destinationFile.path);
}
@override
List<Target> get dependencies => const <Target>[
FlutterPlugins(),
UnpackMacOS(),
KernelSnapshot(),
CopyAssets(),
DebugMacOSPodInstall(),
DebugMacOSFramework(),
UnpackMacOS(),
];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/app.dill')
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.behavior(MacOSAssetBehavior()),
Source.pattern('{BUILD_DIR}/app.dill'),
Source.artifact(Artifact.isolateSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
Source.artifact(Artifact.vmSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
];
@override
String get name => 'debug_macos_application';
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/flutter_assets/kernel_blob.bin'),
Source.behavior(MacOSAssetBehavior()),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/AssetManifest.json'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/FontManifest.json'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/LICENSE'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/kernel_blob.bin'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/vm_snapshot_data'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/isolate_snapshot_data'),
];
}
// TODO(jonahwilliams): real AOT implementation.
class ReleaseMacOSApplication extends DebugMacOSApplication {
const ReleaseMacOSApplication();
@override
String get name => 'release_macos_application';
}
class ProfileMacOSApplication extends DebugMacOSApplication {
const ProfileMacOSApplication();
@override
String get name => 'profile_macos_application';
}
......@@ -32,9 +32,8 @@ const List<Target> _kDefaultTargets = <Target>[
AotElfRelease(),
AotAssemblyProfile(),
AotAssemblyRelease(),
DebugMacOSApplication(),
ProfileMacOSApplication(),
ReleaseMacOSApplication(),
DebugMacOSFramework(),
DebugBundleFlutterAssets(),
];
/// Assemble provides a low level API to interact with the flutter tool build
......
......@@ -11,6 +11,9 @@ import 'device.dart';
/// Kills a process on linux or macOS.
Future<bool> killProcess(String executable) async {
if (executable == null) {
return false;
}
final RegExp whitespace = RegExp(r'\s+');
bool succeeded = true;
try {
......
......@@ -141,7 +141,7 @@ class BuildableMacOSApp extends MacOSApp {
return null;
}
final _ExecutableAndId executableAndId = MacOSApp._executableFromBundle(fs.directory(directory));
return executableAndId.executable;
return executableAndId?.executable;
}
}
......
......@@ -36,6 +36,13 @@ Future<void> buildMacOS({
setSymroot: false,
);
await processPodsIfNeeded(flutterProject.macos, getMacOSBuildDirectory(), buildInfo.mode);
// If the xcfilelists do not exist, create empty version.
if (!flutterProject.macos.inputFileList.existsSync()) {
flutterProject.macos.inputFileList.createSync(recursive: true);
}
if (!flutterProject.macos.outputFileList.existsSync()) {
flutterProject.macos.outputFileList.createSync(recursive: true);
}
// Set debug or release mode.
String config = 'Debug';
......
......@@ -2,22 +2,48 @@
// 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/build.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.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/exceptions.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/macos.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/macos/cocoapods.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import '../../../src/common.dart';
import '../../../src/testbed.dart';
const String _kInputPrefix = 'bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework';
const String _kOutputPrefix = 'macos/Flutter/ephemeral/FlutterMacOS.framework';
final List<File> inputs = <File>[
fs.file('$_kInputPrefix/FlutterMacOS'),
// Headers
fs.file('$_kInputPrefix/Headers/FlutterDartProject.h'),
fs.file('$_kInputPrefix/Headers/FlutterEngine.h'),
fs.file('$_kInputPrefix/Headers/FlutterViewController.h'),
fs.file('$_kInputPrefix/Headers/FlutterBinaryMessenger.h'),
fs.file('$_kInputPrefix/Headers/FlutterChannels.h'),
fs.file('$_kInputPrefix/Headers/FlutterCodecs.h'),
fs.file('$_kInputPrefix/Headers/FlutterMacros.h'),
fs.file('$_kInputPrefix/Headers/FlutterPluginMacOS.h'),
fs.file('$_kInputPrefix/Headers/FlutterPluginRegistrarMacOS.h'),
fs.file('$_kInputPrefix/Headers/FlutterMacOS.h'),
// Modules
fs.file('$_kInputPrefix/Modules/module.modulemap'),
// Resources
fs.file('$_kInputPrefix/Resources/icudtl.dat'),
fs.file('$_kInputPrefix/Resources/Info.plist'),
// Ignore Versions folder for now
fs.file('packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
];
void main() {
Testbed testbed;
Environment environment;
......@@ -37,44 +63,11 @@ void main() {
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);
}
defines: <String, String>{
kBuildMode: 'debug',
kTargetPlatform: 'darwin-x64',
}
return FakeProcessResult()..exitCode = 0;
});
);
}, overrides: <Type, Generator>{
ProcessManager: () => MockProcessManager(),
Platform: () => mockPlatform,
......@@ -82,115 +75,69 @@ void main() {
});
test('Copies files to correct cache directory', () => testbed.run(() async {
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;
});
await const UnpackMacOS().build(<File>[], environment);
expect(fs.directory('macos/Flutter/ephemeral/FlutterMacOS.framework').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/FlutterMacOS').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FLEViewController.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterChannels.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterCodecs.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterMacOS.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Modules/module.modulemap').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Resources/icudtl.dat').existsSync(), true);
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Resources/info.plist').existsSync(), true);
expect(fs.directory('$_kOutputPrefix').existsSync(), true);
for (File file in inputs) {
expect(fs.file(file.path.replaceFirst(_kInputPrefix, _kOutputPrefix)).existsSync(), true);
}
}));
test('debug macOS application copies kernel blob', () => testbed.run(() async {
test('debug macOS application fails if App.framework missing', () => testbed.run(() async {
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin');
fs.file(inputKernel)
..createSync(recursive: true)
..writeAsStringSync('testing');
await const DebugMacOSApplication().build(<File>[], environment);
expect(fs.file(outputKernel).readAsStringSync(), 'testing');
}));
test('profile macOS application copies kernel blob', () => testbed.run(() async {
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin');
fs.file(inputKernel)
..createSync(recursive: true)
..writeAsStringSync('testing');
await const ProfileMacOSApplication().build(<File>[], environment);
expect(fs.file(outputKernel).readAsStringSync(), 'testing');
expect(() async => await const DebugBundleFlutterAssets().build(<File>[], environment),
throwsA(isInstanceOf<Exception>()));
}));
test('release macOS application copies kernel blob', () => testbed.run(() async {
test('debug macOS application copies kernel blob', () => testbed.run(() async {
fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'vm_isolate_snapshot.bin')).createSync(recursive: true);
fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'isolate_snapshot.bin')).createSync(recursive: true);
final String frameworkPath = fs.path.join(environment.projectDir.path,
'macos', 'Flutter', 'ephemeral', 'App.framework');
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin');
fs.directory(frameworkPath).createSync(recursive: true);
final String outputKernel = fs.path.join(frameworkPath, 'flutter_assets', 'kernel_blob.bin');
fs.file(inputKernel)
..createSync(recursive: true)
..writeAsStringSync('testing');
await const ReleaseMacOSApplication().build(<File>[], environment);
await const DebugBundleFlutterAssets().build(<File>[], environment);
expect(fs.file(outputKernel).readAsStringSync(), 'testing');
}));
// Changing target names will require a corresponding update in flutter_tools/bin/macos_build_flutter_assets.sh.
test('Target names match those expected by bin scripts', () => testbed.run(() async {
expect(const DebugMacOSApplication().name, 'debug_macos_application');
expect(const ProfileMacOSApplication().name, 'profile_macos_application');
expect(const ReleaseMacOSApplication().name, 'release_macos_application');
}));
test('DebugMacOSPodInstall throws if missing build mode', () => testbed.run(() async {
expect(() => const DebugMacOSPodInstall().build(<File>[], environment),
throwsA(isInstanceOf<MissingDefineException>()));
}));
test('DebugMacOSPodInstall skips if podfile does not exist', () => testbed.run(() async {
await const DebugMacOSPodInstall().build(<File>[], Environment(
projectDir: fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'debug'
}
));
verifyNever(cocoaPods.processPods(
xcodeProject: anyNamed('xcodeProject'),
engineDir: anyNamed('engineDir'),
isSwift: true,
dependenciesChanged: true));
}, overrides: <Type, Generator>{
CocoaPods: () => MockCocoaPods(),
}));
test('DebugMacOSPodInstall invokes processPods with podfile', () => testbed.run(() async {
fs.file(fs.path.join('macos', 'Podfile')).createSync(recursive: true);
await const DebugMacOSPodInstall().build(<File>[], Environment(
projectDir: fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'debug'
}
));
verify(cocoaPods.processPods(
xcodeProject: anyNamed('xcodeProject'),
engineDir: anyNamed('engineDir'),
isSwift: true,
dependenciesChanged: true)).called(1);
}, overrides: <Type, Generator>{
CocoaPods: () => MockCocoaPods(),
}));
test('b', () => testbed.run(() async {
}));
}
class MockPlatform extends Mock implements Platform {}
class MockCocoaPods extends Mock implements CocoaPods {}
class MockProcessManager extends Mock implements ProcessManager {}
class MockGenSnapshot extends Mock implements GenSnapshot {}
class MockXCode extends Mock implements Xcode {}
class FakeProcessResult implements ProcessResult {
@override
int exitCode;
......@@ -204,5 +151,3 @@ class FakeProcessResult implements ProcessResult {
@override
String stdout = '';
}
......@@ -73,7 +73,7 @@ void main() {
return BuildResult(
success: true,
inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()],
outputFiles: <File>[fs.file('bar')]);
outputFiles: <File>[fs.file('bar'), fs.file(fs.path.join('.dart_tool', 'fizz2'))..createSync(recursive: true)]);
});
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment