var util = require('util'), | |
f = util.format, | |
EventEmitter = require('events').EventEmitter, | |
path = require('path'), | |
uuid = require('node-uuid'), | |
fork = require('child_process').fork, | |
pbxWriter = require('./pbxWriter'), | |
pbxFile = require('./pbxFile'), | |
fs = require('fs'), | |
parser = require('./parser/pbxproj'), | |
COMMENT_KEY = /_comment$/ | |
function pbxProject(filename) { | |
if (!(this instanceof pbxProject)) | |
return new pbxProject(filename); | |
this.filepath = path.resolve(filename) | |
} | |
util.inherits(pbxProject, EventEmitter) | |
pbxProject.prototype.parse = function(cb) { | |
var worker = fork(__dirname + '/parseJob.js', [this.filepath]) | |
worker.on('message', function(msg) { | |
if (msg.name == 'SyntaxError' || msg.code) { | |
this.emit('error', msg); | |
} else { | |
this.hash = msg; | |
this.emit('end', null, msg) | |
} | |
}.bind(this)); | |
if (cb) { | |
this.on('error', cb); | |
this.on('end', cb); | |
} | |
return this; | |
} | |
pbxProject.prototype.parseSync = function() { | |
var file_contents = fs.readFileSync(this.filepath, 'utf-8'); | |
this.hash = parser.parse(file_contents); | |
return this; | |
} | |
pbxProject.prototype.writeSync = function() { | |
this.writer = new pbxWriter(this.hash); | |
return this.writer.writeSync(); | |
} | |
pbxProject.prototype.allUuids = function() { | |
var sections = this.hash.project.objects, | |
uuids = [], | |
section; | |
for (key in sections) { | |
section = sections[key] | |
uuids = uuids.concat(Object.keys(section)) | |
} | |
uuids = uuids.filter(function(str) { | |
return !COMMENT_KEY.test(str) && str.length == 24; | |
}); | |
return uuids; | |
} | |
pbxProject.prototype.generateUuid = function() { | |
var id = uuid.v4() | |
.replace(/-/g, '') | |
.substr(0, 24) | |
.toUpperCase() | |
if (this.allUuids().indexOf(id) >= 0) { | |
return this.generateUuid(); | |
} else { | |
return id; | |
} | |
} | |
pbxProject.prototype.addPluginFile = function(path, opt) { | |
var file = new pbxFile(path, opt); | |
file.plugin = true; // durr | |
correctForPluginsPath(file, this); | |
// null is better for early errors | |
if (this.hasFile(file.path)) return null; | |
file.fileRef = this.generateUuid(); | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
this.addToPluginsPbxGroup(file); // PBXGroup | |
return file; | |
} | |
pbxProject.prototype.removePluginFile = function(path, opt) { | |
var file = new pbxFile(path, opt); | |
correctForPluginsPath(file, this); | |
this.removeFromPbxFileReferenceSection(file); // PBXFileReference | |
this.removeFromPluginsPbxGroup(file); // PBXGroup | |
return file; | |
} | |
pbxProject.prototype.addProductFile = function(targetPath, opt) { | |
var file = new pbxFile(targetPath, opt); | |
file.includeInIndex = 0; | |
file.fileRef = this.generateUuid(); | |
file.target = opt ? opt.target : undefined; | |
file.group = opt ? opt.group : undefined; | |
file.uuid = this.generateUuid(); | |
file.path = file.basename; | |
this.addToPbxFileReferenceSection(file); | |
this.addToProductsPbxGroup(file); // PBXGroup | |
return file; | |
} | |
pbxProject.prototype.removeProductFile = function(path, opt) { | |
var file = new pbxFile(path, opt); | |
this.removeFromProductsPbxGroup(file); // PBXGroup | |
return file; | |
} | |
/** | |
* | |
* @param path {String} | |
* @param opt {Object} see pbxFile for avail options | |
* @param group {String} group key | |
* @returns {Object} file; see pbxFile | |
*/ | |
pbxProject.prototype.addSourceFile = function (path, opt, group) { | |
var file; | |
if (group) { | |
file = this.addFile(path, group, opt); | |
} | |
else { | |
file = this.addPluginFile(path, opt); | |
} | |
if (!file) return false; | |
file.target = opt ? opt.target : undefined; | |
file.uuid = this.generateUuid(); | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
this.addToPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase | |
return file; | |
} | |
/** | |
* | |
* @param path {String} | |
* @param opt {Object} see pbxFile for avail options | |
* @param group {String} group key | |
* @returns {Object} file; see pbxFile | |
*/ | |
pbxProject.prototype.removeSourceFile = function (path, opt, group) { | |
var file; | |
if (group) { | |
file = this.removeFile(path, group, opt); | |
} | |
else { | |
file = this.removePluginFile(path, opt); | |
} | |
file.target = opt ? opt.target : undefined; | |
this.removeFromPbxBuildFileSection(file); // PBXBuildFile | |
this.removeFromPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase | |
return file; | |
} | |
/** | |
* | |
* @param path {String} | |
* @param opt {Object} see pbxFile for avail options | |
* @param group {String} group key | |
* @returns {Object} file; see pbxFile | |
*/ | |
pbxProject.prototype.addHeaderFile = function (path, opt, group) { | |
if (group) { | |
return this.addFile(path, group, opt); | |
} | |
else { | |
return this.addPluginFile(path, opt); | |
} | |
} | |
/** | |
* | |
* @param path {String} | |
* @param opt {Object} see pbxFile for avail options | |
* @param group {String} group key | |
* @returns {Object} file; see pbxFile | |
*/ | |
pbxProject.prototype.removeHeaderFile = function (path, opt, group) { | |
if (group) { | |
return this.removeFile(path, group, opt); | |
} | |
else { | |
return this.removePluginFile(path, opt); | |
} | |
} | |
pbxProject.prototype.addResourceFile = function(path, opt) { | |
opt = opt || {}; | |
var file; | |
if (opt.plugin) { | |
file = this.addPluginFile(path, opt); | |
if (!file) return false; | |
} else { | |
file = new pbxFile(path, opt); | |
if (this.hasFile(file.path)) return false; | |
} | |
file.uuid = this.generateUuid(); | |
file.target = opt ? opt.target : undefined; | |
if (!opt.plugin) { | |
correctForResourcesPath(file, this); | |
file.fileRef = this.generateUuid(); | |
} | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
this.addToPbxResourcesBuildPhase(file); // PBXResourcesBuildPhase | |
if (!opt.plugin) { | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
this.addToResourcesPbxGroup(file); // PBXGroup | |
} | |
return file; | |
} | |
pbxProject.prototype.removeResourceFile = function(path, opt) { | |
var file = new pbxFile(path, opt); | |
file.target = opt ? opt.target : undefined; | |
correctForResourcesPath(file, this); | |
this.removeFromPbxBuildFileSection(file); // PBXBuildFile | |
this.removeFromPbxFileReferenceSection(file); // PBXFileReference | |
this.removeFromResourcesPbxGroup(file); // PBXGroup | |
this.removeFromPbxResourcesBuildPhase(file); // PBXResourcesBuildPhase | |
return file; | |
} | |
pbxProject.prototype.addFramework = function(fpath, opt) { | |
var file = new pbxFile(fpath, opt); | |
file.uuid = this.generateUuid(); | |
file.fileRef = this.generateUuid(); | |
file.target = opt ? opt.target : undefined; | |
if (this.hasFile(file.path)) return false; | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
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(fpath, opt) { | |
var file = new pbxFile(fpath, opt); | |
file.target = opt ? opt.target : undefined; | |
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; | |
} | |
pbxProject.prototype.addCopyfile = function(fpath, opt) { | |
var file = new pbxFile(fpath, opt); | |
// catch duplicates | |
if (this.hasFile(file.path)) { | |
file = this.hasFile(file.path); | |
} | |
file.fileRef = file.uuid = this.generateUuid(); | |
file.target = opt ? opt.target : undefined; | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
this.addToPbxCopyfilesBuildPhase(file); // PBXCopyFilesBuildPhase | |
return file; | |
} | |
pbxProject.prototype.pbxCopyfilesBuildPhaseObj = function(target) { | |
return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', target); | |
} | |
pbxProject.prototype.addToPbxCopyfilesBuildPhase = function(file) { | |
var sources = this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', file.target); | |
sources.files.push(pbxBuildPhaseObj(file)); | |
} | |
pbxProject.prototype.removeCopyfile = function(fpath, opt) { | |
var file = new pbxFile(fpath, opt); | |
file.target = opt ? opt.target : undefined; | |
this.removeFromPbxBuildFileSection(file); // PBXBuildFile | |
this.removeFromPbxFileReferenceSection(file); // PBXFileReference | |
this.removeFromPbxCopyfilesBuildPhase(file); // PBXFrameworksBuildPhase | |
return file; | |
} | |
pbxProject.prototype.removeFromPbxCopyfilesBuildPhase = function(file) { | |
var sources = this.pbxCopyfilesBuildPhaseObj(file.target); | |
for (i in sources.files) { | |
if (sources.files[i].comment == longComment(file)) { | |
sources.files.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addStaticLibrary = function(path, opt) { | |
opt = opt || {}; | |
var file; | |
if (opt.plugin) { | |
file = this.addPluginFile(path, opt); | |
if (!file) return false; | |
} else { | |
file = new pbxFile(path, opt); | |
if (this.hasFile(file.path)) return false; | |
} | |
file.uuid = this.generateUuid(); | |
file.target = opt ? opt.target : undefined; | |
if (!opt.plugin) { | |
file.fileRef = this.generateUuid(); | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
} | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase | |
this.addToLibrarySearchPaths(file); // make sure it gets built! | |
return file; | |
} | |
// helper addition functions | |
pbxProject.prototype.addToPbxBuildFileSection = function(file) { | |
var commentKey = f("%s_comment", file.uuid); | |
this.pbxBuildFileSection()[file.uuid] = pbxBuildFileObj(file); | |
this.pbxBuildFileSection()[commentKey] = pbxBuildFileComment(file); | |
} | |
pbxProject.prototype.removeFromPbxBuildFileSection = function(file) { | |
var uuid; | |
for (uuid in this.pbxBuildFileSection()) { | |
if (this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) { | |
file.uuid = uuid; | |
delete this.pbxBuildFileSection()[uuid]; | |
} | |
} | |
var commentKey = f("%s_comment", file.uuid); | |
delete this.pbxBuildFileSection()[commentKey]; | |
} | |
pbxProject.prototype.addPbxGroup = function(filePathsArray, name, path, sourceTree) { | |
var groups = this.hash.project.objects['PBXGroup'], | |
pbxGroupUuid = this.generateUuid(), | |
commentKey = f("%s_comment", pbxGroupUuid), | |
pbxGroup = { | |
isa: 'PBXGroup', | |
children: [], | |
name: name, | |
path: path, | |
sourceTree: sourceTree ? sourceTree : '"<group>"' | |
}, | |
fileReferenceSection = this.pbxFileReferenceSection(), | |
filePathToReference = {}; | |
for (var key in fileReferenceSection) { | |
// only look for comments | |
if (!COMMENT_KEY.test(key)) continue; | |
var fileReferenceKey = key.split(COMMENT_KEY)[0], | |
fileReference = fileReferenceSection[fileReferenceKey]; | |
filePathToReference[fileReference.path] = { fileRef: fileReferenceKey, basename: fileReferenceSection[key] }; | |
} | |
for (var index = 0; index < filePathsArray.length; index++) { | |
var filePath = filePathsArray[index], | |
filePathQuoted = "\"" + filePath + "\""; | |
if (filePathToReference[filePath]) { | |
pbxGroup.children.push(pbxGroupChild(filePathToReference[filePath])); | |
continue; | |
} else if (filePathToReference[filePathQuoted]) { | |
pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted])); | |
continue; | |
} | |
var file = new pbxFile(filePath); | |
file.uuid = this.generateUuid(); | |
file.fileRef = this.generateUuid(); | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
pbxGroup.children.push(pbxGroupChild(file)); | |
} | |
if (groups) { | |
groups[pbxGroupUuid] = pbxGroup; | |
groups[commentKey] = name; | |
} | |
return { uuid: pbxGroupUuid, pbxGroup: pbxGroup }; | |
} | |
pbxProject.prototype.addToPbxProjectSection = function(target) { | |
var newTarget = { | |
value: target.uuid, | |
comment: pbxNativeTargetComment(target.pbxNativeTarget) | |
}; | |
this.pbxProjectSection()[this.getFirstProject()['uuid']]['targets'].push(newTarget); | |
} | |
pbxProject.prototype.addToPbxNativeTargetSection = function(target) { | |
var commentKey = f("%s_comment", target.uuid); | |
this.pbxNativeTargetSection()[target.uuid] = target.pbxNativeTarget; | |
this.pbxNativeTargetSection()[commentKey] = target.pbxNativeTarget.name; | |
} | |
pbxProject.prototype.addToPbxFileReferenceSection = function(file) { | |
var commentKey = f("%s_comment", file.fileRef); | |
this.pbxFileReferenceSection()[file.fileRef] = pbxFileReferenceObj(file); | |
this.pbxFileReferenceSection()[commentKey] = pbxFileReferenceComment(file); | |
} | |
pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) { | |
var i; | |
var refObj = pbxFileReferenceObj(file); | |
for (i in this.pbxFileReferenceSection()) { | |
if (this.pbxFileReferenceSection()[i].name == refObj.name || | |
('"' + this.pbxFileReferenceSection()[i].name + '"') == refObj.name || | |
this.pbxFileReferenceSection()[i].path == refObj.path || | |
('"' + this.pbxFileReferenceSection()[i].path + '"') == refObj.path) { | |
file.fileRef = file.uuid = i; | |
delete this.pbxFileReferenceSection()[i]; | |
break; | |
} | |
} | |
var commentKey = f("%s_comment", file.fileRef); | |
if (this.pbxFileReferenceSection()[commentKey] != undefined) { | |
delete this.pbxFileReferenceSection()[commentKey]; | |
} | |
return file; | |
} | |
pbxProject.prototype.addToPluginsPbxGroup = function(file) { | |
var pluginsGroup = this.pbxGroupByName('Plugins'); | |
pluginsGroup.children.push(pbxGroupChild(file)); | |
} | |
pbxProject.prototype.removeFromPluginsPbxGroup = function(file) { | |
var pluginsGroupChildren = this.pbxGroupByName('Plugins').children, i; | |
for (i in pluginsGroupChildren) { | |
if (pbxGroupChild(file).value == pluginsGroupChildren[i].value && | |
pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) { | |
pluginsGroupChildren.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addToResourcesPbxGroup = function(file) { | |
var pluginsGroup = this.pbxGroupByName('Resources'); | |
pluginsGroup.children.push(pbxGroupChild(file)); | |
} | |
pbxProject.prototype.removeFromResourcesPbxGroup = function(file) { | |
var pluginsGroupChildren = this.pbxGroupByName('Resources').children, i; | |
for (i in pluginsGroupChildren) { | |
if (pbxGroupChild(file).value == pluginsGroupChildren[i].value && | |
pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) { | |
pluginsGroupChildren.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addToFrameworksPbxGroup = function(file) { | |
var pluginsGroup = this.pbxGroupByName('Frameworks'); | |
pluginsGroup.children.push(pbxGroupChild(file)); | |
} | |
pbxProject.prototype.removeFromFrameworksPbxGroup = function(file) { | |
var pluginsGroupChildren = this.pbxGroupByName('Frameworks').children; | |
for (i in pluginsGroupChildren) { | |
if (pbxGroupChild(file).value == pluginsGroupChildren[i].value && | |
pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) { | |
pluginsGroupChildren.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addToProductsPbxGroup = function(file) { | |
var productsGroup = this.pbxGroupByName('Products'); | |
productsGroup.children.push(pbxGroupChild(file)); | |
} | |
pbxProject.prototype.removeFromProductsPbxGroup = function(file) { | |
var productsGroupChildren = this.pbxGroupByName('Products').children, i; | |
for (i in productsGroupChildren) { | |
if (pbxGroupChild(file).value == productsGroupChildren[i].value && | |
pbxGroupChild(file).comment == productsGroupChildren[i].comment) { | |
productsGroupChildren.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addToPbxSourcesBuildPhase = function(file) { | |
var sources = this.pbxSourcesBuildPhaseObj(file.target); | |
sources.files.push(pbxBuildPhaseObj(file)); | |
} | |
pbxProject.prototype.removeFromPbxSourcesBuildPhase = function(file) { | |
var sources = this.pbxSourcesBuildPhaseObj(file.target), i; | |
for (i in sources.files) { | |
if (sources.files[i].comment == longComment(file)) { | |
sources.files.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addToPbxResourcesBuildPhase = function(file) { | |
var sources = this.pbxResourcesBuildPhaseObj(file.target); | |
sources.files.push(pbxBuildPhaseObj(file)); | |
} | |
pbxProject.prototype.removeFromPbxResourcesBuildPhase = function(file) { | |
var sources = this.pbxResourcesBuildPhaseObj(file.target), i; | |
for (i in sources.files) { | |
if (sources.files[i].comment == longComment(file)) { | |
sources.files.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addToPbxFrameworksBuildPhase = function(file) { | |
var sources = this.pbxFrameworksBuildPhaseObj(file.target); | |
sources.files.push(pbxBuildPhaseObj(file)); | |
} | |
pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function(file) { | |
var sources = this.pbxFrameworksBuildPhaseObj(file.target); | |
for (i in sources.files) { | |
if (sources.files[i].comment == longComment(file)) { | |
sources.files.splice(i, 1); | |
break; | |
} | |
} | |
} | |
pbxProject.prototype.addXCConfigurationList = function(configurationObjectsArray, defaultConfigurationName, comment) { | |
var pbxBuildConfigurationSection = this.pbxXCBuildConfigurationSection(), | |
pbxXCConfigurationListSection = this.pbxXCConfigurationList(), | |
xcConfigurationListUuid = this.generateUuid(), | |
commentKey = f("%s_comment", xcConfigurationListUuid), | |
xcConfigurationList = { | |
isa: 'XCConfigurationList', | |
buildConfigurations: [], | |
defaultConfigurationIsVisible: 0, | |
defaultConfigurationName: defaultConfigurationName | |
}; | |
for (var index = 0; index < configurationObjectsArray.length; index++) { | |
var configuration = configurationObjectsArray[index], | |
configurationUuid = this.generateUuid(), | |
configurationCommentKey = f("%s_comment", configurationUuid); | |
pbxBuildConfigurationSection[configurationUuid] = configuration; | |
pbxBuildConfigurationSection[configurationCommentKey] = configuration.name; | |
xcConfigurationList.buildConfigurations.push({ value: configurationUuid, comment: configuration.name }); | |
} | |
if (pbxXCConfigurationListSection) { | |
pbxXCConfigurationListSection[xcConfigurationListUuid] = xcConfigurationList; | |
pbxXCConfigurationListSection[commentKey] = comment; | |
} | |
return { uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList }; | |
} | |
pbxProject.prototype.addTargetDependency = function(target, dependencyTargets) { | |
if (!target) | |
return undefined; | |
var nativeTargets = this.pbxNativeTargetSection(); | |
if (typeof nativeTargets[target] == "undefined") | |
throw new Error("Invalid target: " + target); | |
for (var index = 0; index < dependencyTargets.length; index++) { | |
var dependencyTarget = dependencyTargets[index]; | |
if (typeof nativeTargets[dependencyTarget] == "undefined") | |
throw new Error("Invalid target: " + dependencyTarget); | |
} | |
var pbxTargetDependency = 'PBXTargetDependency', | |
pbxContainerItemProxy = 'PBXContainerItemProxy', | |
pbxTargetDependencySection = this.hash.project.objects[pbxTargetDependency], | |
pbxContainerItemProxySection = this.hash.project.objects[pbxContainerItemProxy]; | |
for (var index = 0; index < dependencyTargets.length; index++) { | |
var dependencyTargetUuid = dependencyTargets[index], | |
dependencyTargetCommentKey = f("%s_comment", dependencyTargetUuid), | |
targetDependencyUuid = this.generateUuid(), | |
targetDependencyCommentKey = f("%s_comment", targetDependencyUuid), | |
itemProxyUuid = this.generateUuid(), | |
itemProxyCommentKey = f("%s_comment", itemProxyUuid), | |
itemProxy = { | |
isa: pbxContainerItemProxy, | |
containerPortal: this.hash.project['rootObject'], | |
containerPortal_comment: this.hash.project['rootObject_comment'], | |
proxyType: 1, | |
remoteGlobalIDString: dependencyTargetUuid, | |
remoteInfo: nativeTargets[dependencyTargetUuid].name | |
}, | |
targetDependency = { | |
isa: pbxTargetDependency, | |
target: dependencyTargetUuid, | |
target_comment: nativeTargets[dependencyTargetCommentKey], | |
targetProxy: itemProxyUuid, | |
targetProxy_comment: pbxContainerItemProxy | |
}; | |
if (pbxContainerItemProxySection && pbxTargetDependencySection) { | |
pbxContainerItemProxySection[itemProxyUuid] = itemProxy; | |
pbxContainerItemProxySection[itemProxyCommentKey] = pbxContainerItemProxy; | |
pbxTargetDependencySection[targetDependencyUuid] = targetDependency; | |
pbxTargetDependencySection[targetDependencyCommentKey] = pbxTargetDependency; | |
nativeTargets[target].dependencies.push({ value: targetDependencyUuid, comment: pbxTargetDependency }) | |
} | |
} | |
return { uuid: target, target: nativeTargets[target] }; | |
} | |
pbxProject.prototype.addBuildPhase = function(filePathsArray, buildPhaseType, comment, target, folderType, subfolderPath) { | |
var buildPhaseSection, | |
fileReferenceSection = this.pbxFileReferenceSection(), | |
buildFileSection = this.pbxBuildFileSection(), | |
buildPhaseUuid = this.generateUuid(), | |
buildPhaseTargetUuid = target || this.getFirstTarget().uuid, | |
commentKey = f("%s_comment", buildPhaseUuid), | |
buildPhase = { | |
isa: buildPhaseType, | |
buildActionMask: 2147483647, | |
files: [], | |
runOnlyForDeploymentPostprocessing: 0 | |
}, | |
filePathToBuildFile = {}; | |
if (buildPhaseType === 'PBXCopyFilesBuildPhase') { | |
buildPhase = pbxCopyFilesBuildPhaseObj(buildPhase, folderType, subfolderPath, comment); | |
} | |
if (!this.hash.project.objects[buildPhaseType]) { | |
this.hash.project.objects[buildPhaseType] = new Object(); | |
} | |
if (!this.hash.project.objects[buildPhaseType][buildPhaseUuid]) { | |
this.hash.project.objects[buildPhaseType][buildPhaseUuid] = buildPhase; | |
this.hash.project.objects[buildPhaseType][commentKey] = comment; | |
} | |
if (this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases']) { | |
this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases'].push({ | |
value: buildPhaseUuid, | |
comment: comment | |
}) | |
} | |
for (var key in buildFileSection) { | |
// only look for comments | |
if (!COMMENT_KEY.test(key)) continue; | |
var buildFileKey = key.split(COMMENT_KEY)[0], | |
buildFile = buildFileSection[buildFileKey]; | |
fileReference = fileReferenceSection[buildFile.fileRef]; | |
if (!fileReference) continue; | |
var pbxFileObj = new pbxFile(fileReference.path); | |
filePathToBuildFile[fileReference.path] = { uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group }; | |
} | |
for (var index = 0; index < filePathsArray.length; index++) { | |
var filePath = filePathsArray[index], | |
filePathQuoted = "\"" + filePath + "\"", | |
file = new pbxFile(filePath); | |
if (filePathToBuildFile[filePath]) { | |
buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePath])); | |
continue; | |
} else if (filePathToBuildFile[filePathQuoted]) { | |
buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePathQuoted])); | |
continue; | |
} | |
file.uuid = this.generateUuid(); | |
file.fileRef = this.generateUuid(); | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
this.addToPbxBuildFileSection(file); // PBXBuildFile | |
buildPhase.files.push(pbxBuildPhaseObj(file)); | |
} | |
if (buildPhaseSection) { | |
buildPhaseSection[buildPhaseUuid] = buildPhase; | |
buildPhaseSection[commentKey] = comment; | |
} | |
return { uuid: buildPhaseUuid, buildPhase: buildPhase }; | |
} | |
// helper access functions | |
pbxProject.prototype.pbxProjectSection = function() { | |
return this.hash.project.objects['PBXProject']; | |
} | |
pbxProject.prototype.pbxBuildFileSection = function() { | |
return this.hash.project.objects['PBXBuildFile']; | |
} | |
pbxProject.prototype.pbxXCBuildConfigurationSection = function() { | |
return this.hash.project.objects['XCBuildConfiguration']; | |
} | |
pbxProject.prototype.pbxFileReferenceSection = function() { | |
return this.hash.project.objects['PBXFileReference']; | |
} | |
pbxProject.prototype.pbxNativeTargetSection = function() { | |
return this.hash.project.objects['PBXNativeTarget']; | |
} | |
pbxProject.prototype.pbxXCConfigurationList = function() { | |
return this.hash.project.objects['XCConfigurationList']; | |
} | |
pbxProject.prototype.pbxGroupByName = function(name) { | |
var groups = this.hash.project.objects['PBXGroup'], | |
key, groupKey; | |
for (key in groups) { | |
// only look for comments | |
if (!COMMENT_KEY.test(key)) continue; | |
if (groups[key] == name) { | |
groupKey = key.split(COMMENT_KEY)[0]; | |
return groups[groupKey]; | |
} | |
} | |
return null; | |
} | |
pbxProject.prototype.pbxTargetByName = function(name) { | |
return this.pbxItemByComment(name, 'PBXNativeTarget'); | |
} | |
pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) { | |
var section = this.hash.project.objects[pbxSectionName], | |
key, itemKey; | |
for (key in section) { | |
// only look for comments | |
if (!COMMENT_KEY.test(key)) continue; | |
if (section[key] == name) { | |
itemKey = key.split(COMMENT_KEY)[0]; | |
return section[itemKey]; | |
} | |
} | |
return null; | |
} | |
pbxProject.prototype.pbxSourcesBuildPhaseObj = function(target) { | |
return this.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', target); | |
} | |
pbxProject.prototype.pbxResourcesBuildPhaseObj = function(target) { | |
return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources', target); | |
} | |
pbxProject.prototype.pbxFrameworksBuildPhaseObj = function(target) { | |
return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks', target); | |
} | |
// Find Build Phase from group/target | |
pbxProject.prototype.buildPhase = function(group, target) { | |
if (!target) | |
return undefined; | |
var nativeTargets = this.pbxNativeTargetSection(); | |
if (typeof nativeTargets[target] == "undefined") | |
throw new Error("Invalid target: " + target); | |
var nativeTarget = nativeTargets[target]; | |
var buildPhases = nativeTarget.buildPhases; | |
for(var i in buildPhases) | |
{ | |
var buildPhase = buildPhases[i]; | |
if (buildPhase.comment==group) | |
return buildPhase.value + "_comment"; | |
} | |
} | |
pbxProject.prototype.buildPhaseObject = function(name, group, target) { | |
var section = this.hash.project.objects[name], | |
obj, sectionKey, key; | |
var buildPhase = this.buildPhase(group, target); | |
for (key in section) { | |
// only look for comments | |
if (!COMMENT_KEY.test(key)) continue; | |
// select the proper buildPhase | |
if (buildPhase && buildPhase!=key) | |
continue; | |
if (section[key] == group) { | |
sectionKey = key.split(COMMENT_KEY)[0]; | |
return section[sectionKey]; | |
} | |
} | |
return null; | |
} | |
pbxProject.prototype.addBuildProperty = function(prop, value, build_name) { | |
var configurations = nonComments(this.pbxXCBuildConfigurationSection()), | |
key, configuration; | |
for (key in configurations){ | |
configuration = configurations[key]; | |
if (!build_name || configuration.name === build_name){ | |
configuration.buildSettings[prop] = value; | |
} | |
} | |
} | |
pbxProject.prototype.removeBuildProperty = function(prop, build_name) { | |
var configurations = nonComments(this.pbxXCBuildConfigurationSection()), | |
key, configuration; | |
for (key in configurations){ | |
configuration = configurations[key]; | |
if (configuration.buildSettings[prop] && | |
!build_name || configuration.name === build_name){ | |
delete configuration.buildSettings[prop]; | |
} | |
} | |
} | |
/** | |
* | |
* @param prop {String} | |
* @param value {String|Array|Object|Number|Boolean} | |
* @param build {String} Release or Debug | |
*/ | |
pbxProject.prototype.updateBuildProperty = function(prop, value, build) { | |
var configs = this.pbxXCBuildConfigurationSection(); | |
for (var configName in configs) { | |
if (!COMMENT_KEY.test(configName)) { | |
var config = configs[configName]; | |
if ( (build && config.name === build) || (!build) ) { | |
config.buildSettings[prop] = value; | |
} | |
} | |
} | |
} | |
pbxProject.prototype.updateProductName = function(name) { | |
this.updateBuildProperty('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)"', | |
SEARCH_PATHS = 'LIBRARY_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.addToLibrarySearchPaths = 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['LIBRARY_SEARCH_PATHS'] | |
|| buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) { | |
buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED]; | |
} | |
if (typeof file === 'string') { | |
buildSettings['LIBRARY_SEARCH_PATHS'].push(file); | |
} else { | |
buildSettings['LIBRARY_SEARCH_PATHS'].push(searchPathForFile(file, this)); | |
} | |
} | |
} | |
pbxProject.prototype.removeFromHeaderSearchPaths = function(file) { | |
var configurations = nonComments(this.pbxXCBuildConfigurationSection()), | |
INHERITED = '"$(inherited)"', | |
SEARCH_PATHS = 'HEADER_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; | |
if (buildSettings[SEARCH_PATHS]) { | |
var matches = buildSettings[SEARCH_PATHS].filter(function(p) { | |
return p.indexOf(new_path) > -1; | |
}); | |
matches.forEach(function(m) { | |
var idx = buildSettings[SEARCH_PATHS].indexOf(m); | |
buildSettings[SEARCH_PATHS].splice(idx, 1); | |
}); | |
} | |
} | |
} | |
pbxProject.prototype.addToHeaderSearchPaths = 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['HEADER_SEARCH_PATHS']) { | |
buildSettings['HEADER_SEARCH_PATHS'] = [INHERITED]; | |
} | |
if (typeof file === 'string') { | |
buildSettings['HEADER_SEARCH_PATHS'].push(file); | |
} else { | |
buildSettings['HEADER_SEARCH_PATHS'].push(searchPathForFile(file, this)); | |
} | |
} | |
} | |
// a JS getter. hmmm | |
pbxProject.prototype.__defineGetter__("productName", function() { | |
var configurations = nonComments(this.pbxXCBuildConfigurationSection()), | |
config, productName; | |
for (config in configurations) { | |
productName = configurations[config].buildSettings['PRODUCT_NAME']; | |
if (productName) { | |
return unquote(productName); | |
} | |
} | |
}); | |
// check if file is present | |
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 file; | |
} | |
} | |
return false; | |
} | |
pbxProject.prototype.addTarget = function(name, type, subfolder) { | |
// Setup uuid and name of new target | |
var targetUuid = this.generateUuid(), | |
targetType = type, | |
targetSubfolder = subfolder || name, | |
targetName = name.trim(); | |
// Check type against list of allowed target types | |
if (!targetName) { | |
throw new Error("Target name missing."); | |
} | |
// Check type against list of allowed target types | |
if (!targetType) { | |
throw new Error("Target type missing."); | |
} | |
// Check type against list of allowed target types | |
if (!producttypeForTargettype(targetType)) { | |
throw new Error("Target type invalid: " + targetType); | |
} | |
// Build Configuration: Create | |
var buildConfigurationsList = [ | |
{ | |
name: 'Debug', | |
isa: 'XCBuildConfiguration', | |
buildSettings: { | |
GCC_PREPROCESSOR_DEFINITIONS: ['"DEBUG=1"', '"$(inherited)"'], | |
INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'), | |
LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"', | |
PRODUCT_NAME: '"' + targetName + '"', | |
SKIP_INSTALL: 'YES' | |
} | |
}, | |
{ | |
name: 'Release', | |
isa: 'XCBuildConfiguration', | |
buildSettings: { | |
INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'), | |
LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"', | |
PRODUCT_NAME: '"' + targetName + '"', | |
SKIP_INSTALL: 'YES' | |
} | |
} | |
]; | |
// Build Configuration: Add | |
var buildConfigurations = this.addXCConfigurationList(buildConfigurationsList, 'Release', 'Build configuration list for PBXNativeTarget "' + targetName +'"'); | |
// Product: Create | |
var productName = targetName, | |
productType = producttypeForTargettype(targetType), | |
productFileType = filetypeForProducttype(productType), | |
productFile = this.addProductFile(productName, { group: 'Copy Files', 'target': targetUuid, 'explicitFileType': productFileType}), | |
productFileName = productFile.basename; | |
// Product: Add to build file list | |
this.addToPbxBuildFileSection(productFile); | |
// Target: Create | |
var target = { | |
uuid: targetUuid, | |
pbxNativeTarget: { | |
isa: 'PBXNativeTarget', | |
name: '"' + targetName + '"', | |
productName: '"' + targetName + '"', | |
productReference: productFile.fileRef, | |
productType: '"' + producttypeForTargettype(targetType) + '"', | |
buildConfigurationList: buildConfigurations.uuid, | |
buildPhases: [], | |
buildRules: [], | |
dependencies: [] | |
} | |
}; | |
// Target: Add to PBXNativeTarget section | |
this.addToPbxNativeTargetSection(target) | |
// Product: Embed (only for "extension"-type targets) | |
if (targetType === 'app_extension') { | |
// Create CopyFiles phase in first target | |
this.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Copy Files', this.getFirstTarget().uuid, targetType) | |
// Add product to CopyFiles phase | |
this.addToPbxCopyfilesBuildPhase(productFile) | |
// this.addBuildPhaseToTarget(newPhase.buildPhase, this.getFirstTarget().uuid) | |
}; | |
// Target: Add uuid to root project | |
this.addToPbxProjectSection(target); | |
// Target: Add dependency for this target to first (main) target | |
this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]); | |
// Return target on success | |
return target; | |
}; | |
// helper recursive prop search+replace | |
function propReplace(obj, prop, value) { | |
var o = {}; | |
for (var p in obj) { | |
if (o.hasOwnProperty.call(obj, p)) { | |
if (typeof obj[p] == 'object' && !Array.isArray(obj[p])) { | |
propReplace(obj[p], prop, value); | |
} else if (p == prop) { | |
obj[p] = value; | |
} | |
} | |
} | |
} | |
// helper object creation functions | |
function pbxBuildFileObj(file) { | |
var obj = Object.create(null); | |
obj.isa = 'PBXBuildFile'; | |
obj.fileRef = file.fileRef; | |
obj.fileRef_comment = file.basename; | |
if (file.settings) obj.settings = file.settings; | |
return obj; | |
} | |
function pbxFileReferenceObj(file) { | |
var fileObject = { | |
isa: "PBXFileReference", | |
name: "\"" + file.basename + "\"", | |
path: "\"" + file.path.replace(/\\/g, '/') + "\"", | |
sourceTree: file.sourceTree, | |
fileEncoding: file.fileEncoding, | |
lastKnownFileType: file.lastKnownFileType, | |
explicitFileType: file.explicitFileType, | |
includeInIndex: file.includeInIndex | |
}; | |
return fileObject; | |
} | |
function pbxGroupChild(file) { | |
var obj = Object.create(null); | |
obj.value = file.fileRef; | |
obj.comment = file.basename; | |
return obj; | |
} | |
function pbxBuildPhaseObj(file) { | |
var obj = Object.create(null); | |
obj.value = file.uuid; | |
obj.comment = longComment(file); | |
return obj; | |
} | |
function pbxCopyFilesBuildPhaseObj(obj, folderType, subfolderPath, phaseName){ | |
// Add additional properties for 'CopyFiles' build phase | |
var DESTINATION_BY_TARGETTYPE = { | |
application: 'wrapper', | |
app_extension: 'plugins', | |
bundle: 'wrapper', | |
command_line_tool: 'wrapper', | |
dynamic_library: 'products_directory', | |
framework: 'shared_frameworks', | |
static_library: 'products_directory', | |
unit_test_bundle: 'wrapper', | |
watch_app: 'wrapper', | |
watch_extension: 'plugins' | |
} | |
var SUBFOLDERSPEC_BY_DESTINATION = { | |
absolute_path: 0, | |
executables: 6, | |
frameworks: 10, | |
java_resources: 15, | |
plugins: 13, | |
products_directory: 16, | |
resources: 7, | |
shared_frameworks: 11, | |
shared_support: 12, | |
wrapper: 1, | |
xpc_services: 0 | |
} | |
obj.name = '"' + phaseName + '"'; | |
obj.dstPath = subfolderPath || '""'; | |
obj.dstSubfolderSpec = SUBFOLDERSPEC_BY_DESTINATION[DESTINATION_BY_TARGETTYPE[folderType]]; | |
return obj; | |
} | |
function pbxBuildFileComment(file) { | |
return longComment(file); | |
} | |
function pbxFileReferenceComment(file) { | |
return file.basename || path.basename(file.path); | |
} | |
function pbxNativeTargetComment(target) { | |
return target.name; | |
} | |
function longComment(file) { | |
return f("%s in %s", file.basename, file.group); | |
} | |
// respect <group> path | |
function correctForPluginsPath(file, project) { | |
return correctForPath(file, project, 'Plugins'); | |
} | |
function correctForResourcesPath(file, project) { | |
return correctForPath(file, project, 'Resources'); | |
} | |
function correctForFrameworksPath(file, project) { | |
return correctForPath(file, project, 'Frameworks'); | |
} | |
function correctForPath(file, project, group) { | |
var r_group_dir = new RegExp('^' + group + '[\\\\/]'); | |
if (project.pbxGroupByName(group).path) | |
file.path = file.path.replace(r_group_dir, ''); | |
return file; | |
} | |
function searchPathForFile(file, proj) { | |
var plugins = proj.pbxGroupByName('Plugins'), | |
pluginsPath = plugins ? plugins.path : null, | |
fileDir = path.dirname(file.path); | |
if (fileDir == '.') { | |
fileDir = ''; | |
} else { | |
fileDir = '/' + fileDir; | |
} | |
if (file.plugin && pluginsPath) { | |
return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""'; | |
} else if (file.customFramework && file.dirname) { | |
return '"\\"' + file.dirname + '\\""'; | |
} else { | |
return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""'; | |
} | |
} | |
function nonComments(obj) { | |
var keys = Object.keys(obj), | |
newObj = {}, i = 0; | |
for (i; i < keys.length; i++) { | |
if (!COMMENT_KEY.test(keys[i])) { | |
newObj[keys[i]] = obj[keys[i]]; | |
} | |
} | |
return newObj; | |
} | |
function unquote(str) { | |
if (str) return str.replace(/^"(.*)"$/, "$1"); | |
} | |
function buildPhaseNameForIsa (isa) { | |
BUILDPHASENAME_BY_ISA = { | |
PBXCopyFilesBuildPhase: 'Copy Files', | |
PBXResourcesBuildPhase: 'Resources', | |
PBXSourcesBuildPhase: 'Sources', | |
PBXFrameworksBuildPhase: 'Frameworks' | |
} | |
return BUILDPHASENAME_BY_ISA[isa] | |
} | |
function producttypeForTargettype (targetType) { | |
PRODUCTTYPE_BY_TARGETTYPE = { | |
application: 'com.apple.product-type.application', | |
app_extension: 'com.apple.product-type.app-extension', | |
bundle: 'com.apple.product-type.bundle', | |
command_line_tool: 'com.apple.product-type.tool', | |
dynamic_library: 'com.apple.product-type.library.dynamic', | |
framework: 'com.apple.product-type.framework', | |
static_library: 'com.apple.product-type.library.static', | |
unit_test_bundle: 'com.apple.product-type.bundle.unit-test', | |
watch_app: 'com.apple.product-type.application.watchapp', | |
watch_extension: 'com.apple.product-type.watchkit-extension' | |
}; | |
return PRODUCTTYPE_BY_TARGETTYPE[targetType] | |
} | |
function filetypeForProducttype (productType) { | |
FILETYPE_BY_PRODUCTTYPE = { | |
'com.apple.product-type.application': '"wrapper.application"', | |
'com.apple.product-type.app-extension': '"wrapper.app-extension"', | |
'com.apple.product-type.bundle': '"wrapper.plug-in"', | |
'com.apple.product-type.tool': '"compiled.mach-o.dylib"', | |
'com.apple.product-type.library.dynamic': '"compiled.mach-o.dylib"', | |
'com.apple.product-type.framework': '"wrapper.framework"', | |
'com.apple.product-type.library.static': '"archive.ar"', | |
'com.apple.product-type.bundle.unit-test': '"wrapper.cfbundle"', | |
'com.apple.product-type.application.watchapp': '"wrapper.application"', | |
'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"' | |
}; | |
return FILETYPE_BY_PRODUCTTYPE[productType] | |
} | |
pbxProject.prototype.getFirstProject = function() { | |
// Get pbxProject container | |
var pbxProjectContainer = this.pbxProjectSection(); | |
// Get first pbxProject UUID | |
var firstProjectUuid = Object.keys(pbxProjectContainer)[0]; | |
// Get first pbxProject | |
var firstProject = pbxProjectContainer[firstProjectUuid]; | |
return { | |
uuid: firstProjectUuid, | |
firstProject: firstProject | |
} | |
} | |
pbxProject.prototype.getFirstTarget = function() { | |
// Get first targets UUID | |
var firstTargetUuid = this.getFirstProject()['firstProject']['targets'][0].value; | |
// Get first pbxNativeTarget | |
var firstTarget = this.pbxNativeTargetSection()[firstTargetUuid]; | |
return { | |
uuid: firstTargetUuid, | |
firstTarget: firstTarget | |
} | |
} | |
/*** NEW ***/ | |
pbxProject.prototype.addToPbxGroup = function (file, groupKey) { | |
var group = this.getPBXGroupByKey(groupKey); | |
if (group && group.children !== undefined) { | |
if (typeof file === 'string') { | |
//Group Key | |
var childGroup = { | |
value:file, | |
comment: this.getPBXGroupByKey(file).name | |
}; | |
group.children.push(childGroup); | |
} | |
else { | |
//File Object | |
group.children.push(pbxGroupChild(file)); | |
} | |
} | |
} | |
pbxProject.prototype.removeFromPbxGroup = function (file, groupKey) { | |
var group = this.getPBXGroupByKey(groupKey); | |
if (group) { | |
var groupChildren = group.children, i; | |
for(i in groupChildren) { | |
if(pbxGroupChild(file).value == groupChildren[i].value && | |
pbxGroupChild(file).comment == groupChildren[i].comment) { | |
groupChildren.splice(i, 1); | |
break; | |
} | |
} | |
} | |
} | |
pbxProject.prototype.getPBXGroupByKey = function(key) { | |
return this.hash.project.objects['PBXGroup'][key]; | |
}; | |
pbxProject.prototype.findPBXGroupKey = function(criteria) { | |
var groups = this.hash.project.objects['PBXGroup']; | |
var target; | |
for (var key in groups) { | |
// only look for comments | |
if (COMMENT_KEY.test(key)) continue; | |
var group = groups[key]; | |
if (criteria && criteria.path && criteria.name) { | |
if (criteria.path === group.path && criteria.name === group.name) { | |
target = key; | |
break | |
} | |
} | |
else if (criteria && criteria.path) { | |
if (criteria.path === group.path) { | |
target = key; | |
break | |
} | |
} | |
else if (criteria && criteria.name) { | |
if (criteria.name === group.name) { | |
target = key; | |
break | |
} | |
} | |
} | |
return target; | |
} | |
pbxProject.prototype.pbxCreateGroup = function(name, pathName) { | |
//Create object | |
var model = { | |
isa:"PBXGroup", | |
children: [], | |
name: name, | |
path: pathName, | |
sourceTree: '"<group>"' | |
}; | |
var key = this.generateUuid(); | |
//Create comment | |
var commendId = key + '_comment'; | |
//add obj and commentObj to groups; | |
var groups = this.hash.project.objects['PBXGroup']; | |
groups[commendId] = name; | |
groups[key] = model; | |
return key; | |
} | |
pbxProject.prototype.getPBXObject = function(name) { | |
return this.hash.project.objects[name]; | |
} | |
pbxProject.prototype.addFile = function (path, group, opt) { | |
var file = new pbxFile(path, opt); | |
// null is better for early errors | |
if (this.hasFile(file.path)) return null; | |
file.fileRef = this.generateUuid(); | |
this.addToPbxFileReferenceSection(file); // PBXFileReference | |
this.addToPbxGroup(file, group); // PBXGroup | |
return file; | |
} | |
pbxProject.prototype.removeFile = function (path, group, opt) { | |
var file = new pbxFile(path, opt); | |
this.removeFromPbxFileReferenceSection(file); // PBXFileReference | |
this.removeFromPbxGroup(file, group); // PBXGroup | |
return file; | |
} | |
pbxProject.prototype.getBuildProperty = function(prop, build) { | |
var target; | |
var configs = this.pbxXCBuildConfigurationSection(); | |
for (var configName in configs) { | |
if (!COMMENT_KEY.test(configName)) { | |
var config = configs[configName]; | |
if ( (build && config.name === build) || (build === undefined) ) { | |
if (config.buildSettings[prop] !== undefined) { | |
target = config.buildSettings[prop]; | |
} | |
} | |
} | |
} | |
return target; | |
} | |
pbxProject.prototype.getBuildConfigByName = function(name) { | |
var target = {}; | |
var configs = this.pbxXCBuildConfigurationSection(); | |
for (var configName in configs) { | |
if (!COMMENT_KEY.test(configName)) { | |
var config = configs[configName]; | |
if (config.name === name) { | |
target[configName] = config; | |
} | |
} | |
} | |
return target; | |
} | |
module.exports = pbxProject; |