Skip to content

feature: Add groups created by addPbxGroup() to the meta PBXGroup #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 22, 2018
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
46 changes: 41 additions & 5 deletions lib/pbxFile.js
Original file line number Diff line number Diff line change
@@ -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"',
Expand All @@ -11,13 +15,42 @@ var path = require('path'),
DEFAULT_SOURCE_TREE = '"<group>"',
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;

Expand Down Expand Up @@ -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) {
Expand All @@ -102,4 +135,7 @@ function pbxFile(filepath, opt) {
}
}

module.exports = pbxFile;
module.exports = {
pbxFile: pbxFile,
fileTypes: fileTypes
}
125 changes: 103 additions & 22 deletions lib/pbxProject.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
var util = require('util'),
f = util.format,
EventEmitter = require('events').EventEmitter,
path = require('path'),
$path = require('path'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why we are using $ before path.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to differentiate between the global (used for path module) and local (passed in most of the methods) variable.

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)
Expand Down Expand Up @@ -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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case when we have more than one candidate, shouldn't return the first one? Actually is it possible to have more than one candidate?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have exactly one candidate as this will be the root group. If they are more than one there should be an error. Maybe we can throw an exception.

}

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 : '"<group>"'
},
},//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]) {
Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 = '';
Expand Down