Unverified Commit 35afe1bd authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Stop embedding bitcode for iOS in tool (#112831)

parent 8c0aa6c6
......@@ -171,7 +171,6 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
);
await _checkDylib(appFrameworkPath);
await _checkBitcode(appFrameworkPath, mode);
final String aotSymbols = await _dylibSymbols(appFrameworkPath);
......@@ -228,15 +227,14 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
section("Check all modes' engine dylib");
for (final String mode in <String>['Debug', 'Profile', 'Release']) {
final String engineBinary = path.join(
checkFileExists(path.join(
outputPath,
mode,
'Flutter.xcframework',
'ios-arm64',
'Flutter.framework',
'Flutter',
);
await _checkBitcode(engineBinary, mode);
));
checkFileExists(path.join(
outputPath,
......@@ -836,15 +834,6 @@ Future<void> _checkStatic(String pathToLibrary) async {
}
}
Future<void> _checkBitcode(String frameworkPath, String mode) async {
checkFileExists(frameworkPath);
// Bitcode only needed in Release mode for archiving.
if (mode == 'Release' && !await containsBitcode(frameworkPath)) {
throw TaskResult.failure('$frameworkPath does not contain bitcode');
}
}
Future<String> _dylibSymbols(String pathToDylib) {
return eval('nm', <String>[
'-g',
......
......@@ -47,43 +47,6 @@ Future<String?> minPhoneOSVersion(String pathToBinary) async {
return minVersion;
}
Future<bool> containsBitcode(String pathToBinary) async {
// See: https://stackoverflow.com/questions/32755775/how-to-check-a-static-library-is-built-contain-bitcode
final String loadCommands = await eval('otool', <String>[
'-l',
'-arch',
'arm64',
pathToBinary,
]);
if (!loadCommands.contains('__LLVM')) {
return false;
}
// Presence of the section may mean a bitcode marker was embedded (size=1), but there is no content.
if (!loadCommands.contains('size 0x0000000000000001')) {
return true;
}
// Check the false positives: size=1 wasn't referencing the __LLVM section.
bool emptyBitcodeMarkerFound = false;
// Section
// sectname __bundle
// segname __LLVM
// addr 0x003c4000
// size 0x0042b633
// offset 3932160
// ...
final List<String> lines = LineSplitter.split(loadCommands).toList();
lines.asMap().forEach((int index, String line) {
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
emptyBitcodeMarkerFound |= lines
.skip(index - 1)
.take(4)
.any((String line) => line.contains(' size 0x0000000000000001'));
}
});
return !emptyBitcodeMarkerFound;
}
/// Creates and boots a new simulator, passes the new simulator's identifier to
/// `testFunction`.
///
......
......@@ -65,6 +65,9 @@ def flutter_additional_ios_build_settings(target)
# Skip other updates if it's not a Flutter plugin (transitive dependency).
next unless target.dependencies.any? { |dependency| dependency.name == 'Flutter' }
# Bitcode is deprecated, Flutter.framework bitcode blob will have been stripped.
build_configuration.build_settings['ENABLE_BITCODE'] = 'NO'
# Profile can't be derived from the CocoaPods build configuration. Use release framework (for linking only).
configuration_engine_dir = build_configuration.type == :debug ? debug_framework_dir : release_framework_dir
Dir.new(configuration_engine_dir).each_child do |xcframework_file|
......
......@@ -333,11 +333,6 @@ class Context {
);
}
String bitcodeFlag = '';
if (environment['ENABLE_BITCODE'] == 'YES' && environment['ACTION'] == 'install') {
bitcodeFlag = 'true';
}
final List<String> flutterArgs = <String>[];
if (verbose) {
......@@ -365,7 +360,6 @@ class Context {
'-dTreeShakeIcons=${environment['TREE_SHAKE_ICONS'] ?? ''}',
'-dTrackWidgetCreation=${environment['TRACK_WIDGET_CREATION'] ?? ''}',
'-dDartObfuscation=${environment['DART_OBFUSCATION'] ?? ''}',
'-dEnableBitcode=$bitcodeFlag',
'-dAction=${environment['ACTION'] ?? ''}',
'--ExtraGenSnapshotOptions=${environment['EXTRA_GEN_SNAPSHOT_OPTIONS'] ?? ''}',
'--DartDefines=${environment['DART_DEFINES'] ?? ''}',
......
......@@ -116,16 +116,11 @@ class AOTSnapshotter {
DarwinArch? darwinArch,
String? sdkRoot,
List<String> extraGenSnapshotOptions = const <String>[],
required bool bitcode,
String? splitDebugInfo,
required bool dartObfuscation,
bool quiet = false,
}) async {
assert(platform != TargetPlatform.ios || darwinArch != null);
if (bitcode && platform != TargetPlatform.ios) {
_logger.printError('Bitcode is only supported for iOS.');
return 1;
}
if (!_isValidAotPlatform(platform, buildMode)) {
_logger.printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.');
......@@ -244,7 +239,6 @@ class AOTSnapshotter {
sdkRoot: sdkRoot,
assemblyPath: assembly,
outputPath: outputDir.path,
bitcode: bitcode,
quiet: quiet,
stripAfterBuild: stripAfterBuild,
extractAppleDebugSymbols: extractAppleDebugSymbols
......@@ -262,7 +256,6 @@ class AOTSnapshotter {
String? sdkRoot,
required String assemblyPath,
required String outputPath,
required bool bitcode,
required bool quiet,
required bool stripAfterBuild,
required bool extractAppleDebugSymbols
......@@ -286,12 +279,10 @@ class AOTSnapshotter {
],
];
const String embedBitcodeArg = '-fembed-bitcode';
final String assemblyO = _fileSystem.path.join(outputPath, 'snapshot_assembly.o');
final RunResult compileResult = await _xcode.cc(<String>[
...commonBuildOptions,
if (bitcode) embedBitcodeArg,
'-c',
assemblyPath,
'-o',
......@@ -311,7 +302,6 @@ class AOTSnapshotter {
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
'-install_name', '@rpath/App.framework/App',
if (bitcode) embedBitcodeArg,
'-o', appLib,
assemblyO,
];
......
......@@ -942,9 +942,6 @@ const String kTargetPlatform = 'TargetPlatform';
/// The define to control what target file is used.
const String kTargetFile = 'TargetFile';
/// The define to control whether the AOT snapshot is built with bitcode.
const String kBitcodeFlag = 'EnableBitcode';
/// Whether to enable or disable track widget creation.
const String kTrackWidgetCreation = 'TrackWidgetCreation';
......
......@@ -250,7 +250,6 @@ class AndroidAot extends AotElfBase {
buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path,
outputPath: output.path,
bitcode: false,
extraGenSnapshotOptions: extraGenSnapshotOptions,
splitDebugInfo: splitDebugInfo,
dartObfuscation: dartObfuscation,
......
......@@ -296,7 +296,6 @@ abstract class AotElfBase extends Target {
buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path,
outputPath: outputPath,
bitcode: false,
extraGenSnapshotOptions: extraGenSnapshotOptions,
splitDebugInfo: splitDebugInfo,
dartObfuscation: dartObfuscation,
......
......@@ -56,7 +56,6 @@ abstract class AotAssemblyBase extends Target {
}
final List<String> extraGenSnapshotOptions = decodeCommaSeparated(environment.defines, kExtraGenSnapshotOptions);
final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
final BuildMode buildMode = getBuildModeForName(environmentBuildMode);
final TargetPlatform targetPlatform = getTargetPlatformForName(environmentTargetPlatform);
final String? splitDebugInfo = environment.defines[kSplitDebugInfo];
......@@ -101,7 +100,6 @@ abstract class AotAssemblyBase extends Target {
outputPath: environment.fileSystem.path.join(buildOutputPath, getNameForDarwinArch(darwinArch)),
darwinArch: darwinArch,
sdkRoot: sdkRoot,
bitcode: bitcode,
quiet: true,
splitDebugInfo: splitDebugInfo,
dartObfuscation: dartObfuscation,
......@@ -287,9 +285,6 @@ abstract class UnpackIOS extends Target {
if (archs == null) {
throw MissingDefineException(kIosArchs, name);
}
if (environment.defines[kBitcodeFlag] == null) {
throw MissingDefineException(kBitcodeFlag, name);
}
_copyFramework(environment, sdkRoot);
final File frameworkBinary = environment.outputDir.childDirectory('Flutter.framework').childFile('Flutter');
......@@ -298,7 +293,9 @@ abstract class UnpackIOS extends Target {
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
}
_thinFramework(environment, frameworkBinaryPath, archs);
_bitcodeStripFramework(environment, frameworkBinaryPath);
if (buildMode == BuildMode.release) {
_bitcodeStripFramework(environment, frameworkBinaryPath);
}
_signFramework(environment, frameworkBinaryPath, buildMode);
}
......@@ -373,16 +370,14 @@ abstract class UnpackIOS extends Target {
}
}
/// Destructively strip bitcode from the framework, if needed.
/// Destructively strip bitcode from the framework. This can be removed
/// when the framework is no longer built with bitcode.
void _bitcodeStripFramework(Environment environment, String frameworkBinaryPath) {
if (environment.defines[kBitcodeFlag] == 'true') {
return;
}
final ProcessResult stripResult = environment.processManager.runSync(<String>[
'xcrun',
'bitcode_strip',
frameworkBinaryPath,
'-m', // leave the bitcode marker.
'-r', // Delete the bitcode segment.
'-o',
frameworkBinaryPath,
]);
......@@ -669,7 +664,6 @@ Future<void> _createStubAppFramework(File outputFile, Environment environment,
for (String arch in iosArchNames ?? <String>{}) ...<String>['-arch', arch],
stubSource.path,
'-dynamiclib',
'-fembed-bitcode-marker',
// Keep version in sync with AOTSnapshotter flag
if (environmentType == EnvironmentType.physical)
'-miphoneos-version-min=11.0'
......
......@@ -285,7 +285,6 @@ class CompileMacOSFramework extends Target {
}
pending.add(snapshotter.build(
bitcode: false,
buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path,
outputPath: environment.fileSystem.path.join(buildOutputPath, getNameForDarwinArch(darwinArch)),
......
......@@ -238,25 +238,12 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
<plist version="1.0">
<dict>
<key>method</key>
''');
plistContents.write('''
<string>${stringArgDeprecated('export-method')}</string>
''');
if (xcodeBuildResult?.xcodeBuildExecution?.buildSettings['ENABLE_BITCODE'] != 'YES') {
// Bitcode is off by default in Flutter iOS apps.
plistContents.write('''
<key>uploadBitcode</key>
<key>uploadBitcode</key>
<false/>
</dict>
</plist>
''');
} else {
plistContents.write('''
</dict>
</plist>
''');
}
final File tempPlist = globals.fs.systemTempDirectory
.createTempSync('flutter_build_ios.').childFile('ExportOptions.plist');
......
......@@ -152,7 +152,7 @@ abstract class BuildFrameworkCommand extends BuildSubCommand {
...framework.parent
.listSync()
.where((FileSystemEntity entity) =>
entity.basename.endsWith('bcsymbolmap') || entity.basename.endsWith('dSYM'))
entity.basename.endsWith('dSYM'))
.map((FileSystemEntity entity) => <String>['-debug-symbols', entity.path])
.expand<String>((List<String> parameter) => parameter),
],
......@@ -421,7 +421,6 @@ end
defines: <String, String>{
kTargetFile: targetFile,
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
kBitcodeFlag: 'true',
kIosArchs: defaultIOSArchsForEnvironment(sdkType, globals.artifacts!)
.map(getNameForDarwinArch)
.join(' '),
......@@ -480,9 +479,6 @@ end
' ├─Building plugins...'
);
try {
final String bitcodeGenerationMode = mode == BuildMode.release ?
'bitcode' : 'marker'; // In release, force bitcode embedding without archiving.
List<String> pluginsBuildCommand = <String>[
...globals.xcode!.xcrunCommand(),
'xcodebuild',
......@@ -492,7 +488,6 @@ end
'-configuration',
xcodeBuildConfiguration,
'SYMROOT=${iPhoneBuildOutput.path}',
'BITCODE_GENERATION_MODE=$bitcodeGenerationMode',
'ONLY_ACTIVE_ARCH=NO', // No device targeted, so build all valid architectures.
'BUILD_LIBRARY_FOR_DISTRIBUTION=YES',
if (boolArg('static') ?? false)
......@@ -519,7 +514,6 @@ end
'-configuration',
simulatorConfiguration,
'SYMROOT=${simulatorBuildOutput.path}',
'ENABLE_BITCODE=YES', // Support host apps with bitcode enabled.
'ONLY_ACTIVE_ARCH=NO', // No device targeted, so build all valid architectures.
'BUILD_LIBRARY_FOR_DISTRIBUTION=YES',
if (boolArg('static') ?? false)
......
// 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 '../artifacts.dart';
import '../base/common.dart';
import '../base/context.dart';
import '../base/process.dart';
import '../base/version.dart';
import '../build_info.dart';
import '../globals.dart' as globals;
import '../macos/xcode.dart';
const bool kBitcodeEnabledDefault = false;
Future<void> validateBitcode(BuildMode buildMode, TargetPlatform targetPlatform, EnvironmentType environmentType) async {
final Artifacts? localArtifacts = globals.artifacts;
final String? flutterFrameworkPath = localArtifacts?.getArtifactPath(
Artifact.flutterFramework,
mode: buildMode,
platform: targetPlatform,
environmentType: environmentType,
);
final Xcode? xcode = context.get<Xcode>();
final RunResult? clangResult = await xcode?.clang(<String>['--version']);
final String? clangVersion = clangResult?.stdout.split('\n').first;
final String? engineClangVersion = flutterFrameworkPath == null
? null
: globals.plistParser.getStringValueFromFile(
globals.fs.path.join(flutterFrameworkPath, 'Info.plist'),
'ClangVersion',
);
final Version engineClangSemVer = _parseVersionFromClang(engineClangVersion);
final Version clangSemVer = _parseVersionFromClang(clangVersion);
if (engineClangSemVer > clangSemVer) {
throwToolExit(
'The Flutter.framework at $flutterFrameworkPath was built '
'with "${engineClangVersion ?? 'unknown'}", but the current version '
'of clang is "$clangVersion". This will result in failures when trying to '
'archive an IPA. To resolve this issue, update your version of Xcode to '
'at least $engineClangSemVer.',
);
}
}
Version _parseVersionFromClang(String? clangVersion) {
final RegExp pattern = RegExp(r'Apple (LLVM|clang) version (\d+\.\d+\.\d+) ');
Never invalid() {
throwToolExit('Unable to parse Clang version from "$clangVersion". '
'Expected a string like "Apple (LLVM|clang) #.#.# (clang-####.#.##.#)".');
}
if (clangVersion == null || clangVersion.isEmpty) {
invalid();
}
final RegExpMatch? match = pattern.firstMatch(clangVersion);
if (match == null || match.groupCount != 2) {
invalid();
}
final Version? version = Version.parse(match.group(2));
if (version == null) {
invalid();
}
return version;
}
......@@ -539,12 +539,10 @@ void main() {
testWithoutContext('created with symbols', () async {
final Directory parentA = fileSystem.directory('FrameworkA')..createSync();
final File bcsymbolmapA = parentA.childFile('ABC123.bcsymbolmap')..createSync();
final File dSYMA = parentA.childFile('FrameworkA.framework.dSYM')..createSync();
final Directory frameworkA = parentA.childDirectory('FrameworkA.framework')..createSync();
final Directory parentB = fileSystem.directory('FrameworkB')..createSync();
final File bcsymbolmapB = parentB.childFile('ZYX987.bcsymbolmap')..createSync();
final File dSYMB = parentB.childFile('FrameworkB.framework.dSYM')..createSync();
final Directory frameworkB = parentB.childDirectory('FrameworkB.framework')..createSync();
final Directory output = fileSystem.directory('output');
......@@ -557,14 +555,10 @@ void main() {
'-framework',
frameworkA.path,
'-debug-symbols',
bcsymbolmapA.path,
'-debug-symbols',
dSYMA.path,
'-framework',
frameworkB.path,
'-debug-symbols',
bcsymbolmapB.path,
'-debug-symbols',
dSYMB.path,
'-output',
output.childDirectory('Combine.xcframework').path,
......
......@@ -444,42 +444,6 @@ void main() {
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
});
testUsingContext('ipa build invokes xcodebuild and archives with bitcode on', () async {
final File cachedExportOptionsPlist = fileSystem.file('/CachedExportOptions.plist');
final BuildCommand command = BuildCommand();
fakeProcessManager.addCommands(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(),
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist, cachePlist: cachedExportOptionsPlist),
]);
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run(
const <String>['build', 'ipa', '--no-pub',]
);
const String expectedIpaPlistContents = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
</dict>
</plist>
''';
final String actualIpaPlistContents = fileSystem.file(cachedExportOptionsPlist).readAsStringSync();
expect(actualIpaPlistContents, expectedIpaPlistContents);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => fakeProcessManager,
Platform: () => macosPlatform,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(
overrides: <String, String>{'ENABLE_BITCODE': 'YES'},
),
});
testUsingContext('ipa build invokes xcode build with verbosity', () async {
final BuildCommand command = BuildCommand();
fakeProcessManager.addCommands(<FakeCommand>[
......
......@@ -165,7 +165,6 @@ void main() {
buildMode: BuildMode.debug,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: false,
), isNot(equals(0)));
});
......@@ -178,7 +177,6 @@ void main() {
buildMode: BuildMode.debug,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: false,
), isNot(0));
});
......@@ -191,99 +189,10 @@ void main() {
buildMode: BuildMode.debug,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: false,
), isNot(0));
});
testWithoutContext('builds iOS with bitcode', () async {
final String outputPath = fileSystem.path.join('build', 'foo');
final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S');
final String genSnapshotPath = artifacts.getArtifactPath(
Artifact.genSnapshot,
platform: TargetPlatform.ios,
mode: BuildMode.profile,
);
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
'${genSnapshotPath}_arm64',
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=$assembly',
'main.dill',
]),
kWhichSysctlCommand,
kARMCheckCommand,
const FakeCommand(command: <String>[
'xcrun',
'cc',
'-arch',
'arm64',
'-miphoneos-version-min=11.0',
'-isysroot',
'path/to/sdk',
'-fembed-bitcode',
'-c',
'build/foo/snapshot_assembly.S',
'-o',
'build/foo/snapshot_assembly.o',
]),
const FakeCommand(command: <String>[
'xcrun',
'clang',
'-arch',
'arm64',
'-miphoneos-version-min=11.0',
'-isysroot',
'path/to/sdk',
'-dynamiclib',
'-Xlinker',
'-rpath',
'-Xlinker',
'@executable_path/Frameworks',
'-Xlinker',
'-rpath',
'-Xlinker',
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
'-fembed-bitcode',
'-o',
'build/foo/App.framework/App',
'build/foo/snapshot_assembly.o',
]),
const FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'build/foo/App.framework.dSYM',
'build/foo/App.framework/App',
]),
const FakeCommand(command: <String>[
'xcrun',
'strip',
'-x',
'build/foo/App.framework/App',
'-o',
'build/foo/App.framework/App',
]),
]);
final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.ios,
buildMode: BuildMode.profile,
mainPath: 'main.dill',
outputPath: outputPath,
darwinArch: DarwinArch.arm64,
sdkRoot: 'path/to/sdk',
bitcode: true,
dartObfuscation: false,
);
expect(genSnapshotExitCode, 0);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('builds iOS snapshot with dwarfStackTraces', () async {
final String outputPath = fileSystem.path.join('build', 'foo');
final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S');
......@@ -349,7 +258,6 @@ void main() {
outputPath: outputPath,
darwinArch: DarwinArch.arm64,
sdkRoot: 'path/to/sdk',
bitcode: false,
splitDebugInfo: 'foo',
dartObfuscation: false,
);
......@@ -421,7 +329,6 @@ void main() {
outputPath: outputPath,
darwinArch: DarwinArch.arm64,
sdkRoot: 'path/to/sdk',
bitcode: false,
dartObfuscation: true,
);
......@@ -490,7 +397,6 @@ void main() {
outputPath: outputPath,
darwinArch: DarwinArch.arm64,
sdkRoot: 'path/to/sdk',
bitcode: false,
dartObfuscation: false,
);
......@@ -518,7 +424,6 @@ void main() {
buildMode: BuildMode.release,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: false,
);
......@@ -549,7 +454,6 @@ void main() {
buildMode: BuildMode.release,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
splitDebugInfo: 'foo',
dartObfuscation: false,
);
......@@ -579,7 +483,6 @@ void main() {
buildMode: BuildMode.release,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: true,
);
......@@ -607,7 +510,6 @@ void main() {
buildMode: BuildMode.release,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
splitDebugInfo: '',
dartObfuscation: false,
);
......@@ -634,7 +536,6 @@ void main() {
buildMode: BuildMode.release,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: false,
);
......@@ -659,7 +560,6 @@ void main() {
buildMode: BuildMode.release,
mainPath: 'main.dill',
outputPath: outputPath,
bitcode: false,
dartObfuscation: false,
extraGenSnapshotOptions: const <String>['--no-strip'],
);
......
......@@ -465,98 +465,10 @@ void main() {
ProcessManager: () => processManager,
});
testUsingContext('AotAssemblyProfile with bitcode sends correct argument to snapshotter', () async {
iosEnvironment.defines[kIosArchs] = 'arm64';
iosEnvironment.defines[kBitcodeFlag] = 'true';
iosEnvironment.defines[kSdkRoot] = 'path/to/iPhoneOS.sdk';
final String build = iosEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
// This path is not known by the cache due to the iOS gen_snapshot split.
'Artifact.genSnapshot.TargetPlatform.ios.profile_arm64',
'--deterministic',
kAssemblyAot,
'--assembly=$build/arm64/snapshot_assembly.S',
'$build/app.dill',
]),
FakeCommand(command: <String>[
'xcrun',
'cc',
'-arch',
'arm64',
'-miphoneos-version-min=11.0',
'-isysroot',
'path/to/iPhoneOS.sdk',
// Contains bitcode flag.
'-fembed-bitcode',
'-c',
'$build/arm64/snapshot_assembly.S',
'-o',
'$build/arm64/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'xcrun',
'clang',
'-arch',
'arm64',
'-miphoneos-version-min=11.0',
'-isysroot',
'path/to/iPhoneOS.sdk',
'-dynamiclib',
'-Xlinker',
'-rpath',
'-Xlinker',
'@executable_path/Frameworks',
'-Xlinker',
'-rpath',
'-Xlinker',
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
// Contains bitcode flag.
'-fembed-bitcode',
'-o',
'$build/arm64/App.framework/App',
'$build/arm64/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'$build/arm64/App.framework.dSYM',
'$build/arm64/App.framework/App',
]),
FakeCommand(command: <String>[
'xcrun',
'strip',
'-x',
'$build/arm64/App.framework/App',
'-o',
'$build/arm64/App.framework/App',
]),
FakeCommand(command: <String>[
'lipo',
'$build/arm64/App.framework/App',
'-create',
'-output',
'$build/App.framework/App',
]),
]);
await const AotAssemblyProfile().build(iosEnvironment);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
Platform: () => macPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
testUsingContext('AotAssemblyRelease configures gen_snapshot with code size directory', () async {
iosEnvironment.defines[kCodeSizeDirectory] = 'code_size_1';
iosEnvironment.defines[kIosArchs] = 'arm64';
iosEnvironment.defines[kSdkRoot] = 'path/to/iPhoneOS.sdk';
iosEnvironment.defines[kBitcodeFlag] = 'true';
final String build = iosEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[
......@@ -577,8 +489,6 @@ void main() {
'-miphoneos-version-min=11.0',
'-isysroot',
'path/to/iPhoneOS.sdk',
// Contains bitcode flag.
'-fembed-bitcode',
'-c',
'$build/arm64/snapshot_assembly.S',
'-o',
......@@ -603,8 +513,6 @@ void main() {
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
// Contains bitcode flag.
'-fembed-bitcode',
'-o',
'$build/arm64/App.framework/App',
'$build/arm64/snapshot_assembly.o',
......
......@@ -22,7 +22,6 @@ final Platform macPlatform = FakePlatform(operatingSystem: 'macos', environment:
const List<String> _kSharedConfig = <String>[
'-dynamiclib',
'-fembed-bitcode-marker',
'-miphoneos-version-min=11.0',
'-Xlinker',
'-rpath',
......@@ -87,7 +86,6 @@ void main() {
fileSystem.path.absolute(fileSystem.path.join(
'.tmp_rand0', 'flutter_tools_stub_source.rand0', 'debug_app.cc')),
'-dynamiclib',
'-fembed-bitcode-marker',
'-miphonesimulator-version-min=11.0',
'-Xlinker',
'-rpath',
......@@ -393,7 +391,6 @@ void main() {
late FakeCommand copyPhysicalFrameworkCommand;
late FakeCommand lipoCommandNonFatResult;
late FakeCommand lipoVerifyArm64Command;
late FakeCommand bitcodeStripCommand;
late FakeCommand adHocCodesignCommand;
setUp(() {
......@@ -423,15 +420,6 @@ void main() {
'arm64',
]);
bitcodeStripCommand = FakeCommand(command: <String>[
'xcrun',
'bitcode_strip',
binary.path,
'-m',
'-o',
binary.path,
]);
adHocCodesignCommand = FakeCommand(command: <String>[
'codesign',
'--force',
......@@ -453,7 +441,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'x86_64',
kSdkRoot: 'path/to/iPhoneSimulator.sdk',
kBitcodeFlag: 'true',
},
);
......@@ -495,7 +482,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
},
);
processManager.addCommand(copyPhysicalFrameworkCommand);
......@@ -521,7 +507,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64 armv7',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
},
);
......@@ -564,7 +549,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64 armv7',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
},
);
......@@ -618,7 +602,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: 'true',
},
);
......@@ -648,7 +631,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64 armv7',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: 'true',
},
);
......@@ -696,26 +678,33 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
},
);
processManager.addCommands(<FakeCommand>[
copyPhysicalFrameworkCommand,
FakeCommand(command: <String>[
'rsync',
'-av',
'--delete',
'--filter',
'- .DS_Store/',
'Artifact.flutterFramework.TargetPlatform.ios.release.EnvironmentType.physical',
outputDir.path,
]),
lipoCommandNonFatResult,
lipoVerifyArm64Command,
FakeCommand(command: <String>[
'xcrun',
'bitcode_strip',
binary.path,
'-m',
'-r',
'-o',
binary.path,
], exitCode: 1, stderr: 'bitcode_strip error'),
]);
await expectLater(
const DebugUnpackIOS().build(environment),
const ReleaseUnpackIOS().build(environment),
throwsA(isException.having(
(Exception exception) => exception.toString(),
'description',
......@@ -739,7 +728,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
},
);
......@@ -747,7 +735,6 @@ void main() {
copyPhysicalFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
bitcodeStripCommand,
adHocCodesignCommand,
]);
await const DebugUnpackIOS().build(environment);
......@@ -768,7 +755,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
kCodesignIdentity: 'ABC123',
},
);
......@@ -777,7 +763,6 @@ void main() {
copyPhysicalFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
bitcodeStripCommand,
FakeCommand(command: <String>[
'codesign',
'--force',
......@@ -813,7 +798,6 @@ void main() {
defines: <String, String>{
kIosArchs: 'arm64',
kSdkRoot: 'path/to/iPhoneOS.sdk',
kBitcodeFlag: '',
kCodesignIdentity: 'ABC123',
},
);
......@@ -822,7 +806,6 @@ void main() {
copyPhysicalFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
bitcodeStripCommand,
FakeCommand(command: <String>[
'codesign',
'--force',
......
......@@ -31,7 +31,6 @@ void main() {
<String, String>{
'ACTION': 'build',
'BUILT_PRODUCTS_DIR': buildDir.path,
'ENABLE_BITCODE': 'YES',
'FLUTTER_ROOT': flutterRoot.path,
'INFOPLIST_PATH': 'Info.plist',
},
......@@ -51,7 +50,6 @@ void main() {
'-dTreeShakeIcons=',
'-dTrackWidgetCreation=',
'-dDartObfuscation=',
'-dEnableBitcode=',
'-dAction=build',
'--ExtraGenSnapshotOptions=',
'--DartDefines=',
......@@ -85,7 +83,6 @@ void main() {
<String, String>{
'BUILT_PRODUCTS_DIR': buildDir.path,
'CONFIGURATION': buildMode,
'ENABLE_BITCODE': 'YES',
'FLUTTER_ROOT': flutterRoot.path,
'INFOPLIST_PATH': 'Info.plist',
},
......@@ -105,7 +102,6 @@ void main() {
'-dTreeShakeIcons=',
'-dTrackWidgetCreation=',
'-dDartObfuscation=',
'-dEnableBitcode=',
'-dAction=',
'--ExtraGenSnapshotOptions=',
'--DartDefines=',
......@@ -154,7 +150,6 @@ void main() {
'CONFIGURATION': buildMode,
'DART_DEFINES': dartDefines,
'DART_OBFUSCATION': dartObfuscation,
'ENABLE_BITCODE': 'YES',
'EXPANDED_CODE_SIGN_IDENTITY': expandedCodeSignIdentity,
'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions,
'EXTRA_GEN_SNAPSHOT_OPTIONS': extraGenSnapshotOptions,
......@@ -181,7 +176,6 @@ void main() {
'-dTreeShakeIcons=$treeShake',
'-dTrackWidgetCreation=$trackWidgetCreation',
'-dDartObfuscation=$dartObfuscation',
'-dEnableBitcode=true',
'-dAction=install',
'--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
'--DartDefines=$dartDefines',
......
......@@ -7,10 +7,11 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/convert.dart';
import '../integration.shard/test_utils.dart';
import '../src/common.dart';
import '../src/darwin_common.dart';
import '../src/fake_process_manager.dart';
void main() {
group('iOS app validation', () {
......@@ -152,10 +153,8 @@ void main() {
expect(vmSnapshot.existsSync(), buildMode == BuildMode.debug);
// Archiving should contain a bitcode blob, but not building.
// This mimics Xcode behavior and prevents a developer from having to install a
// 300+MB app.
expect(containsBitcode(outputFlutterFrameworkBinary.path, processManager), isFalse);
// Builds should not contain deprecated bitcode.
expect(_containsBitcode(outputFlutterFrameworkBinary.path, processManager), isFalse);
});
testWithoutContext('Info.plist dart observatory Bonjour service', () {
......@@ -358,3 +357,46 @@ void main() {
timeout: const Timeout(Duration(minutes: 7))
);
}
bool _containsBitcode(String pathToBinary, ProcessManager processManager) {
// See: https://stackoverflow.com/questions/32755775/how-to-check-a-static-library-is-built-contain-bitcode
final ProcessResult result = processManager.runSync(<String>[
'otool',
'-l',
'-arch',
'arm64',
pathToBinary,
]);
final String loadCommands = result.stdout as String;
if (!loadCommands.contains('__LLVM')) {
return false;
}
// Presence of the section may mean a bitcode marker was embedded (size=1), but there is no content.
if (!loadCommands.contains('size 0x0000000000000001')) {
return true;
}
// Check the false positives: size=1 wasn't referencing the __LLVM section.
bool emptyBitcodeMarkerFound = false;
// Section
// sectname __bundle
// segname __LLVM
// addr 0x003c4000
// size 0x0042b633
// offset 3932160
// ...
final List<String> lines = LineSplitter.split(loadCommands).toList();
lines.asMap().forEach((int index, String line) {
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
final bool bitcodeMarkerFound = lines
.skip(index - 1)
.take(4)
.any((String line) => line.contains(' size 0x0000000000000001'));
if (bitcodeMarkerFound) {
emptyBitcodeMarkerFound = true;
return;
}
}
});
return !emptyBitcodeMarkerFound;
}
......@@ -8,7 +8,6 @@ import 'package:flutter_tools/src/base/io.dart';
import '../integration.shard/test_utils.dart';
import '../src/common.dart';
import '../src/darwin_common.dart';
void main() {
final String flutterBin = fileSystem.path.join(
......@@ -150,18 +149,6 @@ void main() {
expect(outputFlutterFramework.childLink('Modules'), isNot(exists));
expect(outputFlutterFramework.childDirectory('Modules'), isNot(exists));
// Archiving should contain a bitcode blob, but not building.
// This mimics Xcode behavior and prevents a developer from having to install a
// 300+MB app.
final File outputFlutterFrameworkBinary = outputFlutterFramework
.childDirectory('Versions')
.childDirectory('A')
.childFile('FlutterMacOS');
expect(
containsBitcode(outputFlutterFrameworkBinary.path, processManager),
isFalse,
);
// Build again without cleaning.
final ProcessResult secondBuild = processManager.runSync(buildCommand, workingDirectory: workingDirectory);
......
// 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 'dart:convert';
import 'package:flutter_tools/src/base/io.dart';
import 'package:process/process.dart';
bool containsBitcode(String pathToBinary, ProcessManager processManager) {
// See: https://stackoverflow.com/questions/32755775/how-to-check-a-static-library-is-built-contain-bitcode
final ProcessResult result = processManager.runSync(<String>[
'otool',
'-l',
'-arch',
'arm64',
pathToBinary,
]);
final String loadCommands = result.stdout as String;
if (!loadCommands.contains('__LLVM')) {
return false;
}
// Presence of the section may mean a bitcode marker was embedded (size=1), but there is no content.
if (!loadCommands.contains('size 0x0000000000000001')) {
return true;
}
// Check the false positives: size=1 wasn't referencing the __LLVM section.
bool emptyBitcodeMarkerFound = false;
// Section
// sectname __bundle
// segname __LLVM
// addr 0x003c4000
// size 0x0042b633
// offset 3932160
// ...
final List<String> lines = LineSplitter.split(loadCommands).toList();
lines.asMap().forEach((int index, String line) {
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
final bool bitcodeMarkerFound = lines
.skip(index - 1)
.take(4)
.any((String line) => line.contains(' size 0x0000000000000001'));
if (bitcodeMarkerFound) {
emptyBitcodeMarkerFound = true;
return;
}
}
});
return !emptyBitcodeMarkerFound;
}
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