Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt Flutter.xcframework in tool #71495

Merged
merged 2 commits into from Dec 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 40 additions & 17 deletions dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
Expand Up @@ -102,6 +102,7 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
options: <String>[
'ios-framework',
'--universal',
'--verbose',
'--output=$outputDirectoryName'
],
);
Expand Down Expand Up @@ -164,16 +165,43 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
'App',
);

// This seemed easier than an explicit Xcode version check.
String xcodeArmDirectoryName;
// Based on the locally installed version of Xcode.
String localXcodeArmDirectoryName;
if (exists(File(xcode11AppFrameworkDirectory))) {
xcodeArmDirectoryName = xcode11ArmDirectoryName;
localXcodeArmDirectoryName = xcode11ArmDirectoryName;
} else if (exists(File(xcode12AppFrameworkDirectory))) {
xcodeArmDirectoryName = xcode12ArmDirectoryName;
localXcodeArmDirectoryName = xcode12ArmDirectoryName;
} else {
throw const FileSystemException('Expected App.framework binary to exist.');
}

final String xcode11FlutterFrameworkDirectory = path.join(
outputPath,
'Debug',
'Flutter.xcframework',
xcode11ArmDirectoryName,
'Flutter.framework',
'Flutter',
);
final String xcode12FlutterFrameworkDirectory = path.join(
outputPath,
'Debug',
'Flutter.xcframework',
xcode12ArmDirectoryName,
'Flutter.framework',
'Flutter',
);

// Based on the version of Xcode installed on the engine builder.
String builderXcodeArmDirectoryName;
if (exists(File(xcode11FlutterFrameworkDirectory))) {
builderXcodeArmDirectoryName = xcode11ArmDirectoryName;
} else if (exists(File(xcode12FlutterFrameworkDirectory))) {
builderXcodeArmDirectoryName = xcode12ArmDirectoryName;
} else {
throw const FileSystemException('Expected Flutter.framework binary to exist.');
}

checkFileExists(path.join(
outputPath,
'Debug',
Expand Down Expand Up @@ -214,7 +242,7 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
outputPath,
mode,
'App.xcframework',
xcodeArmDirectoryName,
localXcodeArmDirectoryName,
'App.framework',
'App',
));
Expand All @@ -239,30 +267,25 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
'Flutter',
);

await _checkFrameworkArchs(engineFrameworkPath, mode == 'Debug');
await _checkFrameworkArchs(engineFrameworkPath, true);
await _checkBitcode(engineFrameworkPath, mode);

checkFileExists(path.join(
outputPath,
mode,
'Flutter.xcframework',
xcodeArmDirectoryName,
builderXcodeArmDirectoryName,
'Flutter.framework',
'Flutter',
));
final String simulatorFrameworkPath = path.join(
checkFileExists(path.join(
outputPath,
mode,
'Flutter.xcframework',
'ios-x86_64-simulator',
'Flutter.framework',
'Flutter',
);
if (mode == 'Debug') {
checkFileExists(simulatorFrameworkPath);
} else {
checkFileNotExists(simulatorFrameworkPath);
}
));
}

section("Check all modes' engine header");
Expand All @@ -287,7 +310,7 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
outputPath,
mode,
'device_info.xcframework',
xcodeArmDirectoryName,
localXcodeArmDirectoryName,
'device_info.framework',
'device_info',
));
Expand All @@ -296,7 +319,7 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
outputPath,
mode,
'device_info.xcframework',
xcodeArmDirectoryName,
localXcodeArmDirectoryName,
'device_info.framework',
'Headers',
'DeviceInfoPlugin.h',
Expand Down Expand Up @@ -357,7 +380,7 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
outputPath,
mode,
'FlutterPluginRegistrant.xcframework',
xcodeArmDirectoryName,
localXcodeArmDirectoryName,
'FlutterPluginRegistrant.framework',
'Headers',
'GeneratedPluginRegistrant.h',
Expand Down
2 changes: 2 additions & 0 deletions dev/devicelab/lib/framework/ios.dart
Expand Up @@ -22,6 +22,8 @@ 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',
Comment on lines +25 to +26
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now release add-to-app fat Flutter.framework contains debug x86 so it can be built on the simulator. otool -l doesn't show the bitcode section for a fat framework if any slices don't contain bitcode. Specify arm64 only since that's the part that actually gets shipped and requires bitcode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about armv7?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has mostly been useful as a sanity check to make sure a debug version wasn't incorrectly embedded, not that the builder is producing the right artifacts, so that was a shortcut. I can check both if you prefer!

