Skip to content

Commit 71118fb

Browse files
author
Anis Kadri
committed
Issue NativeScript#17 adding/removing custom frameworks
1 parent 533d9b8 commit 71118fb

File tree

5 files changed

+235
-12
lines changed

5 files changed

+235
-12
lines changed

lib/pbxFile.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,26 @@ function detectLastType(path) {
3636
}
3737

3838
function fileEncoding(file) {
39-
if (file.lastType != BUNDLE) {
39+
if (file.lastType != BUNDLE && !file.customFramework) {
4040
return DEFAULT_FILE_ENCODING;
4141
}
4242
}
4343

4444
function defaultSourceTree(file) {
45-
if (file.lastType == DYLIB || file.lastType == FRAMEWORK) {
45+
if (( file.lastType == DYLIB || file.lastType == FRAMEWORK ) && !file.customFramework) {
4646
return 'SDKROOT';
4747
} else {
4848
return DEFAULT_SOURCE_TREE;
4949
}
5050
}
5151

5252
function correctPath(file, filepath) {
53-
if (file.lastType == FRAMEWORK) {
53+
if (file.lastType == FRAMEWORK && !file.customFramework) {
5454
return 'System/Library/Frameworks/' + filepath;
5555
} else if (file.lastType == DYLIB) {
5656
return 'usr/lib/' + filepath;
57+
} else if (file.customFramework == true) {
58+
return file.basename;
5759
} else {
5860
return filepath;
5961
}
@@ -62,7 +64,7 @@ function correctPath(file, filepath) {
6264
function correctGroup(file) {
6365
if (file.lastType == SOURCE_FILE) {
6466
return 'Sources';
65-
} else if (file.lastType == DYLIB || file.lastType == ARCHIVE) {
67+
} else if (file.lastType == DYLIB || file.lastType == ARCHIVE || file.lastType == FRAMEWORK) {
6668
return 'Frameworks';
6769
} else {
6870
return 'Resources';
@@ -74,8 +76,14 @@ function pbxFile(filepath, opt) {
7476

7577
this.lastType = opt.lastType || detectLastType(filepath);
7678

77-
this.path = correctPath(this, filepath);
79+
// for custom frameworks
80+
if(opt.customFramework == true) {
81+
this.customFramework = true;
82+
this.dirname = path.dirname(filepath);
83+
}
84+
7885
this.basename = path.basename(filepath);
86+
this.path = correctPath(this, filepath);
7987
this.group = correctGroup(this);
8088

8189
this.sourceTree = opt.sourceTree || defaultSourceTree(this);

lib/pbxProject.js

+71-7
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,8 @@ pbxProject.prototype.removeResourceFile = function (path, opt) {
181181
return file;
182182
}
183183

184-
pbxProject.prototype.addFramework = function (path, opt) {
185-
var file = new pbxFile(path, opt);
186-
184+
pbxProject.prototype.addFramework = function (fpath, opt) {
185+
var file = new pbxFile(fpath, opt);
187186
// catch duplicates
188187
if (this.hasFile(file.path)) return false;
189188

@@ -194,17 +193,25 @@ pbxProject.prototype.addFramework = function (path, opt) {
194193
this.addToPbxFileReferenceSection(file); // PBXFileReference
195194
this.addToFrameworksPbxGroup(file); // PBXGroup
196195
this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
196+
197+
if(opt && opt.customFramework == true) {
198+
this.addToFrameworkSearchPaths(file);
199+
}
197200

198201
return file;
199202
}
200203

201-
pbxProject.prototype.removeFramework = function (path, opt) {
202-
var file = new pbxFile(path, opt);
204+
pbxProject.prototype.removeFramework = function (fpath, opt) {
205+
var file = new pbxFile(fpath, opt);
203206

204207
this.removeFromPbxBuildFileSection(file); // PBXBuildFile
205208
this.removeFromPbxFileReferenceSection(file); // PBXFileReference
206209
this.removeFromFrameworksPbxGroup(file); // PBXGroup
207210
this.removeFromPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
211+
212+
if(opt && opt.customFramework) {
213+
this.removeFromFrameworkSearchPaths(path.dirname(fpath));
214+
}
208215

209216
return file;
210217
}
@@ -443,6 +450,54 @@ pbxProject.prototype.updateProductName = function(name) {
443450
propReplace(config, 'PRODUCT_NAME', '"' + name + '"');
444451
}
445452

453+
pbxProject.prototype.removeFromFrameworkSearchPaths = function (file) {
454+
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
455+
INHERITED = '"$(inherited)"',
456+
SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',
457+
config, buildSettings, searchPaths;
458+
var new_path = searchPathForFile(file, this);
459+
460+
for (config in configurations) {
461+
buildSettings = configurations[config].buildSettings;
462+
463+
if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
464+
continue;
465+
466+
searchPaths = buildSettings[SEARCH_PATHS];
467+
468+
if (searchPaths) {
469+
var matches = searchPaths.filter(function(p) {
470+
return p.indexOf(new_path) > -1;
471+
});
472+
matches.forEach(function(m) {
473+
var idx = searchPaths.indexOf(m);
474+
searchPaths.splice(idx, 1);
475+
});
476+
}
477+
478+
}
479+
}
480+
481+
pbxProject.prototype.addToFrameworkSearchPaths = function (file) {
482+
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
483+
INHERITED = '"$(inherited)"',
484+
config, buildSettings, searchPaths;
485+
486+
for (config in configurations) {
487+
buildSettings = configurations[config].buildSettings;
488+
489+
if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
490+
continue;
491+
492+
if (!buildSettings['FRAMEWORK_SEARCH_PATHS']
493+
|| buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
494+
buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];
495+
}
496+
497+
buildSettings['FRAMEWORK_SEARCH_PATHS'].push(searchPathForFile(file, this));
498+
}
499+
}
500+
446501
pbxProject.prototype.removeFromLibrarySearchPaths = function (file) {
447502
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
448503
INHERITED = '"$(inherited)"',
@@ -552,10 +607,8 @@ pbxProject.prototype.__defineGetter__("productName", function () {
552607
pbxProject.prototype.hasFile = function (filePath) {
553608
var files = nonComments(this.pbxFileReferenceSection()),
554609
file, id;
555-
556610
for (id in files) {
557611
file = files[id];
558-
559612
if (file.path == filePath || file.path == ('"' + filePath + '"')) {
560613
return true;
561614
}
@@ -655,6 +708,15 @@ function correctForResourcesPath(file, project) {
655708
return file;
656709
}
657710

711+
function correctForFrameworksPath(file, project) {
712+
var r_resources_dir = /^Frameworks\//;
713+
714+
if (project.pbxGroupByName('Frameworks').path)
715+
file.path = file.path.replace(r_resources_dir, '');
716+
717+
return file;
718+
}
719+
658720
function searchPathForFile(file, proj) {
659721
var plugins = proj.pbxGroupByName('Plugins')
660722
pluginsPath = plugins ? plugins.path : null,
@@ -668,6 +730,8 @@ function searchPathForFile(file, proj) {
668730

669731
if (file.plugin && pluginsPath) {
670732
return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""';
733+
} else if (file.customFramework && file.dirname) {
734+
return '"' + file.dirname + '"'
671735
} else {
672736
return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""';
673737
}

test/FrameworkSearchPaths.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
var pbxFile = {
8+
path:'some/path/include',
9+
dirname: 'some/path',
10+
customFramework: true
11+
}
12+
function cleanHash() {
13+
return JSON.parse(fullProjectStr);
14+
}
15+
16+
exports.setUp = function (callback) {
17+
proj.hash = cleanHash();
18+
callback();
19+
}
20+
21+
var PRODUCT_NAME = '"KitchenSinktablet"';
22+
23+
exports.addAndRemoveToFromFrameworkSearchPaths = {
24+
'add should add the path to each configuration section':function(test) {
25+
proj.addToFrameworkSearchPaths(pbxFile);
26+
var config = proj.pbxXCBuildConfigurationSection();
27+
for (var ref in config) {
28+
if (ref.indexOf('_comment') > -1 || config[ref].buildSettings.PRODUCT_NAME != PRODUCT_NAME) continue;
29+
var lib = config[ref].buildSettings.FRAMEWORK_SEARCH_PATHS;
30+
test.ok(lib[1].indexOf('some/path') > -1);
31+
}
32+
test.done();
33+
},
34+
'remove should remove from the path to each configuration section':function(test) {
35+
proj.addToFrameworkSearchPaths(pbxFile);
36+
proj.removeFromFrameworkSearchPaths(pbxFile);
37+
var config = proj.pbxXCBuildConfigurationSection();
38+
for (var ref in config) {
39+
if (ref.indexOf('_comment') > -1 || config[ref].buildSettings.PRODUCT_NAME != PRODUCT_NAME) continue;
40+
var lib = config[ref].buildSettings.FRAMEWORK_SEARCH_PATHS;
41+
test.ok(lib.length === 1);
42+
test.ok(lib[0].indexOf('some/path') == -1);
43+
}
44+
test.done();
45+
}
46+
}

test/addFramework.js

+54
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,39 @@ exports.setUp = function (callback) {
1313
callback();
1414
}
1515

16+
function nonComments(obj) {
17+
var keys = Object.keys(obj),
18+
newObj = {}, i = 0;
19+
20+
for (i; i < keys.length; i++) {
21+
if (!/_comment$/.test(keys[i])) {
22+
newObj[keys[i]] = obj[keys[i]];
23+
}
24+
}
25+
26+
return newObj;
27+
}
28+
29+
function frameworkSearchPaths(proj) {
30+
var configs = nonComments(proj.pbxXCBuildConfigurationSection()),
31+
allPaths = [],
32+
ids = Object.keys(configs), i, buildSettings;
33+
34+
for (i = 0; i< ids.length; i++) {
35+
buildSettings = configs[ids[i]].buildSettings;
36+
37+
if (buildSettings['FRAMEWORK_SEARCH_PATHS']) {
38+
allPaths.push(buildSettings['FRAMEWORK_SEARCH_PATHS']);
39+
}
40+
}
41+
42+
return allPaths;
43+
}
44+
1645
exports.addFramework = {
1746
'should return a pbxFile': function (test) {
1847
var newFile = proj.addFramework('libsqlite3.dylib');
48+
console.log(newFile);
1949

2050
test.equal(newFile.constructor, pbxFile);
2151
test.done()
@@ -141,5 +171,29 @@ exports.addFramework = {
141171
test.ok(!proj.addFramework('libsqlite3.dylib'));
142172
test.done();
143173
}
174+
},
175+
'should pbxFile correctly for custom frameworks': function (test) {
176+
var newFile = proj.addFramework('/path/to/Custom.framework', {customFramework: true});
177+
178+
test.ok(newFile.customFramework);
179+
test.ok(!newFile.fileEncoding);
180+
test.equal(newFile.sourceTree, '"<group>"');
181+
test.equal(newFile.group, 'Frameworks');
182+
test.equal(newFile.basename, 'Custom.framework');
183+
test.equal(newFile.dirname, '/path/to');
184+
// XXX framework has to be copied over to PROJECT root. That is what XCode does when you drag&drop
185+
test.equal(newFile.path, 'Custom.framework');
186+
187+
188+
// should add path to framework search path
189+
var frameworkPaths = frameworkSearchPaths(proj);
190+
expectedPath = '"/path/to"';
191+
192+
for (i = 0; i < frameworkPaths.length; i++) {
193+
var current = frameworkPaths[i];
194+
test.ok(current.indexOf('"$(inherited)"') >= 0);
195+
test.ok(current.indexOf(expectedPath) >= 0);
196+
}
197+
test.done();
144198
}
145199
}

test/removeFramework.js

+51
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,35 @@ exports.setUp = function (callback) {
1313
callback();
1414
}
1515

16+
function nonComments(obj) {
17+
var keys = Object.keys(obj),
18+
newObj = {}, i = 0;
19+
20+
for (i; i < keys.length; i++) {
21+
if (!/_comment$/.test(keys[i])) {
22+
newObj[keys[i]] = obj[keys[i]];
23+
}
24+
}
25+
26+
return newObj;
27+
}
28+
29+
function frameworkSearchPaths(proj) {
30+
var configs = nonComments(proj.pbxXCBuildConfigurationSection()),
31+
allPaths = [],
32+
ids = Object.keys(configs), i, buildSettings;
33+
34+
for (i = 0; i< ids.length; i++) {
35+
buildSettings = configs[ids[i]].buildSettings;
36+
37+
if (buildSettings['FRAMEWORK_SEARCH_PATHS']) {
38+
allPaths.push(buildSettings['FRAMEWORK_SEARCH_PATHS']);
39+
}
40+
}
41+
42+
return allPaths;
43+
}
44+
1645
exports.removeFramework = {
1746
'should return a pbxFile': function (test) {
1847
var newFile = proj.addFramework('libsqlite3.dylib');
@@ -98,6 +127,28 @@ exports.removeFramework = {
98127

99128
test.equal(frameworks.files.length, 15);
100129

130+
test.done();
131+
},
132+
'should remove custom frameworks': function (test) {
133+
var newFile = proj.addFramework('/path/to/Custom.framework'),
134+
frameworks = proj.pbxFrameworksBuildPhaseObj();
135+
136+
test.equal(frameworks.files.length, 16);
137+
138+
var deletedFile = proj.removeFramework('/path/to/Custom.framework'),
139+
frameworks = proj.pbxFrameworksBuildPhaseObj();
140+
141+
test.equal(frameworks.files.length, 15);
142+
143+
var frameworkPaths = frameworkSearchPaths(proj);
144+
expectedPath = '"/path/to"';
145+
146+
for (i = 0; i < frameworkPaths.length; i++) {
147+
var current = frameworkPaths[i];
148+
test.ok(current.indexOf('"$(inherited)"') == -1);
149+
test.ok(current.indexOf(expectedPath) == -1);
150+
}
151+
101152
test.done();
102153
}
103154
}

0 commit comments

Comments
 (0)