diff options
-rw-r--r-- | devtools/create_project/xcode.cpp | 515 | ||||
-rw-r--r-- | devtools/create_project/xcode.h | 287 | ||||
-rw-r--r-- | devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj | 20 |
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 = ( ); |