Skip to content

Commit 0e0386a

Browse files
l3enderChris Brody
authored andcommitted
Support watch2 apps/extensions (#56)
* adds watch2 target/product types * support adding watch2 app/extension targets * add watch app/extension test coverage * project formatting consistency * coverage for correct watch app extension path name * coverage for target type * ensure non-watch2 extensions additions don't modify watch2 app * add test coverage for watch2 product types * watch2 file/product type test coverage * watch2 coverage for target name/extension * clarify watch2 test descriptions * update comment to keep consistent w/ project
1 parent a80e27b commit 0e0386a

5 files changed

Lines changed: 449 additions & 7 deletions

File tree

lib/pbxProject.js

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,14 +1476,42 @@ pbxProject.prototype.addTarget = function(name, type, subfolder) {
14761476
this.addToPbxCopyfilesBuildPhase(productFile)
14771477

14781478
// this.addBuildPhaseToTarget(newPhase.buildPhase, this.getFirstTarget().uuid)
1479-
1480-
};
1479+
} else if (targetType === 'watch2_app') {
1480+
// Create CopyFiles phase in first target
1481+
this.addBuildPhase(
1482+
[targetName + '.app'],
1483+
'PBXCopyFilesBuildPhase',
1484+
'Embed Watch Content',
1485+
this.getFirstTarget().uuid,
1486+
targetType,
1487+
'"$(CONTENTS_FOLDER_PATH)/Watch"'
1488+
);
1489+
} else if (targetType === 'watch2_extension') {
1490+
// Create CopyFiles phase in watch target (if exists)
1491+
var watch2Target = this.getTarget(producttypeForTargettype('watch2_app'));
1492+
if (watch2Target) {
1493+
this.addBuildPhase(
1494+
[targetName + '.appex'],
1495+
'PBXCopyFilesBuildPhase',
1496+
'Embed App Extensions',
1497+
watch2Target.uuid,
1498+
targetType
1499+
);
1500+
}
1501+
}
14811502

14821503
// Target: Add uuid to root project
14831504
this.addToPbxProjectSection(target);
14841505

1485-
// Target: Add dependency for this target to first (main) target
1486-
this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]);
1506+
// Target: Add dependency for this target to other targets
1507+
if (targetType === 'watch2_extension') {
1508+
var watch2Target = this.getTarget(producttypeForTargettype('watch2_app'));
1509+
if (watch2Target) {
1510+
this.addTargetDependency(watch2Target.uuid, [target.uuid]);
1511+
}
1512+
} else {
1513+
this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]);
1514+
}
14871515

14881516

14891517
// Return target on success
@@ -1550,7 +1578,9 @@ function pbxCopyFilesBuildPhaseObj(obj, folderType, subfolderPath, phaseName) {
15501578
static_library: 'products_directory',
15511579
unit_test_bundle: 'wrapper',
15521580
watch_app: 'wrapper',
1553-
watch_extension: 'plugins'
1581+
watch2_app: 'products_directory',
1582+
watch_extension: 'plugins',
1583+
watch2_extension: 'plugins'
15541584
}
15551585
var SUBFOLDERSPEC_BY_DESTINATION = {
15561586
absolute_path: 0,
@@ -1683,7 +1713,9 @@ function producttypeForTargettype (targetType) {
16831713
static_library: 'com.apple.product-type.library.static',
16841714
unit_test_bundle: 'com.apple.product-type.bundle.unit-test',
16851715
watch_app: 'com.apple.product-type.application.watchapp',
1686-
watch_extension: 'com.apple.product-type.watchkit-extension'
1716+
watch2_app: 'com.apple.product-type.application.watchapp2',
1717+
watch_extension: 'com.apple.product-type.watchkit-extension',
1718+
watch2_extension: 'com.apple.product-type.watchkit2-extension'
16871719
};
16881720

16891721
return PRODUCTTYPE_BY_TARGETTYPE[targetType]
@@ -1701,7 +1733,9 @@ function filetypeForProducttype (productType) {
17011733
'com.apple.product-type.library.static': '"archive.ar"',
17021734
'com.apple.product-type.bundle.unit-test': '"wrapper.cfbundle"',
17031735
'com.apple.product-type.application.watchapp': '"wrapper.application"',
1704-
'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"'
1736+
'com.apple.product-type.application.watchapp2': '"wrapper.application"',
1737+
'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"',
1738+
'com.apple.product-type.watchkit2-extension': '"wrapper.app-extension"'
17051739
};
17061740

17071741
return FILETYPE_BY_PRODUCTTYPE[productType]
@@ -1737,6 +1771,26 @@ pbxProject.prototype.getFirstTarget = function() {
17371771
}
17381772
}
17391773

