UNPKG

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