aboutsummaryrefslogtreecommitdiff
path: root/devtools
diff options
context:
space:
mode:
Diffstat (limited to 'devtools')
-rw-r--r--devtools/create_project/xcode.cpp515
-rw-r--r--devtools/create_project/xcode.h287
-rw-r--r--devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj20
3 files changed, 799 insertions, 23 deletions
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index f26c847c83..65491d2f87 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -36,26 +36,535 @@
namespace CreateProjectTool {
+#define DEBUG_XCODE_HASH 0
+
+#define ADD_DEFINE(defines, name) \
+ defines.push_back(name);
+
+#define ADD_SETTING(config, key, value) \
+ config.settings[key] = Setting(value, "", SettingsNoQuote);
+
+#define ADD_SETTING_ORDER(config, key, value, order) \
+ config.settings[key] = Setting(value, "", SettingsNoQuote, 0, order);
+
+#define ADD_SETTING_ORDER_NOVALUE(config, key, comment, order) \
+ config.settings[key] = Setting("", comment, SettingsNoValue, 0, order);
+
+#define ADD_SETTING_QUOTE(config, key, value) \
+ config.settings[key] = Setting(value);
+
+#define ADD_SETTING_QUOTE_VAR(config, key, value) \
+ config.settings[key] = Setting(value, "", SettingsQuoteVariable);
+
+#define ADD_SETTING_LIST(config, key, values, flags, indent) \
+ config.settings[key] = Setting(values, flags, indent);
+
+#define REMOVE_SETTING(config, key) \
+ config.settings.erase(key);
+
+#define ADD_BUILD_FILE(id, name, comment) { \
+ Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); \
+ buildFile->addProperty("fileRef", getHash(name), name, SettingsNoValue); \
+ _buildFile.add(buildFile); \
+ _buildFile.flags = SettingsSingleItem; \
+}
+
+#define ADD_FILE_REFERENCE(name, properties) { \
+ Object *fileRef = new Object(this, name, name, "PBXFileReference", "PBXFileReference", name); \
+ if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); \
+ if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue); \
+ if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue); \
+ if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue); \
+ if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); \
+ _fileReference.add(fileRef); \
+ _fileReference.flags = SettingsSingleItem; \
+}
+
XCodeProvider::XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version)
: ProjectProvider(global_warnings, project_warnings, version) {
}
void XCodeProvider::createWorkspace(const BuildSetup &setup) {
- // TODO
+ // Create project folder
+ std::string workspace = setup.outputDir + '/' + "scummvm.xcodeproj";
+
+#if defined(_WIN32) || defined(WIN32)
+ if (!CreateDirectory(workspace.c_str(), NULL))
+ if (GetLastError() != ERROR_ALREADY_EXISTS)
+ error("Could not create folder \"" + setup.outputDir + '/' + "scummvm.xcodeproj\"");
+#else
+ if (mkdir(workspace.c_str(), 0777) == -1) {
+ if (errno == EEXIST) {
+ // Try to open as a folder (might be a file / symbolic link)
+ DIR *dirp = opendir(workspace.c_str());
+ if (dirp == NULL) {
+ error("Could not create folder \"" + setup.outputDir + '/' + "scummvm.xcodeproj\"");
+ } else {
+ // The folder exists, just close the stream and return
+ closedir(dirp);
+ }
+ } else {
+ error("Could not create folder \"" + setup.outputDir + '/' + "scummvm.xcodeproj\"");
+ }
+ }
+#endif
+
+ // Setup global objects
+ setupDefines(setup);
+ _targets.push_back("ScummVM-iPhone");
+ _targets.push_back("ScummVM-OS X");
+ _targets.push_back("ScummVM-Simulator");
+
+ setupCopyFilesBuildPhase();
+ setupFrameworksBuildPhase();
+ setupNativeTarget();
+ setupProject();
+ setupResourcesBuildPhase();
+ setupBuildConfiguration();
}
+// We are done with constructing all the object graph and we got through every project, output the main project file
+// (this is kind of a hack since other providers use separate project files)
void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) {
- // TODO
+ // This needs to be done at the end when all build files have been accounted for
+ setupSourcesBuildPhase();
+
+ ouputMainProjectFile(setup);
}
+// Store information about a project here, for use at the end
void XCodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir,
const StringList &includeList, const StringList &excludeList) {
- // TODO
+ std::string modulePath;
+ if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) {
+ modulePath = moduleDir.substr(setup.srcDir.size());
+ if (!modulePath.empty() && modulePath.at(0) == '/')
+ modulePath.erase(0, 1);
+ }
+
+ std::ofstream project;
+ if (modulePath.size())
+ addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath);
+ else
+ addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Main Project file
+//////////////////////////////////////////////////////////////////////////
+void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) {
+ std::ofstream project((setup.outputDir + '/' + "scummvm.xcodeproj" + '/' + "project.pbxproj").c_str());
+ if (!project)
+ error("Could not open \"" + setup.outputDir + '/' + "scummvm.xcodeproj" + '/' + "project.pbxproj\" for writing");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Header
+ project << "// !$*UTF8*$!\n"
+ "{\n"
+ "\t" << writeSetting("archiveVersion", "1", "", SettingsNoQuote) << ";\n"
+ "\tclasses = {\n"
+ "\t};\n"
+ "\t" << writeSetting("objectVersion", "46", "", SettingsNoQuote) << ";\n"
+ "\tobjects = {\n";
+
+ //////////////////////////////////////////////////////////////////////////
+ // List of objects
+ project << _buildFile.toString();
+ project << _copyFilesBuildPhase.toString();
+ project << _fileReference.toString();
+ project << _frameworksBuildPhase.toString();
+ project << _groups.toString();
+ project << _nativeTarget.toString();
+ project << _project.toString();
+ project << _resourcesBuildPhase.toString();
+ project << _sourcesBuildPhase.toString();
+ project << _buildConfiguration.toString();
+ project << _configurationList.toString();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Footer
+ project << "\t};\n"
+ "\t" << writeSetting("rootObject", getHash("PBXProject"), "Project object", SettingsNoQuote) << ";\n"
+ "}\n";
+
}
+//////////////////////////////////////////////////////////////////////////
+// Files
+//////////////////////////////////////////////////////////////////////////
void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) {
+
+ // Init root group
+ _groups.comment = "PBXGroup";
+ Object *group = new Object(this, "PBXGroup", "PBXGroup", "PBXGroup", "", "");
+
+ //Property children;
+ //children.flags = SettingsAsList;
+ //group->properties["children"] = children;
+ group->addProperty("children", "", "", SettingsNoValue|SettingsAsList);
+
+ group->addProperty("sourceTree", "<group>", "", SettingsNoValue|SettingsQuoteVariable);
+
+ _groups.add(group);
+
+ // TODO Add files
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup functions
+//////////////////////////////////////////////////////////////////////////
+void XCodeProvider::setupCopyFilesBuildPhase() {
// TODO
}
+/**
+ * Sets up the frameworks build phase.
+ *
+ * (each native target has different build rules)
+ */
+void XCodeProvider::setupFrameworksBuildPhase() {
+ // TODO
+}
+
+void XCodeProvider::setupNativeTarget() {
+ // TODO
+}
+
+void XCodeProvider::setupProject() {
+ _project.comment = "PBXProject";
+
+ Object *project = new Object(this, "PBXProject", "PBXProject", "PBXProject", "", "Project object");
+
+ project->addProperty("buildConfigurationList", getHash("XCConfigurationList_scummvm"), "Build configuration list for PBXProject \"scummvm\"", SettingsNoValue);
+ project->addProperty("compatibilityVersion", "Xcode 3.2", "", SettingsNoValue|SettingsQuoteVariable);
+ project->addProperty("developmentRegion", "English", "", SettingsNoValue);
+ project->addProperty("hasScannedForEncodings", "1", "", SettingsNoValue);
+
+ // List of known regions
+ Property regions;
+ regions.flags = SettingsAsList;
+ ADD_SETTING_ORDER_NOVALUE(regions, "English", "", 0);
+ ADD_SETTING_ORDER_NOVALUE(regions, "Japanese", "", 1);
+ ADD_SETTING_ORDER_NOVALUE(regions, "French", "", 2);
+ ADD_SETTING_ORDER_NOVALUE(regions, "German", "", 3);
+ project->properties["knownRegions"] = regions;
+
+ project->addProperty("mainGroup", getHash("PBXGroup_CustomTemplate"), "CustomTemplate", SettingsNoValue);
+ project->addProperty("projectDirPath", "", "", SettingsNoValue|SettingsQuoteVariable);
+ project->addProperty("projectRoot", "", "", SettingsNoValue|SettingsQuoteVariable);
+
+ // List of targets
+ //Property targets;
+ //targets.flags = SettingsAsList;
+ // TODO
+ //project->properties["targets"] = targets;
+ project->addProperty("targets", "", "", SettingsNoValue|SettingsAsList);
+
+ _project.add(project);
+}
+
+void XCodeProvider::setupResourcesBuildPhase() {
+ // TODO
+}
+
+void XCodeProvider::setupSourcesBuildPhase() {
+ // TODO
+}
+
+// Setup all build configurations
+void XCodeProvider::setupBuildConfiguration() {
+
+ _buildConfiguration.comment = "XCBuildConfiguration";
+ _buildConfiguration.flags = SettingsAsList;
+
+ ///****************************************
+ // * iPhone
+ // ****************************************/
+
+ //// Debug
+ Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_ScummVM-iPhone_Debug", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
+ Property iPhone_Debug;
+ iPhone_Debug.flags = SettingsSingleItem;
+ // TODO Add settings
+
+ iPhone_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue);
+ iPhone_Debug_Object->properties["buildSettings"] = iPhone_Debug;
+
+ //// Release
+ Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_ScummVM-iPhone_Release", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
+ Property iPhone_Release(iPhone_Debug);
+ // TODO Add settings
+
+ iPhone_Release_Object->addProperty("name", "Release", "", SettingsNoValue);
+ iPhone_Release_Object->properties["buildSettings"] = iPhone_Release;
+
+ _buildConfiguration.add(iPhone_Debug_Object);
+ _buildConfiguration.add(iPhone_Release_Object);
+
+ /****************************************
+ * scummvm
+ ****************************************/
+
+ // Debug
+ Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_scummvm_Debug", "scummvm", "XCBuildConfiguration", "PBXProject", "Debug");
+ Property scummvm_Debug;
+ scummvm_Debug.flags = SettingsSingleItem;
+ // TODO Add settings
+
+ scummvm_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue);
+ scummvm_Debug_Object->properties["buildSettings"] = scummvm_Debug;
+
+ // Release
+ Object *scummvm_Release_Object = new Object(this, "XCBuildConfiguration_scummvm_Release", "scummvm", "XCBuildConfiguration", "PBXProject", "Release");
+ Property scummvm_Release(scummvm_Debug);
+ // TODO Add settings
+
+ scummvm_Release_Object->addProperty("name", "Release", "", SettingsNoValue);
+ scummvm_Release_Object->properties["buildSettings"] = scummvm_Release;
+
+ _buildConfiguration.add(scummvm_Debug_Object);
+ _buildConfiguration.add(scummvm_Release_Object);
+
+ /****************************************
+ * ScummVM-OS X
+ ****************************************/
+
+ // Debug
+ Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_ScummVM-OSX_Debug", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
+ Property scummvmOSX_Debug;
+ scummvmOSX_Debug.flags = SettingsSingleItem;
+ // TODO Add settings
+
+ scummvmOSX_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue);
+ scummvmOSX_Debug_Object->properties["buildSettings"] = scummvmOSX_Debug;
+
+ // Release
+ Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_ScummVMOSX_Release", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
+ Property scummvmOSX_Release(scummvmOSX_Debug);
+ // TODO Add settings
+
+ scummvmOSX_Release_Object->addProperty("name", "Release", "", SettingsNoValue);
+ scummvmOSX_Release_Object->properties["buildSettings"] = scummvmOSX_Release;
+
+ _buildConfiguration.add(scummvmOSX_Debug_Object);
+ _buildConfiguration.add(scummvmOSX_Release_Object);
+
+ /****************************************
+ * ScummVM-Simulator
+ ****************************************/
+
+ // Debug
+ Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_ScummVM-Simulator_Debug", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
+ Property scummvmSimulator_Debug(iPhone_Debug);
+ // TODO Add settings
+
+ scummvmSimulator_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue);
+ scummvmSimulator_Debug_Object->properties["buildSettings"] = scummvmSimulator_Debug;
+
+ // Release
+ Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_ScummVM-Simulator_Release", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
+ Property scummvmSimulator_Release(scummvmSimulator_Debug);
+ /// TODO Add settings
+
+ scummvmSimulator_Release_Object->addProperty("name", "Release", "", SettingsNoValue);
+ scummvmSimulator_Release_Object->properties["buildSettings"] = scummvmSimulator_Release;
+
+ _buildConfiguration.add(scummvmSimulator_Debug_Object);
+ _buildConfiguration.add(scummvmSimulator_Release_Object);
+
+ ////////////////////////////////////////////////////////////////////////////
+ //// Configuration List
+ _configurationList.comment = "XCConfigurationList";
+ _configurationList.flags = SettingsAsList;
+
+ // Warning: This assumes we have all configurations with a Debug & Release pair
+ for (std::vector<Object *>::iterator config = _buildConfiguration.objects.begin(); config != _buildConfiguration.objects.end(); config++) {
+
+ Object *configList = new Object(this, "XCConfigurationList_" + (*config)->name, (*config)->name, "XCConfigurationList", "", "Build configuration list for " + (*config)->refType + " \"" + (*config)->name + "\"");
+
+ Property buildConfigs;
+ buildConfigs.flags = SettingsAsList;
+
+ buildConfigs.settings[getHash((*config)->id)] = Setting("", "Debug", SettingsNoValue, 0, 0);
+ buildConfigs.settings[getHash((*(++config))->id)] = Setting("", "Release", SettingsNoValue, 0, 1);
+
+ configList->properties["buildConfigurations"] = buildConfigs;
+
+ configList->addProperty("defaultConfigurationIsVisible", "0", "", SettingsNoValue);
+ configList->addProperty("defaultConfigurationName", "Release", "", SettingsNoValue);
+
+ _configurationList.add(configList);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+
+// Setup global defines
+void XCodeProvider::setupDefines(const BuildSetup &setup) {
+
+ for (StringList::const_iterator i = setup.defines.begin(); i != setup.defines.end(); ++i) {
+ if (*i == "HAVE_NASM") // Not supported on Mac (TODO: change how it's handled in main class or add it only in MSVC/CodeBlocks providers?)
+ continue;
+
+ ADD_DEFINE(_defines, *i);
+ }
+ // Add special defines for Mac support
+ ADD_DEFINE(_defines, "CONFIG_H");
+ ADD_DEFINE(_defines, "SCUMM_NEED_ALIGNMENT");
+ ADD_DEFINE(_defines, "SCUMM_LITTLE_ENDIAN");
+ ADD_DEFINE(_defines, "UNIX");
+ ADD_DEFINE(_defines, "SCUMMVM");
+ ADD_DEFINE(_defines, "USE_TREMOR");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Object hash
+//////////////////////////////////////////////////////////////////////////
+
+// TODO use md5 to compute a file hash (and fall back to standard key generation if not passed a file)
+std::string XCodeProvider::getHash(std::string key) {
+
+#if DEBUG_XCODE_HASH
+ return key;
+#else
+ // Check to see if the key is already in the dictionary
+ std::map<std::string, std::string>::iterator hashIterator = _hashDictionnary.find(key);
+ if (hashIterator != _hashDictionnary.end())
+ return hashIterator->second;
+
+ // Generate a new key from the file hash and insert it into the dictionary
+ std::string hash = newHash();
+ _hashDictionnary[key] = hash;
+
+ return hash;
+#endif
+}
+
+bool isSeparator (char s) { return (s == '-'); }
+
+std::string XCodeProvider::newHash() const {
+ std::string hash = createUUID();
+
+ // Remove { and - from UUID and resize to 96-bits uppercase hex string
+ hash.erase(remove_if(hash.begin(), hash.end(), isSeparator), hash.end());
+
+ hash.resize(24);
+ std::transform(hash.begin(), hash.end(), hash.begin(), toupper);
+
+ return hash;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Output
+//////////////////////////////////////////////////////////////////////////
+
+std::string replace(std::string input, const std::string find, std::string replaceStr) {
+ std::string::size_type pos = 0;
+ std::string::size_type findLen = find.length();
+ std::string::size_type replaceLen = replaceStr.length();
+
+ if (findLen == 0 )
+ return input;
+
+ for (;(pos = input.find(find, pos)) != std::string::npos;) {
+ input.replace(pos, findLen, replaceStr);
+ pos += replaceLen;
+ }
+
+ return input;
+}
+
+std::string XCodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const {
+ std::string output;
+
+ output += (flags & SettingsSingleItem ? "" : "\t\t\t") + variable + " = ";
+
+ if (prop.settings.size() > 1 || (prop.flags & SettingsSingleItem))
+ output += (prop.flags & SettingsAsList) ? "(\n" : "{\n";
+
+ OrderedSettingList settings = prop.getOrderedSettingList();
+ for (OrderedSettingList::const_iterator setting = settings.begin(); setting != settings.end(); ++setting) {
+ if (settings.size() > 1 || (prop.flags & SettingsSingleItem))
+ output += (flags & SettingsSingleItem ? " " : "\t\t\t\t");
+
+ output += writeSetting((*setting).first, (*setting).second);
+
+ if ((prop.flags & SettingsAsList) && prop.settings.size() > 1) {
+ output += (prop.settings.size() > 0) ? ",\n" : "\n";
+ } else {
+ output += ";";
+ output += (flags & SettingsSingleItem ? " " : "\n");
+ }
+ }
+
+ if (prop.settings.size() > 1 || (prop.flags & SettingsSingleItem))
+ output += (prop.flags & SettingsAsList) ? "\t\t\t);\n" : "\t\t\t};\n";
+
+ return output;
+}
+
+std::string XCodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const {
+ return writeSetting(variable, Setting(value, comment, flags, indent));
+}
+// Heavily modified (not in a good way) function, imported from QMake XCode project generator (licensed under the QT license)
+std::string XCodeProvider::writeSetting(const std::string &variable, const Setting &setting) const {
+ std::string output;
+ const std::string quote = (setting.flags & SettingsNoQuote) ? "" : "\"";
+ const std::string escape_quote = quote.empty() ? "" : "\\" + quote;
+ std::string newline = "\n";
+
+ // Get indent level
+ for (int i = 0; i < setting.indent; ++i)
+ newline += "\t";
+
+ // Setup variable
+ std::string var = (setting.flags & SettingsQuoteVariable) ? "\"" + variable + "\"" : variable;
+
+ // Output a list
+ if (setting.flags & SettingsAsList) {
+
+ output += var + ((setting.flags & SettingsNoValue) ? "(" : " = (") + newline;
+
+ for (unsigned int i = 0, count = 0; i < setting.entries.size(); ++i) {
+
+ std::string value = setting.entries.at(i).value;
+ if(!value.empty()) {
+ if (count++ > 0)
+ output += "," + newline;
+
+ output += quote + replace(value, quote, escape_quote) + quote;
+
+ std::string comment = setting.entries.at(i).comment;
+ if (!comment.empty())
+ output += " /* " + comment + " */";
+ }
+
+ }
+ // Add closing ")" on new line
+ newline.resize(newline.size() - 1);
+ output += (setting.flags & SettingsNoValue) ? "\t\t\t)" : "," + newline + ")";
+ } else {
+ output += var;
+
+ output += (setting.flags & SettingsNoValue) ? "" : " = " + quote;
+
+ for(unsigned int i = 0; i < setting.entries.size(); ++i) {
+ std::string value = setting.entries.at(i).value;
+ if(i)
+ output += " ";
+ output += value;
+
+ std::string comment = setting.entries.at(i).comment;
+ if (!comment.empty())
+ output += " /* " + comment + " */";
+ }
+
+ output += (setting.flags & SettingsNoValue) ? "" : quote;
+ }
+ return output;
+}
+
} // End of CreateProjectTool namespace
diff --git a/devtools/create_project/xcode.h b/devtools/create_project/xcode.h
index a5810dbe0e..f86e7c555c 100644
--- a/devtools/create_project/xcode.h
+++ b/devtools/create_project/xcode.h
@@ -20,7 +20,6 @@
*
*/
-
#ifndef TOOLS_CREATE_PROJECT_XCODE_H
#define TOOLS_CREATE_PROJECT_XCODE_H
@@ -30,25 +29,279 @@
#include <vector>
namespace CreateProjectTool {
-
- class XCodeProvider : public ProjectProvider {
+
+class XCodeProvider : public ProjectProvider {
+public:
+ XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version = 0);
+
+protected:
+
+ void createWorkspace(const BuildSetup &setup);
+
+ void createOtherBuildFiles(const BuildSetup &setup);
+
+ void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
+ const StringList &includeList, const StringList &excludeList);
+
+ void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
+ const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix);
+
+private:
+ enum {
+ SettingsAsList = 0x01,
+ SettingsSingleItem = 0x02,
+ SettingsNoQuote = 0x04,
+ SettingsQuoteVariable = 0x08,
+ SettingsNoValue = 0x10
+ };
+
+ // File properties
+ struct FileProperty {
+ std::string fileEncoding;
+ std::string lastKnownFileType;
+ std::string fileName;
+ std::string filePath;
+ std::string sourceTree;
+
+ FileProperty(std::string fileType = "", std::string name = "", std::string path = "", std::string source = "") :
+ fileEncoding(""), lastKnownFileType(fileType), fileName(name), filePath(path), sourceTree(source)
+ {
+ }
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ // XCObject and children
+ typedef std::vector<std::string> ValueList;
+
+ struct Entry {
+ std::string value;
+ std::string comment;
+
+ Entry(std::string val, std::string cmt) : value(val), comment(cmt) {}
+ };
+
+ typedef std::vector<Entry> EntryList;
+
+ struct Setting {
+ EntryList entries;
+ int flags;
+ int indent;
+ int order;
+
+ explicit Setting(std::string value = "", std::string comment = "", int flgs = 0, int idt = 0, int ord = -1) : flags(flgs), indent(idt), order(ord) {
+ entries.push_back(Entry(value, comment));
+ }
+
+ explicit Setting(ValueList values, int flgs = 0, int idt = 0, int ord = -1) : flags(flgs), indent(idt), order(ord) {
+ for (unsigned int i = 0; i < values.size(); i++)
+ entries.push_back(Entry(values[i], ""));
+ }
+
+ explicit Setting(EntryList ents, int flgs = 0, int idt = 0, int ord = -1) : entries(ents), flags(flgs), indent(idt), order(ord) {}
+
+ void addEntry(std::string value, std::string comment = "") {
+ entries.push_back(Entry(value, comment));
+ }
+ };
+
+ typedef std::map<std::string, Setting> SettingList;
+ typedef std::pair<std::string, Setting> SettingPair;
+ typedef std::vector<SettingPair> OrderedSettingList;
+
+ static bool OrderSortPredicate(const SettingPair& s1, const SettingPair& s2) {
+ return s1.second.order < s2.second.order;
+ }
+
+ struct Property {
public:
- XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version = 0);
-
- protected:
-
- void createWorkspace(const BuildSetup &setup);
-
- void createOtherBuildFiles(const BuildSetup &setup);
-
- void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
- const StringList &includeList, const StringList &excludeList);
-
- void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
- const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix);
+ SettingList settings;
+ int flags;
+ bool hasOrder;
+
+ Property() : flags(0), hasOrder(false) {}
+
+ // Constructs a simple Property
+ explicit Property(std::string name, std::string value = "", std::string comment = "", int flgs = 0, int indent = 0, bool order = false) : flags(flgs), hasOrder(order) {
+ Setting setting(value, comment, flags, indent);
+
+ settings[name] = setting;
+ }
+ Property(std::string name, ValueList values, int flgs = 0, int indent = 0, bool order = false) : flags(flgs), hasOrder(order) {
+ Setting setting(values, flags, indent);
+
+ settings[name] = setting;
+ }
+
+ // Copy constructor
+ Property(const Property &rhs) {
+ settings = rhs.settings;
+ flags = rhs.flags;
+ }
+
+ OrderedSettingList getOrderedSettingList() {
+ OrderedSettingList list;
+
+ // Prepare vector to sort
+ for (SettingList::const_iterator setting = settings.begin(); setting != settings.end(); ++setting)
+ list.push_back(SettingPair(setting->first, setting->second));
+
+ // Sort vector using setting order
+ if (hasOrder)
+ std::sort(list.begin(), list.end(), OrderSortPredicate);
+
+ return list;
+ }
};
-
+
+ typedef std::map<std::string, Property> PropertyList;
+
+ // Main object struct
+ // This is all a big hack unfortunately, but making everything all properly abstracted would
+ // be overkill since we only have to generate a single project
+ struct Object {
+ public:
+ std::string id; // Unique identifier for this object
+ std::string name; // Name (may not be unique - for ex. configuration entries)
+ std::string refType; // Type of object this references (if any)
+ std::string comment; // Main comment (empty for no comment)
+
+ PropertyList properties; // List of object properties, including output configuration
+
+ // Constructs an object and add a default type property
+ Object(XCodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "")
+ : id(objectId), name(objectName), refType(objectRefType), comment(objectComment), parent(objectParent) {
+ assert(objectParent);
+ assert(!objectId.empty());
+ assert(!objectName.empty());
+ assert(!objectType.empty());
+
+ addProperty("isa", objectType, "", SettingsNoQuote|SettingsNoValue);
+ }
+
+ // Add a simple Property with just a name and a value
+ void addProperty(std::string propName, std::string propValue, std::string propComment = "", int propFlags = 0, int propIndent = 0) {
+ properties[propName] = Property(propValue, "", propComment, propFlags, propIndent);
+ }
+
+ std::string toString(int flags = 0) {
+ std::string output;
+ output = "\t\t" + parent->getHash(id) + (comment.empty() ? "" : " /* " + comment + " */") + " = {";
+
+ if (flags & SettingsAsList)
+ output += "\n";
+
+ // Special case: always output the isa property first
+ output += parent->writeProperty("isa", properties["isa"], flags);
+
+ // Write each property
+ for (PropertyList::iterator property = properties.begin(); property != properties.end(); ++property) {
+ if ((*property).first == "isa")
+ continue;
+
+ output += parent->writeProperty((*property).first, (*property).second, flags);
+ }
+
+ if (flags & SettingsAsList)
+ output += "\t\t";
+
+ output += "};\n";
+
+ return output;
+ }
+
+ private:
+ XCodeProvider *parent;
+
+ // Returns the type property (should always be the first in the properties map)
+ std::string getType() {
+ assert(!properties.empty());
+ assert(!properties["isa"].settings.empty());
+
+ SettingList::iterator it = properties["isa"].settings.begin();
+
+ return (*it).first;
+ }
+ };
+
+ struct ObjectList {
+ private:
+ std::map<std::string, bool> objectMap;
+
+ public:
+ std::vector<Object *> objects;
+ std::string comment;
+ int flags;
+
+ void add(Object *obj) {
+ std::map<std::string, bool>::iterator it = objectMap.find(obj->id);
+ if (it != objectMap.end() && it->second == true)
+ return;
+
+ objects.push_back(obj);
+ objectMap[obj->id] = true;
+ }
+
+ std::string toString() {
+ std::string output;
+
+ if (!comment.empty())
+ output = "\n/* Begin " + comment + " section */\n";
+
+ for (std::vector<Object *>::iterator object = objects.begin(); object != objects.end(); ++object)
+ output += (*object)->toString(flags);
+
+ if (!comment.empty())
+ output += "/* End " + comment + " section */\n";
+
+ return output;
+ }
+ };
+
+ // All objects
+ std::map<std::string, std::string> _hashDictionnary;
+ ValueList _defines;
+
+ // Targets
+ ValueList _targets;
+
+ // Lists of objects
+ ObjectList _buildFile;
+ ObjectList _copyFilesBuildPhase;
+ ObjectList _fileReference;
+ ObjectList _frameworksBuildPhase;
+ ObjectList _groups;
+ ObjectList _nativeTarget;
+ ObjectList _project;
+ ObjectList _resourcesBuildPhase;
+ ObjectList _sourcesBuildPhase;
+ ObjectList _buildConfiguration;
+ ObjectList _configurationList;
+
+ void ouputMainProjectFile(const BuildSetup &setup);
+
+ // Setup objects
+ void setupCopyFilesBuildPhase();
+ void setupFrameworksBuildPhase();
+ void setupNativeTarget();
+ void setupProject();
+ void setupResourcesBuildPhase();
+ void setupSourcesBuildPhase();
+ void setupBuildConfiguration();
+
+ // Misc
+ void setupDefines(const BuildSetup &setup); // Setup the list of defines to be used on build configurations
+
+ // Hash generation
+ std::string getHash(std::string key);
+ std::string newHash() const;
+
+ // Output
+ std::string writeProperty(const std::string &variable, Property &property, int flags = 0) const;
+ std::string writeSetting(const std::string &variable, std::string name, std::string comment = "", int flags = 0, int indent = 0) const;
+ std::string writeSetting(const std::string &variable, const Setting &setting) const;
+};
+
} // End of CreateProjectTool namespace
#endif // TOOLS_CREATE_PROJECT_XCODE_H
diff --git a/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj b/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj
index 3a3f6b1d4d..f13bcf6969 100644
--- a/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj
+++ b/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj
@@ -13,17 +13,30 @@
F9A66C6C1396D4DF00CEE494 /* msvc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C671396D4DF00CEE494 /* msvc.cpp */; };
F9A66C6F1396D4E800CEE494 /* visualstudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C6D1396D4E800CEE494 /* visualstudio.cpp */; };
F9A66C871396E2F500CEE494 /* xcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C861396E2F500CEE494 /* xcode.cpp */; };
+ F9A66C91139704A400CEE494 /* create_project in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9A66C271396D36100CEE494 /* create_project */; };
+ F9BA99141398064E00C276C2 /* create_project in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9A66C271396D36100CEE494 /* create_project */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
F9A66C251396D36100CEE494 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 12;
+ dstPath = ../../../../../dists/iphone;
+ dstSubfolderSpec = 16;
+ files = (
+ F9A66C91139704A400CEE494 /* create_project in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9BA99131398063A00C276C2 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
- dstPath = /usr/share/man/man1/;
- dstSubfolderSpec = 0;
+ dstPath = ../../../../../dists/macosx;
+ dstSubfolderSpec = 16;
files = (
+ F9BA99141398064E00C276C2 /* create_project in CopyFiles */,
);
- runOnlyForDeploymentPostprocessing = 1;
+ runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
@@ -111,6 +124,7 @@
F9A66C231396D36100CEE494 /* Sources */,
F9A66C241396D36100CEE494 /* Frameworks */,
F9A66C251396D36100CEE494 /* CopyFiles */,
+ F9BA99131398063A00C276C2 /* CopyFiles */,
);
buildRules = (
);