Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions lib/pbxFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,26 @@ function detectLastType(path) {
}

function fileEncoding(file) {
if (file.lastType != BUNDLE) {
if (file.lastType != BUNDLE && !file.customFramework) {
return DEFAULT_FILE_ENCODING;
}
}

function defaultSourceTree(file) {
if (file.lastType == DYLIB || file.lastType == FRAMEWORK) {
if (( file.lastType == DYLIB || file.lastType == FRAMEWORK ) && !file.customFramework) {
return 'SDKROOT';
} else {
return DEFAULT_SOURCE_TREE;
}
}

function correctPath(file, filepath) {
if (file.lastType == FRAMEWORK) {
if (file.lastType == FRAMEWORK && !file.customFramework) {
return 'System/Library/Frameworks/' + filepath;
} else if (file.lastType == DYLIB) {
return 'usr/lib/' + filepath;
} else if (file.customFramework == true) {
return file.basename;
} else {
return filepath;
}
Expand All @@ -62,7 +64,7 @@ function correctPath(file, filepath) {
function correctGroup(file) {
if (file.lastType == SOURCE_FILE) {
return 'Sources';
} else if (file.lastType == DYLIB || file.lastType == ARCHIVE) {
} else if (file.lastType == DYLIB || file.lastType == ARCHIVE || file.lastType == FRAMEWORK) {
return 'Frameworks';
} else {
return 'Resources';
Expand All @@ -74,8 +76,14 @@ function pbxFile(filepath, opt) {

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

this.path = correctPath(this, filepath);
// for custom frameworks
if(opt.customFramework == true) {
this.customFramework = true;
this.dirname = path.dirname(filepath);
}

this.basename = path.basename(filepath);
this.path = correctPath(this, filepath);
this.group = correctGroup(this);

this.sourceTree = opt.sourceTree || defaultSourceTree(this);
Expand Down
78 changes: 71 additions & 7 deletions lib/pbxProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,8 @@ pbxProject.prototype.removeResourceFile = function (path, opt) {
return file;
}

pbxProject.prototype.addFramework = function (path, opt) {
var file = new pbxFile(path, opt);

pbxProject.prototype.addFramework = function (fpath, opt) {
var file = new pbxFile(fpath, opt);
// catch duplicates
if (this.hasFile(file.path)) return false;

Expand All @@ -194,17 +193,25 @@ pbxProject.prototype.addFramework = function (path, opt) {
this.addToPbxFileReferenceSection(file); // PBXFileReference
this.addToFrameworksPbxGroup(file); // PBXGroup
this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase

if(opt && opt.customFramework == true) {
this.addToFrameworkSearchPaths(file);
}

return file;
}

pbxProject.prototype.removeFramework = function (path, opt) {
var file = new pbxFile(path, opt);
pbxProject.prototype.removeFramework = function (fpath, opt) {
var file = new pbxFile(fpath, opt);

this.removeFromPbxBuildFileSection(file); // PBXBuildFile
this.removeFromPbxFileReferenceSection(file); // PBXFileReference
this.removeFromFrameworksPbxGroup(file); // PBXGroup
this.removeFromPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase

if(opt && opt.customFramework) {
this.removeFromFrameworkSearchPaths(path.dirname(fpath));
}

return file;
}
Expand Down Expand Up @@ -443,6 +450,54 @@ pbxProject.prototype.updateProductName = function(name) {
propReplace(config, 'PRODUCT_NAME', '"' + name + '"');
}

pbxProject.prototype.removeFromFrameworkSearchPaths = function (file) {
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
INHERITED = '"$(inherited)"',
SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',
config, buildSettings, searchPaths;
var new_path = searchPathForFile(file, this);

for (config in configurations) {
buildSettings = configurations[config].buildSettings;

if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
continue;

searchPaths = buildSettings[SEARCH_PATHS];

if (searchPaths) {
var matches = searchPaths.filter(function(p) {
return p.indexOf(new_path) > -1;
});
matches.forEach(function(m) {
var idx = searchPaths.indexOf(m);
searchPaths.splice(idx, 1);
});
}

}
}

pbxProject.prototype.addToFrameworkSearchPaths = function (file) {
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
INHERITED = '"$(inherited)"',
config, buildSettings, searchPaths;

for (config in configurations) {
buildSettings = configurations[config].buildSettings;

if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
continue;

if (!buildSettings['FRAMEWORK_SEARCH_PATHS']
|| buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];
}

buildSettings['FRAMEWORK_SEARCH_PATHS'].push(searchPathForFile(file, this));
}
}

pbxProject.prototype.removeFromLibrarySearchPaths = function (file) {
var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
INHERITED = '"$(inherited)"',
Expand Down Expand Up @@ -552,10 +607,8 @@ pbxProject.prototype.__defineGetter__("productName", function () {
pbxProject.prototype.hasFile = function (filePath) {
var files = nonComments(this.pbxFileReferenceSection()),
file, id;

for (id in files) {
file = files[id];

if (file.path == filePath || file.path == ('"' + filePath + '"')) {
return true;
}
Expand Down Expand Up @@ -655,6 +708,15 @@ function correctForResourcesPath(file, project) {
return file;
}

function correctForFrameworksPath(file, project) {
var r_resources_dir = /^Frameworks\//;

if (project.pbxGroupByName('Frameworks').path)
file.path = file.path.replace(r_resources_dir, '');

return file;
}

function searchPathForFile(file, proj) {
var plugins = proj.pbxGroupByName('Plugins')
pluginsPath = plugins ? plugins.path : null,
Expand All @@ -668,6 +730,8 @@ function searchPathForFile(file, proj) {

if (file.plugin && pluginsPath) {
return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""';
} else if (file.customFramework && file.dirname) {
return '"' + file.dirname + '"'
} else {
return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""';
}
Expand Down
46 changes: 46 additions & 0 deletions test/FrameworkSearchPaths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var fullProject = require('./fixtures/full-project')
fullProjectStr = JSON.stringify(fullProject),
pbx = require('../lib/pbxProject'),
pbxFile = require('../lib/pbxFile'),
proj = new pbx('.');

var pbxFile = {
path:'some/path/include',
dirname: 'some/path',
customFramework: true
}
function cleanHash() {
return JSON.parse(fullProjectStr);
}

exports.setUp = function (callback) {
proj.hash = cleanHash();
callback();
}

var PRODUCT_NAME = '"KitchenSinktablet"';

exports.addAndRemoveToFromFrameworkSearchPaths = {
'add should add the path to each configuration section':function(test) {
proj.addToFrameworkSearchPaths(pbxFile);
var config = proj.pbxXCBuildConfigurationSection();
for (var ref in config) {
if (ref.indexOf('_comment') > -1 || config[ref].buildSettings.PRODUCT_NAME != PRODUCT_NAME) continue;
var lib = config[ref].buildSettings.FRAMEWORK_SEARCH_PATHS;
test.ok(lib[1].indexOf('some/path') > -1);
}
test.done();
},
'remove should remove from the path to each configuration section':function(test) {
proj.addToFrameworkSearchPaths(pbxFile);
proj.removeFromFrameworkSearchPaths(pbxFile);
var config = proj.pbxXCBuildConfigurationSection();
for (var ref in config) {
if (ref.indexOf('_comment') > -1 || config[ref].buildSettings.PRODUCT_NAME != PRODUCT_NAME) continue;
var lib = config[ref].buildSettings.FRAMEWORK_SEARCH_PATHS;
test.ok(lib.length === 1);
test.ok(lib[0].indexOf('some/path') == -1);
}
test.done();
}
}
54 changes: 54 additions & 0 deletions test/addFramework.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,39 @@ exports.setUp = function (callback) {
callback();
}

function nonComments(obj) {
var keys = Object.keys(obj),
newObj = {}, i = 0;

for (i; i < keys.length; i++) {
if (!/_comment$/.test(keys[i])) {
newObj[keys[i]] = obj[keys[i]];
}
}

return newObj;
}

function frameworkSearchPaths(proj) {
var configs = nonComments(proj.pbxXCBuildConfigurationSection()),
allPaths = [],
ids = Object.keys(configs), i, buildSettings;

for (i = 0; i< ids.length; i++) {
buildSettings = configs[ids[i]].buildSettings;

if (buildSettings['FRAMEWORK_SEARCH_PATHS']) {
allPaths.push(buildSettings['FRAMEWORK_SEARCH_PATHS']);
}
}

return allPaths;
}

exports.addFramework = {
'should return a pbxFile': function (test) {
var newFile = proj.addFramework('libsqlite3.dylib');
console.log(newFile);

test.equal(newFile.constructor, pbxFile);
test.done()
Expand Down Expand Up @@ -141,5 +171,29 @@ exports.addFramework = {
test.ok(!proj.addFramework('libsqlite3.dylib'));
test.done();
}
},
'should pbxFile correctly for custom frameworks': function (test) {
var newFile = proj.addFramework('/path/to/Custom.framework', {customFramework: true});

test.ok(newFile.customFramework);
test.ok(!newFile.fileEncoding);
test.equal(newFile.sourceTree, '"<group>"');
test.equal(newFile.group, 'Frameworks');
test.equal(newFile.basename, 'Custom.framework');
test.equal(newFile.dirname, '/path/to');
// XXX framework has to be copied over to PROJECT root. That is what XCode does when you drag&drop
test.equal(newFile.path, 'Custom.framework');


// should add path to framework search path
var frameworkPaths = frameworkSearchPaths(proj);
expectedPath = '"/path/to"';

for (i = 0; i < frameworkPaths.length; i++) {
var current = frameworkPaths[i];
test.ok(current.indexOf('"$(inherited)"') >= 0);
test.ok(current.indexOf(expectedPath) >= 0);
}
test.done();
}
}
51 changes: 51 additions & 0 deletions test/removeFramework.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,35 @@ exports.setUp = function (callback) {
callback();
}

function nonComments(obj) {
var keys = Object.keys(obj),
newObj = {}, i = 0;

for (i; i < keys.length; i++) {
if (!/_comment$/.test(keys[i])) {
newObj[keys[i]] = obj[keys[i]];
}
}

return newObj;
}

function frameworkSearchPaths(proj) {
var configs = nonComments(proj.pbxXCBuildConfigurationSection()),
allPaths = [],
ids = Object.keys(configs), i, buildSettings;

for (i = 0; i< ids.length; i++) {
buildSettings = configs[ids[i]].buildSettings;

if (buildSettings['FRAMEWORK_SEARCH_PATHS']) {
allPaths.push(buildSettings['FRAMEWORK_SEARCH_PATHS']);
}
}

return allPaths;
}

exports.removeFramework = {
'should return a pbxFile': function (test) {
var newFile = proj.addFramework('libsqlite3.dylib');
Expand Down Expand Up @@ -98,6 +127,28 @@ exports.removeFramework = {

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

test.done();
},
'should remove custom frameworks': function (test) {
var newFile = proj.addFramework('/path/to/Custom.framework'),
frameworks = proj.pbxFrameworksBuildPhaseObj();

test.equal(frameworks.files.length, 16);

var deletedFile = proj.removeFramework('/path/to/Custom.framework'),
frameworks = proj.pbxFrameworksBuildPhaseObj();

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

var frameworkPaths = frameworkSearchPaths(proj);
expectedPath = '"/path/to"';

for (i = 0; i < frameworkPaths.length; i++) {
var current = frameworkPaths[i];
test.ok(current.indexOf('"$(inherited)"') == -1);
test.ok(current.indexOf(expectedPath) == -1);
}

test.done();
}
}