1774+
pbxProject.prototype.getTarget = function(productType) {
1775+
// Find target by product type
1776+
var targets = this.getFirstProject()['firstProject']['targets'];
1777+
var nativeTargets = this.pbxNativeTargetSection();
1778+
for (var i = 0; i < targets.length; i++) {
1779+
var target = targets[i];
1780+
var targetUuid = target.value;
1781+
if (nativeTargets[targetUuid]['productType'] === '"' + productType + '"') {
1782+
// Get pbxNativeTarget
1783+
var nativeTarget = this.pbxNativeTargetSection()[targetUuid];
1784+
return {
1785+
uuid: targetUuid,
1786+
target: nativeTarget
1787+
};
1788+
}
1789+
}
1790+
1791+
return null;
1792+
}
1793+
17401794
/*** NEW ***/
17411795

17421796
pbxProject.prototype.addToPbxGroupType = function (file, groupKey, groupType) {

test/addBuildPhase.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,21 @@ exports.addBuildPhase = {
172172
test.equal(buildPhase.dstSubfolderSpec, 1);
173173
test.done();
174174
},
175+
'should set target to Products Directory given \'watch2_app\' as target': function (test) {
176+
var buildPhase = proj.addBuildPhase(['file.m'], 'PBXCopyFilesBuildPhase', 'Copy Files', proj.getFirstTarget().uuid, 'watch2_app').buildPhase;
177+
test.equal(buildPhase.dstSubfolderSpec, 16);
178+
test.done();
179+
},
175180
'should set target to Plugins given \'watch_extension\' as target': function (test) {
176181
var buildPhase = proj.addBuildPhase(['file.m'], 'PBXCopyFilesBuildPhase', 'Copy Files', proj.getFirstTarget().uuid, 'watch_extension').buildPhase;
177182
test.equal(buildPhase.dstSubfolderSpec, 13);
178183
test.done();
179184
},
185+
'should set target to Plugins given \'watch2_extension\' as target': function (test) {
186+
var buildPhase = proj.addBuildPhase(['file.m'], 'PBXCopyFilesBuildPhase', 'Copy Files', proj.getFirstTarget().uuid, 'watch2_extension').buildPhase;
187+
test.equal(buildPhase.dstSubfolderSpec, 13);
188+
test.done();
189+
},
180190
'should add a script build phase to echo "hello world!"': function(test) {
181191
var options = {shellPath: '/bin/sh', shellScript: 'echo "hello world!"'};
182192
var buildPhase = proj.addBuildPhase([], 'PBXShellScriptBuildPhase', 'Run a script', proj.getFirstTarget().uuid, options).buildPhase;

test/addTarget.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,19 @@ exports.addTarget = {
260260

261261
test.done();
262262
},
263+
'should have "wrapper.application" filetype for watch2_app product': function (test) {
264+
var target = proj.addTarget(TARGET_NAME, 'watch2_app');
265+
test.ok(target);
266+
test.ok(target.pbxNativeTarget);
267+
test.ok(target.pbxNativeTarget.productReference);
268+
269+
var productFile = proj.pbxFileReferenceSection()[target.pbxNativeTarget.productReference];
270+
test.ok(productFile);
271+
test.ok(productFile.explicitFileType);
272+
test.equal(productFile.explicitFileType, '"wrapper.application"');
273+
274+
test.done();
275+
},
263276
'should have "wrapper.app-extension" filetype for watch_extension product': function (test) {
264277
var target = proj.addTarget(TARGET_NAME, 'watch_extension');
265278
test.ok(target);
@@ -271,6 +284,19 @@ exports.addTarget = {
271284
test.ok(productFile.explicitFileType);
272285
test.equal(productFile.explicitFileType, '"wrapper.app-extension"');
273286

287+
test.done();
288+
},
289+
'should have "wrapper.app-extension" filetype for watch2_extension product': function (test) {
290+
var target = proj.addTarget(TARGET_NAME, 'watch2_extension');
291+
test.ok(target);
292+
test.ok(target.pbxNativeTarget);
293+
test.ok(target.pbxNativeTarget.productReference);
294+
295+
var productFile = proj.pbxFileReferenceSection()[target.pbxNativeTarget.productReference];
296+
test.ok(productFile);
297+
test.ok(productFile.explicitFileType);
298+
test.equal(productFile.explicitFileType, '"wrapper.app-extension"');
299+
274300
test.done();
275301
}
276302
}

test/addWatch2App.js

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
'License'); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
Unless required by applicable law or agreed to in writing,
11+
software distributed under the License is distributed on an
12+
'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13+
KIND, either express or implied. See the License for the
14+
specific language governing permissions and limitations
15+
under the License.
16+
*/
17+
18+
var fullProject = require('./fixtures/full-project')
19+
fullProjectStr = JSON.stringify(fullProject),
20+
pbx = require('../lib/pbxProject'),
21+
pbxFile = require('../lib/pbxFile'),
22+
proj = new pbx('.');
23+
24+
function cleanHash() {
25+
return JSON.parse(fullProjectStr);
26+
}
27+
28+
var TARGET_NAME = 'TestWatchApp',
29+
TARGET_TYPE = 'watch2_app',
30+
TARGET_SUBFOLDER_NAME = 'TestWatchAppFiles';
31+
32+
exports.setUp = function (callback) {
33+
proj.hash = cleanHash();
34+
callback();
35+
}
36+
37+
exports.addWatchApp = {
38+
'should create a new watch2 app target with the correct product type': function (test) {
39+
var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME);
40+
41+
test.ok(typeof target == 'object');
42+
test.ok(target.uuid);
43+
test.ok(target.pbxNativeTarget);
44+
test.ok(target.pbxNativeTarget.isa);
45+
test.ok(target.pbxNativeTarget.name);
46+
test.ok(target.pbxNativeTarget.productName);
47+
test.ok(target.pbxNativeTarget.productReference);
48+
test.ok(target.pbxNativeTarget.productType);
49+
test.ok(target.pbxNativeTarget.buildConfigurationList);
50+
test.ok(target.pbxNativeTarget.buildPhases);
51+
test.ok(target.pbxNativeTarget.buildRules);
52+
test.ok(target.pbxNativeTarget.dependencies);
53+
54+
test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.application.watchapp2"');
55+
56+
test.done();
57+
},
58+
'should create a new watch2 app target with the correct product type, without needing a subfolder name': function (test) {
59+
var target = proj.addTarget(TARGET_NAME, TARGET_TYPE);
60+
61+
test.ok(typeof target == 'object');
62+
test.ok(target.uuid);
63+
test.ok(target.pbxNativeTarget);
64+
test.ok(target.pbxNativeTarget.isa);
65+
test.ok(target.pbxNativeTarget.name);
66+
test.ok(target.pbxNativeTarget.productName);
67+
test.ok(target.pbxNativeTarget.productReference);
68+
test.ok(target.pbxNativeTarget.productType);
69+
test.ok(target.pbxNativeTarget.buildConfigurationList);
70+
test.ok(target.pbxNativeTarget.buildPhases);
71+
test.ok(target.pbxNativeTarget.buildRules);
72+
test.ok(target.pbxNativeTarget.dependencies);
73+
74+
test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.application.watchapp2"');
75+
76+
test.done();
77+
},
78+
'should create a new watch2 app target and add source, framework, resource and header files and the corresponding build phases': function (test) {
79+
var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME),
80+
options = { 'target' : target.uuid };
81+
82+
var sourceFile = proj.addSourceFile('Plugins/file.m', options),
83+
sourcePhase = proj.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid),
84+
resourceFile = proj.addResourceFile('assets.bundle', options),
85+
resourcePhase = proj.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid),
86+
frameworkFile = proj.addFramework('libsqlite3.dylib', options);
87+
frameworkPhase = proj.addBuildPhase([], 'PBXFrameworkBuildPhase', 'Frameworks', target.uuid),
88+
headerFile = proj.addHeaderFile('file.h', options);
89+
90+
test.ok(sourcePhase);
91+
test.ok(resourcePhase);
92+
test.ok(frameworkPhase);
93+
94+
test.equal(sourceFile.constructor, pbxFile);
95+
test.equal(resourceFile.constructor, pbxFile);
96+
test.equal(frameworkFile.constructor, pbxFile);
97+
test.equal(headerFile.constructor, pbxFile);
98+
99+
test.ok(typeof target == 'object');
100+
test.ok(target.uuid);
101+
test.ok(target.pbxNativeTarget);
102+
test.ok(target.pbxNativeTarget.isa);
103+
test.ok(target.pbxNativeTarget.name);
104+
test.ok(target.pbxNativeTarget.productName);
105+
test.ok(target.pbxNativeTarget.productReference);
106+
test.ok(target.pbxNativeTarget.productType);
107+
test.ok(target.pbxNativeTarget.buildConfigurationList);
108+
test.ok(target.pbxNativeTarget.buildPhases);
109+
test.ok(target.pbxNativeTarget.buildRules);
110+
test.ok(target.pbxNativeTarget.dependencies);
111+
112+
test.done();
113+
},
114+
'should create a new watch2 app target and add watch build phase': function (test) {
115+
var target = proj.addTarget(TARGET_NAME, TARGET_TYPE);
116+
117+
test.ok(typeof target == 'object');
118+
test.ok(target.uuid);
119+
test.ok(target.pbxNativeTarget);
120+
test.ok(target.pbxNativeTarget.isa);
121+
test.ok(target.pbxNativeTarget.name);
122+
test.ok(target.pbxNativeTarget.productName);
123+
test.ok(target.pbxNativeTarget.productReference);
124+
test.ok(target.pbxNativeTarget.productType);
125+
test.ok(target.pbxNativeTarget.buildConfigurationList);
126+
test.ok(target.pbxNativeTarget.buildPhases);
127+
test.ok(target.pbxNativeTarget.buildRules);
128+
test.ok(target.pbxNativeTarget.dependencies);
129+
130+
test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.application.watchapp2"');
131+
132+
var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Watch Content', target.uuid);
133+
134+
test.ok(buildPhase);
135+
test.ok(buildPhase.files);
136+
test.equal(buildPhase.files.length, 1);
137+
test.ok(buildPhase.dstPath);
138+
test.equal(buildPhase.dstPath, '"$(CONTENTS_FOLDER_PATH)/Watch"');
139+
test.equal(buildPhase.dstSubfolderSpec, 16);
140+
141+
test.done();
142+
},
143+
'should create a new watch2 app with appropriate target extension': function (test) {
144+
var target = proj.addTarget(TARGET_NAME, TARGET_TYPE);
145+
146+
var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Watch Content', target.uuid)
147+
148+
var buildPhaseFile = buildPhase.files[0];
149+
test.ok(buildPhaseFile.value);
150+
var buildPhaseFileSection = proj.pbxBuildFileSection()[buildPhaseFile.value];
151+
test.ok(buildPhaseFileSection);
152+
test.ok(buildPhaseFileSection.fileRef);
153+
154+
var buildPhaseFileRef = proj.pbxFileReferenceSection()[buildPhaseFileSection.fileRef];
155+
test.ok(buildPhaseFileRef);
156+
test.ok(buildPhaseFileRef.name);
157+
test.ok(buildPhaseFileRef.path);
158+
159+
var quotedTargetPath = "\"" + TARGET_NAME + ".app\"";
160+
test.equal(buildPhaseFileRef.name, quotedTargetPath);
161+
test.equal(buildPhaseFileRef.path, quotedTargetPath);
162+
163+
test.done();
164+
}
165+
}

0 commit comments

Comments
 (0)