Repositorio del curso CCOM4030 el semestre B91 del proyecto kilometro0

pbxProject.js 65KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105
  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. var util = require('util'),
  18. f = util.format,
  19. EventEmitter = require('events').EventEmitter,
  20. path = require('path'),
  21. uuid = require('uuid'),
  22. fork = require('child_process').fork,
  23. pbxWriter = require('./pbxWriter'),
  24. pbxFile = require('./pbxFile'),
  25. fs = require('fs'),
  26. parser = require('./parser/pbxproj'),
  27. plist = require('simple-plist'),
  28. COMMENT_KEY = /_comment$/
  29. function pbxProject(filename) {
  30. if (!(this instanceof pbxProject))
  31. return new pbxProject(filename);
  32. this.filepath = path.resolve(filename)
  33. }
  34. util.inherits(pbxProject, EventEmitter)
  35. pbxProject.prototype.parse = function(cb) {
  36. var worker = fork(__dirname + '/parseJob.js', [this.filepath])
  37. worker.on('message', function(msg) {
  38. if (msg.name == 'SyntaxError' || msg.code) {
  39. this.emit('error', msg);
  40. } else {
  41. this.hash = msg;
  42. this.emit('end', null, msg)
  43. }
  44. }.bind(this));
  45. if (cb) {
  46. this.on('error', cb);
  47. this.on('end', cb);
  48. }
  49. return this;
  50. }
  51. pbxProject.prototype.parseSync = function() {
  52. var file_contents = fs.readFileSync(this.filepath, 'utf-8');
  53. this.hash = parser.parse(file_contents);
  54. return this;
  55. }
  56. pbxProject.prototype.writeSync = function(options) {
  57. this.writer = new pbxWriter(this.hash, options);
  58. return this.writer.writeSync();
  59. }
  60. pbxProject.prototype.allUuids = function() {
  61. var sections = this.hash.project.objects,
  62. uuids = [],
  63. section;
  64. for (key in sections) {
  65. section = sections[key]
  66. uuids = uuids.concat(Object.keys(section))
  67. }
  68. uuids = uuids.filter(function(str) {
  69. return !COMMENT_KEY.test(str) && str.length == 24;
  70. });
  71. return uuids;
  72. }
  73. pbxProject.prototype.generateUuid = function() {
  74. var id = uuid.v4()
  75. .replace(/-/g, '')
  76. .substr(0, 24)
  77. .toUpperCase()
  78. if (this.allUuids().indexOf(id) >= 0) {
  79. return this.generateUuid();
  80. } else {
  81. return id;
  82. }
  83. }
  84. pbxProject.prototype.addPluginFile = function(path, opt) {
  85. var file = new pbxFile(path, opt);
  86. file.plugin = true; // durr
  87. correctForPluginsPath(file, this);
  88. // null is better for early errors
  89. if (this.hasFile(file.path)) return null;
  90. file.fileRef = this.generateUuid();
  91. this.addToPbxFileReferenceSection(file); // PBXFileReference
  92. this.addToPluginsPbxGroup(file); // PBXGroup
  93. return file;
  94. }
  95. pbxProject.prototype.removePluginFile = function(path, opt) {
  96. var file = new pbxFile(path, opt);
  97. correctForPluginsPath(file, this);
  98. this.removeFromPbxFileReferenceSection(file); // PBXFileReference
  99. this.removeFromPluginsPbxGroup(file); // PBXGroup
  100. return file;
  101. }
  102. pbxProject.prototype.addProductFile = function(targetPath, opt) {
  103. var file = new pbxFile(targetPath, opt);
  104. file.includeInIndex = 0;
  105. file.fileRef = this.generateUuid();
  106. file.target = opt ? opt.target : undefined;
  107. file.group = opt ? opt.group : undefined;
  108. file.uuid = this.generateUuid();
  109. file.path = file.basename;
  110. this.addToPbxFileReferenceSection(file);
  111. this.addToProductsPbxGroup(file); // PBXGroup
  112. return file;
  113. }
  114. pbxProject.prototype.removeProductFile = function(path, opt) {
  115. var file = new pbxFile(path, opt);
  116. this.removeFromProductsPbxGroup(file); // PBXGroup
  117. return file;
  118. }
  119. /**
  120. *
  121. * @param path {String}
  122. * @param opt {Object} see pbxFile for avail options
  123. * @param group {String} group key
  124. * @returns {Object} file; see pbxFile
  125. */
  126. pbxProject.prototype.addSourceFile = function (path, opt, group) {
  127. var file;
  128. if (group) {
  129. file = this.addFile(path, group, opt);
  130. }
  131. else {
  132. file = this.addPluginFile(path, opt);
  133. }
  134. if (!file) return false;
  135. file.target = opt ? opt.target : undefined;
  136. file.uuid = this.generateUuid();
  137. this.addToPbxBuildFileSection(file); // PBXBuildFile
  138. this.addToPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase
  139. return file;
  140. }
  141. /**
  142. *
  143. * @param path {String}
  144. * @param opt {Object} see pbxFile for avail options
  145. * @param group {String} group key
  146. * @returns {Object} file; see pbxFile
  147. */
  148. pbxProject.prototype.removeSourceFile = function (path, opt, group) {
  149. var file;
  150. if (group) {
  151. file = this.removeFile(path, group, opt);
  152. }
  153. else {
  154. file = this.removePluginFile(path, opt);
  155. }
  156. file.target = opt ? opt.target : undefined;
  157. this.removeFromPbxBuildFileSection(file); // PBXBuildFile
  158. this.removeFromPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase
  159. return file;
  160. }
  161. /**
  162. *
  163. * @param path {String}
  164. * @param opt {Object} see pbxFile for avail options
  165. * @param group {String} group key
  166. * @returns {Object} file; see pbxFile
  167. */
  168. pbxProject.prototype.addHeaderFile = function (path, opt, group) {
  169. if (group) {
  170. return this.addFile(path, group, opt);
  171. }
  172. else {
  173. return this.addPluginFile(path, opt);
  174. }
  175. }
  176. /**
  177. *
  178. * @param path {String}
  179. * @param opt {Object} see pbxFile for avail options
  180. * @param group {String} group key
  181. * @returns {Object} file; see pbxFile
  182. */
  183. pbxProject.prototype.removeHeaderFile = function (path, opt, group) {
  184. if (group) {
  185. return this.removeFile(path, group, opt);
  186. }
  187. else {
  188. return this.removePluginFile(path, opt);
  189. }
  190. }
  191. /**
  192. *
  193. * @param path {String}
  194. * @param opt {Object} see pbxFile for avail options
  195. * @param group {String} group key
  196. * @returns {Object} file; see pbxFile
  197. */
  198. pbxProject.prototype.addResourceFile = function(path, opt, group) {
  199. opt = opt || {};
  200. var file;
  201. if (opt.plugin) {
  202. file = this.addPluginFile(path, opt);
  203. if (!file) return false;
  204. } else {
  205. file = new pbxFile(path, opt);
  206. if (this.hasFile(file.path)) return false;
  207. }
  208. file.uuid = this.generateUuid();
  209. file.target = opt ? opt.target : undefined;
  210. if (!opt.plugin) {
  211. correctForResourcesPath(file, this);
  212. file.fileRef = this.generateUuid();
  213. }
  214. if (!opt.variantGroup) {
  215. this.addToPbxBuildFileSection(file); // PBXBuildFile
  216. this.addToPbxResourcesBuildPhase(file); // PBXResourcesBuildPhase
  217. }
  218. if (!opt.plugin) {
  219. this.addToPbxFileReferenceSection(file); // PBXFileReference
  220. if (group) {
  221. if (this.getPBXGroupByKey(group)) {
  222. this.addToPbxGroup(file, group); //Group other than Resources (i.e. 'splash')
  223. }
  224. else if (this.getPBXVariantGroupByKey(group)) {
  225. this.addToPbxVariantGroup(file, group); // PBXVariantGroup
  226. }
  227. }
  228. else {
  229. this.addToResourcesPbxGroup(file); // PBXGroup
  230. }
  231. }
  232. return file;
  233. }
  234. /**
  235. *
  236. * @param path {String}
  237. * @param opt {Object} see pbxFile for avail options
  238. * @param group {String} group key
  239. * @returns {Object} file; see pbxFile
  240. */
  241. pbxProject.prototype.removeResourceFile = function(path, opt, group) {
  242. var file = new pbxFile(path, opt);
  243. file.target = opt ? opt.target : undefined;
  244. correctForResourcesPath(file, this);
  245. this.removeFromPbxBuildFileSection(file); // PBXBuildFile
  246. this.removeFromPbxFileReferenceSection(file); // PBXFileReference
  247. if (group) {
  248. if (this.getPBXGroupByKey(group)) {
  249. this.removeFromPbxGroup(file, group); //Group other than Resources (i.e. 'splash')
  250. }
  251. else if (this.getPBXVariantGroupByKey(group)) {
  252. this.removeFromPbxVariantGroup(file, group); // PBXVariantGroup
  253. }
  254. }
  255. else {
  256. this.removeFromResourcesPbxGroup(file); // PBXGroup
  257. }
  258. this.removeFromPbxResourcesBuildPhase(file); // PBXResourcesBuildPhase
  259. return file;
  260. }
  261. pbxProject.prototype.addFramework = function(fpath, opt) {
  262. var customFramework = opt && opt.customFramework == true;
  263. var link = !opt || (opt.link == undefined || opt.link); //defaults to true if not specified
  264. var embed = opt && opt.embed; //defaults to false if not specified
  265. if (opt) {
  266. delete opt.embed;
  267. }
  268. var file = new pbxFile(fpath, opt);
  269. file.uuid = this.generateUuid();
  270. file.fileRef = this.generateUuid();
  271. file.target = opt ? opt.target : undefined;
  272. if (this.hasFile(file.path)) return false;
  273. this.addToPbxBuildFileSection(file); // PBXBuildFile
  274. this.addToPbxFileReferenceSection(file); // PBXFileReference
  275. this.addToFrameworksPbxGroup(file); // PBXGroup
  276. if (link) {
  277. this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
  278. }
  279. if (customFramework) {
  280. this.addToFrameworkSearchPaths(file);
  281. if (embed) {
  282. opt.embed = embed;
  283. var embeddedFile = new pbxFile(fpath, opt);
  284. embeddedFile.uuid = this.generateUuid();
  285. embeddedFile.fileRef = file.fileRef;
  286. //keeping a separate PBXBuildFile entry for Embed Frameworks
  287. this.addToPbxBuildFileSection(embeddedFile); // PBXBuildFile
  288. this.addToPbxEmbedFrameworksBuildPhase(embeddedFile); // PBXCopyFilesBuildPhase
  289. return embeddedFile;
  290. }
  291. }
  292. return file;
  293. }
  294. pbxProject.prototype.removeFramework = function(fpath, opt) {
  295. var embed = opt && opt.embed;
  296. if (opt) {
  297. delete opt.embed;
  298. }
  299. var file = new pbxFile(fpath, opt);
  300. file.target = opt ? opt.target : undefined;
  301. this.removeFromPbxBuildFileSection(file); // PBXBuildFile
  302. this.removeFromPbxFileReferenceSection(file); // PBXFileReference
  303. this.removeFromFrameworksPbxGroup(file); // PBXGroup
  304. this.removeFromPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
  305. if (opt && opt.customFramework) {
  306. this.removeFromFrameworkSearchPaths(file);
  307. }
  308. opt = opt || {};
  309. opt.embed = true;
  310. var embeddedFile = new pbxFile(fpath, opt);
  311. embeddedFile.fileRef = file.fileRef;
  312. this.removeFromPbxBuildFileSection(embeddedFile); // PBXBuildFile
  313. this.removeFromPbxEmbedFrameworksBuildPhase(embeddedFile); // PBXCopyFilesBuildPhase
  314. return file;
  315. }
  316. pbxProject.prototype.addCopyfile = function(fpath, opt) {
  317. var file = new pbxFile(fpath, opt);
  318. // catch duplicates
  319. if (this.hasFile(file.path)) {
  320. file = this.hasFile(file.path);
  321. }
  322. file.fileRef = file.uuid = this.generateUuid();
  323. file.target = opt ? opt.target : undefined;
  324. this.addToPbxBuildFileSection(file); // PBXBuildFile
  325. this.addToPbxFileReferenceSection(file); // PBXFileReference
  326. this.addToPbxCopyfilesBuildPhase(file); // PBXCopyFilesBuildPhase
  327. return file;
  328. }
  329. pbxProject.prototype.pbxCopyfilesBuildPhaseObj = function(target) {
  330. return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', target);
  331. }
  332. pbxProject.prototype.addToPbxCopyfilesBuildPhase = function(file) {
  333. var sources = this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', file.target);
  334. sources.files.push(pbxBuildPhaseObj(file));
  335. }
  336. pbxProject.prototype.removeCopyfile = function(fpath, opt) {
  337. var file = new pbxFile(fpath, opt);
  338. file.target = opt ? opt.target : undefined;
  339. this.removeFromPbxBuildFileSection(file); // PBXBuildFile
  340. this.removeFromPbxFileReferenceSection(file); // PBXFileReference
  341. this.removeFromPbxCopyfilesBuildPhase(file); // PBXFrameworksBuildPhase
  342. return file;
  343. }
  344. pbxProject.prototype.removeFromPbxCopyfilesBuildPhase = function(file) {
  345. var sources = this.pbxCopyfilesBuildPhaseObj(file.target);
  346. for (i in sources.files) {
  347. if (sources.files[i].comment == longComment(file)) {
  348. sources.files.splice(i, 1);
  349. break;
  350. }
  351. }
  352. }
  353. pbxProject.prototype.addStaticLibrary = function(path, opt) {
  354. opt = opt || {};
  355. var file;
  356. if (opt.plugin) {
  357. file = this.addPluginFile(path, opt);
  358. if (!file) return false;
  359. } else {
  360. file = new pbxFile(path, opt);
  361. if (this.hasFile(file.path)) return false;
  362. }
  363. file.uuid = this.generateUuid();
  364. file.target = opt ? opt.target : undefined;
  365. if (!opt.plugin) {
  366. file.fileRef = this.generateUuid();
  367. this.addToPbxFileReferenceSection(file); // PBXFileReference
  368. }
  369. this.addToPbxBuildFileSection(file); // PBXBuildFile
  370. this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
  371. this.addToLibrarySearchPaths(file); // make sure it gets built!
  372. return file;
  373. }
  374. // helper addition functions
  375. pbxProject.prototype.addToPbxBuildFileSection = function(file) {
  376. var commentKey = f("%s_comment", file.uuid);
  377. this.pbxBuildFileSection()[file.uuid] = pbxBuildFileObj(file);
  378. this.pbxBuildFileSection()[commentKey] = pbxBuildFileComment(file);
  379. }
  380. pbxProject.prototype.removeFromPbxBuildFileSection = function(file) {
  381. var uuid;
  382. for (uuid in this.pbxBuildFileSection()) {
  383. if (this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) {
  384. file.uuid = uuid;
  385. delete this.pbxBuildFileSection()[uuid];
  386. var commentKey = f("%s_comment", uuid);
  387. delete this.pbxBuildFileSection()[commentKey];
  388. }
  389. }
  390. }
  391. pbxProject.prototype.addPbxGroup = function(filePathsArray, name, path, sourceTree) {
  392. var groups = this.hash.project.objects['PBXGroup'],
  393. pbxGroupUuid = this.generateUuid(),
  394. commentKey = f("%s_comment", pbxGroupUuid),
  395. pbxGroup = {
  396. isa: 'PBXGroup',
  397. children: [],
  398. name: name,
  399. path: path,
  400. sourceTree: sourceTree ? sourceTree : '"<group>"'
  401. },
  402. fileReferenceSection = this.pbxFileReferenceSection(),
  403. filePathToReference = {};
  404. for (var key in fileReferenceSection) {
  405. // only look for comments
  406. if (!COMMENT_KEY.test(key)) continue;
  407. var fileReferenceKey = key.split(COMMENT_KEY)[0],
  408. fileReference = fileReferenceSection[fileReferenceKey];
  409. filePathToReference[fileReference.path] = { fileRef: fileReferenceKey, basename: fileReferenceSection[key] };
  410. }
  411. for (var index = 0; index < filePathsArray.length; index++) {
  412. var filePath = filePathsArray[index],
  413. filePathQuoted = "\"" + filePath + "\"";
  414. if (filePathToReference[filePath]) {
  415. pbxGroup.children.push(pbxGroupChild(filePathToReference[filePath]));
  416. continue;
  417. } else if (filePathToReference[filePathQuoted]) {
  418. pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted]));
  419. continue;
  420. }
  421. var file = new pbxFile(filePath);
  422. file.uuid = this.generateUuid();
  423. file.fileRef = this.generateUuid();
  424. this.addToPbxFileReferenceSection(file); // PBXFileReference
  425. this.addToPbxBuildFileSection(file); // PBXBuildFile
  426. pbxGroup.children.push(pbxGroupChild(file));
  427. }
  428. if (groups) {
  429. groups[pbxGroupUuid] = pbxGroup;
  430. groups[commentKey] = name;
  431. }
  432. return { uuid: pbxGroupUuid, pbxGroup: pbxGroup };
  433. }
  434. pbxProject.prototype.removePbxGroup = function (groupName) {
  435. var section = this.hash.project.objects['PBXGroup'],
  436. key, itemKey;
  437. for (key in section) {
  438. // only look for comments
  439. if (!COMMENT_KEY.test(key)) continue;
  440. if (section[key] == groupName) {
  441. itemKey = key.split(COMMENT_KEY)[0];
  442. delete section[itemKey];
  443. }
  444. }
  445. }
  446. pbxProject.prototype.addToPbxProjectSection = function(target) {
  447. var newTarget = {
  448. value: target.uuid,
  449. comment: pbxNativeTargetComment(target.pbxNativeTarget)
  450. };
  451. this.pbxProjectSection()[this.getFirstProject()['uuid']]['targets'].push(newTarget);
  452. }
  453. pbxProject.prototype.addToPbxNativeTargetSection = function(target) {
  454. var commentKey = f("%s_comment", target.uuid);
  455. this.pbxNativeTargetSection()[target.uuid] = target.pbxNativeTarget;
  456. this.pbxNativeTargetSection()[commentKey] = target.pbxNativeTarget.name;
  457. }
  458. pbxProject.prototype.addToPbxFileReferenceSection = function(file) {
  459. var commentKey = f("%s_comment", file.fileRef);
  460. this.pbxFileReferenceSection()[file.fileRef] = pbxFileReferenceObj(file);
  461. this.pbxFileReferenceSection()[commentKey] = pbxFileReferenceComment(file);
  462. }
  463. pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) {
  464. var i;
  465. var refObj = pbxFileReferenceObj(file);
  466. for (i in this.pbxFileReferenceSection()) {
  467. if (this.pbxFileReferenceSection()[i].name == refObj.name ||
  468. ('"' + this.pbxFileReferenceSection()[i].name + '"') == refObj.name ||
  469. this.pbxFileReferenceSection()[i].path == refObj.path ||
  470. ('"' + this.pbxFileReferenceSection()[i].path + '"') == refObj.path) {
  471. file.fileRef = file.uuid = i;
  472. delete this.pbxFileReferenceSection()[i];
  473. break;
  474. }
  475. }
  476. var commentKey = f("%s_comment", file.fileRef);
  477. if (this.pbxFileReferenceSection()[commentKey] != undefined) {
  478. delete this.pbxFileReferenceSection()[commentKey];
  479. }
  480. return file;
  481. }
  482. pbxProject.prototype.addToXcVersionGroupSection = function(file) {
  483. if (!file.models || !file.currentModel) {
  484. throw new Error("Cannot create a XCVersionGroup section from not a data model document file");
  485. }
  486. var commentKey = f("%s_comment", file.fileRef);
  487. if (!this.xcVersionGroupSection()[file.fileRef]) {
  488. this.xcVersionGroupSection()[file.fileRef] = {
  489. isa: 'XCVersionGroup',
  490. children: file.models.map(function (el) { return el.fileRef; }),
  491. currentVersion: file.currentModel.fileRef,
  492. name: path.basename(file.path),
  493. path: file.path,
  494. sourceTree: '"<group>"',
  495. versionGroupType: 'wrapper.xcdatamodel'
  496. };
  497. this.xcVersionGroupSection()[commentKey] = path.basename(file.path);
  498. }
  499. }
  500. pbxProject.prototype.addToPluginsPbxGroup = function(file) {
  501. var pluginsGroup = this.pbxGroupByName('Plugins');
  502. if (!pluginsGroup) {
  503. this.addPbxGroup([file.path], 'Plugins');
  504. } else {
  505. pluginsGroup.children.push(pbxGroupChild(file));
  506. }
  507. }
  508. pbxProject.prototype.removeFromPluginsPbxGroup = function(file) {
  509. if (!this.pbxGroupByName('Plugins')) {
  510. return null;
  511. }
  512. var pluginsGroupChildren = this.pbxGroupByName('Plugins').children, i;
  513. for (i in pluginsGroupChildren) {
  514. if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
  515. pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
  516. pluginsGroupChildren.splice(i, 1);
  517. break;
  518. }
  519. }
  520. }
  521. pbxProject.prototype.addToResourcesPbxGroup = function(file) {
  522. var pluginsGroup = this.pbxGroupByName('Resources');
  523. if (!pluginsGroup) {
  524. this.addPbxGroup([file.path], 'Resources');
  525. } else {
  526. pluginsGroup.children.push(pbxGroupChild(file));
  527. }
  528. }
  529. pbxProject.prototype.removeFromResourcesPbxGroup = function(file) {
  530. if (!this.pbxGroupByName('Resources')) {
  531. return null;
  532. }
  533. var pluginsGroupChildren = this.pbxGroupByName('Resources').children, i;
  534. for (i in pluginsGroupChildren) {
  535. if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
  536. pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
  537. pluginsGroupChildren.splice(i, 1);
  538. break;
  539. }
  540. }
  541. }
  542. pbxProject.prototype.addToFrameworksPbxGroup = function(file) {
  543. var pluginsGroup = this.pbxGroupByName('Frameworks');
  544. if (!pluginsGroup) {
  545. this.addPbxGroup([file.path], 'Frameworks');
  546. } else {
  547. pluginsGroup.children.push(pbxGroupChild(file));
  548. }
  549. }
  550. pbxProject.prototype.removeFromFrameworksPbxGroup = function(file) {
  551. if (!this.pbxGroupByName('Frameworks')) {
  552. return null;
  553. }
  554. var pluginsGroupChildren = this.pbxGroupByName('Frameworks').children;
  555. for (i in pluginsGroupChildren) {
  556. if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
  557. pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
  558. pluginsGroupChildren.splice(i, 1);
  559. break;
  560. }
  561. }
  562. }
  563. pbxProject.prototype.addToPbxEmbedFrameworksBuildPhase = function (file) {
  564. var sources = this.pbxEmbedFrameworksBuildPhaseObj(file.target);
  565. if (sources) {
  566. sources.files.push(pbxBuildPhaseObj(file));
  567. }
  568. }
  569. pbxProject.prototype.removeFromPbxEmbedFrameworksBuildPhase = function (file) {
  570. var sources = this.pbxEmbedFrameworksBuildPhaseObj(file.target);
  571. if (sources) {
  572. var files = [];
  573. for (i in sources.files) {
  574. if (sources.files[i].comment != longComment(file)) {
  575. files.push(sources.files[i]);
  576. }
  577. }
  578. sources.files = files;
  579. }
  580. }
  581. pbxProject.prototype.addToProductsPbxGroup = function(file) {
  582. var productsGroup = this.pbxGroupByName('Products');
  583. if (!productsGroup) {
  584. this.addPbxGroup([file.path], 'Products');
  585. } else {
  586. productsGroup.children.push(pbxGroupChild(file));
  587. }
  588. }
  589. pbxProject.prototype.removeFromProductsPbxGroup = function(file) {
  590. if (!this.pbxGroupByName('Products')) {
  591. return null;
  592. }
  593. var productsGroupChildren = this.pbxGroupByName('Products').children, i;
  594. for (i in productsGroupChildren) {
  595. if (pbxGroupChild(file).value == productsGroupChildren[i].value &&
  596. pbxGroupChild(file).comment == productsGroupChildren[i].comment) {
  597. productsGroupChildren.splice(i, 1);
  598. break;
  599. }
  600. }
  601. }
  602. pbxProject.prototype.addToPbxSourcesBuildPhase = function(file) {
  603. var sources = this.pbxSourcesBuildPhaseObj(file.target);
  604. sources.files.push(pbxBuildPhaseObj(file));
  605. }
  606. pbxProject.prototype.removeFromPbxSourcesBuildPhase = function(file) {
  607. var sources = this.pbxSourcesBuildPhaseObj(file.target), i;
  608. for (i in sources.files) {
  609. if (sources.files[i].comment == longComment(file)) {
  610. sources.files.splice(i, 1);
  611. break;
  612. }
  613. }
  614. }
  615. pbxProject.prototype.addToPbxResourcesBuildPhase = function(file) {
  616. var sources = this.pbxResourcesBuildPhaseObj(file.target);
  617. sources.files.push(pbxBuildPhaseObj(file));
  618. }
  619. pbxProject.prototype.removeFromPbxResourcesBuildPhase = function(file) {
  620. var sources = this.pbxResourcesBuildPhaseObj(file.target), i;
  621. for (i in sources.files) {
  622. if (sources.files[i].comment == longComment(file)) {
  623. sources.files.splice(i, 1);
  624. break;
  625. }
  626. }
  627. }
  628. pbxProject.prototype.addToPbxFrameworksBuildPhase = function(file) {
  629. var sources = this.pbxFrameworksBuildPhaseObj(file.target);
  630. sources.files.push(pbxBuildPhaseObj(file));
  631. }
  632. pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function(file) {
  633. var sources = this.pbxFrameworksBuildPhaseObj(file.target);
  634. for (i in sources.files) {
  635. if (sources.files[i].comment == longComment(file)) {
  636. sources.files.splice(i, 1);
  637. break;
  638. }
  639. }
  640. }
  641. pbxProject.prototype.addXCConfigurationList = function(configurationObjectsArray, defaultConfigurationName, comment) {
  642. var pbxBuildConfigurationSection = this.pbxXCBuildConfigurationSection(),
  643. pbxXCConfigurationListSection = this.pbxXCConfigurationList(),
  644. xcConfigurationListUuid = this.generateUuid(),
  645. commentKey = f("%s_comment", xcConfigurationListUuid),
  646. xcConfigurationList = {
  647. isa: 'XCConfigurationList',
  648. buildConfigurations: [],
  649. defaultConfigurationIsVisible: 0,
  650. defaultConfigurationName: defaultConfigurationName
  651. };
  652. for (var index = 0; index < configurationObjectsArray.length; index++) {
  653. var configuration = configurationObjectsArray[index],
  654. configurationUuid = this.generateUuid(),
  655. configurationCommentKey = f("%s_comment", configurationUuid);
  656. pbxBuildConfigurationSection[configurationUuid] = configuration;
  657. pbxBuildConfigurationSection[configurationCommentKey] = configuration.name;
  658. xcConfigurationList.buildConfigurations.push({ value: configurationUuid, comment: configuration.name });
  659. }
  660. if (pbxXCConfigurationListSection) {
  661. pbxXCConfigurationListSection[xcConfigurationListUuid] = xcConfigurationList;
  662. pbxXCConfigurationListSection[commentKey] = comment;
  663. }
  664. return { uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList };
  665. }
  666. pbxProject.prototype.addTargetDependency = function(target, dependencyTargets) {
  667. if (!target)
  668. return undefined;
  669. var nativeTargets = this.pbxNativeTargetSection();
  670. if (typeof nativeTargets[target] == "undefined")
  671. throw new Error("Invalid target: " + target);
  672. for (var index = 0; index < dependencyTargets.length; index++) {
  673. var dependencyTarget = dependencyTargets[index];
  674. if (typeof nativeTargets[dependencyTarget] == "undefined")
  675. throw new Error("Invalid target: " + dependencyTarget);
  676. }
  677. var pbxTargetDependency = 'PBXTargetDependency',
  678. pbxContainerItemProxy = 'PBXContainerItemProxy',
  679. pbxTargetDependencySection = this.hash.project.objects[pbxTargetDependency],
  680. pbxContainerItemProxySection = this.hash.project.objects[pbxContainerItemProxy];
  681. for (var index = 0; index < dependencyTargets.length; index++) {
  682. var dependencyTargetUuid = dependencyTargets[index],
  683. dependencyTargetCommentKey = f("%s_comment", dependencyTargetUuid),
  684. targetDependencyUuid = this.generateUuid(),
  685. targetDependencyCommentKey = f("%s_comment", targetDependencyUuid),
  686. itemProxyUuid = this.generateUuid(),
  687. itemProxyCommentKey = f("%s_comment", itemProxyUuid),
  688. itemProxy = {
  689. isa: pbxContainerItemProxy,
  690. containerPortal: this.hash.project['rootObject'],
  691. containerPortal_comment: this.hash.project['rootObject_comment'],
  692. proxyType: 1,
  693. remoteGlobalIDString: dependencyTargetUuid,
  694. remoteInfo: nativeTargets[dependencyTargetUuid].name
  695. },
  696. targetDependency = {
  697. isa: pbxTargetDependency,
  698. target: dependencyTargetUuid,
  699. target_comment: nativeTargets[dependencyTargetCommentKey],
  700. targetProxy: itemProxyUuid,
  701. targetProxy_comment: pbxContainerItemProxy
  702. };
  703. if (pbxContainerItemProxySection && pbxTargetDependencySection) {
  704. pbxContainerItemProxySection[itemProxyUuid] = itemProxy;
  705. pbxContainerItemProxySection[itemProxyCommentKey] = pbxContainerItemProxy;
  706. pbxTargetDependencySection[targetDependencyUuid] = targetDependency;
  707. pbxTargetDependencySection[targetDependencyCommentKey] = pbxTargetDependency;
  708. nativeTargets[target].dependencies.push({ value: targetDependencyUuid, comment: pbxTargetDependency })
  709. }
  710. }
  711. return { uuid: target, target: nativeTargets[target] };
  712. }
  713. pbxProject.prototype.addBuildPhase = function(filePathsArray, buildPhaseType, comment, target, optionsOrFolderType, subfolderPath) {
  714. var buildPhaseSection,
  715. fileReferenceSection = this.pbxFileReferenceSection(),
  716. buildFileSection = this.pbxBuildFileSection(),
  717. buildPhaseUuid = this.generateUuid(),
  718. buildPhaseTargetUuid = target || this.getFirstTarget().uuid,
  719. commentKey = f("%s_comment", buildPhaseUuid),
  720. buildPhase = {
  721. isa: buildPhaseType,
  722. buildActionMask: 2147483647,
  723. files: [],
  724. runOnlyForDeploymentPostprocessing: 0
  725. },
  726. filePathToBuildFile = {};
  727. if (buildPhaseType === 'PBXCopyFilesBuildPhase') {
  728. buildPhase = pbxCopyFilesBuildPhaseObj(buildPhase, optionsOrFolderType, subfolderPath, comment);
  729. } else if (buildPhaseType === 'PBXShellScriptBuildPhase') {
  730. buildPhase = pbxShellScriptBuildPhaseObj(buildPhase, optionsOrFolderType, comment)
  731. }
  732. if (!this.hash.project.objects[buildPhaseType]) {
  733. this.hash.project.objects[buildPhaseType] = new Object();
  734. }
  735. if (!this.hash.project.objects[buildPhaseType][buildPhaseUuid]) {
  736. this.hash.project.objects[buildPhaseType][buildPhaseUuid] = buildPhase;
  737. this.hash.project.objects[buildPhaseType][commentKey] = comment;
  738. }
  739. if (this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases']) {
  740. this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases'].push({
  741. value: buildPhaseUuid,
  742. comment: comment
  743. })
  744. }
  745. for (var key in buildFileSection) {
  746. // only look for comments
  747. if (!COMMENT_KEY.test(key)) continue;
  748. var buildFileKey = key.split(COMMENT_KEY)[0],
  749. buildFile = buildFileSection[buildFileKey];
  750. fileReference = fileReferenceSection[buildFile.fileRef];
  751. if (!fileReference) continue;
  752. var pbxFileObj = new pbxFile(fileReference.path);
  753. filePathToBuildFile[fileReference.path] = { uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group };
  754. }
  755. for (var index = 0; index < filePathsArray.length; index++) {
  756. var filePath = filePathsArray[index],
  757. filePathQuoted = "\"" + filePath + "\"",
  758. file = new pbxFile(filePath);
  759. if (filePathToBuildFile[filePath]) {
  760. buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePath]));
  761. continue;
  762. } else if (filePathToBuildFile[filePathQuoted]) {
  763. buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePathQuoted]));
  764. continue;
  765. }
  766. file.uuid = this.generateUuid();
  767. file.fileRef = this.generateUuid();
  768. this.addToPbxFileReferenceSection(file); // PBXFileReference
  769. this.addToPbxBuildFileSection(file); // PBXBuildFile
  770. buildPhase.files.push(pbxBuildPhaseObj(file));
  771. }
  772. if (buildPhaseSection) {
  773. buildPhaseSection[buildPhaseUuid] = buildPhase;
  774. buildPhaseSection[commentKey] = comment;
  775. }
  776. return { uuid: buildPhaseUuid, buildPhase: buildPhase };
  777. }
  778. // helper access functions
  779. pbxProject.prototype.pbxProjectSection = function() {
  780. return this.hash.project.objects['PBXProject'];
  781. }
  782. pbxProject.prototype.pbxBuildFileSection = function() {
  783. return this.hash.project.objects['PBXBuildFile'];
  784. }
  785. pbxProject.prototype.pbxXCBuildConfigurationSection = function() {
  786. return this.hash.project.objects['XCBuildConfiguration'];
  787. }
  788. pbxProject.prototype.pbxFileReferenceSection = function() {
  789. return this.hash.project.objects['PBXFileReference'];
  790. }
  791. pbxProject.prototype.pbxNativeTargetSection = function() {
  792. return this.hash.project.objects['PBXNativeTarget'];
  793. }
  794. pbxProject.prototype.xcVersionGroupSection = function () {
  795. if (typeof this.hash.project.objects['XCVersionGroup'] !== 'object') {
  796. this.hash.project.objects['XCVersionGroup'] = {};
  797. }
  798. return this.hash.project.objects['XCVersionGroup'];
  799. }
  800. pbxProject.prototype.pbxXCConfigurationList = function() {
  801. return this.hash.project.objects['XCConfigurationList'];
  802. }
  803. pbxProject.prototype.pbxGroupByName = function(name) {
  804. var groups = this.hash.project.objects['PBXGroup'],
  805. key, groupKey;
  806. for (key in groups) {
  807. // only look for comments
  808. if (!COMMENT_KEY.test(key)) continue;
  809. if (groups[key] == name) {
  810. groupKey = key.split(COMMENT_KEY)[0];
  811. return groups[groupKey];
  812. }
  813. }
  814. return null;
  815. }
  816. pbxProject.prototype.pbxTargetByName = function(name) {
  817. return this.pbxItemByComment(name, 'PBXNativeTarget');
  818. }
  819. pbxProject.prototype.findTargetKey = function(name) {
  820. var targets = this.hash.project.objects['PBXNativeTarget'];
  821. for (var key in targets) {
  822. // only look for comments
  823. if (COMMENT_KEY.test(key)) continue;
  824. var target = targets[key];
  825. if (target.name === name) {
  826. return key;
  827. }
  828. }
  829. return null;
  830. }
  831. pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) {
  832. var section = this.hash.project.objects[pbxSectionName],
  833. key, itemKey;
  834. for (key in section) {
  835. // only look for comments
  836. if (!COMMENT_KEY.test(key)) continue;
  837. if (section[key] == name) {
  838. itemKey = key.split(COMMENT_KEY)[0];
  839. return section[itemKey];
  840. }
  841. }
  842. return null;
  843. }
  844. pbxProject.prototype.pbxSourcesBuildPhaseObj = function(target) {
  845. return this.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', target);
  846. }
  847. pbxProject.prototype.pbxResourcesBuildPhaseObj = function(target) {
  848. return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources', target);
  849. }
  850. pbxProject.prototype.pbxFrameworksBuildPhaseObj = function(target) {
  851. return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks', target);
  852. }
  853. pbxProject.prototype.pbxEmbedFrameworksBuildPhaseObj = function (target) {
  854. return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Frameworks', target);
  855. };
  856. // Find Build Phase from group/target
  857. pbxProject.prototype.buildPhase = function(group, target) {
  858. if (!target)
  859. return undefined;
  860. var nativeTargets = this.pbxNativeTargetSection();
  861. if (typeof nativeTargets[target] == "undefined")
  862. throw new Error("Invalid target: " + target);
  863. var nativeTarget = nativeTargets[target];
  864. var buildPhases = nativeTarget.buildPhases;
  865. for(var i in buildPhases)
  866. {
  867. var buildPhase = buildPhases[i];
  868. if (buildPhase.comment==group)
  869. return buildPhase.value + "_comment";
  870. }
  871. }
  872. pbxProject.prototype.buildPhaseObject = function(name, group, target) {
  873. var section = this.hash.project.objects[name],
  874. obj, sectionKey, key;
  875. var buildPhase = this.buildPhase(group, target);
  876. for (key in section) {
  877. // only look for comments
  878. if (!COMMENT_KEY.test(key)) continue;
  879. // select the proper buildPhase
  880. if (buildPhase && buildPhase!=key)
  881. continue;
  882. if (section[key] == group) {
  883. sectionKey = key.split(COMMENT_KEY)[0];
  884. return section[sectionKey];
  885. }
  886. }
  887. return null;
  888. }
  889. pbxProject.prototype.addBuildProperty = function(prop, value, build_name) {
  890. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  891. key, configuration;
  892. for (key in configurations){
  893. configuration = configurations[key];
  894. if (!build_name || configuration.name === build_name){
  895. configuration.buildSettings[prop] = value;
  896. }
  897. }
  898. }
  899. pbxProject.prototype.removeBuildProperty = function(prop, build_name) {
  900. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  901. key, configuration;
  902. for (key in configurations){
  903. configuration = configurations[key];
  904. if (configuration.buildSettings[prop] &&
  905. !build_name || configuration.name === build_name){
  906. delete configuration.buildSettings[prop];
  907. }
  908. }
  909. }
  910. /**
  911. *
  912. * @param prop {String}
  913. * @param value {String|Array|Object|Number|Boolean}
  914. * @param build {String} Release or Debug
  915. */
  916. pbxProject.prototype.updateBuildProperty = function(prop, value, build) {
  917. var configs = this.pbxXCBuildConfigurationSection();
  918. for (var configName in configs) {
  919. if (!COMMENT_KEY.test(configName)) {
  920. var config = configs[configName];
  921. if ( (build && config.name === build) || (!build) ) {
  922. config.buildSettings[prop] = value;
  923. }
  924. }
  925. }
  926. }
  927. pbxProject.prototype.updateProductName = function(name) {
  928. this.updateBuildProperty('PRODUCT_NAME', '"' + name + '"');
  929. }
  930. pbxProject.prototype.removeFromFrameworkSearchPaths = function(file) {
  931. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  932. INHERITED = '"$(inherited)"',
  933. SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',
  934. config, buildSettings, searchPaths;
  935. var new_path = searchPathForFile(file, this);
  936. for (config in configurations) {
  937. buildSettings = configurations[config].buildSettings;
  938. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  939. continue;
  940. searchPaths = buildSettings[SEARCH_PATHS];
  941. if (searchPaths && Array.isArray(searchPaths)) {
  942. var matches = searchPaths.filter(function(p) {
  943. return p.indexOf(new_path) > -1;
  944. });
  945. matches.forEach(function(m) {
  946. var idx = searchPaths.indexOf(m);
  947. searchPaths.splice(idx, 1);
  948. });
  949. }
  950. }
  951. }
  952. pbxProject.prototype.addToFrameworkSearchPaths = function(file) {
  953. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  954. INHERITED = '"$(inherited)"',
  955. config, buildSettings, searchPaths;
  956. for (config in configurations) {
  957. buildSettings = configurations[config].buildSettings;
  958. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  959. continue;
  960. if (!buildSettings['FRAMEWORK_SEARCH_PATHS']
  961. || buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
  962. buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];
  963. }
  964. buildSettings['FRAMEWORK_SEARCH_PATHS'].push(searchPathForFile(file, this));
  965. }
  966. }
  967. pbxProject.prototype.removeFromLibrarySearchPaths = function(file) {
  968. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  969. INHERITED = '"$(inherited)"',
  970. SEARCH_PATHS = 'LIBRARY_SEARCH_PATHS',
  971. config, buildSettings, searchPaths;
  972. var new_path = searchPathForFile(file, this);
  973. for (config in configurations) {
  974. buildSettings = configurations[config].buildSettings;
  975. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  976. continue;
  977. searchPaths = buildSettings[SEARCH_PATHS];
  978. if (searchPaths && Array.isArray(searchPaths)) {
  979. var matches = searchPaths.filter(function(p) {
  980. return p.indexOf(new_path) > -1;
  981. });
  982. matches.forEach(function(m) {
  983. var idx = searchPaths.indexOf(m);
  984. searchPaths.splice(idx, 1);
  985. });
  986. }
  987. }
  988. }
  989. pbxProject.prototype.addToLibrarySearchPaths = function(file) {
  990. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  991. INHERITED = '"$(inherited)"',
  992. config, buildSettings, searchPaths;
  993. for (config in configurations) {
  994. buildSettings = configurations[config].buildSettings;
  995. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  996. continue;
  997. if (!buildSettings['LIBRARY_SEARCH_PATHS']
  998. || buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) {
  999. buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED];
  1000. }
  1001. if (typeof file === 'string') {
  1002. buildSettings['LIBRARY_SEARCH_PATHS'].push(file);
  1003. } else {
  1004. buildSettings['LIBRARY_SEARCH_PATHS'].push(searchPathForFile(file, this));
  1005. }
  1006. }
  1007. }
  1008. pbxProject.prototype.removeFromHeaderSearchPaths = function(file) {
  1009. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1010. INHERITED = '"$(inherited)"',
  1011. SEARCH_PATHS = 'HEADER_SEARCH_PATHS',
  1012. config, buildSettings, searchPaths;
  1013. var new_path = searchPathForFile(file, this);
  1014. for (config in configurations) {
  1015. buildSettings = configurations[config].buildSettings;
  1016. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  1017. continue;
  1018. if (buildSettings[SEARCH_PATHS]) {
  1019. var matches = buildSettings[SEARCH_PATHS].filter(function(p) {
  1020. return p.indexOf(new_path) > -1;
  1021. });
  1022. matches.forEach(function(m) {
  1023. var idx = buildSettings[SEARCH_PATHS].indexOf(m);
  1024. buildSettings[SEARCH_PATHS].splice(idx, 1);
  1025. });
  1026. }
  1027. }
  1028. }
  1029. pbxProject.prototype.addToHeaderSearchPaths = function(file) {
  1030. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1031. INHERITED = '"$(inherited)"',
  1032. config, buildSettings, searchPaths;
  1033. for (config in configurations) {
  1034. buildSettings = configurations[config].buildSettings;
  1035. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  1036. continue;
  1037. if (!buildSettings['HEADER_SEARCH_PATHS']) {
  1038. buildSettings['HEADER_SEARCH_PATHS'] = [INHERITED];
  1039. }
  1040. if (typeof file === 'string') {
  1041. buildSettings['HEADER_SEARCH_PATHS'].push(file);
  1042. } else {
  1043. buildSettings['HEADER_SEARCH_PATHS'].push(searchPathForFile(file, this));
  1044. }
  1045. }
  1046. }
  1047. pbxProject.prototype.addToOtherLinkerFlags = function (flag) {
  1048. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1049. INHERITED = '"$(inherited)"',
  1050. OTHER_LDFLAGS = 'OTHER_LDFLAGS',
  1051. config, buildSettings;
  1052. for (config in configurations) {
  1053. buildSettings = configurations[config].buildSettings;
  1054. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
  1055. continue;
  1056. if (!buildSettings[OTHER_LDFLAGS]
  1057. || buildSettings[OTHER_LDFLAGS] === INHERITED) {
  1058. buildSettings[OTHER_LDFLAGS] = [INHERITED];
  1059. }
  1060. buildSettings[OTHER_LDFLAGS].push(flag);
  1061. }
  1062. }
  1063. pbxProject.prototype.removeFromOtherLinkerFlags = function (flag) {
  1064. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1065. OTHER_LDFLAGS = 'OTHER_LDFLAGS',
  1066. config, buildSettings;
  1067. for (config in configurations) {
  1068. buildSettings = configurations[config].buildSettings;
  1069. if (unquote(buildSettings['PRODUCT_NAME']) != this.productName) {
  1070. continue;
  1071. }
  1072. if (buildSettings[OTHER_LDFLAGS]) {
  1073. var matches = buildSettings[OTHER_LDFLAGS].filter(function (p) {
  1074. return p.indexOf(flag) > -1;
  1075. });
  1076. matches.forEach(function (m) {
  1077. var idx = buildSettings[OTHER_LDFLAGS].indexOf(m);
  1078. buildSettings[OTHER_LDFLAGS].splice(idx, 1);
  1079. });
  1080. }
  1081. }
  1082. }
  1083. pbxProject.prototype.addToBuildSettings = function (buildSetting, value) {
  1084. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1085. config, buildSettings;
  1086. for (config in configurations) {
  1087. buildSettings = configurations[config].buildSettings;
  1088. buildSettings[buildSetting] = value;
  1089. }
  1090. }
  1091. pbxProject.prototype.removeFromBuildSettings = function (buildSetting) {
  1092. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1093. config, buildSettings;
  1094. for (config in configurations) {
  1095. buildSettings = configurations[config].buildSettings;
  1096. if (buildSettings[buildSetting]) {
  1097. delete buildSettings[buildSetting];
  1098. }
  1099. }
  1100. }
  1101. // a JS getter. hmmm
  1102. pbxProject.prototype.__defineGetter__("productName", function() {
  1103. var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
  1104. config, productName;
  1105. for (config in configurations) {
  1106. productName = configurations[config].buildSettings['PRODUCT_NAME'];
  1107. if (productName) {
  1108. return unquote(productName);
  1109. }
  1110. }
  1111. });
  1112. // check if file is present
  1113. pbxProject.prototype.hasFile = function(filePath) {
  1114. var files = nonComments(this.pbxFileReferenceSection()),
  1115. file, id;
  1116. for (id in files) {
  1117. file = files[id];
  1118. if (file.path == filePath || file.path == ('"' + filePath + '"')) {
  1119. return file;
  1120. }
  1121. }
  1122. return false;
  1123. }
  1124. pbxProject.prototype.addTarget = function(name, type, subfolder) {
  1125. // Setup uuid and name of new target
  1126. var targetUuid = this.generateUuid(),
  1127. targetType = type,
  1128. targetSubfolder = subfolder || name,
  1129. targetName = name.trim();
  1130. // Check type against list of allowed target types
  1131. if (!targetName) {
  1132. throw new Error("Target name missing.");
  1133. }
  1134. // Check type against list of allowed target types
  1135. if (!targetType) {
  1136. throw new Error("Target type missing.");
  1137. }
  1138. // Check type against list of allowed target types
  1139. if (!producttypeForTargettype(targetType)) {
  1140. throw new Error("Target type invalid: " + targetType);
  1141. }
  1142. // Build Configuration: Create
  1143. var buildConfigurationsList = [
  1144. {
  1145. name: 'Debug',
  1146. isa: 'XCBuildConfiguration',
  1147. buildSettings: {
  1148. GCC_PREPROCESSOR_DEFINITIONS: ['"DEBUG=1"', '"$(inherited)"'],
  1149. INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'),
  1150. LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"',
  1151. PRODUCT_NAME: '"' + targetName + '"',
  1152. SKIP_INSTALL: 'YES'
  1153. }
  1154. },
  1155. {
  1156. name: 'Release',
  1157. isa: 'XCBuildConfiguration',
  1158. buildSettings: {
  1159. INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'),
  1160. LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"',
  1161. PRODUCT_NAME: '"' + targetName + '"',
  1162. SKIP_INSTALL: 'YES'
  1163. }
  1164. }
  1165. ];
  1166. // Build Configuration: Add
  1167. var buildConfigurations = this.addXCConfigurationList(buildConfigurationsList, 'Release', 'Build configuration list for PBXNativeTarget "' + targetName +'"');
  1168. // Product: Create
  1169. var productName = targetName,
  1170. productType = producttypeForTargettype(targetType),
  1171. productFileType = filetypeForProducttype(productType),
  1172. productFile = this.addProductFile(productName, { group: 'Copy Files', 'target': targetUuid, 'explicitFileType': productFileType}),
  1173. productFileName = productFile.basename;
  1174. // Product: Add to build file list
  1175. this.addToPbxBuildFileSection(productFile);
  1176. // Target: Create
  1177. var target = {
  1178. uuid: targetUuid,
  1179. pbxNativeTarget: {
  1180. isa: 'PBXNativeTarget',
  1181. name: '"' + targetName + '"',
  1182. productName: '"' + targetName + '"',
  1183. productReference: productFile.fileRef,
  1184. productType: '"' + producttypeForTargettype(targetType) + '"',
  1185. buildConfigurationList: buildConfigurations.uuid,
  1186. buildPhases: [],
  1187. buildRules: [],
  1188. dependencies: []
  1189. }
  1190. };
  1191. // Target: Add to PBXNativeTarget section
  1192. this.addToPbxNativeTargetSection(target)
  1193. // Product: Embed (only for "extension"-type targets)
  1194. if (targetType === 'app_extension') {
  1195. // Create CopyFiles phase in first target
  1196. this.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Copy Files', this.getFirstTarget().uuid, targetType)
  1197. // Add product to CopyFiles phase
  1198. this.addToPbxCopyfilesBuildPhase(productFile)
  1199. // this.addBuildPhaseToTarget(newPhase.buildPhase, this.getFirstTarget().uuid)
  1200. };
  1201. // Target: Add uuid to root project
  1202. this.addToPbxProjectSection(target);
  1203. // Target: Add dependency for this target to first (main) target
  1204. this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]);
  1205. // Return target on success
  1206. return target;
  1207. };
  1208. // helper recursive prop search+replace
  1209. function propReplace(obj, prop, value) {
  1210. var o = {};
  1211. for (var p in obj) {
  1212. if (o.hasOwnProperty.call(obj, p)) {
  1213. if (typeof obj[p] == 'object' && !Array.isArray(obj[p])) {
  1214. propReplace(obj[p], prop, value);
  1215. } else if (p == prop) {
  1216. obj[p] = value;
  1217. }
  1218. }
  1219. }
  1220. }
  1221. // helper object creation functions
  1222. function pbxBuildFileObj(file) {
  1223. var obj = Object.create(null);
  1224. obj.isa = 'PBXBuildFile';
  1225. obj.fileRef = file.fileRef;
  1226. obj.fileRef_comment = file.basename;
  1227. if (file.settings) obj.settings = file.settings;
  1228. return obj;
  1229. }
  1230. function pbxFileReferenceObj(file) {
  1231. var fileObject = {
  1232. isa: "PBXFileReference",
  1233. name: "\"" + file.basename + "\"",
  1234. path: "\"" + file.path.replace(/\\/g, '/') + "\"",
  1235. sourceTree: file.sourceTree,
  1236. fileEncoding: file.fileEncoding,
  1237. lastKnownFileType: file.lastKnownFileType,
  1238. explicitFileType: file.explicitFileType,
  1239. includeInIndex: file.includeInIndex
  1240. };
  1241. return fileObject;
  1242. }
  1243. function pbxGroupChild(file) {
  1244. var obj = Object.create(null);
  1245. obj.value = file.fileRef;
  1246. obj.comment = file.basename;
  1247. return obj;
  1248. }
  1249. function pbxBuildPhaseObj(file) {
  1250. var obj = Object.create(null);
  1251. obj.value = file.uuid;
  1252. obj.comment = longComment(file);
  1253. return obj;
  1254. }
  1255. function pbxCopyFilesBuildPhaseObj(obj, folderType, subfolderPath, phaseName) {
  1256. // Add additional properties for 'CopyFiles' build phase
  1257. var DESTINATION_BY_TARGETTYPE = {
  1258. application: 'wrapper',
  1259. app_extension: 'plugins',
  1260. bundle: 'wrapper',
  1261. command_line_tool: 'wrapper',
  1262. dynamic_library: 'products_directory',
  1263. framework: 'shared_frameworks',
  1264. frameworks: 'frameworks',
  1265. static_library: 'products_directory',
  1266. unit_test_bundle: 'wrapper',
  1267. watch_app: 'wrapper',
  1268. watch_extension: 'plugins'
  1269. }
  1270. var SUBFOLDERSPEC_BY_DESTINATION = {
  1271. absolute_path: 0,
  1272. executables: 6,
  1273. frameworks: 10,
  1274. java_resources: 15,
  1275. plugins: 13,
  1276. products_directory: 16,
  1277. resources: 7,
  1278. shared_frameworks: 11,
  1279. shared_support: 12,
  1280. wrapper: 1,
  1281. xpc_services: 0
  1282. }
  1283. obj.name = '"' + phaseName + '"';
  1284. obj.dstPath = subfolderPath || '""';
  1285. obj.dstSubfolderSpec = SUBFOLDERSPEC_BY_DESTINATION[DESTINATION_BY_TARGETTYPE[folderType]];
  1286. return obj;
  1287. }
  1288. function pbxShellScriptBuildPhaseObj(obj, options, phaseName) {
  1289. obj.name = '"' + phaseName + '"';
  1290. obj.inputPaths = options.inputPaths || [];
  1291. obj.outputPaths = options.outputPaths || [];
  1292. obj.shellPath = options.shellPath;
  1293. obj.shellScript = '"' + options.shellScript.replace(/"/g, '\\"') + '"';
  1294. return obj;
  1295. }
  1296. function pbxBuildFileComment(file) {
  1297. return longComment(file);
  1298. }
  1299. function pbxFileReferenceComment(file) {
  1300. return file.basename || path.basename(file.path);
  1301. }
  1302. function pbxNativeTargetComment(target) {
  1303. return target.name;
  1304. }
  1305. function longComment(file) {
  1306. return f("%s in %s", file.basename, file.group);
  1307. }
  1308. // respect <group> path
  1309. function correctForPluginsPath(file, project) {
  1310. return correctForPath(file, project, 'Plugins');
  1311. }
  1312. function correctForResourcesPath(file, project) {
  1313. return correctForPath(file, project, 'Resources');
  1314. }
  1315. function correctForFrameworksPath(file, project) {
  1316. return correctForPath(file, project, 'Frameworks');
  1317. }
  1318. function correctForPath(file, project, group) {
  1319. var r_group_dir = new RegExp('^' + group + '[\\\\/]');
  1320. if (project.pbxGroupByName(group).path)
  1321. file.path = file.path.replace(r_group_dir, '');
  1322. return file;
  1323. }
  1324. function searchPathForFile(file, proj) {
  1325. var plugins = proj.pbxGroupByName('Plugins'),
  1326. pluginsPath = plugins ? plugins.path : null,
  1327. fileDir = path.dirname(file.path);
  1328. if (fileDir == '.') {
  1329. fileDir = '';
  1330. } else {
  1331. fileDir = '/' + fileDir;
  1332. }
  1333. if (file.plugin && pluginsPath) {
  1334. return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""';
  1335. } else if (file.customFramework && file.dirname) {
  1336. return '"\\"' + file.dirname + '\\""';
  1337. } else {
  1338. return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""';
  1339. }
  1340. }
  1341. function nonComments(obj) {
  1342. var keys = Object.keys(obj),
  1343. newObj = {}, i = 0;
  1344. for (i; i < keys.length; i++) {
  1345. if (!COMMENT_KEY.test(keys[i])) {
  1346. newObj[keys[i]] = obj[keys[i]];
  1347. }
  1348. }
  1349. return newObj;
  1350. }
  1351. function unquote(str) {
  1352. if (str) return str.replace(/^"(.*)"$/, "$1");
  1353. }
  1354. function buildPhaseNameForIsa (isa) {
  1355. BUILDPHASENAME_BY_ISA = {
  1356. PBXCopyFilesBuildPhase: 'Copy Files',
  1357. PBXResourcesBuildPhase: 'Resources',
  1358. PBXSourcesBuildPhase: 'Sources',
  1359. PBXFrameworksBuildPhase: 'Frameworks'
  1360. }
  1361. return BUILDPHASENAME_BY_ISA[isa]
  1362. }
  1363. function producttypeForTargettype (targetType) {
  1364. PRODUCTTYPE_BY_TARGETTYPE = {
  1365. application: 'com.apple.product-type.application',
  1366. app_extension: 'com.apple.product-type.app-extension',
  1367. bundle: 'com.apple.product-type.bundle',
  1368. command_line_tool: 'com.apple.product-type.tool',
  1369. dynamic_library: 'com.apple.product-type.library.dynamic',
  1370. framework: 'com.apple.product-type.framework',
  1371. static_library: 'com.apple.product-type.library.static',
  1372. unit_test_bundle: 'com.apple.product-type.bundle.unit-test',
  1373. watch_app: 'com.apple.product-type.application.watchapp',
  1374. watch_extension: 'com.apple.product-type.watchkit-extension'
  1375. };
  1376. return PRODUCTTYPE_BY_TARGETTYPE[targetType]
  1377. }
  1378. function filetypeForProducttype (productType) {
  1379. FILETYPE_BY_PRODUCTTYPE = {
  1380. 'com.apple.product-type.application': '"wrapper.application"',
  1381. 'com.apple.product-type.app-extension': '"wrapper.app-extension"',
  1382. 'com.apple.product-type.bundle': '"wrapper.plug-in"',
  1383. 'com.apple.product-type.tool': '"compiled.mach-o.dylib"',
  1384. 'com.apple.product-type.library.dynamic': '"compiled.mach-o.dylib"',
  1385. 'com.apple.product-type.framework': '"wrapper.framework"',
  1386. 'com.apple.product-type.library.static': '"archive.ar"',
  1387. 'com.apple.product-type.bundle.unit-test': '"wrapper.cfbundle"',
  1388. 'com.apple.product-type.application.watchapp': '"wrapper.application"',
  1389. 'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"'
  1390. };
  1391. return FILETYPE_BY_PRODUCTTYPE[productType]
  1392. }
  1393. pbxProject.prototype.getFirstProject = function() {
  1394. // Get pbxProject container
  1395. var pbxProjectContainer = this.pbxProjectSection();
  1396. // Get first pbxProject UUID
  1397. var firstProjectUuid = Object.keys(pbxProjectContainer)[0];
  1398. // Get first pbxProject
  1399. var firstProject = pbxProjectContainer[firstProjectUuid];
  1400. return {
  1401. uuid: firstProjectUuid,
  1402. firstProject: firstProject
  1403. }
  1404. }
  1405. pbxProject.prototype.getFirstTarget = function() {
  1406. // Get first targets UUID
  1407. var firstTargetUuid = this.getFirstProject()['firstProject']['targets'][0].value;
  1408. // Get first pbxNativeTarget
  1409. var firstTarget = this.pbxNativeTargetSection()[firstTargetUuid];
  1410. return {
  1411. uuid: firstTargetUuid,
  1412. firstTarget: firstTarget
  1413. }
  1414. }
  1415. /*** NEW ***/
  1416. pbxProject.prototype.addToPbxGroupType = function (file, groupKey, groupType) {
  1417. var group = this.getPBXGroupByKeyAndType(groupKey, groupType);
  1418. if (group && group.children !== undefined) {
  1419. if (typeof file === 'string') {
  1420. //Group Key
  1421. var childGroup = {
  1422. value:file,
  1423. };
  1424. if (this.getPBXGroupByKey(file)) {
  1425. childGroup.comment = this.getPBXGroupByKey(file).name;
  1426. }
  1427. else if (this.getPBXVariantGroupByKey(file)) {
  1428. childGroup.comment = this.getPBXVariantGroupByKey(file).name;
  1429. }
  1430. group.children.push(childGroup);
  1431. }
  1432. else {
  1433. //File Object
  1434. group.children.push(pbxGroupChild(file));
  1435. }
  1436. }
  1437. }
  1438. pbxProject.prototype.addToPbxVariantGroup = function (file, groupKey) {
  1439. this.addToPbxGroupType(file, groupKey, 'PBXVariantGroup');
  1440. }
  1441. pbxProject.prototype.addToPbxGroup = function (file, groupKey) {
  1442. this.addToPbxGroupType(file, groupKey, 'PBXGroup');
  1443. }
  1444. pbxProject.prototype.pbxCreateGroupWithType = function(name, pathName, groupType) {
  1445. //Create object
  1446. var model = {
  1447. isa: '"' + groupType + '"',
  1448. children: [],
  1449. name: name,
  1450. sourceTree: '"<group>"'
  1451. };
  1452. if (pathName) model.path = pathName;
  1453. var key = this.generateUuid();
  1454. //Create comment
  1455. var commendId = key + '_comment';
  1456. //add obj and commentObj to groups;
  1457. var groups = this.hash.project.objects[groupType];
  1458. if (!groups) {
  1459. groups = this.hash.project.objects[groupType] = new Object();
  1460. }
  1461. groups[commendId] = name;
  1462. groups[key] = model;
  1463. return key;
  1464. }
  1465. pbxProject.prototype.pbxCreateVariantGroup = function(name) {
  1466. return this.pbxCreateGroupWithType(name, undefined, 'PBXVariantGroup')
  1467. }
  1468. pbxProject.prototype.pbxCreateGroup = function(name, pathName) {
  1469. return this.pbxCreateGroupWithType(name, pathName, 'PBXGroup');
  1470. }
  1471. pbxProject.prototype.removeFromPbxGroupAndType = function (file, groupKey, groupType) {
  1472. var group = this.getPBXGroupByKeyAndType(groupKey, groupType);
  1473. if (group) {
  1474. var groupChildren = group.children, i;
  1475. for(i in groupChildren) {
  1476. if(pbxGroupChild(file).value == groupChildren[i].value &&
  1477. pbxGroupChild(file).comment == groupChildren[i].comment) {
  1478. groupChildren.splice(i, 1);
  1479. break;
  1480. }
  1481. }
  1482. }
  1483. }
  1484. pbxProject.prototype.removeFromPbxGroup = function (file, groupKey) {
  1485. this.removeFromPbxGroupAndType(file, groupKey, 'PBXGroup');
  1486. }
  1487. pbxProject.prototype.removeFromPbxVariantGroup = function (file, groupKey) {
  1488. this.removeFromPbxGroupAndType(file, groupKey, 'PBXVariantGroup');
  1489. }
  1490. pbxProject.prototype.getPBXGroupByKeyAndType = function(key, groupType) {
  1491. return this.hash.project.objects[groupType][key];
  1492. };
  1493. pbxProject.prototype.getPBXGroupByKey = function(key) {
  1494. return this.hash.project.objects['PBXGroup'][key];
  1495. };
  1496. pbxProject.prototype.getPBXVariantGroupByKey = function(key) {
  1497. return this.hash.project.objects['PBXVariantGroup'][key];
  1498. };
  1499. pbxProject.prototype.findPBXGroupKeyAndType = function(criteria, groupType) {
  1500. var groups = this.hash.project.objects[groupType];
  1501. var target;
  1502. for (var key in groups) {
  1503. // only look for comments
  1504. if (COMMENT_KEY.test(key)) continue;
  1505. var group = groups[key];
  1506. if (criteria && criteria.path && criteria.name) {
  1507. if (criteria.path === group.path && criteria.name === group.name) {
  1508. target = key;
  1509. break
  1510. }
  1511. }
  1512. else if (criteria && criteria.path) {
  1513. if (criteria.path === group.path) {
  1514. target = key;
  1515. break
  1516. }
  1517. }
  1518. else if (criteria && criteria.name) {
  1519. if (criteria.name === group.name) {
  1520. target = key;
  1521. break
  1522. }
  1523. }
  1524. }
  1525. return target;
  1526. }
  1527. pbxProject.prototype.findPBXGroupKey = function(criteria) {
  1528. return this.findPBXGroupKeyAndType(criteria, 'PBXGroup');
  1529. }
  1530. pbxProject.prototype.findPBXVariantGroupKey = function(criteria) {
  1531. return this.findPBXGroupKeyAndType(criteria, 'PBXVariantGroup');
  1532. }
  1533. pbxProject.prototype.addLocalizationVariantGroup = function(name) {
  1534. var groupKey = this.pbxCreateVariantGroup(name);
  1535. var resourceGroupKey = this.findPBXGroupKey({name: 'Resources'});
  1536. this.addToPbxGroup(groupKey, resourceGroupKey);
  1537. var localizationVariantGroup = {
  1538. uuid: this.generateUuid(),
  1539. fileRef: groupKey,
  1540. basename: name
  1541. }
  1542. this.addToPbxBuildFileSection(localizationVariantGroup); // PBXBuildFile
  1543. this.addToPbxResourcesBuildPhase(localizationVariantGroup); //PBXResourcesBuildPhase
  1544. return localizationVariantGroup;
  1545. };
  1546. pbxProject.prototype.addKnownRegion = function (name) {
  1547. if (!this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions']) {
  1548. this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'] = [];
  1549. }
  1550. if (!this.hasKnownRegion(name)) {
  1551. this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'].push(name);
  1552. }
  1553. }
  1554. pbxProject.prototype.removeKnownRegion = function (name) {
  1555. var regions = this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'];
  1556. if (regions) {
  1557. for (var i = 0; i < regions.length; i++) {
  1558. if (regions[i] === name) {
  1559. regions.splice(i, 1);
  1560. break;
  1561. }
  1562. }
  1563. this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'] = regions;
  1564. }
  1565. }
  1566. pbxProject.prototype.hasKnownRegion = function (name) {
  1567. var regions = this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'];
  1568. if (regions) {
  1569. for (var i in regions) {
  1570. if (regions[i] === name) {
  1571. return true;
  1572. }
  1573. }
  1574. }
  1575. return false;
  1576. }
  1577. pbxProject.prototype.getPBXObject = function(name) {
  1578. return this.hash.project.objects[name];
  1579. }
  1580. pbxProject.prototype.addFile = function (path, group, opt) {
  1581. var file = new pbxFile(path, opt);
  1582. // null is better for early errors
  1583. if (this.hasFile(file.path)) return null;
  1584. file.fileRef = this.generateUuid();
  1585. this.addToPbxFileReferenceSection(file); // PBXFileReference
  1586. if (this.getPBXGroupByKey(group)) {
  1587. this.addToPbxGroup(file, group); // PBXGroup
  1588. }
  1589. else if (this.getPBXVariantGroupByKey(group)) {
  1590. this.addToPbxVariantGroup(file, group); // PBXVariantGroup
  1591. }
  1592. return file;
  1593. }
  1594. pbxProject.prototype.removeFile = function (path, group, opt) {
  1595. var file = new pbxFile(path, opt);
  1596. this.removeFromPbxFileReferenceSection(file); // PBXFileReference
  1597. if (this.getPBXGroupByKey(group)) {
  1598. this.removeFromPbxGroup(file, group); // PBXGroup
  1599. }
  1600. else if (this.getPBXVariantGroupByKey(group)) {
  1601. this.removeFromPbxVariantGroup(file, group); // PBXVariantGroup
  1602. }
  1603. return file;
  1604. }
  1605. pbxProject.prototype.getBuildProperty = function(prop, build) {
  1606. var target;
  1607. var configs = this.pbxXCBuildConfigurationSection();
  1608. for (var configName in configs) {
  1609. if (!COMMENT_KEY.test(configName)) {
  1610. var config = configs[configName];
  1611. if ( (build && config.name === build) || (build === undefined) ) {
  1612. if (config.buildSettings[prop] !== undefined) {
  1613. target = config.buildSettings[prop];
  1614. }
  1615. }
  1616. }
  1617. }
  1618. return target;
  1619. }
  1620. pbxProject.prototype.getBuildConfigByName = function(name) {
  1621. var target = {};
  1622. var configs = this.pbxXCBuildConfigurationSection();
  1623. for (var configName in configs) {
  1624. if (!COMMENT_KEY.test(configName)) {
  1625. var config = configs[configName];
  1626. if (config.name === name) {
  1627. target[configName] = config;
  1628. }
  1629. }
  1630. }
  1631. return target;
  1632. }
  1633. pbxProject.prototype.addDataModelDocument = function(filePath, group, opt) {
  1634. if (!group) {
  1635. group = 'Resources';
  1636. }
  1637. if (!this.getPBXGroupByKey(group)) {
  1638. group = this.findPBXGroupKey({ name: group });
  1639. }
  1640. var file = new pbxFile(filePath, opt);
  1641. if (!file || this.hasFile(file.path)) return null;
  1642. file.fileRef = this.generateUuid();
  1643. this.addToPbxGroup(file, group);
  1644. if (!file) return false;
  1645. file.target = opt ? opt.target : undefined;
  1646. file.uuid = this.generateUuid();
  1647. this.addToPbxBuildFileSection(file);
  1648. this.addToPbxSourcesBuildPhase(file);
  1649. file.models = [];
  1650. var currentVersionName;
  1651. var modelFiles = fs.readdirSync(file.path);
  1652. for (var index in modelFiles) {
  1653. var modelFileName = modelFiles[index];
  1654. var modelFilePath = path.join(filePath, modelFileName);
  1655. if (modelFileName == '.xccurrentversion') {
  1656. currentVersionName = plist.readFileSync(modelFilePath)._XCCurrentVersionName;
  1657. continue;
  1658. }
  1659. var modelFile = new pbxFile(modelFilePath);
  1660. modelFile.fileRef = this.generateUuid();
  1661. this.addToPbxFileReferenceSection(modelFile);
  1662. file.models.push(modelFile);
  1663. if (currentVersionName && currentVersionName === modelFileName) {
  1664. file.currentModel = modelFile;
  1665. }
  1666. }
  1667. if (!file.currentModel) {
  1668. file.currentModel = file.models[0];
  1669. }
  1670. this.addToXcVersionGroupSection(file);
  1671. return file;
  1672. }
  1673. pbxProject.prototype.addTargetAttribute = function(prop, value, target) {
  1674. var attributes = this.getFirstProject()['firstProject']['attributes'];
  1675. if (attributes['TargetAttributes'] === undefined) {
  1676. attributes['TargetAttributes'] = {};
  1677. }
  1678. target = target || this.getFirstTarget();
  1679. if (attributes['TargetAttributes'][target.uuid] === undefined) {
  1680. attributes['TargetAttributes'][target.uuid] = {};
  1681. }
  1682. attributes['TargetAttributes'][target.uuid][prop] = value;
  1683. }
  1684. pbxProject.prototype.removeTargetAttribute = function(prop, target) {
  1685. var attributes = this.getFirstProject()['firstProject']['attributes'];
  1686. target = target || this.getFirstTarget();
  1687. if (attributes['TargetAttributes'] &&
  1688. attributes['TargetAttributes'][target.uuid]) {
  1689. delete attributes['TargetAttributes'][target.uuid][prop];
  1690. }
  1691. }
  1692. module.exports = pbxProject;