Unverified Commit 868b0e6d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] Reland migrate xcode_backend.sh to flutter assemble (#50200)

parent 105582ed
...@@ -51,6 +51,10 @@ BuildApp() { ...@@ -51,6 +51,10 @@ BuildApp() {
derived_dir="${project_path}/.ios/Flutter" derived_dir="${project_path}/.ios/Flutter"
fi fi
RunCommand mkdir -p -- "$derived_dir"
AssertExists "$derived_dir"
RunCommand rm -rf -- "${derived_dir}/App.framework"
# Default value of assets_path is flutter_assets # Default value of assets_path is flutter_assets
local assets_path="flutter_assets" local assets_path="flutter_assets"
# The value of assets_path can set by add FLTAssetsPath to AppFrameworkInfo.plist # The value of assets_path can set by add FLTAssetsPath to AppFrameworkInfo.plist
...@@ -96,15 +100,6 @@ BuildApp() { ...@@ -96,15 +100,6 @@ BuildApp() {
fi fi
local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}" local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}"
AssertExists "${framework_path}"
AssertExists "${project_path}"
RunCommand mkdir -p -- "$derived_dir"
AssertExists "$derived_dir"
RunCommand rm -rf -- "${derived_dir}/App.framework"
local flutter_engine_flag="" local flutter_engine_flag=""
local local_engine_flag="" local local_engine_flag=""
local flutter_framework="${framework_path}/Flutter.framework" local flutter_framework="${framework_path}/Flutter.framework"
...@@ -134,9 +129,10 @@ BuildApp() { ...@@ -134,9 +129,10 @@ BuildApp() {
local bitcode_flag="" local bitcode_flag=""
if [[ $ENABLE_BITCODE == "YES" ]]; then if [[ $ENABLE_BITCODE == "YES" ]]; then
bitcode_flag="--bitcode" bitcode_flag="true"
fi fi
# TODO(jonahwilliams): move engine copying to build system.
if [[ -e "${project_path}/.ios" ]]; then if [[ -e "${project_path}/.ios" ]]; then
RunCommand rm -rf -- "${derived_dir}/engine" RunCommand rm -rf -- "${derived_dir}/engine"
mkdir "${derived_dir}/engine" mkdir "${derived_dir}/engine"
...@@ -150,102 +146,29 @@ BuildApp() { ...@@ -150,102 +146,29 @@ BuildApp() {
RunCommand pushd "${project_path}" > /dev/null RunCommand pushd "${project_path}" > /dev/null
AssertExists "${target_path}"
local verbose_flag="" local verbose_flag=""
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
verbose_flag="--verbose" verbose_flag="--verbose"
fi fi
local build_dir="${FLUTTER_BUILD_DIR:-build}"
local track_widget_creation_flag="" local track_widget_creation_flag=""
if [[ -n "$TRACK_WIDGET_CREATION" ]]; then if [[ -n "$TRACK_WIDGET_CREATION" ]]; then
track_widget_creation_flag="--track-widget-creation" track_widget_creation_flag="true"
fi
if [[ "${build_mode}" != "debug" ]]; then
StreamOutput " ├─Building Dart code..."
# Transform ARCHS to comma-separated list of target architectures.
local archs="${ARCHS// /,}"
if [[ $archs =~ .*i386.* || $archs =~ .*x86_64.* ]]; then
EchoError "========================================================================"
EchoError "ERROR: Flutter does not support running in profile or release mode on"
EchoError "the Simulator (this build was: '$build_mode')."
EchoError "You can ensure Flutter runs in Debug mode with your host app in release"
EchoError "mode by setting FLUTTER_BUILD_MODE=debug in the .xcconfig associated"
EchoError "with the ${CONFIGURATION} build configuration."
EchoError "========================================================================"
exit -1
fi
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
${verbose_flag} \
build aot \
--output-dir="${build_dir}/aot" \
--target-platform=ios \
--target="${target_path}" \
--${build_mode} \
--ios-arch="${archs}" \
${flutter_engine_flag} \
${local_engine_flag} \
${bitcode_flag}
if [[ $? -ne 0 ]]; then
EchoError "Failed to build ${project_path}."
exit -1
fi
StreamOutput "done"
local app_framework="${build_dir}/aot/App.framework"
RunCommand cp -r -- "${app_framework}" "${derived_dir}"
else
RunCommand mkdir -p -- "${derived_dir}/App.framework"
# Build stub for all requested architectures.
local arch_flags=""
read -r -a archs <<< "$ARCHS"
for arch in "${archs[@]}"; do
arch_flags="${arch_flags}-arch $arch "
done
RunCommand eval "$(echo "static const int Moo = 88;" | xcrun clang -x c \
${arch_flags} \
-fembed-bitcode-marker \
-dynamiclib \
-Xlinker -rpath -Xlinker '@executable_path/Frameworks' \
-Xlinker -rpath -Xlinker '@loader_path/Frameworks' \
-install_name '@rpath/App.framework/App' \
-o "${derived_dir}/App.framework/App" -)"
fi
local plistPath="${project_path}/ios/Flutter/AppFrameworkInfo.plist"
if [[ -e "${project_path}/.ios" ]]; then
plistPath="${project_path}/.ios/Flutter/AppFrameworkInfo.plist"
fi
RunCommand cp -- "$plistPath" "${derived_dir}/App.framework/Info.plist"
local precompilation_flag=""
if [[ "$CURRENT_ARCH" != "x86_64" ]] && [[ "$build_mode" != "debug" ]]; then
precompilation_flag="--precompiled"
fi fi
StreamOutput " ├─Assembling Flutter resources..." RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
RunCommand "${FLUTTER_ROOT}/bin/flutter" \ ${verbose_flag} \
${verbose_flag} \ ${flutter_engine_flag} \
build bundle \ ${local_engine_flag} \
--target-platform=ios \ assemble \
--target="${target_path}" \ --output="${derived_dir}/" \
--${build_mode} \ -dTargetPlatform=ios \
--depfile="${build_dir}/snapshot_blob.bin.d" \ -dTargetFile="${target_path}" \
--asset-dir="${derived_dir}/App.framework/${assets_path}" \ -dBuildMode=${build_mode} \
${precompilation_flag} \ -dIosArchs="${ARCHS}" \
${flutter_engine_flag} \ -dTrackWidgetCreation="${track_widget_creation_flag}" \
${local_engine_flag} \ -dEnableBitcode="${bitcode_flag}" \
${track_widget_creation_flag} "${build_mode}_ios_bundle_flutter_assets"
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
EchoError "Failed to package ${project_path}." EchoError "Failed to package ${project_path}."
......
...@@ -12,14 +12,9 @@ import 'base/io.dart'; ...@@ -12,14 +12,9 @@ import 'base/io.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/process.dart'; import 'base/process.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'build_system/build_system.dart';
import 'build_system/targets/dart.dart';
import 'build_system/targets/ios.dart';
import 'cache.dart';
import 'dart/package_map.dart'; import 'dart/package_map.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'ios/bitcode.dart'; import 'ios/bitcode.dart';
import 'project.dart';
/// Builds AOT snapshots given a platform, build mode and a path to a Dart /// Builds AOT snapshots given a platform, build mode and a path to a Dart
/// library. /// library.
...@@ -42,21 +37,6 @@ class AotBuilder { ...@@ -42,21 +37,6 @@ class AotBuilder {
throwToolExit('No AOT build platform specified'); throwToolExit('No AOT build platform specified');
} }
if (_canUseAssemble(platform)
&& extraGenSnapshotOptions?.isEmpty != false
&& extraFrontEndOptions?.isEmpty != false) {
await _buildWithAssemble(
targetFile: mainDartFile,
outputDir: outputPath,
targetPlatform: platform,
buildMode: buildMode,
quiet: quiet,
iosArchs: iosBuildArchs ?? defaultIOSArchs,
bitcode: bitcode ?? kBitcodeEnabledDefault,
);
return;
}
if (bitcode) { if (bitcode) {
if (platform != TargetPlatform.ios) { if (platform != TargetPlatform.ios) {
throwToolExit('Bitcode is only supported on iOS (TargetPlatform is $platform).'); throwToolExit('Bitcode is only supported on iOS (TargetPlatform is $platform).');
...@@ -174,81 +154,4 @@ class AotBuilder { ...@@ -174,81 +154,4 @@ class AotBuilder {
} }
return; return;
} }
bool _canUseAssemble(TargetPlatform targetPlatform) {
switch (targetPlatform) {
case TargetPlatform.ios:
return true;
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x86:
case TargetPlatform.darwin_x64:
case TargetPlatform.android_x64:
case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64:
case TargetPlatform.tester:
case TargetPlatform.web_javascript:
default:
return false;
}
}
Future<void> _buildWithAssemble({
TargetPlatform targetPlatform,
BuildMode buildMode,
String targetFile,
String outputDir,
bool quiet,
Iterable<DarwinArch> iosArchs,
bool bitcode,
}) async {
Status status;
if (!quiet) {
final String typeName = globals.artifacts.getEngineType(targetPlatform, buildMode);
status = globals.logger.startProgress(
'Building AOT snapshot in ${getFriendlyModeName(buildMode)} mode ($typeName)...',
timeout: timeoutConfiguration.slowOperation,
);
}
final FlutterProject flutterProject = FlutterProject.current();
final Target target = buildMode == BuildMode.profile
? const AotAssemblyProfile()
: const AotAssemblyRelease();
final BuildResult result = await buildSystem.build(target, Environment(
projectDir: flutterProject.directory,
cacheDir: globals.cache.getRoot(),
flutterRootDir: globals.fs.directory(Cache.flutterRoot),
outputDir: globals.fs.directory(outputDir),
buildDir: flutterProject.directory
.childDirectory('.dart_tool')
.childDirectory('flutter_build'),
defines: <String, String>{
kBuildMode: getNameForBuildMode(buildMode),
kTargetPlatform: getNameForTargetPlatform(targetPlatform),
kTargetFile: targetFile,
kIosArchs: iosArchs.map(getNameForDarwinArch).join(','),
kBitcodeFlag: bitcode.toString()
}
));
status?.stop();
if (!result.success) {
for (final ExceptionMeasurement measurement in result.exceptions.values) {
globals.printError('Target ${measurement.target} failed: ${measurement.exception}',
stackTrace: measurement.fatal
? measurement.stackTrace
: null,
);
}
throwToolExit('Failed to build aot.');
}
final String builtMessage = 'Built to $outputDir${globals.fs.path.separator}.';
if (quiet) {
globals.printTrace(builtMessage);
} else {
globals.printStatus(builtMessage);
}
}
} }
...@@ -385,6 +385,8 @@ DarwinArch getIOSArchForName(String arch) { ...@@ -385,6 +385,8 @@ DarwinArch getIOSArchForName(String arch) {
return DarwinArch.armv7; return DarwinArch.armv7;
case 'arm64': case 'arm64':
return DarwinArch.arm64; return DarwinArch.arm64;
case 'x86_64':
return DarwinArch.x86_64;
} }
assert(false); assert(false);
return null; return null;
......
...@@ -11,8 +11,11 @@ import '../../base/process.dart'; ...@@ -11,8 +11,11 @@ import '../../base/process.dart';
import '../../build_info.dart'; import '../../build_info.dart';
import '../../globals.dart' as globals; import '../../globals.dart' as globals;
import '../../macos/xcode.dart'; import '../../macos/xcode.dart';
import '../../project.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart'; import '../exceptions.dart';
import 'assets.dart';
import 'dart.dart'; import 'dart.dart';
/// Supports compiling a dart kernel file to an assembly file. /// Supports compiling a dart kernel file to an assembly file.
...@@ -25,7 +28,7 @@ abstract class AotAssemblyBase extends Target { ...@@ -25,7 +28,7 @@ abstract class AotAssemblyBase extends Target {
@override @override
Future<void> build(Environment environment) async { Future<void> build(Environment environment) async {
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false); final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false);
final String buildOutputPath = environment.outputDir.path; final String buildOutputPath = environment.buildDir.path;
if (environment.defines[kBuildMode] == null) { if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'aot_assembly'); throw MissingDefineException(kBuildMode, 'aot_assembly');
} }
...@@ -35,8 +38,11 @@ abstract class AotAssemblyBase extends Target { ...@@ -35,8 +38,11 @@ abstract class AotAssemblyBase extends Target {
final bool bitcode = environment.defines[kBitcodeFlag] == 'true'; final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]); final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
final List<DarwinArch> iosArchs = environment.defines[kIosArchs]?.split(',')?.map(getIOSArchForName)?.toList() final List<DarwinArch> iosArchs = environment.defines[kIosArchs]
?? <DarwinArch>[DarwinArch.arm64]; ?.split(' ')
?.map(getIOSArchForName)
?.toList()
?? <DarwinArch>[DarwinArch.arm64];
if (targetPlatform != TargetPlatform.ios) { if (targetPlatform != TargetPlatform.ios) {
throw Exception('aot_assembly is only supported for iOS applications'); throw Exception('aot_assembly is only supported for iOS applications');
} }
...@@ -60,7 +66,7 @@ abstract class AotAssemblyBase extends Target { ...@@ -60,7 +66,7 @@ abstract class AotAssemblyBase extends Target {
if (results.any((int result) => result != 0)) { if (results.any((int result) => result != 0)) {
throw Exception('AOT snapshotter exited with code ${results.join()}'); throw Exception('AOT snapshotter exited with code ${results.join()}');
} }
final String resultPath = globals.fs.path.join(environment.outputDir.path, 'App.framework', 'App'); final String resultPath = globals.fs.path.join(environment.buildDir.path, 'App.framework', 'App');
globals.fs.directory(resultPath).parent.createSync(recursive: true); globals.fs.directory(resultPath).parent.createSync(recursive: true);
final ProcessResult result = await globals.processManager.run(<String>[ final ProcessResult result = await globals.processManager.run(<String>[
'lipo', 'lipo',
...@@ -145,6 +151,202 @@ class AotAssemblyProfile extends AotAssemblyBase { ...@@ -145,6 +151,202 @@ class AotAssemblyProfile extends AotAssemblyBase {
]; ];
} }
/// Create a trivial App.framework file for debug iOS builds.
class DebugUniveralFramework extends Target {
const DebugUniveralFramework();
@override
String get name => 'debug_universal_framework';
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart'),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/App')
];
@override
Future<void> build(Environment environment) async {
// Generate a trivial App.framework.
final File iphoneFile = environment.buildDir.childFile('iphone_framework');
final File simulatorFile = environment.buildDir.childFile('simulator_framework');
final File lipoOutputFile = environment.buildDir.childFile('App');
final RunResult iphoneResult = await createStubAppFramework(
iphoneFile,
SdkType.iPhone,
);
final RunResult simulatorResult = await createStubAppFramework(
simulatorFile,
SdkType.iPhoneSimulator,
);
if (iphoneResult.exitCode != 0 || simulatorResult.exitCode != 0) {
throw Exception('Failed to create App.framework.');
}
final List<String> lipoCommand = <String>[
'xcrun',
'lipo',
'-create',
iphoneFile.path,
simulatorFile.path,
'-output',
lipoOutputFile.path
];
final RunResult lipoResult = await processUtils.run(
lipoCommand,
);
if (lipoResult.exitCode != 0) {
throw Exception('Failed to create App.framework.');
}
}
}
/// The base class for all iOS bundle targets.
///
/// This is responsible for setting up the basic App.framework structure, including:
/// * Copying the app.dill/kernel_blob.bin from the build directory to assets (debug)
/// * Copying the precompiled isolate/vm data from the engine (debug)
/// * Copying the flutter assets to App.framework/flutter_assets
/// * Copying either the stub or real App assembly file to App.framework/App
abstract class IosAssetBundle extends Target {
const IosAssetBundle();
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/App'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/App.framework/App'),
Source.pattern('{OUTPUT_DIR}/App.framework/Info.plist')
];
@override
List<String> get depfiles => <String>[
'flutter_assets.d',
];
@override
Future<void> build(Environment environment) async {
if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets');
frameworkDirectory.createSync(recursive: true);
assetDirectory.createSync();
// Only copy the prebuilt runtimes and kernel blob in debug mode.
if (buildMode == BuildMode.debug) {
// Copy the App.framework to the output directory.
environment.buildDir.childFile('App')
.copySync(frameworkDirectory.childFile('App').path);
final String vmSnapshotData = globals.artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: BuildMode.debug);
final String isolateSnapshotData = globals.artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: BuildMode.debug);
environment.buildDir.childFile('app.dill')
.copySync(assetDirectory.childFile('kernel_blob.bin').path);
globals.fs.file(vmSnapshotData)
.copySync(assetDirectory.childFile('vm_snapshot_data').path);
globals.fs.file(isolateSnapshotData)
.copySync(assetDirectory.childFile('isolate_snapshot_data').path);
} else {
environment.buildDir.childDirectory('App.framework').childFile('App')
.copySync(frameworkDirectory.childFile('App').path);
}
// Copy the assets.
final Depfile assetDepfile = await copyAssets(environment, assetDirectory);
assetDepfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
// Copy the plist from either the project or module.
// TODO(jonahwilliams): add plist to inputs
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory plistRoot = flutterProject.isModule
? flutterProject.ios.ephemeralDirectory
: environment.projectDir.childDirectory('ios');
plistRoot
.childDirectory('Flutter')
.childFile('AppFrameworkInfo.plist')
.copySync(environment.outputDir
.childDirectory('App.framework')
.childFile('Info.plist').path);
}
}
/// Build a debug iOS application bundle.
class DebugIosApplicationBundle extends IosAssetBundle {
const DebugIosApplicationBundle();
@override
String get name => 'debug_ios_bundle_flutter_assets';
@override
List<Source> get inputs => <Source>[
const Source.artifact(Artifact.vmSnapshotData, mode: BuildMode.debug),
const Source.artifact(Artifact.isolateSnapshotData, mode: BuildMode.debug),
const Source.pattern('{BUILD_DIR}/app.dill'),
...super.inputs,
];
@override
List<Source> get outputs => <Source>[
const Source.pattern('{OUTPUT_DIR}/App.framework/flutter_assets/vm_snapshot_data'),
const Source.pattern('{OUTPUT_DIR}/App.framework/flutter_assets/isolate_snapshot_data'),
const Source.pattern('{OUTPUT_DIR}/App.framework/flutter_assets/kernel_blob.bin'),
...super.outputs,
];
@override
List<Target> get dependencies => <Target>[
const DebugUniveralFramework(),
...super.dependencies,
];
}
/// Build a profile iOS application bundle.
class ProfileIosApplicationBundle extends IosAssetBundle {
const ProfileIosApplicationBundle();
@override
String get name => 'profile_ios_bundle_flutter_assets';
@override
List<Target> get dependencies => const <Target>[
AotAssemblyProfile(),
];
}
/// Build a release iOS application bundle.
class ReleaseIosApplicationBundle extends IosAssetBundle {
const ReleaseIosApplicationBundle();
@override
String get name => 'release_ios_bundle_flutter_assets';
@override
List<Target> get dependencies => const <Target>[
AotAssemblyRelease(),
];
}
/// Create an App.framework for debug iOS targets. /// Create an App.framework for debug iOS targets.
/// ///
/// This framework needs to exist for the Xcode project to link/bundle, /// This framework needs to exist for the Xcode project to link/bundle,
......
...@@ -50,6 +50,9 @@ const List<Target> _kDefaultTargets = <Target>[ ...@@ -50,6 +50,9 @@ const List<Target> _kDefaultTargets = <Target>[
androidArmReleaseBundle, androidArmReleaseBundle,
androidArm64ReleaseBundle, androidArm64ReleaseBundle,
androidx64ReleaseBundle, androidx64ReleaseBundle,
DebugIosApplicationBundle(),
ProfileIosApplicationBundle(),
ReleaseIosApplicationBundle(),
]; ];
/// 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
......
...@@ -315,7 +315,7 @@ flutter_tools:lib/'''); ...@@ -315,7 +315,7 @@ flutter_tools:lib/''');
})); }));
test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async { test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] ='armv7,arm64'; iosEnvironment.defines[kIosArchs] ='armv7 arm64';
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App')) globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true); .createSync(recursive: true);
...@@ -360,7 +360,7 @@ flutter_tools:lib/'''); ...@@ -360,7 +360,7 @@ flutter_tools:lib/''');
})); }));
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (mutli arch)', () => testbed.run(() async { test('aot_assembly_profile with bitcode sends correct argument to snapshotter (mutli arch)', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] = 'armv7,arm64'; iosEnvironment.defines[kIosArchs] = 'armv7 arm64';
iosEnvironment.defines[kBitcodeFlag] = 'true'; iosEnvironment.defines[kBitcodeFlag] = 'true';
final FakeProcessResult fakeProcessResult = FakeProcessResult( final FakeProcessResult fakeProcessResult = FakeProcessResult(
...@@ -377,9 +377,8 @@ flutter_tools:lib/'''); ...@@ -377,9 +377,8 @@ flutter_tools:lib/''');
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment); await const AotAssemblyProfile().build(iosEnvironment);
expect(result.success, true);
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2); verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2);
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2); verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
...@@ -388,7 +387,7 @@ flutter_tools:lib/'''); ...@@ -388,7 +387,7 @@ flutter_tools:lib/''');
})); }));
test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async { test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] = 'armv7,arm64'; iosEnvironment.defines[kIosArchs] = 'armv7 arm64';
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App')) globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true); .createSync(recursive: true);
......
// Copyright 2014 The Flutter 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:file_testing/file_testing.dart';
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/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/ios.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:mockito/mockito.dart';
import '../../../src/common.dart';
import '../../../src/fake_process_manager.dart';
import '../../../src/testbed.dart';
const List<String> _kSharedConfig = <String>[
'-dynamiclib',
'-fembed-bitcode-marker',
'-Xlinker',
'-rpath',
'-Xlinker',
'@executable_path/Frameworks',
'-Xlinker',
'-rpath',
'-Xlinker',
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
'-isysroot',
];
void main() {
Testbed testbed;
Environment environment;
ProcessManager processManager;
setUp(() {
testbed = Testbed(setup: () {
environment = Environment.test(globals.fs.currentDirectory, defines: <String, String>{
kTargetPlatform: 'ios',
});
});
});
test('DebugUniveralFramework creates expected binary', () => testbed.run(() async {
processManager = FakeProcessManager.list(<FakeCommand>[
// Create iphone stub.
const FakeCommand(command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path']),
FakeCommand(command: <String>[
'xcrun',
'clang',
'-x',
'c',
// iphone gets both arm arches
'-arch',
'armv7',
'-arch',
'arm64',
globals.fs.path.absolute(globals.fs.path.join('.tmp_rand0', 'flutter_tools_stub_source.rand0', 'debug_app.cc')),
..._kSharedConfig,
'',
'-o',
environment.buildDir.childFile('iphone_framework').path
]),
// Create simulator stub.
const FakeCommand(command: <String>['xcrun', '--sdk', 'iphonesimulator', '--show-sdk-path']),
FakeCommand(command: <String>[
'xcrun',
'clang',
'-x',
'c',
// Simulator only as x86_64 arch
'-arch',
'x86_64',
globals.fs.path.absolute(globals.fs.path.join('.tmp_rand0', 'flutter_tools_stub_source.rand0', 'debug_app.cc')),
..._kSharedConfig,
'',
'-o',
environment.buildDir.childFile('simulator_framework').path
]),
// Lipo stubs together.
FakeCommand(command: <String>[
'xcrun',
'lipo',
'-create',
environment.buildDir.childFile('iphone_framework').path,
environment.buildDir.childFile('simulator_framework').path,
'-output',
environment.buildDir.childFile('App').path,
]),
]);
await const DebugUniveralFramework().build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
test('DebugIosApplicationBundle', () => testbed.run(() async {
environment.defines[kBuildMode] = 'debug';
// Precompiled dart data
when(globals.artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: BuildMode.debug))
.thenReturn('vm_snapshot_data');
when(globals.artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: BuildMode.debug))
.thenReturn('isolate_snapshot_data');
globals.fs.file('vm_snapshot_data').createSync();
globals.fs.file('isolate_snapshot_data').createSync();
// Project info
globals.fs.file('pubspec.yaml').writeAsStringSync('name: hello');
globals.fs.file('.packages').writeAsStringSync('\n');
// Plist file
globals.fs.file(globals.fs.path.join('ios', 'Flutter', 'AppFrameworkInfo.plist'))
..createSync(recursive: true);
// App kernel
environment.buildDir.childFile('app.dill').createSync(recursive: true);
// Stub framework
environment.buildDir.childFile('App').createSync();
await const DebugIosApplicationBundle().build(environment);
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
expect(frameworkDirectory.childFile('App'), exists);
expect(frameworkDirectory.childFile('Info.plist'), exists);
final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets');
expect(assetDirectory.childFile('kernel_blob.bin'), exists);
expect(assetDirectory.childFile('AssetManifest.json'), exists);
expect(assetDirectory.childFile('vm_snapshot_data'), exists);
expect(assetDirectory.childFile('isolate_snapshot_data'), exists);
}, overrides: <Type, Generator>{
Artifacts: () => MockArtifacts(),
}));
test('ReleaseIosApplicationBundle', () => testbed.run(() async {
environment.defines[kBuildMode] = 'release';
// Project info
globals.fs.file('pubspec.yaml').writeAsStringSync('name: hello');
globals.fs.file('.packages').writeAsStringSync('\n');
// Plist file
globals.fs.file(globals.fs.path.join('ios', 'Flutter', 'AppFrameworkInfo.plist'))
..createSync(recursive: true);
// Real framework
environment.buildDir
.childDirectory('App.framework')
.childFile('App')
.createSync(recursive: true);
await const ReleaseIosApplicationBundle().build(environment);
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
expect(frameworkDirectory.childFile('App'), exists);
expect(frameworkDirectory.childFile('Info.plist'), exists);
final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets');
expect(assetDirectory.childFile('kernel_blob.bin'), isNot(exists));
expect(assetDirectory.childFile('AssetManifest.json'), exists);
expect(assetDirectory.childFile('vm_snapshot_data'), isNot(exists));
expect(assetDirectory.childFile('isolate_snapshot_data'), isNot(exists));
}));
}
class MockArtifacts extends Mock implements Artifacts {}
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