diff --git a/dev/devicelab/bin/tasks/ios_content_validation_test.dart b/dev/devicelab/bin/tasks/ios_content_validation_test.dart index 2137618964..2f120975e5 100644 --- a/dev/devicelab/bin/tasks/ios_content_validation_test.dart +++ b/dev/devicelab/bin/tasks/ios_content_validation_test.dart @@ -24,19 +24,32 @@ Future main() async { '--release', '--obfuscate', '--split-debug-info=foo/', + '--no-codesign', ]); }); + final String buildPath = path.join( + flutterProject.rootPath, + 'build', + 'ios', + 'iphoneos', + ); final String outputAppPath = path.join( - flutterProject.rootPath, - 'build/ios/iphoneos/Runner.app', + buildPath, + 'Runner.app', ); - final String outputAppFramework = path.join( - flutterProject.rootPath, + final Directory outputAppFramework = Directory(path.join( outputAppPath, - 'Frameworks/App.framework/App', - ); - if (!File(outputAppFramework).existsSync()) { - fail('Failed to produce expected output at $outputAppFramework'); + 'Frameworks', + 'App.framework', + )); + + final File outputAppFrameworkBinary = File(path.join( + outputAppFramework.path, + 'App', + )); + + if (!outputAppFrameworkBinary.existsSync()) { + fail('Failed to produce expected output at ${outputAppFrameworkBinary.path}'); } section('Validate obfuscation'); @@ -46,7 +59,7 @@ Future main() async { await inDirectory(flutterProject.rootPath, () async { final String response = await eval( 'grep', - [flutterProject.name, outputAppFramework], + [flutterProject.name, outputAppFrameworkBinary.path], canFail: true, ); if (response.trim().contains('matches')) { @@ -56,16 +69,63 @@ Future main() async { section('Validate bitcode'); - final String outputFlutterFramework = path.join( + final Directory outputFlutterFramework = Directory(path.join( flutterProject.rootPath, outputAppPath, - 'Frameworks/Flutter.framework/Flutter', + 'Frameworks', + 'Flutter.framework', + )); + final File outputFlutterFrameworkBinary = File(path.join( + outputFlutterFramework.path, + 'Flutter', + )); + + if (!outputFlutterFrameworkBinary.existsSync()) { + fail('Failed to produce expected output at ${outputFlutterFrameworkBinary.path}'); + } + bitcode = await containsBitcode(outputFlutterFrameworkBinary.path); + + section('Xcode backend script'); + + outputFlutterFramework.deleteSync(recursive: true); + outputAppFramework.deleteSync(recursive: true); + if (outputFlutterFramework.existsSync() || outputAppFramework.existsSync()) { + fail('Failed to delete embedded frameworks'); + } + + final String xcodeBackendPath = path.join( + flutterDirectory.path, + 'packages', + 'flutter_tools', + 'bin', + 'xcode_backend.sh' ); - if (!File(outputFlutterFramework).existsSync()) { - fail('Failed to produce expected output at $outputFlutterFramework'); + // Simulate a commonly Xcode build setting misconfiguration + // where FLUTTER_APPLICATION_PATH is missing + final int result = await exec( + xcodeBackendPath, + ['embed_and_thin'], + environment: { + 'SOURCE_ROOT': flutterProject.iosPath, + 'TARGET_BUILD_DIR': buildPath, + 'FRAMEWORKS_FOLDER_PATH': 'Runner.app/Frameworks', + 'VERBOSE_SCRIPT_LOGGING': '1', + 'ACTION': 'install', // Skip bitcode stripping since we just checked that above. + }, + ); + + if (result != 0) { + fail('xcode_backend embed_and_thin failed'); + } + + if (!outputFlutterFrameworkBinary.existsSync()) { + fail('Failed to re-embed ${outputFlutterFrameworkBinary.path}'); + } + + if (!outputAppFrameworkBinary.existsSync()) { + fail('Failed to re-embed ${outputAppFrameworkBinary.path}'); } - bitcode = await containsBitcode(outputFlutterFramework); }); if (foundProjectName) { diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart index fc9b5abb1c..e272144f5b 100644 --- a/dev/devicelab/lib/framework/apk_utils.dart +++ b/dev/devicelab/lib/framework/apk_utils.dart @@ -226,6 +226,7 @@ class FlutterProject { String get rootPath => path.join(parent.path, name); String get androidPath => path.join(rootPath, 'android'); + String get iosPath => path.join(rootPath, 'ios'); Future addCustomBuildType(String name, {String initWith}) async { final File buildScript = File( diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh index 92d9b8163c..ad4a018414 100755 --- a/packages/flutter_tools/bin/xcode_backend.sh +++ b/packages/flutter_tools/bin/xcode_backend.sh @@ -264,15 +264,18 @@ ThinAppFrameworks() { # Adds the App.framework as an embedded binary and the flutter_assets as # resources. EmbedFlutterFrameworks() { - AssertExists "${FLUTTER_APPLICATION_PATH}" + local project_path="${SOURCE_ROOT}/.." + if [[ -n "$FLUTTER_APPLICATION_PATH" ]]; then + project_path="${FLUTTER_APPLICATION_PATH}" + fi # Prefer the hidden .ios folder, but fallback to a visible ios folder if .ios # doesn't exist. - local flutter_ios_out_folder="${FLUTTER_APPLICATION_PATH}/.ios/Flutter" - local flutter_ios_engine_folder="${FLUTTER_APPLICATION_PATH}/.ios/Flutter/engine" + local flutter_ios_out_folder="${project_path}/.ios/Flutter" + local flutter_ios_engine_folder="${project_path}/.ios/Flutter/engine" if [[ ! -d ${flutter_ios_out_folder} ]]; then - flutter_ios_out_folder="${FLUTTER_APPLICATION_PATH}/ios/Flutter" - flutter_ios_engine_folder="${FLUTTER_APPLICATION_PATH}/ios/Flutter" + flutter_ios_out_folder="${project_path}/ios/Flutter" + flutter_ios_engine_folder="${project_path}/ios/Flutter" fi AssertExists "${flutter_ios_out_folder}"