build.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /*jshint node: true*/
  20. var Q = require('q'),
  21. nopt = require('nopt'),
  22. path = require('path'),
  23. shell = require('shelljs'),
  24. spawn = require('./spawn'),
  25. check_reqs = require('./check_reqs'),
  26. fs = require('fs');
  27. var projectPath = path.join(__dirname, '..', '..');
  28. var projectName = null;
  29. module.exports.run = function (argv) {
  30. var args = nopt({
  31. // "archs": String, // TODO: add support for building different archs
  32. 'debug': Boolean,
  33. 'release': Boolean,
  34. 'device': Boolean,
  35. 'emulator': Boolean,
  36. 'codeSignIdentity': String,
  37. 'codeSignResourceRules': String,
  38. 'provisioningProfile': String,
  39. 'buildConfig' : String
  40. }, {'-r': '--release'}, argv);
  41. if (args.debug && args.release) {
  42. return Q.reject('Only one of "debug"/"release" options should be specified');
  43. }
  44. if (args.device && args.emulator) {
  45. return Q.reject('Only one of "device"/"emulator" options should be specified');
  46. }
  47. if(args.buildConfig) {
  48. if(!fs.existsSync(args.buildConfig)) {
  49. return Q.reject('Build config file does not exist:' + args.buildConfig);
  50. }
  51. console.log('Reading build config file:', path.resolve(args.buildConfig));
  52. var buildConfig = JSON.parse(fs.readFileSync(args.buildConfig, 'utf-8'));
  53. if(buildConfig.ios) {
  54. var buildType = args.release ? 'release' : 'debug';
  55. var config = buildConfig.ios[buildType];
  56. if(config) {
  57. ['codeSignIdentity', 'codeSignResourceRules', 'provisioningProfile'].forEach(
  58. function(key) {
  59. args[key] = args[key] || config[key];
  60. });
  61. }
  62. }
  63. }
  64. return check_reqs.run().then(function () {
  65. return findXCodeProjectIn(projectPath);
  66. }).then(function (name) {
  67. projectName = name;
  68. var extraConfig = '';
  69. if (args.codeSignIdentity) {
  70. extraConfig += 'CODE_SIGN_IDENTITY = ' + args.codeSignIdentity + '\n';
  71. extraConfig += 'CODE_SIGN_IDENTITY[sdk=iphoneos*] = ' + args.codeSignIdentity + '\n';
  72. }
  73. if (args.codeSignResourceRules) {
  74. extraConfig += 'CODE_SIGN_RESOURCE_RULES_PATH = ' + args.codeSignResourceRules + '\n';
  75. }
  76. if (args.provisioningProfile) {
  77. extraConfig += 'PROVISIONING_PROFILE = ' + args.provisioningProfile + '\n';
  78. }
  79. return Q.nfcall(fs.writeFile, path.join(__dirname, '..', 'build-extras.xcconfig'), extraConfig, 'utf-8');
  80. }).then(function () {
  81. var configuration = args.release ? 'Release' : 'Debug';
  82. console.log('Building project : ' + path.join(projectPath, projectName + '.xcodeproj'));
  83. console.log('\tConfiguration : ' + configuration);
  84. console.log('\tPlatform : ' + (args.device ? 'device' : 'emulator'));
  85. var xcodebuildArgs = getXcodeArgs(projectName, projectPath, configuration, args.device);
  86. return spawn('xcodebuild', xcodebuildArgs, projectPath);
  87. }).then(function () {
  88. if (!args.device) {
  89. return;
  90. }
  91. var buildOutputDir = path.join(projectPath, 'build', 'device');
  92. var pathToApp = path.join(buildOutputDir, projectName + '.app');
  93. var pathToIpa = path.join(buildOutputDir, projectName + '.ipa');
  94. var xcRunArgs = ['-sdk', 'iphoneos', 'PackageApplication',
  95. '-v', pathToApp,
  96. '-o', pathToIpa];
  97. if (args.codeSignIdentity) {
  98. xcRunArgs.concat('--sign', args.codeSignIdentity);
  99. }
  100. if (args.provisioningProfile) {
  101. xcRunArgs.concat('--embed', args.provisioningProfile);
  102. }
  103. return spawn('xcrun', xcRunArgs, projectPath);
  104. });
  105. };
  106. /**
  107. * Searches for first XCode project in specified folder
  108. * @param {String} projectPath Path where to search project
  109. * @return {Promise} Promise either fulfilled with project name or rejected
  110. */
  111. function findXCodeProjectIn(projectPath) {
  112. // 'Searching for Xcode project in ' + projectPath);
  113. var xcodeProjFiles = shell.ls(projectPath).filter(function (name) {
  114. return path.extname(name) === '.xcodeproj';
  115. });
  116. if (xcodeProjFiles.length === 0) {
  117. return Q.reject('No Xcode project found in ' + projectPath);
  118. }
  119. if (xcodeProjFiles.length > 1) {
  120. console.warn('Found multiple .xcodeproj directories in \n' +
  121. projectPath + '\nUsing first one');
  122. }
  123. var projectName = path.basename(xcodeProjFiles[0], '.xcodeproj');
  124. return Q.resolve(projectName);
  125. }
  126. module.exports.findXCodeProjectIn = findXCodeProjectIn;
  127. /**
  128. * Returns array of arguments for xcodebuild
  129. * @param {String} projectName Name of xcode project
  130. * @param {String} projectPath Path to project file. Will be used to set CWD for xcodebuild
  131. * @param {String} configuration Configuration name: debug|release
  132. * @param {Boolean} isDevice Flag that specify target for package (device/emulator)
  133. * @return {Array} Array of arguments that could be passed directly to spawn method
  134. */
  135. function getXcodeArgs(projectName, projectPath, configuration, isDevice) {
  136. var xcodebuildArgs;
  137. if (isDevice) {
  138. xcodebuildArgs = [
  139. '-xcconfig', path.join(__dirname, '..', 'build-' + configuration.toLowerCase() + '.xcconfig'),
  140. '-project', projectName + '.xcodeproj',
  141. 'ARCHS=armv7 armv7s arm64',
  142. '-target', projectName,
  143. '-configuration', configuration,
  144. '-sdk', 'iphoneos',
  145. 'build',
  146. 'VALID_ARCHS=armv7 armv7s arm64',
  147. 'CONFIGURATION_BUILD_DIR=' + path.join(projectPath, 'build', 'device'),
  148. 'SHARED_PRECOMPS_DIR=' + path.join(projectPath, 'build', 'sharedpch')
  149. ];
  150. } else { // emulator
  151. xcodebuildArgs = [
  152. '-xcconfig', path.join(__dirname, '..', 'build-' + configuration.toLowerCase() + '.xcconfig'),
  153. '-project', projectName + '.xcodeproj',
  154. 'ARCHS=i386',
  155. '-target', projectName ,
  156. '-configuration', configuration,
  157. '-sdk', 'iphonesimulator',
  158. 'build',
  159. 'VALID_ARCHS=i386',
  160. 'CONFIGURATION_BUILD_DIR=' + path.join(projectPath, 'build', 'emulator'),
  161. 'SHARED_PRECOMPS_DIR=' + path.join(projectPath, 'build', 'sharedpch')
  162. ];
  163. }
  164. return xcodebuildArgs;
  165. }
  166. // help/usage function
  167. module.exports.help = function help() {
  168. console.log('');
  169. console.log('Usage: build [--debug | --release] [--archs=\"<list of architectures...>\"]');
  170. console.log(' [--device | --simulator] [--codeSignIdentity=\"<identity>\"]');
  171. console.log(' [--codeSignResourceRules=\"<resourcerules path>\"]');
  172. console.log(' [--provisioningProfile=\"<provisioning profile>\"]');
  173. console.log(' --help : Displays this dialog.');
  174. console.log(' --debug : Builds project in debug mode. (Default)');
  175. console.log(' --release : Builds project in release mode.');
  176. console.log(' -r : Shortcut :: builds project in release mode.');
  177. // TODO: add support for building different archs
  178. // console.log(" --archs : Builds project binaries for specific chip architectures (`anycpu`, `arm`, `x86`, `x64`).");
  179. console.log(' --device, --simulator');
  180. console.log(' : Specifies, what type of project to build');
  181. console.log(' --codeSignIdentity : Type of signing identity used for code signing.');
  182. console.log(' --codeSignResourceRules : Path to ResourceRules.plist.');
  183. console.log(' --provisioningProfile : UUID of the profile.');
  184. console.log('');
  185. console.log('examples:');
  186. console.log(' build ');
  187. console.log(' build --debug');
  188. console.log(' build --release');
  189. console.log(' build --codeSignIdentity="iPhone Distribution" --provisioningProfile="926c2bd6-8de9-4c2f-8407-1016d2d12954"');
  190. // TODO: add support for building different archs
  191. // console.log(" build --release --archs=\"armv7\"");
  192. console.log('');
  193. process.exit(0);
  194. };