pathToBinary,
]);
if (!loadCommands.contains('__LLVM')) {
Expand Down
20 changes: 17 additions & 3 deletions packages/flutter_tools/bin/podhelper.rb
Expand Up @@ -35,13 +35,27 @@ def flutter_additional_ios_build_settings(target)
# This podhelper script is at $FLUTTER_ROOT/packages/flutter_tools/bin.
# Add search paths from $FLUTTER_ROOT/bin/cache/artifacts/engine.
artifacts_dir = File.join('..', '..', '..', '..', 'bin', 'cache', 'artifacts', 'engine')
debug_framework_dir = File.expand_path(File.join(artifacts_dir, 'ios'), __FILE__)
release_framework_dir = File.expand_path(File.join(artifacts_dir, 'ios-release'), __FILE__)
debug_framework_dir = File.expand_path(File.join(artifacts_dir, 'ios', 'Flutter.xcframework'), __FILE__)

unless Dir.exist?(debug_framework_dir)
# iOS artifacts have not been downloaded.
raise "#{debug_framework_dir} must exist. If you're running pod install manually, make sure flutter build ios is executed first"
end

release_framework_dir = File.expand_path(File.join(artifacts_dir, 'ios-release', 'Flutter.xcframework'), __FILE__)

target.build_configurations.each do |build_configuration|
# 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
build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] = "\"#{configuration_engine_dir}\" $(inherited)"
Dir.new(configuration_engine_dir).each_child do |xcframework_file|
if xcframework_file.end_with?("-simulator") # ios-x86_64-simulator
build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]'] = "\"#{configuration_engine_dir}/#{xcframework_file}\" $(inherited)"
elsif xcframework_file.start_with?("ios-") # ios-armv7_arm64
build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]'] = "\"#{configuration_engine_dir}/#{xcframework_file}\" $(inherited)"
else
# Info.plist or another platform.
end
end
build_configuration.build_settings['OTHER_LDFLAGS'] = '$(inherited) -framework Flutter'

build_configuration.build_settings['CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER'] = 'NO'
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter_tools/bin/xcode_backend.sh
Expand Up @@ -115,7 +115,7 @@ is set to release or run \"flutter build ios --release\", then re-run Archive fr
local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}"
local flutter_engine_flag=""
local local_engine_flag=""
local flutter_framework="${framework_path}/Flutter.framework"
local flutter_framework="${framework_path}/Flutter.xcframework"

if [[ -n "$FLUTTER_ENGINE" ]]; then
flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
Expand All @@ -135,7 +135,7 @@ is set to release or run \"flutter build ios --release\", then re-run Archive fr
exit -1
fi
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.framework"
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.xcframework"
fi

local bitcode_flag=""
Expand Down
40 changes: 38 additions & 2 deletions packages/flutter_tools/lib/src/artifacts.dart
Expand Up @@ -5,6 +5,7 @@
import 'package:meta/meta.dart';
import 'package:process/process.dart';

