Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions build/copy_info_plist.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@

Precondition: $CWD/../../flutter is the path to the flutter engine repo.

usage: copy_info_plist.py <src_path> <dest_path> --bitcode=<enable_bitcode>
usage: copy_info_plist.py --source <src_path> --destination <dest_path> --bitcode --minversion=<deployment_target>
"""




import subprocess

import argparse
import sys
import git_revision
import os
Expand All @@ -30,13 +28,25 @@ def GetClangVersion(bitcode) :
return version.splitlines()[0]

def main():
text = open(sys.argv[1]).read()

parser = argparse.ArgumentParser(
description='Copies the Info.plist and adds extra fields to it like the git hash of the engine')

parser.add_argument('--source', help='Path to Info.plist source template', type=str, required=True)
parser.add_argument('--destination', help='Path to destination Info.plist', type=str, required=True)
parser.add_argument('--bitcode', help='Built with bitcode', action='store_true')
parser.add_argument('--minversion', help='Minimum device OS version like "9.0"', type=str)

args = parser.parse_args()

text = open(args.source).read()
engine_path = os.path.join(os.getcwd(), "..", "..", "flutter")
revision = git_revision.GetRepositoryVersion(engine_path)
clang_version = GetClangVersion(sys.argv[3] == "--bitcode=true")
text = text.format(revision, clang_version)
bitcode = args.bitcode is not None;
clang_version = GetClangVersion(bitcode)
text = text.format(revision = revision, clang_version = clang_version, min_version = args.minversion)

with open(sys.argv[2], "w") as outfile:
with open(args.destination, "w") as outfile:
outfile.write(text)

if __name__ == "__main__":
Expand Down
8 changes: 7 additions & 1 deletion shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,16 @@ action("copy_framework_info_plist") {
sources = [ "framework/Info.plist" ]
outputs = [ "$_flutter_framework_dir/Info.plist" ]
args = [
"--source",
rebase_path(sources[0]),
"--destination",
rebase_path(outputs[0]),
"--bitcode=$enable_bitcode",
"--minversion",
ios_deployment_target,
]
if (enable_bitcode) {
args += [ "--bitcode" ]
}
}

copy("copy_framework_module_map") {
Expand Down
6 changes: 3 additions & 3 deletions shell/platform/darwin/ios/framework/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>{min_version}</string>
<key>FlutterEngine</key>
<string>{0}</string>
<string>{revision}</string>
<key>ClangVersion</key>
<string>{1}</string>
<string>{clang_version}</string>
</dict>
</plist>
57 changes: 57 additions & 0 deletions shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>

Expand Down Expand Up @@ -30,6 +31,62 @@ - (void)testCreate {
XCTAssertNotNil(engine);
}

- (void)testInfoPlist {
// Check the embedded Flutter.framework Info.plist, not the linked dylib.
NSURL* flutterFrameworkURL =
[NSBundle.mainBundle.privateFrameworksURL URLByAppendingPathComponent:@"Flutter.framework"];
Copy link
Member

Choose a reason for hiding this comment

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

Instead of attempting to resolve the bundle by path, [NSBundle bundleForClass:[FlutterEngine class]] might be less brittle. For instance, I am not sure why this is in the private frameworks URL (presumably because its in the test bundle but not sure).

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried -[NSBundle bundleForClass:] first, unfortunately it doesn't work, the bundle is nil. Before this PR, the test does not link against/embed/load the Flutter.framework with the Info.plist. Instead, it embeds libios_test_flutter, which has the compiled Flutter and test code.

"-Wl,-install_name,@rpath/Frameworks/libios_test_flutter.dylib",

F7521D7826BB68BC005F15C5 /* libios_test_flutter.dylib in Embed Libraries */,

$ nm -g out/ios_debug_sim_unopt/libios_test_flutter.dylib | grep FlutterEngine
0000000003987fd8 S _OBJC_CLASS_$_FlutterEngine
0000000003988078 S _OBJC_CLASS_$_FlutterEngineGroup
0000000003987880 S _OBJC_CLASS_$_FlutterEngineGroupTest
0000000003987ba0 S _OBJC_CLASS_$_FlutterEnginePartialMock
00000000039878d0 S _OBJC_CLASS_$_FlutterEngineTest
0000000003988028 S _OBJC_METACLASS_$_FlutterEngine
00000000039880a0 S _OBJC_METACLASS_$_FlutterEngineGroup
0000000003987858 S _OBJC_METACLASS_$_FlutterEngineGroupTest
0000000003987b78 S _OBJC_METACLASS_$_FlutterEnginePartialMock
00000000039878a8 S _OBJC_METACLASS_$_FlutterEngineTest

But I want to test the Info.plist in the final bundle, so I embedded it in the app in this PR so I could leverage the NSBundle API. I'm open to suggestions for a better way to test this.

NSBundle* flutterBundle = [NSBundle bundleWithURL:flutterFrameworkURL];
XCTAssertEqualObjects(flutterBundle.bundleIdentifier, @"io.flutter.flutter");

NSDictionary<NSString*, id>* infoDictionary = flutterBundle.infoDictionary;

// OS version can have one, two, or three digits: "8", "8.0", "8.0.0"
NSError* regexError = NULL;
NSRegularExpression* osVersionRegex =
[NSRegularExpression regularExpressionWithPattern:@"((0|[1-9]\\d*)\\.)*(0|[1-9]\\d*)"
options:NSRegularExpressionCaseInsensitive
error:&regexError];
XCTAssertNil(regexError);

// Smoke test the test regex.
NSString* testString = @"9";
NSUInteger versionMatches =
[osVersionRegex numberOfMatchesInString:testString
options:NSMatchingAnchored
range:NSMakeRange(0, testString.length)];
XCTAssertEqual(versionMatches, 1UL);
testString = @"9.1";
versionMatches = [osVersionRegex numberOfMatchesInString:testString
options:NSMatchingAnchored
range:NSMakeRange(0, testString.length)];
XCTAssertEqual(versionMatches, 1UL);
testString = @"9.0.1";
versionMatches = [osVersionRegex numberOfMatchesInString:testString
options:NSMatchingAnchored
range:NSMakeRange(0, testString.length)];
XCTAssertEqual(versionMatches, 1UL);
testString = @".0.1";
versionMatches = [osVersionRegex numberOfMatchesInString:testString
options:NSMatchingAnchored
range:NSMakeRange(0, testString.length)];
XCTAssertEqual(versionMatches, 0UL);

// Test Info.plist values.
NSString* minimumOSVersion = infoDictionary[@"MinimumOSVersion"];
versionMatches = [osVersionRegex numberOfMatchesInString:minimumOSVersion
options:NSMatchingAnchored
range:NSMakeRange(0, minimumOSVersion.length)];
XCTAssertEqual(versionMatches, 1UL);

// SHA length is 40.
XCTAssertEqual(((NSString*)infoDictionary[@"FlutterEngine"]).length, 40UL);

// {clang_version} placeholder is 15 characters. The clang string version
// is longer than that, so check if the placeholder has been replaced, without
// actually checking a literal string, which could be different on various machines.
XCTAssertTrue(((NSString*)infoDictionary[@"ClangVersion"]).length > 15UL);
}

- (void)testDeallocated {
__weak FlutterEngine* weakEngine = nil;
{
Expand Down
6 changes: 5 additions & 1 deletion shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,14 @@ if (is_mac && !embedder_for_target) {
outputs =
[ "$_flutter_embedder_framework_dir/Versions/A/Resources/Info.plist" ]
args = [
"--source",
rebase_path(sources[0]),
"--destination",
rebase_path(outputs[0]),
"--bitcode=$enable_bitcode",
]
if (enable_bitcode) {
args += [ "--bitcode" ]
}
}

copy("copy_module_map") {
Expand Down
4 changes: 2 additions & 2 deletions shell/platform/embedder/assets/EmbedderInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
<key>NSHumanReadableCopyright</key>
<string>Copyright 2013 The Flutter Authors. All rights reserved.</string>
<key>FlutterEngine</key>
<string>{0}</string>
<string>{revision}</string>
<key>ClangVersion</key>
<string>{1}</string>
<string>{clang_version}</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
0D6AB6BE22BB05E200EEE540 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB6BD22BB05E200EEE540 /* Assets.xcassets */; };
0D6AB6C122BB05E200EEE540 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB6BF22BB05E200EEE540 /* LaunchScreen.storyboard */; };
0D6AB6C422BB05E200EEE540 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6C322BB05E200EEE540 /* main.m */; };
0D6AB73F22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */; };
F7521D7826BB68BC005F15C5 /* libios_test_flutter.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = F7521D7226BB671E005F15C5 /* libios_test_flutter.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
F7521D7926BB68BC005F15C5 /* libocmock_shared.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = F7521D7526BB673E005F15C5 /* libocmock_shared.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
F77E081926FA9CE6003E6E4C /* Flutter.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = F77E081726FA9CE6003E6E4C /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -37,6 +37,7 @@
dstSubfolderSpec = 10;
files = (
F7521D7826BB68BC005F15C5 /* libios_test_flutter.dylib in Embed Libraries */,
F77E081926FA9CE6003E6E4C /* Flutter.framework in Embed Libraries */,
F7521D7926BB68BC005F15C5 /* libocmock_shared.dylib in Embed Libraries */,
);
name = "Embed Libraries";
Expand Down Expand Up @@ -70,6 +71,7 @@
0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = "<group>"; };
F7521D7226BB671E005F15C5 /* libios_test_flutter.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libios_test_flutter.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libios_test_flutter.dylib"; sourceTree = "<group>"; };
F7521D7526BB673E005F15C5 /* libocmock_shared.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libocmock_shared.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libocmock_shared.dylib"; sourceTree = "<group>"; };
F77E081726FA9CE6003E6E4C /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = "../../../../out/$(FLUTTER_ENGINE)/Flutter.framework"; sourceTree = "<group>"; };
F7A3FDE026B9E0A300EADD61 /* FlutterAppDelegateTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterAppDelegateTest.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -167,6 +169,7 @@
0D6AB6FC22BC1BC300EEE540 /* Frameworks */ = {
isa = PBXGroup;
children = (
F77E081726FA9CE6003E6E4C /* Flutter.framework */,
F7521D7226BB671E005F15C5 /* libios_test_flutter.dylib */,
F7521D7526BB673E005F15C5 /* libocmock_shared.dylib */,
);
Expand Down Expand Up @@ -264,7 +267,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D6AB73F22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig in Resources */,
Copy link
Member Author

Choose a reason for hiding this comment

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

Not related to this change, but xcconfigs should not be embedded as resources in the app.

Copy link
Member

Choose a reason for hiding this comment

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

Good catch.

);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down