Support for macOS release mode (1 of 3) (#37425)
This commit is contained in:
@@ -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_pod_install';
|
||||
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
|
||||
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 fails if App.framework missing', () => testbed.run(() async {
|
||||
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
|
||||
fs.file(inputKernel)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('testing');
|
||||
|
||||
expect(() async => await const DebugBundleFlutterAssets().build(<File>[], environment),
|
||||
throwsA(isInstanceOf<Exception>()));
|
||||
}));
|
||||
|
||||
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 DebugMacOSApplication().build(<File>[], environment);
|
||||
await const DebugBundleFlutterAssets().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');
|
||||
}));
|
||||
|
||||
test('release 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 ReleaseMacOSApplication().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']);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user