Skip to content

Commit 62e7316

Browse files
committed
[project] addStaticLibrary - support for .a files
a first pass - I'm not entirely happy with the LIBRARY_SEARCH_PATHS stuff, but I'm gonna start testing it out with pluginstall to move forward
1 parent 111392a commit 62e7316

File tree

4 files changed

+235
-4
lines changed

4 files changed

+235
-4
lines changed

lib/pbxFile.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var path = require('path'),
55
XIB_EXTENSION = /[.]xib$/, XIB_FILE = 'file.xib',
66
DYLIB_EXTENSION = /[.]dylib$/, DYLIB = '"compiled.mach-o.dylib"',
77
FRAMEWORK_EXTENSION = /[.]framework/, FRAMEWORK = 'wrapper.framework',
8+
ARCHIVE_EXTENSION = /[.]a$/, ARCHIVE = 'archive.ar',
89
DEFAULT_SOURCE_TREE = '"<group>"',
910
DEFAULT_FILE_ENCODING = 4;
1011

@@ -27,6 +28,9 @@ function detectLastType(path) {
2728
if (DYLIB_EXTENSION.test(path))
2829
return DYLIB;
2930

31+
if (ARCHIVE_EXTENSION.test(path))
32+
return ARCHIVE;
33+
3034
// dunno
3135
return 'unknown';
3236
}
@@ -58,7 +62,7 @@ function correctPath(file, filepath) {
5862
function correctGroup(file) {
5963
if (file.lastType == SOURCE_FILE) {
6064
return 'Sources';
61-
} else if (file.lastType == DYLIB) {
65+
} else if (file.lastType == DYLIB || file.lastType == ARCHIVE) {
6266
return 'Frameworks';
6367
} else {
6468
return 'Resources';

lib/pbxProject.js

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,20 @@ pbxProject.prototype.removeFramework = function (path, opt) {
197197
return file;
198198
}
199199

200+
pbxProject.prototype.addStaticLibrary = function (path, opt) {
201+
var file = new pbxFile(path, opt);
202+
203+
file.uuid = this.generateUuid();
204+
file.fileRef = this.generateUuid();
205+
206+
this.addToPbxBuildFileSection(file); // PBXBuildFile
207+
this.addToPbxFileReferenceSection(file); // PBXFileReference
208+
this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
209+
this.ensureCorrectSearchPaths(file); // make sure it gets built!
210+
211+
return file;
212+
}
213+
200214
// helper addition functions
201215
pbxProject.prototype.addToPbxBuildFileSection = function (file) {
202216
var commentKey = f("%s_comment", file.uuid);
@@ -405,6 +419,25 @@ pbxProject.prototype.updateProductName = function(name) {
405419
propReplace(config, 'PRODUCT_NAME', '"' + name + '"');
406420
}
407421

422+
pbxProject.prototype.ensureCorrectSearchPaths = function (file) {
423+
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
424+
INHERITED = '"$(inherited)"',
425+
config, buildSettings, searchPaths;
426+
427+
for (config in configurations) {
428+
buildSettings = configurations[config].buildSettings;
429+
430+
if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
431+
continue;
432+
433+
if (!buildSettings['LIBRARY_SEARCH_PATHS']) {
434+
buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED];
435+
}
436+
437+
buildSettings['LIBRARY_SEARCH_PATHS'].push(searchPathForFile(file, this));
438+
}
439+
}
440+
408441
// a JS getter. hmmm
409442
pbxProject.prototype.__defineGetter__("productName", function () {
410443
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
@@ -414,7 +447,7 @@ pbxProject.prototype.__defineGetter__("productName", function () {
414447
productName = configurations[config].buildSettings['PRODUCT_NAME'];
415448

416449
if (productName) {
417-
return stripSurroundingQuotes(productName);
450+
return unquote(productName);
418451
}
419452
}
420453
});
@@ -507,6 +540,10 @@ function correctForResourcesPath(file, project) {
507540
return file;
508541
}
509542

543+
function searchPathForFile(file, project) {
544+
return '"\\"$(SRCROOT)/' + project.productName + '\\""';
545+
}
546+
510547
function nonComments(obj) {
511548
var keys = Object.keys(obj),
512549
newObj = {}, i = 0;
@@ -520,8 +557,8 @@ function nonComments(obj) {
520557
return newObj;
521558
}
522559

523-
function stripSurroundingQuotes(str) {
524-
return str.replace(/^"(.*)"$/, "$1");
560+
function unquote(str) {
561+
if (str) return str.replace(/^"(.*)"$/, "$1");
525562
}
526563

527564
module.exports = pbxProject;

test/addStaticLibrary.js

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
var fullProject = require('./fixtures/full-project')
2+
fullProjectStr = JSON.stringify(fullProject),
3+
pbx = require('../lib/pbxProject'),
4+
pbxFile = require('../lib/pbxFile'),
5+
proj = new pbx('.');
6+
7+
function cleanHash() {
8+
return JSON.parse(fullProjectStr);
9+
}
10+
11+
function nonComments(obj) {
12+
var keys = Object.keys(obj),
13+
newObj = {}, i = 0;
14+
15+
for (i; i < keys.length; i++) {
16+
if (!/_comment$/.test(keys[i])) {
17+
newObj[keys[i]] = obj[keys[i]];
18+
}
19+
}
20+
21+
return newObj;
22+
}
23+
24+
exports.setUp = function (callback) {
25+
proj.hash = cleanHash();
26+
callback();
27+
}
28+
29+
exports.addStaticLibrary = {
30+
'should return a pbxFile': function (test) {
31+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
32+
33+
test.equal(newFile.constructor, pbxFile);
34+
test.done()
35+
},
36+
'should set a fileRef on the pbxFile': function (test) {
37+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
38+
39+
test.ok(newFile.fileRef);
40+
test.done()
41+
},
42+
'should populate the PBXBuildFile section with 2 fields': function (test) {
43+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
44+
buildFileSection = proj.pbxBuildFileSection(),
45+
bfsLength = Object.keys(buildFileSection).length;
46+
47+
test.equal(60, bfsLength);
48+
test.ok(buildFileSection[newFile.uuid]);
49+
test.ok(buildFileSection[newFile.uuid + '_comment']);
50+
51+
test.done();
52+
},
53+
'should add the PBXBuildFile comment correctly': function (test) {
54+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
55+
commentKey = newFile.uuid + '_comment',
56+
buildFileSection = proj.pbxBuildFileSection();
57+
58+
test.equal(buildFileSection[commentKey], 'libGoogleAnalytics.a in Frameworks');
59+
test.done();
60+
},
61+
'should add the PBXBuildFile object correctly': function (test) {
62+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
63+
buildFileSection = proj.pbxBuildFileSection(),
64+
buildFileEntry = buildFileSection[newFile.uuid];
65+
66+
test.equal(buildFileEntry.isa, 'PBXBuildFile');
67+
test.equal(buildFileEntry.fileRef, newFile.fileRef);
68+
test.equal(buildFileEntry.fileRef_comment, 'libGoogleAnalytics.a');
69+
70+
test.done();
71+
},
72+
'should populate the PBXFileReference section with 2 fields': function (test) {
73+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
74+
fileRefSection = proj.pbxFileReferenceSection(),
75+
frsLength = Object.keys(fileRefSection).length;
76+
77+
test.equal(68, frsLength);
78+
test.ok(fileRefSection[newFile.fileRef]);
79+
test.ok(fileRefSection[newFile.fileRef + '_comment']);
80+
81+
test.done();
82+
},
83+
'should populate the PBXFileReference comment correctly': function (test) {
84+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a');
85+
fileRefSection = proj.pbxFileReferenceSection(),
86+
commentKey = newFile.fileRef + '_comment';
87+
88+
test.equal(fileRefSection[commentKey], 'libGoogleAnalytics.a');
89+
test.done();
90+
},
91+
'should add the PBXFileReference object correctly': function (test) {
92+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a'),
93+
fileRefSection = proj.pbxFileReferenceSection(),
94+
fileRefEntry = fileRefSection[newFile.fileRef];
95+
96+
test.equal(fileRefEntry.isa, 'PBXFileReference');
97+
test.equal(fileRefEntry.lastKnownFileType, 'archive.ar');
98+
test.equal(fileRefEntry.name, 'libGoogleAnalytics.a');
99+
test.equal(fileRefEntry.path, 'libGoogleAnalytics.a');
100+
test.equal(fileRefEntry.sourceTree, '"<group>"');
101+
102+
test.done();
103+
},
104+
'should add to the PBXFrameworksBuildPhase': function (test) {
105+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a'),
106+
frameworks = proj.pbxFrameworksBuildPhaseObj();
107+
108+
test.equal(frameworks.files.length, 16);
109+
test.done();
110+
},
111+
'should have the right values for the Sources entry': function (test) {
112+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a'),
113+
frameworks = proj.pbxFrameworksBuildPhaseObj(),
114+
framework = frameworks.files[15];
115+
116+
test.equal(framework.comment, 'libGoogleAnalytics.a in Frameworks');
117+
test.equal(framework.value, newFile.uuid);
118+
test.done();
119+
},
120+
'should set LIBRARY_SEARCH_PATHS for appropriate build configurations': function (test) {
121+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a'),
122+
configs = nonComments(proj.pbxXCBuildConfigurationSection()),
123+
ids = Object.keys(configs), i, buildSettings;
124+
125+
for (i = 0; i< ids.length; i++) {
126+
buildSettings = configs[ids[i]].buildSettings;
127+
128+
if (buildSettings['PRODUCT_NAME'] == '"KitchenSinktablet"') {
129+
test.ok(buildSettings['LIBRARY_SEARCH_PATHS']);
130+
}
131+
}
132+
133+
test.done();
134+
},
135+
'should ensure LIBRARY_SEARCH_PATHS inherits defaults correctly': function (test) {
136+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a'),
137+
configs = nonComments(proj.pbxXCBuildConfigurationSection()),
138+
ids = Object.keys(configs), i, current, buildSettings;
139+
140+
for (i = 0; i< ids.length; i++) {
141+
buildSettings = configs[ids[i]].buildSettings;
142+
143+
if (buildSettings['PRODUCT_NAME'] == '"KitchenSinktablet"') {
144+
current = buildSettings['LIBRARY_SEARCH_PATHS'];
145+
146+
test.ok(current.indexOf('"$(inherited)"') >= 0)
147+
}
148+
}
149+
150+
test.done();
151+
},
152+
'should ensure the new library is in LIBRARY_SEARCH_PATHS': function (test) {
153+
var newFile = proj.addStaticLibrary('libGoogleAnalytics.a'),
154+
configs = nonComments(proj.pbxXCBuildConfigurationSection()),
155+
expectedPath = '"\\"$(SRCROOT)/KitchenSinktablet\\""',
156+
ids = Object.keys(configs), i, current, buildSettings;
157+
158+
for (i = 0; i< ids.length; i++) {
159+
buildSettings = configs[ids[i]].buildSettings;
160+
161+
if (buildSettings['PRODUCT_NAME'] == '"KitchenSinktablet"') {
162+
current = buildSettings['LIBRARY_SEARCH_PATHS'];
163+
164+
test.ok(current.indexOf(expectedPath) >= 0)
165+
}
166+
}
167+
168+
test.done();
169+
}
170+
}

test/pbxFile.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ exports['lastType'] = {
4343
test.done();
4444
},
4545

46+
'should detect that a .a path means archive.ar': function (test) {
47+
var sourceFile = new pbxFile('libGoogleAnalytics.a');
48+
49+
test.equal('archive.ar', sourceFile.lastType);
50+
test.done();
51+
},
52+
4653
'should allow lastType to be overridden': function (test) {
4754
var sourceFile = new pbxFile('Plugins/ChildBrowser.m',
4855
{ lastType: 'somestupidtype' });
@@ -79,6 +86,12 @@ exports['group'] = {
7986
test.equal('Resources', headerFile.group);
8087
test.equal('Resources', xibFile.group);
8188
test.done();
89+
},
90+
'should be Frameworks for archives': function (test) {
91+
var archive = new pbxFile('libGoogleAnalytics.a');
92+
93+
test.equal('Frameworks', archive.group);
94+
test.done();
8295
}
8396
}
8497

@@ -119,6 +132,13 @@ exports['sourceTree'] = {
119132

120133
test.equal('SOMETHING', sourceFile.sourceTree);
121134
test.done();
135+
},
136+
137+
'should be "<group>" for archives': function (test) {
138+
var archive = new pbxFile('libGoogleAnalytics.a');
139+
140+
test.equal('"<group>"', archive.sourceTree);
141+
test.done();
122142
}
123143
}
124144

0 commit comments

Comments
 (0)