diff --git a/lib/pbxFile.js b/lib/pbxFile.js index 8a2723b..2f6112c 100644 --- a/lib/pbxFile.js +++ b/lib/pbxFile.js @@ -1,7 +1,11 @@ -var path = require('path'), +var $path = require('path'), util = require('util'), M_EXTENSION = /[.]m$/, SOURCE_FILE = 'sourcecode.c.objc', + C_EXTENSION = /[.]c$/, C_SOURCE_FILE = 'sourcecode.c', H_EXTENSION = /[.]h$/, HEADER_FILE = 'sourcecode.c.h', + MM_EXTENSION = /[.]mm$/, MM_SOURCE_FILE = 'sourcecode.cpp.objcpp', + HPP_EXTENSION = /[.](hpp|hxx|h\+\+|hh)$/, CPP_HEADER_FILE = 'sourcecode.cpp.h', + CPP_EXTENSION = /[.](cpp|cxx|c\+\+|cc)$/, CPP_SOURCE_FILE = 'sourcecode.cpp.cpp', BUNDLE_EXTENSION = /[.]bundle$/, BUNDLE = '"wrapper.plug-in"', XIB_EXTENSION = /[.]xib$/, XIB_FILE = 'file.xib', DYLIB_EXTENSION = /[.]dylib$/, DYLIB = '"compiled.mach-o.dylib"', @@ -11,13 +15,42 @@ var path = require('path'), DEFAULT_SOURCE_TREE = '""', DEFAULT_FILE_ENCODING = 4; +function fileTypes() { + return { + SOURCE_FILE, + C_SOURCE_FILE, + HEADER_FILE, + MM_SOURCE_FILE, + CPP_HEADER_FILE, + CPP_SOURCE_FILE, + BUNDLE, + XIB_FILE, + FRAMEWORK, + DYLIB, + ARCHIVE, + PNG_IMAGE, + } +} + function detectLastType(path) { if (M_EXTENSION.test(path)) return SOURCE_FILE; + if (C_EXTENSION.test(path)) + return C_SOURCE_FILE; + if (H_EXTENSION.test(path)) return HEADER_FILE; + if (MM_EXTENSION.test(path)) + return MM_SOURCE_FILE; + + if (CPP_EXTENSION.test(path)) + return CPP_SOURCE_FILE; + + if (HPP_EXTENSION.test(path)) + return CPP_HEADER_FILE; + if (BUNDLE_EXTENSION.test(path)) return BUNDLE; @@ -82,17 +115,17 @@ function pbxFile(filepath, opt) { // for custom frameworks if(opt.customFramework == true) { this.customFramework = true; - this.dirname = path.dirname(filepath); + this.dirname = $path.dirname(filepath); } - this.basename = path.basename(filepath); + this.basename = $path.basename(filepath); this.path = correctPath(this, filepath); this.group = correctGroup(this); this.sourceTree = opt.sourceTree || defaultSourceTree(this); this.fileEncoding = opt.fileEncoding || fileEncoding(this); - if (opt.weak && opt.weak === true) + if (opt.weak && opt.weak === true) this.settings = { ATTRIBUTES: ['Weak'] }; if (opt.compilerFlags) { @@ -102,4 +135,7 @@ function pbxFile(filepath, opt) { } } -module.exports = pbxFile; +module.exports = { + pbxFile: pbxFile, + fileTypes: fileTypes +} diff --git a/lib/pbxProject.js b/lib/pbxProject.js index 2684436..3712ddc 100644 --- a/lib/pbxProject.js +++ b/lib/pbxProject.js @@ -1,21 +1,24 @@ var util = require('util'), f = util.format, EventEmitter = require('events').EventEmitter, - path = require('path'), + $path = require('path'), uuid = require('node-uuid'), fork = require('child_process').fork, pbxWriter = require('./pbxWriter'), - pbxFile = require('./pbxFile'), + pbxFile = require('./pbxFile').pbxFile, + pbxFileTypes = require('./pbxFile').fileTypes, fs = require('fs'), parser = require('./parser/pbxproj'), COMMENT_KEY = /_comment$/, - NO_SPECIAL_SYMBOLS = /^[a-zA-Z0-9_\.\$]+\.[a-zA-Z]+$/; + NO_SPECIAL_SYMBOLS = /^[a-zA-Z0-9_\.\$]+\.[a-zA-Z]+$/, + HEADER_FILE_TYPE_SUFFIX = ".h", + SOURCE_CODE_FILE_TYPE_PREFIX = "sourcecode."; function pbxProject(filename) { if (!(this instanceof pbxProject)) return new pbxProject(filename); - this.filepath = path.resolve(filename) + this.filepath = $path.resolve(filename) } util.inherits(pbxProject, EventEmitter) @@ -286,31 +289,52 @@ pbxProject.prototype.removeFromPbxBuildFileSection = function (file) { } } -pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceTree) { +pbxProject.prototype.findMainPbxGroup = function () { + var groups = this.hash.project.objects['PBXGroup']; + var candidates = []; + for (var key in groups) { + if (!groups[key].path && !groups[key].name && groups[key].isa) { + candidates.push(groups[key]); + } + } + if (candidates.length == 1) { + return candidates[0]; + } + + return null; +} + +pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceTree, opt) { + + var oldGroup = this.pbxGroupByName(name); + if (oldGroup) { + this.removePbxGroup(name, path); + } + var groups = this.hash.project.objects['PBXGroup'], - pbxGroupUuid = this.generateUuid(), + pbxGroupUuid = opt.uuid || this.generateUuid(), commentKey = f("%s_comment", pbxGroupUuid), pbxGroup = { isa: 'PBXGroup', children: [], name: name, - path: path, sourceTree: sourceTree ? sourceTree : '""' - }, + },//path is mandatory only for the main 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]) { @@ -320,23 +344,80 @@ pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceT 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)); + + var srcRootPath = $path.dirname($path.dirname(this.filepath)); + var file = new pbxFile($path.relative(srcRootPath, filePath)); + if (fs.lstatSync(filePath).isDirectory()) { + file.uuid = this.generateUuid(); + file.fileRef = file.uuid; + this.addToPbxFileReferenceSection(file); // PBXFileReference + this.addToPbxBuildFileSection(file); + pbxGroup.children.push(pbxGroupChild(file)); + var files = fs.readdirSync(filePath).map(p => $path.join(filePath, p)); + this.addPbxGroup(files, $path.basename(filePath), filePath, null, {uuid: file.uuid}); + }else if (file.lastType.startsWith(SOURCE_CODE_FILE_TYPE_PREFIX)) { + file.uuid = this.generateUuid(); + file.fileRef = this.generateUuid(); + this.addToPbxFileReferenceSection(file); // PBXFileReference + this.addToPbxBuildFileSection(file); // PBXBuildFile + if (!file.lastType.endsWith(HEADER_FILE_TYPE_SUFFIX)) { + this.addToPbxSourcesBuildPhase(file); + } + pbxGroup.children.push(pbxGroupChild(file)); + } + } - + if (groups) { groups[pbxGroupUuid] = pbxGroup; groups[commentKey] = name; } - + + if (opt.isMain) { + let mainGroup = this.findMainPbxGroup(); + if (mainGroup) { + var file = new pbxFile($path.relative(this.filepath, path)); + file.fileRef = pbxGroupUuid; + mainGroup.children.push(pbxGroupChild(file)); + } + } + return {uuid: pbxGroupUuid, pbxGroup: pbxGroup}; } +pbxProject.prototype.removePbxGroup = function(name, path) { + var group = this.pbxGroupByName(name); + if (!group) { + return; + } + + var children = group.children; + + for(i in children) { + var file = new pbxFile($path.join(path, children[i].comment)); + file.fileRef = children[i].value; + file.uuid = file.fileRef; + this.removePbxGroup(children[i].comment, $path.join(path, children[i].comment)); + this.removeFromPbxFileReferenceSection(file); + this.removeFromPbxBuildFileSection(file); + this.removeFromPbxSourcesBuildPhase(file); + } + + //copied from https://github.com/alunny/node-xcode/blob/master/lib/pbxProject.js#L527 + var section = this.hash.project.objects['PBXGroup'], + 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]; + delete section[itemKey]; + } + } +} + pbxProject.prototype.addToPbxFileReferenceSection = function (file) { var commentKey = f("%s_comment", file.fileRef); @@ -852,7 +933,7 @@ pbxProject.prototype.removeFromHeaderSearchPaths = function (file) { INHERITED = '"$(inherited)"', SEARCH_PATHS = 'HEADER_SEARCH_PATHS', config, buildSettings, searchPaths; - var new_path = searchPathForFile(file, this); + var new_path = typeof file === 'string' ? file : searchPathForFile(file, this); for (config in configurations) { buildSettings = configurations[config].buildSettings; @@ -1048,7 +1129,7 @@ function searchPathForFile(file, proj) { var plugins = proj.pbxGroupByName('Plugins'), pluginsPath = plugins ? plugins.path : null, - fileDir = path.dirname(file.path); + fileDir = $path.dirname(file.path); if (fileDir == '.') { fileDir = '';