Add a sample project that allow to target iOS This sample project allows building app bundles, framework bundles and to build target on the host (e.g. to build a tool to preprocess some file on the host before compiling them for iOS). This sample does not support code-signature of the bundles (but it will still embeds the necessary entitlements into the application to allow debugging when targetting a simulator build). This sample targets iOS 13.0+ SDK but can be adapted to target older SDKs (only the demo application depends on a recent version of the SDK). Bug: 21 Change-Id: I80dff2c8eba989f84073bc3dd35326939b307080 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/6661 Commit-Queue: Sylvain Defresne <sdefresne@chromium.org> Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/examples/ios/.gitignore b/examples/ios/.gitignore new file mode 100644 index 0000000..6a3417b --- /dev/null +++ b/examples/ios/.gitignore
@@ -0,0 +1 @@ +/out/
diff --git a/examples/ios/.gn b/examples/ios/.gn new file mode 100644 index 0000000..e5b6d4a --- /dev/null +++ b/examples/ios/.gn
@@ -0,0 +1,2 @@ +# The location of the build configuration file. +buildconfig = "//build/BUILDCONFIG.gn"
diff --git a/examples/ios/BUILD.gn b/examples/ios/BUILD.gn new file mode 100644 index 0000000..bd71b4f --- /dev/null +++ b/examples/ios/BUILD.gn
@@ -0,0 +1,10 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +group("all") { + deps = [ + "//app:hello", + "//host:username", + ] +}
diff --git a/examples/ios/app/AppDelegate.h b/examples/ios/app/AppDelegate.h new file mode 100644 index 0000000..54d31eb --- /dev/null +++ b/examples/ios/app/AppDelegate.h
@@ -0,0 +1,9 @@ +// Copyright 2019 The Chromium 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 <UIKit/UIKit.h> + +@interface AppDelegate : UIResponder <UIApplicationDelegate> + +@end
diff --git a/examples/ios/app/AppDelegate.m b/examples/ios/app/AppDelegate.m new file mode 100644 index 0000000..32b3d44 --- /dev/null +++ b/examples/ios/app/AppDelegate.m
@@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium 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 "app/AppDelegate.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + return YES; +} + +#pragma mark - UISceneSession lifecycle + +- (UISceneConfiguration*)application:(UIApplication*)application + configurationForConnectingSceneSession: + (UISceneSession*)connectingSceneSession + options:(UISceneConnectionOptions*)options { + return + [[UISceneConfiguration alloc] initWithName:@"Default Configuration" + sessionRole:connectingSceneSession.role]; +} + +- (void)application:(UIApplication*)application + didDiscardSceneSessions:(NSSet<UISceneSession*>*)sceneSessions { +} + +@end
diff --git a/examples/ios/app/BUILD.gn b/examples/ios/app/BUILD.gn new file mode 100644 index 0000000..eb4b8a9 --- /dev/null +++ b/examples/ios/app/BUILD.gn
@@ -0,0 +1,41 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/templates/ios_app_bundle.gni") +import("//build/config/ios/templates/storyboards.gni") + +ios_app_bundle("hello") { + output_name = "Hello" + + info_plist = "resources/Info.plist" + + sources = [ + "AppDelegate.h", + "AppDelegate.m", + "SceneDelegate.h", + "SceneDelegate.m", + "ViewController.h", + "ViewController.m", + "main.m", + ] + + frameworks = [ + "CoreGraphics.framework", + "Foundation.framework", + "UIKit.framework", + ] + + deps = [ + ":storyboards", + "//shared:hello_framework", + "//shared:hello_framework+bundle", + ] +} + +storyboards("storyboards") { + sources = [ + "resources/LaunchScreen.storyboard", + "resources/Main.storyboard", + ] +}
diff --git a/examples/ios/app/SceneDelegate.h b/examples/ios/app/SceneDelegate.h new file mode 100644 index 0000000..ef1888f --- /dev/null +++ b/examples/ios/app/SceneDelegate.h
@@ -0,0 +1,11 @@ +// Copyright 2019 The Chromium 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 <UIKit/UIKit.h> + +@interface SceneDelegate : UIResponder <UIWindowSceneDelegate> + +@property(strong, nonatomic) UIWindow* window; + +@end
diff --git a/examples/ios/app/SceneDelegate.m b/examples/ios/app/SceneDelegate.m new file mode 100644 index 0000000..5e20b9e --- /dev/null +++ b/examples/ios/app/SceneDelegate.m
@@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium 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 "app/SceneDelegate.h" + +@implementation SceneDelegate + +- (void)scene:(UIScene*)scene + willConnectToSession:(UISceneSession*)session + options:(UISceneConnectionOptions*)connectionOptions { +} + +- (void)sceneDidDisconnect:(UIScene*)scene { +} + +- (void)sceneDidBecomeActive:(UIScene*)scene { +} + +- (void)sceneWillResignActive:(UIScene*)scene { +} + +- (void)sceneWillEnterForeground:(UIScene*)scene { +} + +- (void)sceneDidEnterBackground:(UIScene*)scene { +} + +@end
diff --git a/examples/ios/app/ViewController.h b/examples/ios/app/ViewController.h new file mode 100644 index 0000000..4076e40 --- /dev/null +++ b/examples/ios/app/ViewController.h
@@ -0,0 +1,9 @@ +// Copyright 2019 The Chromium 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 <UIKit/UIKit.h> + +@interface ViewController : UIViewController + +@end
diff --git a/examples/ios/app/ViewController.m b/examples/ios/app/ViewController.m new file mode 100644 index 0000000..e0a80cb --- /dev/null +++ b/examples/ios/app/ViewController.m
@@ -0,0 +1,31 @@ +// Copyright 2019 The Chromium 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 <HelloShared/HelloShared.h> + +#import "app/ViewController.h" + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + UILabel* label = [self labelWithText:[Greetings greet]]; + [self addCenteredView:label toParentView:self.view]; +} + +- (UILabel*)labelWithText:(NSString*)text { + UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero]; + label.text = text; + [label sizeToFit]; + return label; +} + +- (void)addCenteredView:(UIView*)view toParentView:(UIView*)parentView { + view.center = [parentView convertPoint:parentView.center + fromView:parentView.superview]; + [parentView addSubview:view]; +} + +@end
diff --git a/examples/ios/app/main.m b/examples/ios/app/main.m new file mode 100644 index 0000000..b01cb7a --- /dev/null +++ b/examples/ios/app/main.m
@@ -0,0 +1,15 @@ +// Copyright 2019 The Chromium 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 <UIKit/UIKit.h> +#import "app/AppDelegate.h" + +int main(int argc, char** argv) { + NSString* appDelegateClassName; + @autoreleasepool { + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +}
diff --git a/examples/ios/app/resources/Info.plist b/examples/ios/app/resources/Info.plist new file mode 100644 index 0000000..7b6037c --- /dev/null +++ b/examples/ios/app/resources/Info.plist
@@ -0,0 +1,64 @@ +<?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>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>UIApplicationSceneManifest</key> + <dict> + <key>UIApplicationSupportsMultipleScenes</key> + <false/> + <key>UISceneConfigurations</key> + <dict> + <key>UIWindowSceneSessionRoleApplication</key> + <array> + <dict> + <key>UISceneConfigurationName</key> + <string>Default Configuration</string> + <key>UISceneDelegateClassName</key> + <string>SceneDelegate</string> + <key>UISceneStoryboardFile</key> + <string>Main</string> + </dict> + </array> + </dict> + </dict> + <key>UILaunchStoryboardName</key> + <string>LaunchScreen</string> + <key>UIMainStoryboardFile</key> + <string>Main</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>armv7</string> + </array> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> +</dict> +</plist>
diff --git a/examples/ios/app/resources/LaunchScreen.storyboard b/examples/ios/app/resources/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/examples/ios/app/resources/LaunchScreen.storyboard
@@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> + <dependencies> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> + <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="EHf-IW-A2E"> + <objects> + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> + <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="53" y="375"/> + </scene> + </scenes> +</document>
diff --git a/examples/ios/app/resources/Main.storyboard b/examples/ios/app/resources/Main.storyboard new file mode 100644 index 0000000..808a21c --- /dev/null +++ b/examples/ios/app/resources/Main.storyboard
@@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> + <dependencies> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> + <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="tne-QT-ifu"> + <objects> + <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> + <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + </objects> + </scene> + </scenes> +</document>
diff --git a/examples/ios/build/BUILD.gn b/examples/ios/build/BUILD.gn new file mode 100644 index 0000000..aa51efa --- /dev/null +++ b/examples/ios/build/BUILD.gn
@@ -0,0 +1,68 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("compiler") { + configs = [ + ":include_dirs", + ":cpp_standard", + ":objc_use_arc", + ":objc_abi_version", + ] +} + +config("shared_binary") { + if (current_os == "ios" || current_os == "mac") { + configs = [ ":rpath_config" ] + } +} + +config("objc_abi_version") { + cflags_objc = [ "-fobjc-abi-version=2" ] + cflags_objcc = cflags_objc + ldflags = [ + "-Xlinker", + "-objc_abi_version", + "-Xlinker", + "2", + ] +} + +config("include_dirs") { + include_dirs = [ + "//", + root_out_dir, + ] +} + +config("objc_use_arc") { + cflags_objc = [ + "-fobjc-arc", + "-fobjc-weak", + ] + cflags_objcc = cflags_objc +} + +config("cpp_standard") { + cflags_c = [ "--std=c11" ] + cflags_cc = [ + "--std=c++17", + "--stdlib=libc++", + ] + ldflags = [ "--stdlib=libc++" ] +} + +if (current_os == "ios" || current_os == "mac") { + config("rpath_config") { + ldflags = [ + "-Xlinker", + "-rpath", + "-Xlinker", + "@executable_path/Frameworks", + "-Xlinker", + "-rpath", + "-Xlinker", + "@loader_path/Frameworks", + ] + } +}
diff --git a/examples/ios/build/BUILDCONFIG.gn b/examples/ios/build/BUILDCONFIG.gn new file mode 100644 index 0000000..53ee3d9 --- /dev/null +++ b/examples/ios/build/BUILDCONFIG.gn
@@ -0,0 +1,43 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (target_os == "") { + target_os = "ios" +} +if (target_cpu == "") { + target_cpu = host_cpu +} +if (current_cpu == "") { + current_cpu = target_cpu +} +if (current_os == "") { + current_os = target_os +} + +# All binary targets will get this list of configs by default. +_shared_binary_target_configs = [ "//build:compiler" ] + +# Apply that default list to the binary target types. +set_defaults("executable") { + configs = _shared_binary_target_configs + configs += [ "//build:shared_binary" ] +} +set_defaults("static_library") { + configs = _shared_binary_target_configs +} +set_defaults("shared_library") { + configs = _shared_binary_target_configs + configs += [ "//build:shared_binary" ] +} +set_defaults("source_set") { + configs = _shared_binary_target_configs +} + +set_default_toolchain("//build/toolchain/$target_os:clang_$target_cpu") + +if (target_os == "ios") { + host_toolchain = "//build/toolchain/$host_os:clang_$host_cpu" +} else { + host_toolchain = default_toolchain +}
diff --git a/examples/ios/build/config/ios/BUILD.gn b/examples/ios/build/config/ios/BUILD.gn new file mode 100644 index 0000000..47902c9 --- /dev/null +++ b/examples/ios/build/config/ios/BUILD.gn
@@ -0,0 +1,24 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/sdk_info.gni") +import("//build/config/ios/templates/merge_plist.gni") + +merge_plist("compiler_plist") { + substitutions = { + COMPILER_NAME = sdk_info.compiler + MACOS_BUILD = sdk_info.macos_build + PLATFORM_BUILD = sdk_info.sdk_build + PLATFORM_DISPLAY_NAME = sdk_info.platform_name + PLATFORM_NAME = sdk_info.platform + PLATFORM_VERSION = sdk_info.sdk_version + SDK_BUILD = sdk_info.sdk_build + SDK_NAME = sdk_info.sdk + XCODE_BUILD = sdk_info.xcode_build + XCODE_VERSION = sdk_info.xcode_version + } + + output = "$target_out_dir/compiler_plist/Info.plist" + plists = [ "//build/config/ios/resources/compiler-Info.plist" ] +}
diff --git a/examples/ios/build/config/ios/bundle_identifier_prefix.gni b/examples/ios/build/config/ios/bundle_identifier_prefix.gni new file mode 100644 index 0000000..97c47cc --- /dev/null +++ b/examples/ios/build/config/ios/bundle_identifier_prefix.gni
@@ -0,0 +1,8 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + # Default bundle identifier prefix. + default_bundle_identifier_prefix = "com.google" +}
diff --git a/examples/ios/build/config/ios/deployment_target.gni b/examples/ios/build/config/ios/deployment_target.gni new file mode 100644 index 0000000..7180488 --- /dev/null +++ b/examples/ios/build/config/ios/deployment_target.gni
@@ -0,0 +1,9 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + # Maximum deployment target. Automatically detected by sdk_info.py but + # needs to be specified to a version < 11.0 if targetting 32-bit archs. + ios_deployment_target = "13.0" +}
diff --git a/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist b/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist new file mode 100644 index 0000000..0a4badf --- /dev/null +++ b/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist
@@ -0,0 +1,12 @@ +<?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>application-identifier</key> + <string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string> + <key>keychain-access-groups</key> + <array> + <string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string> + </array> +</dict> +</plist>
diff --git a/examples/ios/build/config/ios/resources/Info.plist b/examples/ios/build/config/ios/resources/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/examples/ios/build/config/ios/resources/Info.plist
@@ -0,0 +1,22 @@ +<?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>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>$(CURRENT_PROJECT_VERSION)</string> +</dict> +</plist>
diff --git a/examples/ios/build/config/ios/resources/compiler-Info.plist b/examples/ios/build/config/ios/resources/compiler-Info.plist new file mode 100644 index 0000000..0a02517 --- /dev/null +++ b/examples/ios/build/config/ios/resources/compiler-Info.plist
@@ -0,0 +1,33 @@ +<?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>DTSDKName</key> + <string>$(SDK_NAME)</string> + <key>DTXcode</key> + <string>$(XCODE_VERSION)</string> + <key>DTSDKBuild</key> + <string>$(SDK_BUILD)</string> + <key>BuildMachineOSBuild</key> + <string>$(MACOS_BUILD)</string> + <key>DTPlatformName</key> + <string>$(PLATFORM_NAME)</string> + <key>DTCompiler</key> + <string>$(COMPILER_NAME)</string> + <key>DTPlatformVersion</key> + <string>$(PLATFORM_VERSION)</string> + <key>DTXcodeBuild</key> + <string>$(XCODE_BUILD)</string> + <key>DTPlatformBuild</key> + <string>$(PLATFORM_BUILD)</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>$(PLATFORM_DISPLAY_NAME)</string> + </array> + <key>UIDeviceFamily</key> + <array> + <string>1</string> + <string>2</string> + </array> +</dict> +</plist>
diff --git a/examples/ios/build/config/ios/scripts/compile_storyboard.py b/examples/ios/build/config/ios/scripts/compile_storyboard.py new file mode 100644 index 0000000..2fa1d20 --- /dev/null +++ b/examples/ios/build/config/ios/scripts/compile_storyboard.py
@@ -0,0 +1,53 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Compiles a .storyboard file. +""" + +import argparse +import os +import subprocess +import sys + + +def CompileStoryboard(storyboard, out, ios_deployment_target): + """Compiles |storyboard| storyboard to |out| for |ios_deployment_target|.""" + subprocess.check_call([ + 'ibtool', '--target-device', 'iphone', '--target-device', 'ipad', + '--auto-activate-custom-fonts', '--minimum-deployment-target', + ios_deployment_target, '--compilation-directory', out, + storyboard, + ]) + + +def ParseArgs(argv): + """Parses command line arguments.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + 'input', + help='path to the .storyboard file to compile') + parser.add_argument( + '-o', '--output', required=True, + help='path to the result') + parser.add_argument( + '-t', '--minimum-deployment-target', required=True, + help='iOS deployment target') + + return parser.parse_args(argv) + + +def main(argv): + args = ParseArgs(argv) + + CompileStoryboard( + os.path.abspath(args.input), + os.path.dirname(os.path.abspath(args.output)), + args.minimum_deployment_target) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py b/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py new file mode 100644 index 0000000..cf38b10 --- /dev/null +++ b/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py
@@ -0,0 +1,119 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Finds the team identifier to use for code signing bundle given its +bundle identifier. +""" + +import argparse +import fnmatch +import glob +import json +import os +import plistlib +import subprocess +import sys + + +class ProvisioningProfile(object): + + def __init__(self, mobileprovision_path): + self._path = mobileprovision_path + self._data = plistlib.readPlistFromString( + subprocess.check_output( + ['security', 'cms', '-D', '-i', mobileprovision_path])) + + @property + def application_identifier_pattern(self): + return self._data.get('Entitlements', {}).get('application-identifier', '') + + @property + def app_identifier_prefix(self): + return self._data.get('ApplicationIdentifierPrefix', [''])[0] + + def ValidToSignBundle(self, bundle_identifier): + """Returns whether the provisioning profile can sign |bundle_identifier|.""" + return fnmatch.fnmatch( + self.app_identifier_prefix + '.' + bundle_identifier, + self.application_identifier_pattern) + + +def GetProvisioningProfilesDir(): + """Returns the location of the locally installed provisioning profiles.""" + return os.path.join( + os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') + + +def ListProvisioningProfiles(): + """Returns a list of all installed provisioning profiles.""" + return glob.glob( + os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision')) + + +def LoadProvisioningProfile(mobileprovision_path): + """Loads the Apple Property List embedded in |mobileprovision_path|.""" + return ProvisioningProfile(mobileprovision_path) + + +def ListValidProvisioningProfiles(bundle_identifier): + """Returns a list of provisioning profile valid for |bundle_identifier|.""" + result = [] + for mobileprovision_path in ListProvisioningProfiles(): + mobileprovision = LoadProvisioningProfile(mobileprovision_path) + if mobileprovision.ValidToSignBundle(bundle_identifier): + result.append(mobileprovision) + return result + + +def FindProvisioningProfile(bundle_identifier): + """Returns the path to the provisioning profile for |bundle_identifier|.""" + return max( + ListValidProvisioningProfiles(bundle_identifier), + key=lambda p: len(p.application_identifier_pattern)) + + +def GenerateSubsitutions(bundle_identifier, mobileprovision): + if mobileprovision: + app_identifier_prefix = mobileprovision.app_identifier_prefix + '.' + else: + app_identifier_prefix = '*.' + + return { + 'CFBundleIdentifier': bundle_identifier, + 'AppIdentifierPrefix': app_identifier_prefix + } + + +def ParseArgs(argv): + """Parses command line arguments.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '-b', '--bundle-identifier', required=True, + help='bundle identifier for the application') + parser.add_argument( + '-o', '--output', default='-', + help='path to the result; - means stdout') + + return parser.parse_args(argv) + + +def main(argv): + args = ParseArgs(argv) + + mobileprovision = FindProvisioningProfile(args.bundle_identifier) + substitutions = GenerateSubsitutions(args.bundle_identifier, mobileprovision) + + if args.output == '-': + sys.stdout.write(json.dumps(substitutions)) + else: + with open(args.output, 'w') as output: + output.write(json.dumps(substitutions)) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/examples/ios/build/config/ios/scripts/generate_umbrella_header.py b/examples/ios/build/config/ios/scripts/generate_umbrella_header.py new file mode 100644 index 0000000..1137376 --- /dev/null +++ b/examples/ios/build/config/ios/scripts/generate_umbrella_header.py
@@ -0,0 +1,54 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Generates an umbrella header file that #import all public header of a +binary framework. +""" + +import argparse +import os +import sys + + +def GenerateImport(header): + """Returns a string for importing |header|.""" + return '#import "%s"\n' % os.path.basename(header) + + +def GenerateUmbrellaHeader(headers): + """Returns a string with the content of the umbrella header.""" + return ''.join([ GenerateImport(header) for header in headers ]) + + +def ParseArgs(argv): + """Parses command line arguments.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + 'headers', nargs='+', + help='path to the public heeaders') + parser.add_argument( + '-o', '--output', default='-', + help='path of the output file to create; - means stdout') + + return parser.parse_args(argv) + + +def main(argv): + args = ParseArgs(argv) + + content = GenerateUmbrellaHeader(args.headers) + + if args.output == '-': + sys.stdout.write(content) + else: + with open(args.output, 'w') as output: + output.write(content) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/examples/ios/build/config/ios/scripts/merge_plist.py b/examples/ios/build/config/ios/scripts/merge_plist.py new file mode 100644 index 0000000..452cf53 --- /dev/null +++ b/examples/ios/build/config/ios/scripts/merge_plist.py
@@ -0,0 +1,134 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Merges multiple Apple Property List files (.plist) and perform variables +substitutions $(VARIABLE) in the Property List string values. +""" + +import argparse +import json +import re +import subprocess +import sys + + +# Pattern representing a variable to substitue in a string value. +VARIABLE_PATTERN = re.compile(r'\$\(([^)]*)\)') + + +def LoadPlist(plist_path): + """Loads Apple Property List file at |plist_path|.""" + return json.loads( + subprocess.check_output( + ['plutil', '-convert', 'json', '-o', '-', plist_path])) + + +def SavePlist(plist_path, content, format): + """Saves |content| as Apple Property List in |format| at |plist_path|.""" + proc = subprocess.Popen( + ['plutil', '-convert', format, '-o', plist_path, '-'], + stdin=subprocess.PIPE) + output, _ = proc.communicate(json.dumps(content)) + if proc.returncode: + raise subprocess.CalledProcessError( + proc.returncode, + ['plutil', '-convert', format, '-o', plist_path, '-'], + output) + + +def MergeObjects(obj1, obj2): + """Merges two objects (either dictionary, list, string or numbers).""" + if type(obj1) != type(obj2): + return obj2 + + if isinstance(obj2, dict): + result = dict(obj1) + for key in obj2: + value1 = obj1.get(key, None) + value2 = obj2.get(key, None) + result[key] = MergeObjects(value1, value2) + return result + + if isinstance(obj2, list): + return obj1 + obj2 + + return obj2 + + +def MergePlists(plist_paths): + """Loads and merges all Apple Property List files at |plist_paths|.""" + plist = {} + for plist_path in plist_paths: + plist = MergeObjects(plist, LoadPlist(plist_path)) + return plist + + +def PerformSubstitutions(plist, substitutions): + """Performs variables substitutions in |plist| given by |substitutions|.""" + if isinstance(plist, dict): + result = dict(plist) + for key in plist: + result[key] = PerformSubstitutions(plist[key], substitutions) + return result + + if isinstance(plist, list): + return [ PerformSubstitutions(item, substitutions) for item in plist ] + + if isinstance(plist, (str, unicode)): + result = plist + while True: + match = VARIABLE_PATTERN.search(result) + if not match: + break + + extent = match.span() + expand = substitutions[match.group(1)] + result = result[:extent[0]] + expand + result[extent[1]:] + return result + + return plist + + +def PerformSubstitutionsFrom(plist, substitutions_path): + """Performs variable substitutions in |plist| from |substitutions_path|.""" + with open(substitutions_path) as substitutions_file: + return PerformSubstitutions(plist, json.load(substitutions_file)) + + +def ParseArgs(argv): + """Parses command line arguments.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '-s', '--substitutions', + help='path to a JSON file containing variable substitutions') + parser.add_argument( + '-f', '--format', default='json', choices=('json', 'binary1', 'xml1'), + help='format of the generated file') + parser.add_argument( + '-o', '--output', default='-', + help='path to the result; - means stdout') + parser.add_argument( + 'inputs', nargs='+', + help='path of the input files to merge') + + return parser.parse_args(argv) + + +def main(argv): + args = ParseArgs(argv) + + data = MergePlists(args.inputs) + if args.substitutions: + data = PerformSubstitutionsFrom( + data, args.substitutions) + + SavePlist(args.output, data, args.format) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/examples/ios/build/config/ios/scripts/sdk_info.py b/examples/ios/build/config/ios/scripts/sdk_info.py new file mode 100644 index 0000000..827aee0 --- /dev/null +++ b/examples/ios/build/config/ios/scripts/sdk_info.py
@@ -0,0 +1,147 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Collects information about the SDK and return them as JSON file.""" + +import argparse +import json +import re +import subprocess +import sys + +# Patterns used to extract the Xcode version and build version. +XCODE_VERSION_PATTERN = re.compile(r'Xcode (\d+)\.(\d+)') +XCODE_BUILD_PATTERN = re.compile(r'Build version (.*)') + + +def GetAppleCpuName(target_cpu): + """Returns the name of the |target_cpu| using Apple's convention.""" + return { + 'x64': 'x86_64', + 'arm': 'armv7', + 'x86': 'i386' + }.get(target_cpu, target_cpu) + + +def IsSimulator(target_cpu): + """Returns whether the |target_cpu| corresponds to a simulator build.""" + return not target_cpu.startswith('arm') + + +def GetPlatform(target_cpu): + """Returns the platform for the |target_cpu|.""" + if IsSimulator(target_cpu): + return 'iphonesimulator' + else: + return 'iphoneos' + + +def GetPlaformDisplayName(target_cpu): + """Returns the platform display name for the |target_cpu|.""" + if IsSimulator(target_cpu): + return 'iPhoneSimulator' + else: + return 'iPhoneOS' + + +def ExtractOSVersion(): + """Extract the version of macOS of the current machine.""" + return subprocess.check_output(['sw_vers', '-buildVersion']).strip() + + +def ExtractXcodeInfo(): + """Extract Xcode version and build version.""" + version, build = None, None + for line in subprocess.check_output(['xcodebuild', '-version']).splitlines(): + match = XCODE_VERSION_PATTERN.search(line) + if match: + major, minor = match.group(1), match.group(2) + version = major.rjust(2, '0') + minor.ljust(2, '0') + continue + + match = XCODE_BUILD_PATTERN.search(line) + if match: + build = match.group(1) + continue + + assert version is not None and build is not None + return version, build + + +def ExtractSDKInfo(info, sdk): + """Extract information about the SDK.""" + return subprocess.check_output( + ['xcrun', '--sdk', sdk, '--show-sdk-' + info]).strip() + + +def GetSDKInfoForCpu(target_cpu, sdk_version, deployment_target): + """Returns a dictionary with information about the SDK.""" + platform = GetPlatform(target_cpu) + sdk_version = sdk_version or ExtractSDKInfo('version', platform) + deployment_target = deployment_target or sdk_version + + target = target_cpu + '-apple-ios' + deployment_target + if IsSimulator(target_cpu): + target = target + '-simulator' + + xcode_version, xcode_build = ExtractXcodeInfo() + effective_sdk = platform + sdk_version + + sdk_info = {} + sdk_info['compiler'] = 'com.apple.compilers.llvm.clang.1_0' + sdk_info['is_simulator'] = IsSimulator(target_cpu) + sdk_info['macos_build'] = ExtractOSVersion() + sdk_info['platform'] = platform + sdk_info['platform_name'] = GetPlaformDisplayName(target_cpu) + sdk_info['sdk'] = effective_sdk + sdk_info['sdk_build'] = ExtractSDKInfo('build-version', effective_sdk) + sdk_info['sdk_path'] = ExtractSDKInfo('path', effective_sdk) + sdk_info['sdk_version'] = sdk_version + sdk_info['target'] = target + sdk_info['xcode_build'] = xcode_build + sdk_info['xcode_version'] = xcode_version + + return sdk_info + + +def ParseArgs(argv): + """Parses command line arguments.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '-t', '--target-cpu', default='x64', + choices=('x86', 'x64', 'arm', 'arm64'), + help='target cpu') + parser.add_argument( + '-s', '--sdk-version', + help='version of the sdk') + parser.add_argument( + '-d', '--deployment-target', + help='iOS deployment target') + parser.add_argument( + '-o', '--output', default='-', + help='path of the output file to create; - means stdout') + + return parser.parse_args(argv) + + +def main(argv): + args = ParseArgs(argv) + + sdk_info = GetSDKInfoForCpu( + GetAppleCpuName(args.target_cpu), + args.sdk_version, + args.deployment_target) + + if args.output == '-': + sys.stdout.write(json.dumps(sdk_info)) + else: + with open(args.output, 'w') as output: + output.write(json.dumps(sdk_info)) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/examples/ios/build/config/ios/sdk_info.gni b/examples/ios/build/config/ios/sdk_info.gni new file mode 100644 index 0000000..90b8631 --- /dev/null +++ b/examples/ios/build/config/ios/sdk_info.gni
@@ -0,0 +1,14 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/deployment_target.gni") + +sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py", + [ + "--target-cpu", + current_cpu, + "--deployment-target", + ios_deployment_target, + ], + "json")
diff --git a/examples/ios/build/config/ios/templates/ios_app_bundle.gni b/examples/ios/build/config/ios/templates/ios_app_bundle.gni new file mode 100644 index 0000000..e83a79b --- /dev/null +++ b/examples/ios/build/config/ios/templates/ios_app_bundle.gni
@@ -0,0 +1,159 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/bundle_identifier_prefix.gni") +import("//build/config/ios/sdk_info.gni") +import("//build/config/ios/templates/ios_binary_bundle.gni") +import("//build/config/ios/templates/merge_plist.gni") + +# Template to generate an app bundle. +# +# All the other parameters are forwarded to a shared_library target that will +# generate the bundle binary. In general, you want to pass at least "sources" +# or "deps" to have some binary objects included in your shared library. +# +# Arguments +# +# - info_plist (optional) +# +# path to additional Info.plist to merge into the final bundle Info.plist +# +# - bundle_identifier_prefix (optional) +# +# prefix for the bundle identifier (the full identifier will be defined +# to $bundle_identifier_prefix.$output_name); if unset will defaults to +# default_bundle_identifier_prefix +# +# - output_name (optional) +# +# name of the bundle without the extension; defaults to $target_name +# +template("ios_app_bundle") { + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _bundle_identifier_prefix = default_bundle_identifier_prefix + if (defined(invoker.bundle_identifier_prefix)) { + _bundle_identifier_prefix = invoker.bundle_identifier_prefix + } + + _bundle_identifier = "$_bundle_identifier_prefix.$_output_name" + + _app_prefix_target = target_name + "_app_prefix" + _app_prefix_output = "$target_out_dir/$_app_prefix_target/app_prefix.json" + + action(_app_prefix_target) { + script = "//build/config/ios/scripts/find_app_identifier_prefix.py" + sources = [] + outputs = [ + _app_prefix_output, + ] + args = [ + "-b=" + _bundle_identifier, + "-o=" + rebase_path(_app_prefix_output, root_build_dir), + ] + } + + if (sdk_info.is_simulator) { + _simu_xcent_target = target_name + "_simu_xcent" + _simu_xcent_output = + "$target_out_dir/$_simu_xcent_target/" + "Entitlements-Simulated.plist" + + merge_plist(_simu_xcent_target) { + format = "xml1" + output = _simu_xcent_output + plists = [ "//build/config/ios/resources/Entitlements-Simulated.plist" ] + substitutions_json = _app_prefix_output + deps = [ + ":$_app_prefix_target", + ] + } + } + + _executable_target = target_name + "_executable" + _executable_bundle = target_name + "_executable_bundle" + + executable(_executable_target) { + forward_variables_from(invoker, + "*", + [ + "bundle_extension", + "bundle_identifier_prefix", + "bundle_type", + "display_name", + "info_plist", + "output_name", + "public_headers", + ]) + + output_extension = "" + output_name = _output_name + output_prefix_override = true + output_dir = "$target_out_dir/$_executable_target" + + if (sdk_info.is_simulator) { + if (!defined(deps)) { + deps = [] + } + if (!defined(inputs)) { + inputs = [] + } + if (!defined(ldflags)) { + ldflags = [] + } + + deps += [ ":$_simu_xcent_target" ] + inputs += [ _simu_xcent_output ] + ldflags += [ + "-Xlinker", + "-sectcreate", + "-Xlinker", + "__TEXT", + "-Xlinker", + "__entitlements", + "-Xlinker", + rebase_path(_simu_xcent_output, root_build_dir), + ] + } + } + + bundle_data(_executable_bundle) { + public_deps = [ + ":$_executable_target", + ] + sources = [ + "$target_out_dir/$_executable_target/$_output_name", + ] + outputs = [ + "{{bundle_executable_dir}}/{{source_file_part}}", + ] + } + + ios_binary_bundle(target_name) { + forward_variables_from(invoker, + "*", + [ + "bundle_extension", + "bundle_identifier_prefix", + "bundle_type", + "deps", + "output_name", + "public_deps", + "public_headers", + ]) + + output_name = _output_name + product_type = "com.apple.product-type.application" + + bundle_identifier = _bundle_identifier + bundle_extension = "app" + bundle_type = "AAPL" + + public_deps = [ + ":$_executable_bundle", + ] + } +}
diff --git a/examples/ios/build/config/ios/templates/ios_binary_bundle.gni b/examples/ios/build/config/ios/templates/ios_binary_bundle.gni new file mode 100644 index 0000000..0089cd7 --- /dev/null +++ b/examples/ios/build/config/ios/templates/ios_binary_bundle.gni
@@ -0,0 +1,129 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/templates/merge_plist.gni") + +# Template to create an Apple bundle containing a binary file (e.g. .app or +# .framework bundle). +# +# Arguments +# +# - bundle_extension +# +# extension of the bundle (e.g. "app", "framework", ...); must not +# include the dot preceding the extension +# +# - bundle_type +# +# four letter code corresponding to the bundle type ("FMWK", "AAPL", +# ...); used to fill the "Bundle OS Type code" value in the generated +# Info.plist for the bundle +# +# - bundle_identitier +# +# bundle identitifier +# +# - product_type +# +# type of the generated bundle (used for Xcode project) +# +# - output_name (optional) +# +# name of the bundle without the extension; the bundle binary (i.e. +# the application or the library) must have the same name; defaults +# to $target_name +# +# - display_name (optional) +# +# display name of the bundle (e.g. the name that is displayed to the +# user); defaults to $output_name +# +# - info_plist (optional) +# +# path to additional Info.plist to merge into the final bundle Info.plist +# +template("ios_binary_bundle") { + assert( + defined(invoker.bundle_extension), + "bundle_extension must be defined for ios_binary_bundle ($target_name)") + assert( + defined(invoker.bundle_identifier), + "bundle_identifier must be defined for ios_binary_bundle ($target_name)") + assert(defined(invoker.bundle_type), + "bundle_type must be defined for ios_binary_bundle ($target_name)") + assert(defined(invoker.product_type), + "product_type must be defined for ios_binary_bundle ($target_name)") + + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _display_name = _output_name + if (defined(invoker.display_name)) { + _display_name = invoker.display_name + } + + _plist_target = target_name + "_plist" + _plist_bundle = target_name + "_plist_bundle" + + merge_plist(_plist_target) { + substitutions = { + CURRENT_PROJECT_VERSION = "1" + DEVELOPMENT_LANGUAGE = "en" + EXECUTABLE_NAME = "$_output_name" + PRODUCT_BUNDLE_IDENTIFIER = invoker.bundle_identifier + PRODUCT_BUNDLE_PACKAGE_TYPE = invoker.bundle_type + PRODUCT_NAME = "$_display_name" + } + + format = "binary1" + output = "$target_out_dir/$_plist_target/Info.plist" + plists = [ + get_label_info("//build/config/ios:compiler_plist", "target_out_dir") + + "/compiler_plist/Info.plist", + "//build/config/ios/resources/Info.plist", + ] + + if (defined(invoker.info_plist)) { + plists += [ invoker.info_plist ] + } + + deps = [ + "//build/config/ios:compiler_plist", + ] + } + + bundle_data(_plist_bundle) { + public_deps = [ + ":$_plist_target", + ] + sources = [ + "$target_out_dir/$_plist_target/Info.plist", + ] + outputs = [ + "{{bundle_contents_dir}}/Info.plist", + ] + } + + create_bundle(target_name) { + forward_variables_from(invoker, + "*", + [ + "display_name", + "output_name", + "bundle_extension", + "bundle_type", + ]) + + if (!defined(public_deps)) { + public_deps = [] + } + public_deps += [ ":$_plist_bundle" ] + bundle_root_dir = "$root_out_dir/$_output_name.${invoker.bundle_extension}" + bundle_contents_dir = bundle_root_dir + bundle_executable_dir = bundle_contents_dir + bundle_resources_dir = bundle_contents_dir + } +}
diff --git a/examples/ios/build/config/ios/templates/ios_framework_bundle.gni b/examples/ios/build/config/ios/templates/ios_framework_bundle.gni new file mode 100644 index 0000000..471f4f3 --- /dev/null +++ b/examples/ios/build/config/ios/templates/ios_framework_bundle.gni
@@ -0,0 +1,172 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/bundle_identifier_prefix.gni") +import("//build/config/ios/templates/ios_binary_bundle.gni") + +# Template to generate a framework bundle. +# +# All the other parameters are forwarded to a shared_library target that will +# generate the bundle binary. In general, you want to pass at least "sources" +# or "deps" to have some binary objects included in your shared library. +# +# Arguments +# +# - info_plist (optional) +# +# path to additional Info.plist to merge into the final bundle Info.plist +# +# - bundle_identifier_prefix (optional) +# +# prefix for the bundle identifier (the full identifier will be defined +# to $bundle_identifier_prefix.$output_name); if unset will defaults to +# default_bundle_identifier_prefix +# +# - output_name (optional) +# +# name of the bundle without the extension; defaults to $target_name +# +# - public_headers (optional) +# +# list of public headers files to copy into the framework bundle; this +# does not generate an umbrella header; an umbrella header named after +# the framework bundle will be created +# +template("ios_framework_bundle") { + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _dylib_target = target_name + "_dylib" + _dylib_bundle = target_name + "_dylib_bundle" + + _bundle_identifier_prefix = default_bundle_identifier_prefix + if (defined(invoker.bundle_identifier_prefix)) { + _bundle_identifier_prefix = invoker.bundle_identifier_prefix + } + + _bundle_identifier = "$_bundle_identifier_prefix.$_output_name" + + shared_library(_dylib_target) { + forward_variables_from(invoker, + "*", + [ + "bundle_extension", + "bundle_identifier_prefix", + "bundle_type", + "display_name", + "info_plist", + "output_name", + "public_headers", + ]) + + output_extension = "" + output_name = _output_name + output_prefix_override = true + output_dir = "$target_out_dir/$_dylib_target" + + if (!defined(ldflags)) { + ldflags = [] + } + ldflags += [ + "-Xlinker", + "-install_name", + "-Xlinker", + "@rpath/$_output_name.framework/$_output_name", + ] + } + + bundle_data(_dylib_bundle) { + public_deps = [ + ":$_dylib_target", + ] + sources = [ + "$target_out_dir/$_dylib_target/$_output_name", + ] + outputs = [ + "{{bundle_executable_dir}}/{{source_file_part}}", + ] + } + + if (defined(invoker.public_headers)) { + _umbrella_target = target_name + "_umbrella" + _umbrella_output = "$target_out_dir/$_umbrella_target/$_output_name.h" + + action(_umbrella_target) { + script = "//build/config/ios/scripts/generate_umbrella_header.py" + sources = [] + outputs = [ + _umbrella_output, + ] + args = [ "-o=" + rebase_path(_umbrella_output, root_build_dir) ] + + rebase_path(invoker.public_headers, root_build_dir) + } + + _headers_bundle = target_name + "_headers_bundle" + + bundle_data(_headers_bundle) { + sources = invoker.public_headers + [ _umbrella_output ] + outputs = [ + "{{bundle_resources_dir}}/Headers/{{source_file_part}}", + ] + public_deps = [ + ":$_umbrella_target", + ] + } + } + + _config_name = target_name + "_config" + + config(_config_name) { + framework_dirs = [ root_out_dir ] + frameworks = [ "$_output_name.framework" ] + } + + ios_binary_bundle(target_name) { + forward_variables_from(invoker, + "*", + [ + "bundle_extension", + "bundle_type", + "configs", + "deps", + "output_name", + "public_configs", + "public_deps", + "public_headers", + ]) + + output_name = _output_name + product_type = "com.apple.product-type.framework" + + bundle_identifier = _bundle_identifier + bundle_extension = "framework" + bundle_type = "FMWK" + + public_deps = [ + ":$_dylib_bundle", + ] + + if (defined(invoker.public_headers)) { + public_deps += [ ":$_headers_bundle" ] + } + + public_configs = [ ":$_config_name" ] + } + + _target_name = target_name + + bundle_data("$target_name+bundle") { + public_deps = [ + ":$_target_name", + ] + sources = [ + "$root_out_dir/$_output_name.framework", + ] + outputs = [ + "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}", + ] + } +}
diff --git a/examples/ios/build/config/ios/templates/merge_plist.gni b/examples/ios/build/config/ios/templates/merge_plist.gni new file mode 100644 index 0000000..61d3518 --- /dev/null +++ b/examples/ios/build/config/ios/templates/merge_plist.gni
@@ -0,0 +1,89 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Template to merge multiple Apple Property List file into a single file. +# +# Arguments +# +# - output +# +# path of the file that will be generated (must be in a sub-directory +# of root_build_dir) +# +# - plists +# +# list of path to Apple Property List file to merge (the file may be +# in either "json", "binary1" or "xml1" format) +# +# - format (optional) +# +# format in which the file must be saved; must be one of "json", +# "binary1" or "xml1" (default to "json" if omitted) +# +# - substitutions (optional) +# +# a scope defining variable substitutions to perform when merging the +# Property List files (i.e. if scope define foo = "bar", occurences +# of $(foo) in any string in a property list will be replaced by +# bar) +# +template("merge_plist") { + assert(defined(invoker.output) && invoker.output != "", + "output must be defined for merge_plist ($target_name)") + assert(defined(invoker.plists) && invoker.plists != [], + "plists must be defined for merge_plist ($target_name)") + + if (defined(invoker.substitutions)) { + assert(!defined(invoker.substitutions_json), + "cannot define both substitutions and substitutions_json") + + _substitutions_json = "$target_out_dir/$target_name/substitutions.json" + write_file(_substitutions_json, invoker.substitutions, "json") + } + + if (defined(invoker.substitutions_json)) { + _substitutions_json = invoker.substitutions_json + } + + action(target_name) { + forward_variables_from(invoker, + "*", + [ + "args", + "format", + "inputs", + "output", + "plists", + "script", + "sources", + "substitutions", + "substitutions_json", + ]) + + script = "//build/config/ios/scripts/merge_plist.py" + sources = invoker.plists + outputs = [ + invoker.output, + ] + + _format = "json" + if (defined(invoker.format)) { + _format = invoker.format + } + + args = [ + "-f=" + _format, + "-o=" + rebase_path(invoker.output, root_build_dir), + ] + + if (defined(_substitutions_json)) { + inputs = [ + _substitutions_json, + ] + args += [ "-s=" + rebase_path(_substitutions_json) ] + } + + args += rebase_path(sources, root_build_dir) + } +}
diff --git a/examples/ios/build/config/ios/templates/storyboards.gni b/examples/ios/build/config/ios/templates/storyboards.gni new file mode 100644 index 0000000..ecb5662 --- /dev/null +++ b/examples/ios/build/config/ios/templates/storyboards.gni
@@ -0,0 +1,37 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/deployment_target.gni") + +template("storyboards") { + assert(defined(invoker.sources), + "sources must be defined for storyboard ($target_name)") + + _compile_target = target_name + "_compile" + _compile_output = + "$target_out_dir/$_compile_target/{{source_name_part}}.storyboardc" + + action_foreach(_compile_target) { + script = "//build/config/ios/scripts/compile_storyboard.py" + sources = invoker.sources + outputs = [ + _compile_output, + ] + args = [ + "{{source}}", + "-o=" + rebase_path(_compile_output, root_build_dir), + "--minimum-deployment-target=$ios_deployment_target", + ] + } + + bundle_data(target_name) { + public_deps = [ + ":$_compile_target", + ] + sources = get_target_outputs(":$_compile_target") + outputs = [ + "{{bundle_root_dir}}/Base.lproj/{{source_file_part}}", + ] + } +}
diff --git a/examples/ios/build/toolchain/ios/BUILD.gn b/examples/ios/build/toolchain/ios/BUILD.gn new file mode 100644 index 0000000..758f449 --- /dev/null +++ b/examples/ios/build/toolchain/ios/BUILD.gn
@@ -0,0 +1,142 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/deployment_target.gni") + +template("ios_toolchain") { + toolchain(target_name) { + assert(defined(invoker.toolchain_args), + "Toolchains must declare toolchain_args") + + toolchain_args = { + forward_variables_from(invoker.toolchain_args, "*") + } + + _sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py", + [ + "--target-cpu", + current_cpu, + "--deployment-target", + ios_deployment_target, + ], + "json") + + cc = "clang -target ${_sdk_info.target} -isysroot ${_sdk_info.sdk_path}" + cxx = "clang++ -target ${_sdk_info.target} -isysroot ${_sdk_info.sdk_path}" + + tool("link") { + output = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = output + ".rsp" + rspfile_content = "{{inputs_newline}}" + + outputs = [ + output, + ] + command = "$cxx {{ldflags}} -o $output -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}" + description = "LINK {{output}}" + + default_output_dir = "{{root_out_dir}}" + default_output_extension = "" + output_prefix = "" + } + + tool("solink") { + dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = dylib + ".rsp" + rspfile_content = "{{inputs_newline}}" + + outputs = [ + dylib, + ] + command = "$cxx -dynamiclib {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}" + description = "SOLINK {{output}}" + + default_output_dir = "{{root_out_dir}}" + default_output_extension = ".dylib" + output_prefix = "lib" + } + + tool("cc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CC {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("cxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("objc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJC {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("objcxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJCXX {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("stamp") { + command = "touch {{output}}" + description = "STAMP {{output}}" + } + + tool("copy_bundle_data") { + command = "rm -rf {{output}} && cp -a {{source}} {{output}}" + description = "COPY_BUNDLE_DATA {{output}}" + } + } +} + +ios_toolchain("clang_x86") { + toolchain_args = { + current_cpu = "x86" + current_os = "ios" + } +} + +ios_toolchain("clang_x64") { + toolchain_args = { + current_cpu = "x64" + current_os = "ios" + } +} + +ios_toolchain("clang_arm") { + toolchain_args = { + current_cpu = "arm" + current_os = "ios" + } +} + +ios_toolchain("clang_arm64") { + toolchain_args = { + current_cpu = "arm64" + current_os = "ios" + } +}
diff --git a/examples/ios/build/toolchain/mac/BUILD.gn b/examples/ios/build/toolchain/mac/BUILD.gn new file mode 100644 index 0000000..30937dd --- /dev/null +++ b/examples/ios/build/toolchain/mac/BUILD.gn
@@ -0,0 +1,131 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +template("mac_toolchain") { + toolchain(target_name) { + assert(defined(invoker.toolchain_args), + "Toolchains must declare toolchain_args") + + toolchain_args = { + forward_variables_from(invoker.toolchain_args, "*") + } + + cc = "clang" + cxx = "clang++" + + tool("link") { + output = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = output + ".rsp" + rspfile_content = "{{inputs_newline}}" + + outputs = [ + output, + ] + command = "$cxx {{ldflags}} -o $output -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}" + description = "LINK {{output}}" + + default_output_dir = "{{root_out_dir}}" + default_output_extension = "" + output_prefix = "" + } + + tool("solink") { + dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = dylib + ".rsp" + rspfile_content = "{{inputs_newline}}" + + outputs = [ + dylib, + ] + command = "$cxx -dynamiclib {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}" + description = "SOLINK {{output}}" + + default_output_dir = "{{root_out_dir}}" + default_output_extension = ".dylib" + output_prefix = "lib" + } + + tool("cc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CC {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("cxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("objc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJC {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("objcxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJCXX {{output}}" + outputs = [ + "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o", + ] + } + + tool("stamp") { + command = "touch {{output}}" + description = "STAMP {{output}}" + } + + tool("copy_bundle_data") { + command = "rm -rf {{output}} && cp -a {{source}} {{output}}" + description = "COPY_BUNDLE_DATA {{output}}" + } + } +} + +mac_toolchain("clang_x86") { + toolchain_args = { + current_cpu = "x86" + current_os = "mac" + } +} + +mac_toolchain("clang_x64") { + toolchain_args = { + current_cpu = "x64" + current_os = "mac" + } +} + +mac_toolchain("clang_arm") { + toolchain_args = { + current_cpu = "arm" + current_os = "mac" + } +} + +mac_toolchain("clang_arm64") { + toolchain_args = { + current_cpu = "arm64" + current_os = "mac" + } +}
diff --git a/examples/ios/host/BUILD.gn b/examples/ios/host/BUILD.gn new file mode 100644 index 0000000..5bf2ea0 --- /dev/null +++ b/examples/ios/host/BUILD.gn
@@ -0,0 +1,17 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (current_toolchain == host_toolchain) { + executable("username") { + sources = [ + "main.cc", + ] + } +} else { + group("username") { + deps = [ + ":username($host_toolchain)", + ] + } +}
diff --git a/examples/ios/host/main.cc b/examples/ios/host/main.cc new file mode 100644 index 0000000..1f244ef --- /dev/null +++ b/examples/ios/host/main.cc
@@ -0,0 +1,71 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdlib.h> +#include <iostream> +#include <string> + +namespace { + +// Returns the current user username. +std::string Username() { + const char* username = getenv("USER"); + return username ? std::string(username) : std::string(); +} + +// Writes |string| to |stream| while escaping all C escape sequences. +void EscapeString(std::ostream* stream, const std::string& string) { + for (char c : string) { + switch (c) { + case 0: + *stream << "\\0"; + break; + case '\a': + *stream << "\\a"; + break; + case '\b': + *stream << "\\b"; + break; + case '\e': + *stream << "\\e"; + break; + case '\f': + *stream << "\\f"; + break; + case '\n': + *stream << "\\n"; + break; + case '\r': + *stream << "\\r"; + break; + case '\t': + *stream << "\\t"; + break; + case '\v': + *stream << "\\v"; + break; + case '\\': + *stream << "\\\\"; + break; + case '\"': + *stream << "\\\""; + break; + default: + *stream << c; + break; + } + } +} + +} // namespace + +int main(int argc, char** argv) { + std::string username = Username(); + + std::cout << "{\"username\": \""; + EscapeString(&std::cout, username); + std::cout << "\"}" << std::endl; + + return 0; +}
diff --git a/examples/ios/shared/BUILD.gn b/examples/ios/shared/BUILD.gn new file mode 100644 index 0000000..f4641c3 --- /dev/null +++ b/examples/ios/shared/BUILD.gn
@@ -0,0 +1,18 @@ +# Copyright 2019 The Chromium 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("//build/config/ios/templates/ios_framework_bundle.gni") + +ios_framework_bundle("hello_framework") { + output_name = "HelloShared" + + sources = [ + "hello_shared.h", + "hello_shared.m", + ] + public_headers = [ "hello_shared.h" ] + + defines = [ "HELLO_SHARED_IMPLEMENTATION" ] + frameworks = [ "Foundation.framework" ] +}
diff --git a/examples/ios/shared/hello_shared.h b/examples/ios/shared/hello_shared.h new file mode 100644 index 0000000..b351a50 --- /dev/null +++ b/examples/ios/shared/hello_shared.h
@@ -0,0 +1,13 @@ +// Copyright 2019 The Chromium 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 <Foundation/Foundation.h> + +@interface Greetings : NSObject + ++ (NSString*)greet; + ++ (NSString*)greetWithName:(NSString*)name; + +@end
diff --git a/examples/ios/shared/hello_shared.m b/examples/ios/shared/hello_shared.m new file mode 100644 index 0000000..5e81114 --- /dev/null +++ b/examples/ios/shared/hello_shared.m
@@ -0,0 +1,22 @@ +// Copyright 2019 The Chromium 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 "hello_shared.h" + +@implementation Greetings + ++ (NSString*)greet { + return [NSString stringWithFormat:@"Hello from %@!", [Greetings displayName]]; +} + ++ (NSString*)greetWithName:(NSString*)name { + return [NSString stringWithFormat:@"Hello, %@!", name]; +} + ++ (NSString*)displayName { + NSBundle* bundle = [NSBundle bundleForClass:[Greetings class]]; + return [[bundle infoDictionary] objectForKey:@"CFBundleName"]; +} + +@end