import 'base/common.dart';
import 'base/file_system.dart';
import 'base/platform.dart';
import 'base/utils.dart';
Expand Down Expand Up @@ -305,12 +306,14 @@ class CachedArtifacts implements Artifacts {
BuildMode mode, EnvironmentType environmentType) {
switch (artifact) {
case Artifact.genSnapshot:
case Artifact.flutterFramework:
case Artifact.flutterXcframework:
case Artifact.frontendServerSnapshotForEngineDartSdk:
final String artifactFileName = _artifactToFileName(artifact);
final String engineDir = _getEngineArtifactsPath(platform, mode);
return _fileSystem.path.join(engineDir, artifactFileName);
case Artifact.flutterFramework:
final String engineDir = _getEngineArtifactsPath(platform, mode);
return _getIosEngineArtifactPath(engineDir, environmentType, _fileSystem);
case Artifact.idevicescreenshot:
case Artifact.idevicesyslog:
final String artifactFileName = _artifactToFileName(artifact);
Expand Down Expand Up @@ -527,6 +530,38 @@ HostPlatform _currentHostPlatformAsHost(Platform platform) {
throw UnimplementedError('Host OS not supported.');
}

String _getIosEngineArtifactPath(String engineDirectory,
EnvironmentType environmentType, FileSystem fileSystem) {
final Directory xcframeworkDirectory = fileSystem
.directory(engineDirectory)
.childDirectory(_artifactToFileName(Artifact.flutterXcframework));

if (!xcframeworkDirectory.existsSync()) {
throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter build ios".');
}
Directory flutterFrameworkSource;
for (final Directory platformDirectory
in xcframeworkDirectory.listSync().whereType<Directory>()) {
if (!platformDirectory.basename.startsWith('ios-')) {
continue;
}
// ios-x86_64-simulator, ios-armv7_arm64 (Xcode 11), or ios-arm64_armv7 (Xcode 12).
final bool simulatorDirectory =
platformDirectory.basename.endsWith('-simulator');
if ((environmentType == EnvironmentType.simulator && simulatorDirectory) ||
(environmentType == EnvironmentType.physical && !simulatorDirectory)) {
flutterFrameworkSource = platformDirectory;
}
}
if (flutterFrameworkSource == null) {
throwToolExit('No iOS frameworks found in ${xcframeworkDirectory.path}');
}

return flutterFrameworkSource
.childDirectory(_artifactToFileName(Artifact.flutterFramework))
.path;
}

/// Manages the artifacts of a locally built engine.
class LocalEngineArtifacts implements Artifacts {
LocalEngineArtifacts(
Expand Down Expand Up @@ -578,7 +613,8 @@ class LocalEngineArtifacts implements Artifacts {
case Artifact.platformLibrariesJson:
return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), 'lib', artifactFileName);
case Artifact.flutterFramework:
return _fileSystem.path.join(engineOutPath, artifactFileName);
return _getIosEngineArtifactPath(
engineOutPath, environmentType, _fileSystem);
case Artifact.flutterPatchedSdkPath:
// When using local engine always use [BuildMode.debug] regardless of
// what was specified in [mode] argument because local engine will
Expand Down
Expand Up @@ -249,7 +249,7 @@ abstract class UnpackIOS extends Target {
const Source.pattern(
'{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart'),
Source.artifact(
Artifact.flutterFramework,
Artifact.flutterXcframework,
platform: TargetPlatform.ios,
mode: buildMode,
),
Expand Down
13 changes: 5 additions & 8 deletions packages/flutter_tools/lib/src/cache.dart
Expand Up @@ -924,14 +924,11 @@ abstract class EngineCachedArtifact extends CachedArtifact {

_makeFilesExecutable(dir, operatingSystemUtils);

const List<String> frameworkNames = <String>['Flutter', 'FlutterMacOS'];
for (final String frameworkName in frameworkNames) {
final File frameworkZip = fileSystem.file(fileSystem.path.join(dir.path, '$frameworkName.framework.zip'));
if (frameworkZip.existsSync()) {
final Directory framework = fileSystem.directory(fileSystem.path.join(dir.path, '$frameworkName.framework'));
framework.createSync();
operatingSystemUtils.unzip(frameworkZip, framework);
}
final File frameworkZip = fileSystem.file(fileSystem.path.join(dir.path, 'FlutterMacOS.framework.zip'));
if (frameworkZip.existsSync()) {
final Directory framework = fileSystem.directory(fileSystem.path.join(dir.path, 'FlutterMacOS.framework'));
framework.createSync();
operatingSystemUtils.unzip(frameworkZip, framework);
}
}

Expand Down