diff --git a/lib/pbxProject.js b/lib/pbxProject.js index be9f473..3097678 100644 --- a/lib/pbxProject.js +++ b/lib/pbxProject.js @@ -1476,14 +1476,42 @@ pbxProject.prototype.addTarget = function(name, type, subfolder) { this.addToPbxCopyfilesBuildPhase(productFile) // this.addBuildPhaseToTarget(newPhase.buildPhase, this.getFirstTarget().uuid) - - }; + } else if (targetType === 'watch2_app') { + // Create CopyFiles phase in first target + this.addBuildPhase( + [targetName + '.app'], + 'PBXCopyFilesBuildPhase', + 'Embed Watch Content', + this.getFirstTarget().uuid, + targetType, + '"$(CONTENTS_FOLDER_PATH)/Watch"' + ); + } else if (targetType === 'watch2_extension') { + // Create CopyFiles phase in watch target (if exists) + var watch2Target = this.getTarget(producttypeForTargettype('watch2_app')); + if (watch2Target) { + this.addBuildPhase( + [targetName + '.appex'], + 'PBXCopyFilesBuildPhase', + 'Embed App Extensions', + watch2Target.uuid, + targetType + ); + } + } // Target: Add uuid to root project this.addToPbxProjectSection(target); - // Target: Add dependency for this target to first (main) target - this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]); + // Target: Add dependency for this target to other targets + if (targetType === 'watch2_extension') { + var watch2Target = this.getTarget(producttypeForTargettype('watch2_app')); + if (watch2Target) { + this.addTargetDependency(watch2Target.uuid, [target.uuid]); + } + } else { + this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]); + } // Return target on success @@ -1550,7 +1578,9 @@ function pbxCopyFilesBuildPhaseObj(obj, folderType, subfolderPath, phaseName) { static_library: 'products_directory', unit_test_bundle: 'wrapper', watch_app: 'wrapper', - watch_extension: 'plugins' + watch2_app: 'products_directory', + watch_extension: 'plugins', + watch2_extension: 'plugins' } var SUBFOLDERSPEC_BY_DESTINATION = { absolute_path: 0, @@ -1683,7 +1713,9 @@ function producttypeForTargettype (targetType) { static_library: 'com.apple.product-type.library.static', unit_test_bundle: 'com.apple.product-type.bundle.unit-test', watch_app: 'com.apple.product-type.application.watchapp', - watch_extension: 'com.apple.product-type.watchkit-extension' + watch2_app: 'com.apple.product-type.application.watchapp2', + watch_extension: 'com.apple.product-type.watchkit-extension', + watch2_extension: 'com.apple.product-type.watchkit2-extension' }; return PRODUCTTYPE_BY_TARGETTYPE[targetType] @@ -1701,7 +1733,9 @@ function filetypeForProducttype (productType) { 'com.apple.product-type.library.static': '"archive.ar"', 'com.apple.product-type.bundle.unit-test': '"wrapper.cfbundle"', 'com.apple.product-type.application.watchapp': '"wrapper.application"', - 'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"' + 'com.apple.product-type.application.watchapp2': '"wrapper.application"', + 'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"', + 'com.apple.product-type.watchkit2-extension': '"wrapper.app-extension"' }; return FILETYPE_BY_PRODUCTTYPE[productType] @@ -1737,6 +1771,26 @@ pbxProject.prototype.getFirstTarget = function() { } } +pbxProject.prototype.getTarget = function(productType) { + // Find target by product type + var targets = this.getFirstProject()['firstProject']['targets']; + var nativeTargets = this.pbxNativeTargetSection(); + for (var i = 0; i < targets.length; i++) { + var target = targets[i]; + var targetUuid = target.value; + if (nativeTargets[targetUuid]['productType'] === '"' + productType + '"') { + // Get pbxNativeTarget + var nativeTarget = this.pbxNativeTargetSection()[targetUuid]; + return { + uuid: targetUuid, + target: nativeTarget + }; + } + } + + return null; +} + /*** NEW ***/ pbxProject.prototype.addToPbxGroupType = function (file, groupKey, groupType) { diff --git a/test/addBuildPhase.js b/test/addBuildPhase.js index 93a4040..eb105f8 100644 --- a/test/addBuildPhase.js +++ b/test/addBuildPhase.js @@ -172,11 +172,21 @@ exports.addBuildPhase = { test.equal(buildPhase.dstSubfolderSpec, 1); test.done(); }, + 'should set target to Products Directory given \'watch2_app\' as target': function (test) { + var buildPhase = proj.addBuildPhase(['file.m'], 'PBXCopyFilesBuildPhase', 'Copy Files', proj.getFirstTarget().uuid, 'watch2_app').buildPhase; + test.equal(buildPhase.dstSubfolderSpec, 16); + test.done(); + }, 'should set target to Plugins given \'watch_extension\' as target': function (test) { var buildPhase = proj.addBuildPhase(['file.m'], 'PBXCopyFilesBuildPhase', 'Copy Files', proj.getFirstTarget().uuid, 'watch_extension').buildPhase; test.equal(buildPhase.dstSubfolderSpec, 13); test.done(); }, + 'should set target to Plugins given \'watch2_extension\' as target': function (test) { + var buildPhase = proj.addBuildPhase(['file.m'], 'PBXCopyFilesBuildPhase', 'Copy Files', proj.getFirstTarget().uuid, 'watch2_extension').buildPhase; + test.equal(buildPhase.dstSubfolderSpec, 13); + test.done(); + }, 'should add a script build phase to echo "hello world!"': function(test) { var options = {shellPath: '/bin/sh', shellScript: 'echo "hello world!"'}; var buildPhase = proj.addBuildPhase([], 'PBXShellScriptBuildPhase', 'Run a script', proj.getFirstTarget().uuid, options).buildPhase; diff --git a/test/addTarget.js b/test/addTarget.js index 452262c..c9c5fae 100644 --- a/test/addTarget.js +++ b/test/addTarget.js @@ -240,6 +240,19 @@ exports.addTarget = { test.done(); }, + 'should have "wrapper.application" filetype for watch2_app product': function (test) { + var target = proj.addTarget(TARGET_NAME, 'watch2_app'); + test.ok(target); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.productReference); + + var productFile = proj.pbxFileReferenceSection()[target.pbxNativeTarget.productReference]; + test.ok(productFile); + test.ok(productFile.explicitFileType); + test.equal(productFile.explicitFileType, '"wrapper.application"'); + + test.done(); + }, 'should have "wrapper.app-extension" filetype for watch_extension product': function (test) { var target = proj.addTarget(TARGET_NAME, 'watch_extension'); test.ok(target); @@ -251,6 +264,19 @@ exports.addTarget = { test.ok(productFile.explicitFileType); test.equal(productFile.explicitFileType, '"wrapper.app-extension"'); + test.done(); + }, + 'should have "wrapper.app-extension" filetype for watch2_extension product': function (test) { + var target = proj.addTarget(TARGET_NAME, 'watch2_extension'); + test.ok(target); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.productReference); + + var productFile = proj.pbxFileReferenceSection()[target.pbxNativeTarget.productReference]; + test.ok(productFile); + test.ok(productFile.explicitFileType); + test.equal(productFile.explicitFileType, '"wrapper.app-extension"'); + test.done(); } } diff --git a/test/addWatch2App.js b/test/addWatch2App.js new file mode 100644 index 0000000..2dd72f5 --- /dev/null +++ b/test/addWatch2App.js @@ -0,0 +1,165 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + 'License'); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +var fullProject = require('./fixtures/full-project') + fullProjectStr = JSON.stringify(fullProject), + pbx = require('../lib/pbxProject'), + pbxFile = require('../lib/pbxFile'), + proj = new pbx('.'); + +function cleanHash() { + return JSON.parse(fullProjectStr); +} + +var TARGET_NAME = 'TestWatchApp', + TARGET_TYPE = 'watch2_app', + TARGET_SUBFOLDER_NAME = 'TestWatchAppFiles'; + +exports.setUp = function (callback) { + proj.hash = cleanHash(); + callback(); +} + +exports.addWatchApp = { + 'should create a new watch2 app target with the correct product type': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + + test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.application.watchapp2"'); + + test.done(); + }, + 'should create a new watch2 app target with the correct product type, without needing a subfolder name': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + + test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.application.watchapp2"'); + + test.done(); + }, + 'should create a new watch2 app target and add source, framework, resource and header files and the corresponding build phases': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME), + options = { 'target' : target.uuid }; + + var sourceFile = proj.addSourceFile('Plugins/file.m', options), + sourcePhase = proj.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid), + resourceFile = proj.addResourceFile('assets.bundle', options), + resourcePhase = proj.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid), + frameworkFile = proj.addFramework('libsqlite3.dylib', options); + frameworkPhase = proj.addBuildPhase([], 'PBXFrameworkBuildPhase', 'Frameworks', target.uuid), + headerFile = proj.addHeaderFile('file.h', options); + + test.ok(sourcePhase); + test.ok(resourcePhase); + test.ok(frameworkPhase); + + test.equal(sourceFile.constructor, pbxFile); + test.equal(resourceFile.constructor, pbxFile); + test.equal(frameworkFile.constructor, pbxFile); + test.equal(headerFile.constructor, pbxFile); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + + test.done(); + }, + 'should create a new watch2 app target and add watch build phase': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + + test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.application.watchapp2"'); + + var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Watch Content', target.uuid); + + test.ok(buildPhase); + test.ok(buildPhase.files); + test.equal(buildPhase.files.length, 1); + test.ok(buildPhase.dstPath); + test.equal(buildPhase.dstPath, '"$(CONTENTS_FOLDER_PATH)/Watch"'); + test.equal(buildPhase.dstSubfolderSpec, 16); + + test.done(); + }, + 'should create a new watch2 app with appropriate target extension': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE); + + var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Watch Content', target.uuid) + + var buildPhaseFile = buildPhase.files[0]; + test.ok(buildPhaseFile.value); + var buildPhaseFileSection = proj.pbxBuildFileSection()[buildPhaseFile.value]; + test.ok(buildPhaseFileSection); + test.ok(buildPhaseFileSection.fileRef); + + var buildPhaseFileRef = proj.pbxFileReferenceSection()[buildPhaseFileSection.fileRef]; + test.ok(buildPhaseFileRef); + test.ok(buildPhaseFileRef.name); + test.ok(buildPhaseFileRef.path); + + var quotedTargetPath = "\"" + TARGET_NAME + ".app\""; + test.equal(buildPhaseFileRef.name, quotedTargetPath); + test.equal(buildPhaseFileRef.path, quotedTargetPath); + + test.done(); + } +} diff --git a/test/addWatch2Extension.js b/test/addWatch2Extension.js new file mode 100644 index 0000000..302b752 --- /dev/null +++ b/test/addWatch2Extension.js @@ -0,0 +1,187 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + 'License'); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +var fullProject = require('./fixtures/full-project') + fullProjectStr = JSON.stringify(fullProject), + pbx = require('../lib/pbxProject'), + pbxFile = require('../lib/pbxFile'), + proj = new pbx('.'); + +function cleanHash() { + return JSON.parse(fullProjectStr); +} + +var TARGET_NAME = 'TestWatchExtension', + TARGET_TYPE = 'watch2_extension', + TARGET_SUBFOLDER_NAME = 'TestWatchExtensionFiles'; + +exports.setUp = function (callback) { + proj.hash = cleanHash(); + callback(); +} + +exports.addWatchExtension = { + 'should create a new watch2 extension target with the correct product type': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + test.equal(target.pbxNativeTarget.productType, '"com.apple.product-type.watchkit2-extension"'); + + test.done(); + }, + 'should create a new watch2 extension target and add source, framework, resource and header files and the corresponding build phases': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME), + options = { 'target' : target.uuid }; + + var sourceFile = proj.addSourceFile('Plugins/file.m', options), + sourcePhase = proj.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid), + resourceFile = proj.addResourceFile('assets.bundle', options), + resourcePhase = proj.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid), + frameworkFile = proj.addFramework('libsqlite3.dylib', options); + frameworkPhase = proj.addBuildPhase([], 'PBXFrameworkBuildPhase', 'Frameworks', target.uuid), + headerFile = proj.addHeaderFile('file.h', options); + + test.ok(sourcePhase); + test.ok(resourcePhase); + test.ok(frameworkPhase); + + test.equal(sourceFile.constructor, pbxFile); + test.equal(resourceFile.constructor, pbxFile); + test.equal(frameworkFile.constructor, pbxFile); + test.equal(headerFile.constructor, pbxFile); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + + test.done(); + }, + 'should not create a new watch2 extension build phase if no watch2 app exists': function (test) { + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE); + + test.ok(typeof target == 'object'); + test.ok(target.uuid); + test.ok(target.pbxNativeTarget); + test.ok(target.pbxNativeTarget.isa); + test.ok(target.pbxNativeTarget.name); + test.ok(target.pbxNativeTarget.productName); + test.ok(target.pbxNativeTarget.productReference); + test.ok(target.pbxNativeTarget.productType); + test.ok(target.pbxNativeTarget.buildConfigurationList); + test.ok(target.pbxNativeTarget.buildPhases); + test.ok(target.pbxNativeTarget.buildRules); + test.ok(target.pbxNativeTarget.dependencies); + + var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed App Extensions', target.uuid) + + test.ok(!buildPhase); + + test.done(); + }, + 'should create a new watch2 extension build phase if watch2 app exists': function (test) { + proj.addTarget('TestWatchApp', 'watch2_app'); + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE); + + var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed App Extensions', target.uuid) + + test.ok(buildPhase); + test.ok(buildPhase.files); + test.equal(buildPhase.files.length, 1); + test.ok(buildPhase.dstPath); + test.equal(buildPhase.dstSubfolderSpec, 13); + + test.done(); + }, + 'should create a new watch2 extension and add to existing watch2 app build phase and dependency': function (test) { + var watchApp = proj.addTarget('TestWatchApp', 'watch2_app'); + + var nativeTargets = proj.pbxNativeTargetSection(); + + test.equal(nativeTargets[watchApp.uuid].buildPhases.length, 0); + test.equal(nativeTargets[watchApp.uuid].dependencies.length, 0); + + proj.addTarget(TARGET_NAME, TARGET_TYPE); + + test.equal(nativeTargets[watchApp.uuid].buildPhases.length, 1); + test.equal(nativeTargets[watchApp.uuid].dependencies.length, 1); + + test.done(); + }, + 'should not modify watch2 target unless adding watch2 extension': function (test) { + var watchApp = proj.addTarget('TestWatchApp', 'watch2_app'); + + var nativeTargets = proj.pbxNativeTargetSection(); + + test.equal(nativeTargets[watchApp.uuid].buildPhases.length, 0); + test.equal(nativeTargets[watchApp.uuid].dependencies.length, 0); + + proj.addTarget(TARGET_NAME, "app_extension"); + + test.equal(nativeTargets[watchApp.uuid].buildPhases.length, 0); + test.equal(nativeTargets[watchApp.uuid].dependencies.length, 0); + + proj.addTarget(TARGET_NAME, "watch_extension"); + + test.equal(nativeTargets[watchApp.uuid].buildPhases.length, 0); + test.equal(nativeTargets[watchApp.uuid].dependencies.length, 0); + + test.done(); + }, + 'should create a new watch2 extension with appropriate target extension': function (test) { + proj.addTarget('TestWatchApp', 'watch2_app'); + var target = proj.addTarget(TARGET_NAME, TARGET_TYPE); + + var buildPhase = proj.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed App Extensions', target.uuid) + + var buildPhaseFile = buildPhase.files[0]; + test.ok(buildPhaseFile.value); + var buildPhaseFileSection = proj.pbxBuildFileSection()[buildPhaseFile.value]; + test.ok(buildPhaseFileSection); + test.ok(buildPhaseFileSection.fileRef); + + var buildPhaseFileRef = proj.pbxFileReferenceSection()[buildPhaseFileSection.fileRef]; + test.ok(buildPhaseFileRef); + test.ok(buildPhaseFileRef.name); + test.ok(buildPhaseFileRef.path); + + var quotedTargetPath = "\"" + TARGET_NAME + ".appex\""; + test.equal(buildPhaseFileRef.name, quotedTargetPath); + test.equal(buildPhaseFileRef.path, quotedTargetPath); + + test.done(); + } +}