Skip to content

Commit

Permalink
Add support for iOS WatchKit apps in GYP
Browse files Browse the repository at this point in the history
Adds new target types for Apple Watch apps and their corresponding
WatchKit extensions, along with a test and sample application.

Patch by David Patierno <dmpatierno@google.com>

R=justincohen@chromium.org, sdefresne@chromium.org

Review URL: https://codereview.chromium.org/762673002

git-svn-id: http://gyp.googlecode.com/svn/trunk@2014 78cadc50-ecff-11dd-a971-7dbc132099af
  • Loading branch information
justincohen@chromium.org committed Dec 10, 2014
1 parent ce7a555 commit 201a8c8
Show file tree
Hide file tree
Showing 25 changed files with 720 additions and 8 deletions.
19 changes: 17 additions & 2 deletions pylib/gyp/generator/xcode.py
Expand Up @@ -70,6 +70,8 @@
# configurations.
generator_additional_non_configuration_keys = [
'ios_app_extension',
'ios_watch_app',
'ios_watchkit_extension',
'mac_bundle',
'mac_bundle_resources',
'mac_framework_headers',
Expand Down Expand Up @@ -654,6 +656,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
'loadable_module+xctest': 'com.apple.product-type.bundle.unit-test',
'shared_library+bundle': 'com.apple.product-type.framework',
'executable+extension+bundle': 'com.apple.product-type.app-extension',
'executable+watch+extension+bundle':
'com.apple.product-type.watchkit-extension',
'executable+watch+bundle': 'com.apple.product-type.application.watchapp',
}

target_properties = {
Expand All @@ -664,18 +669,28 @@ def GenerateOutput(target_list, target_dicts, data, params):
type = spec['type']
is_xctest = int(spec.get('mac_xctest_bundle', 0))
is_bundle = int(spec.get('mac_bundle', 0)) or is_xctest
is_extension = int(spec.get('ios_app_extension', 0))
is_app_extension = int(spec.get('ios_app_extension', 0))
is_watchkit_extension = int(spec.get('ios_watchkit_extension', 0))
is_watch_app = int(spec.get('ios_watch_app', 0))
if type != 'none':
type_bundle_key = type
if is_xctest:
type_bundle_key += '+xctest'
assert type == 'loadable_module', (
'mac_xctest_bundle targets must have type loadable_module '
'(target %s)' % target_name)
elif is_extension:
elif is_app_extension:
assert is_bundle, ('ios_app_extension flag requires mac_bundle '
'(target %s)' % target_name)
type_bundle_key += '+extension+bundle'
elif is_watchkit_extension:
assert is_bundle, ('ios_watchkit_extension flag requires mac_bundle '
'(target %s)' % target_name)
type_bundle_key += '+watch+extension+bundle'
elif is_watch_app:
assert is_bundle, ('ios_watch_app flag requires mac_bundle '
'(target %s)' % target_name)
type_bundle_key += '+watch+bundle'
elif is_bundle:
type_bundle_key += '+bundle'

Expand Down
19 changes: 17 additions & 2 deletions pylib/gyp/xcode_emulation.py
Expand Up @@ -221,6 +221,12 @@ def _IsBundle(self):
def _IsIosAppExtension(self):
return int(self.spec.get('ios_app_extension', 0)) != 0

def _IsIosWatchKitExtension(self):
return int(self.spec.get('ios_watchkit_extension', 0)) != 0

def _IsIosWatchApp(self):
return int(self.spec.get('ios_watch_app', 0)) != 0

def GetFrameworkVersion(self):
"""Returns the framework version of the current target. Only valid for
bundles."""
Expand All @@ -240,7 +246,7 @@ def GetWrapperExtension(self):
'WRAPPER_EXTENSION', default=default_wrapper_extension)
return '.' + self.spec.get('product_extension', wrapper_extension)
elif self.spec['type'] == 'executable':
if self._IsIosAppExtension():
if self._IsIosAppExtension() or self._IsIosWatchKitExtension():
return '.' + self.spec.get('product_extension', 'appex')
else:
return '.' + self.spec.get('product_extension', 'app')
Expand Down Expand Up @@ -302,6 +308,14 @@ def GetProductType(self):
assert self._IsBundle(), ('ios_app_extension flag requires mac_bundle '
'(target %s)' % self.spec['target_name'])
return 'com.apple.product-type.app-extension'
if self._IsIosWatchKitExtension():
assert self._IsBundle(), ('ios_watchkit_extension flag requires '
'mac_bundle (target %s)' % self.spec['target_name'])
return 'com.apple.product-type.watchkit-extension'
if self._IsIosWatchApp():
assert self._IsBundle(), ('ios_watch_app flag requires mac_bundle '
'(target %s)' % self.spec['target_name'])
return 'com.apple.product-type.application.watchapp'
if self._IsBundle():
return {
'executable': 'com.apple.product-type.application',
Expand Down Expand Up @@ -804,7 +818,8 @@ def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None):
for directory in framework_dirs:
ldflags.append('-F' + directory.replace('$(SDKROOT)', sdk_root))

if sdk_root and self._IsIosAppExtension():
is_extension = self._IsIosAppExtension() or self._IsIosWatchKitExtension()
if sdk_root and is_extension:
# Adds the link flags for extensions. These flags are common for all
# extensions and provide loader and main function.
# These flags reflect the compilation options used by xcode to compile
Expand Down
3 changes: 3 additions & 0 deletions pylib/gyp/xcode_ninja.py
Expand Up @@ -92,6 +92,9 @@ def _TargetFromSpec(old_spec, params):

ninja_target['mac_bundle'] = old_spec.get('mac_bundle', 0)
ninja_target['ios_app_extension'] = old_spec.get('ios_app_extension', 0)
ninja_target['ios_watchkit_extension'] = \
old_spec.get('ios_watchkit_extension', 0)
ninja_target['ios_watchkit_app'] = old_spec.get('ios_watchkit_app', 0)
ninja_target['type'] = old_spec['type']
if ninja_toplevel:
ninja_target['actions'] = [
Expand Down
12 changes: 8 additions & 4 deletions pylib/gyp/xcodeproj_file.py
Expand Up @@ -2240,10 +2240,14 @@ class PBXNativeTarget(XCTarget):
# prefix : the prefix for the file name
# suffix : the suffix for the file name
_product_filetypes = {
'com.apple.product-type.application': ['wrapper.application',
'', '.app'],
'com.apple.product-type.app-extension': ['wrapper.app-extension',
'', '.appex'],
'com.apple.product-type.application': ['wrapper.application',
'', '.app'],
'com.apple.product-type.application.watchapp': ['wrapper.application',
'', '.app'],
'com.apple.product-type.watchkit-extension': ['wrapper.app-extension',
'', '.appex'],
'com.apple.product-type.app-extension': ['wrapper.app-extension',
'', '.appex'],
'com.apple.product-type.bundle': ['wrapper.cfbundle',
'', '.bundle'],
'com.apple.product-type.framework': ['wrapper.framework',
Expand Down
2 changes: 2 additions & 0 deletions test/ios/extension/extension.gyp
Expand Up @@ -48,6 +48,7 @@
'ARCHS': [ 'armv7' ],
'SDKROOT': 'iphoneos',
'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer',
},
},
{
Expand Down Expand Up @@ -76,6 +77,7 @@
'ARCHS': [ 'armv7' ],
'SDKROOT': 'iphoneos',
'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer',
},
},
],
Expand Down
36 changes: 36 additions & 0 deletions test/ios/gyptest-watch.py
@@ -0,0 +1,36 @@
#!/usr/bin/env python

# Copyright (c) 2014 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Verifies that ios watch extensions and apps are built correctly.
"""

import TestGyp
import TestMac

import sys
if sys.platform == 'darwin' and TestMac.Xcode.Version() >= "0620":
test = TestGyp.TestGyp(formats=['ninja', 'xcode'])

test.run_gyp('watch.gyp', chdir='watch')

test.build(
'watch.gyp',
'WatchContainer',
chdir='watch')

# Test that the extension exists
test.built_file_must_exist(
'WatchContainer.app/PlugIns/WatchKitExtension.appex',
chdir='watch')

# Test that the watch app exists
test.built_file_must_exist(
'WatchContainer.app/PlugIns/WatchKitExtension.appex/WatchApp.app',
chdir='watch')

test.pass_test()

@@ -0,0 +1,62 @@
{
"images" : [
{
"size" : "14.5x14.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "18x18",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29.3x29.3",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "42mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,24 @@
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "watch",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "38mm",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "watch",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "42mm",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
35 changes: 35 additions & 0 deletions test/ios/watch/WatchApp/Info.plist
@@ -0,0 +1,35 @@
<?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>en</string>
<key>CFBundleDisplayName</key>
<string>WatchApp</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.google.gyptest.watch.watchapp</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKCompanionAppBundleIdentifier</key>
<string>com.google.gyptest.watch</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>
15 changes: 15 additions & 0 deletions test/ios/watch/WatchApp/Interface.storyboard
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="6221" systemVersion="13E28" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="AgC-eL-Hgc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6213"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="3733"/>
</dependencies>
<scenes>
<!--Interface Controller-->
<scene sceneID="aou-V4-d1y">
<objects>
<controller id="AgC-eL-Hgc" customClass="InterfaceController" customModuleProvider=""/>
</objects>
</scene>
</scenes>
</document>
12 changes: 12 additions & 0 deletions test/ios/watch/WatchContainer/AppDelegate.h
@@ -0,0 +1,12 @@
// Copyright (c) 2014 Google Inc. 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>

@property (strong, nonatomic) UIWindow *window;

@end

19 changes: 19 additions & 0 deletions test/ios/watch/WatchContainer/AppDelegate.m
@@ -0,0 +1,19 @@
// Copyright (c) 2014 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
// Override point for customization after application launch.
return YES;
}

@end
25 changes: 25 additions & 0 deletions test/ios/watch/WatchContainer/Base.lproj/Main.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="6162" systemVersion="14A238h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6160"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

0 comments on commit 201a8c8

Please sign in to comment.