/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "config.h" #include "xcode.h" #include #include #ifdef MACOSX #include #include #include #endif namespace CreateProjectTool { #define LAST_XCODE_VERSION "0830" #define DEBUG_XCODE_HASH 0 #define IOS_TARGET 0 #define OSX_TARGET 1 #define ADD_DEFINE(defines, name) \ defines.push_back(name); #define REMOVE_DEFINE(defines, name) \ { ValueList::iterator i = std::find(defines.begin(), defines.end(), name); if (i != defines.end()) defines.erase(i); } #define CONTAINS_DEFINE(defines, name) \ (std::find(defines.begin(), defines.end(), name) != defines.end()) #define ADD_SETTING(config, key, value) \ config._settings[key] = Setting(value, "", kSettingsNoQuote); #define ADD_SETTING_ORDER(config, key, value, order) \ config._settings[key] = Setting(value, "", kSettingsNoQuote, 0, order); #define ADD_SETTING_ORDER_NOVALUE(config, key, comment, order) \ config._settings[key] = Setting("", comment, kSettingsNoValue, 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, "", kSettingsQuoteVariable); #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, fileRefId, comment) { \ Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); \ buildFile->addProperty("fileRef", fileRefId, name, kSettingsNoValue); \ _buildFile.add(buildFile); \ _buildFile._flags = kSettingsSingleItem; \ } #define ADD_FILE_REFERENCE(id, name, properties) { \ Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); \ if (!properties._fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties._fileEncoding, "", kSettingsNoValue); \ if (!properties._lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties._lastKnownFileType, "", kSettingsNoValue|kSettingsQuoteVariable); \ if (!properties._fileName.empty()) fileRef->addProperty("name", properties._fileName, "", kSettingsNoValue|kSettingsQuoteVariable); \ if (!properties._filePath.empty()) fileRef->addProperty("path", properties._filePath, "", kSettingsNoValue|kSettingsQuoteVariable); \ if (!properties._sourceTree.empty()) fileRef->addProperty("sourceTree", properties._sourceTree, "", kSettingsNoValue); \ _fileReference.add(fileRef); \ _fileReference._flags = kSettingsSingleItem; \ } bool producesObjectFileOnOSX(const std::string &fileName) { std::string n, ext; splitFilename(fileName, n, ext); // Note that the difference between this and the general producesObjectFile is that // this one adds Objective-C(++), and removes asm-support. if (ext == "cpp" || ext == "c" || ext == "m" || ext == "mm") return true; else return false; } bool targetIsIOS(const std::string &targetName) { return targetName.length() > 4 && targetName.substr(targetName.length() - 4) == "-iOS"; } bool shouldSkipFileForTarget(const std::string &fileID, const std::string &targetName, const std::string &fileName) { // Rules: // - if the parent directory is "backends/platform/ios7", the file belongs to the iOS target. // - if the parent directory is "/sdl", the file belongs to the macOS target. // - if the file has a suffix, like "_osx", or "_ios", the file belongs to one of the target. // - if the file is an macOS icon file (icns), it belongs to the macOS target. std::string name, ext; splitFilename(fileName, name, ext); if (targetIsIOS(targetName)) { // iOS target: we skip all files with the "_osx" suffix if (name.length() > 4 && name.substr(name.length() - 4) == "_osx") { return true; } // We don't need SDL for the iOS target static const std::string sdl_directory = "/sdl/"; static const std::string surfacesdl_directory = "/surfacesdl/"; static const std::string doublebufferdl_directory = "/doublebuffersdl/"; if (fileID.find(sdl_directory) != std::string::npos || fileID.find(surfacesdl_directory) != std::string::npos || fileID.find(doublebufferdl_directory) != std::string::npos) { return true; } if (ext == "icns") { return true; } } else { // Ugly hack: explicitly remove the browser.cpp file. // The problem is that we have only one project for two different targets, // and the parsing of the "mk" files added this file for both targets... if (fileID.length() > 12 && fileID.substr(fileID.length() - 12) == "/browser.cpp") { return true; } // macOS target: we skip all files with the "_ios" suffix if (name.length() > 4 && name.substr(name.length() - 4) == "_ios") { return true; } // macOS target: we skip all files with the "ios7_" prefix if (name.length() > 5 && name.substr(0, 5) == "ios7_") { return true; } // parent directory const std::string directory = fileID.substr(0, fileID.length() - fileName.length()); static const std::string iphone_directory = "backends/platform/ios7"; if (directory.length() > iphone_directory.length() && directory.substr(directory.length() - iphone_directory.length()) == iphone_directory) { return true; } } return false; } XcodeProvider::Group::Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path) : Object(objectParent, uniqueName, groupName, "PBXGroup", "", groupName) { bool path_is_absolute = (path.length() > 0 && path.at(0) == '/'); addProperty("name", _name, "", kSettingsNoValue | kSettingsQuoteVariable); addProperty("sourceTree", path_is_absolute ? "" : "", "", kSettingsNoValue | kSettingsQuoteVariable); if (path != "") { addProperty("path", path, "", kSettingsNoValue | kSettingsQuoteVariable); } _childOrder = 0; _treeName = uniqueName; } void XcodeProvider::Group::ensureChildExists(const std::string &name) { std::map::iterator it = _childGroups.find(name); if (it == _childGroups.end()) { Group *child = new Group(_parent, name, this->_treeName + '/' + name, name); _childGroups[name] = child; addChildGroup(child); _parent->_groups.add(child); } } void XcodeProvider::Group::addChildInternal(const std::string &id, const std::string &comment) { if (_properties.find("children") == _properties.end()) { Property children; children._hasOrder = true; children._flags = kSettingsAsList; _properties["children"] = children; } _properties["children"]._settings[id] = Setting("", comment + " in Sources", kSettingsNoValue, 0, _childOrder++); if (_childOrder == 1) { // Force children to use () even when there is only 1 child. // Also this enforces the use of "," after the single item, instead of ; (see writeProperty) _properties["children"]._flags |= kSettingsSingleItem; } else { _properties["children"]._flags ^= kSettingsSingleItem; } } void XcodeProvider::Group::addChildGroup(const Group *group) { addChildInternal(_parent->getHash(group->_treeName), group->_treeName); } void XcodeProvider::Group::addChildFile(const std::string &name) { std::string id = "FileReference_" + _treeName + "/" + name; addChildInternal(_parent->getHash(id), name); FileProperty property = FileProperty(name, name, name, "\"\""); _parent->addFileReference(id, name, property); if (producesObjectFileOnOSX(name)) { _parent->addBuildFile(_treeName + "/" + name, name, _parent->getHash(id), name + " in Sources"); } } void XcodeProvider::Group::addChildByHash(const std::string &hash, const std::string &name) { addChildInternal(hash, name); } XcodeProvider::Group *XcodeProvider::Group::getChildGroup(const std::string &name) { std::map::iterator it = _childGroups.find(name); assert(it != _childGroups.end()); return it->second; } XcodeProvider::Group *XcodeProvider::touchGroupsForPath(const std::string &path) { if (_rootSourceGroup == NULL) { assert(path == _projectRoot); _rootSourceGroup = new Group(this, "Sources", path, path); _groups.add(_rootSourceGroup); return _rootSourceGroup; } else { assert(path.find(_projectRoot) == 0); std::string subPath = path.substr(_projectRoot.size() + 1); Group *currentGroup = _rootSourceGroup; size_t firstPathComponent = subPath.find_first_of('/'); // We assume here that all paths have trailing '/', otherwise this breaks. while (firstPathComponent != std::string::npos) { currentGroup->ensureChildExists(subPath.substr(0, firstPathComponent)); currentGroup = currentGroup->getChildGroup(subPath.substr(0, firstPathComponent)); subPath = subPath.substr(firstPathComponent + 1); firstPathComponent = subPath.find_first_of('/'); } return currentGroup; } } void XcodeProvider::addFileReference(const std::string &id, const std::string &name, FileProperty properties) { Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); if (!properties._fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties._fileEncoding, "", kSettingsNoValue); if (!properties._lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties._lastKnownFileType, "", kSettingsNoValue | kSettingsQuoteVariable); if (!properties._fileName.empty()) fileRef->addProperty("name", properties._fileName, "", kSettingsNoValue | kSettingsQuoteVariable); if (!properties._filePath.empty()) fileRef->addProperty("path", properties._filePath, "", kSettingsNoValue | kSettingsQuoteVariable); if (!properties._sourceTree.empty()) fileRef->addProperty("sourceTree", properties._sourceTree, "", kSettingsNoValue); _fileReference.add(fileRef); _fileReference._flags = kSettingsSingleItem; } void XcodeProvider::addProductFileReference(const std::string &id, const std::string &name) { Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); fileRef->addProperty("explicitFileType", "wrapper.application", "", kSettingsNoValue | kSettingsQuoteVariable); fileRef->addProperty("includeInIndex", "0", "", kSettingsNoValue); fileRef->addProperty("path", name, "", kSettingsNoValue | kSettingsQuoteVariable); fileRef->addProperty("sourceTree", "BUILT_PRODUCTS_DIR", "", kSettingsNoValue); _fileReference.add(fileRef); _fileReference._flags = kSettingsSingleItem; } void XcodeProvider::addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment) { Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); buildFile->addProperty("fileRef", fileRefId, name, kSettingsNoValue); _buildFile.add(buildFile); _buildFile._flags = kSettingsSingleItem; } XcodeProvider::XcodeProvider(StringList &global_warnings, std::map &project_warnings, const int version) : ProjectProvider(global_warnings, project_warnings, version) { _rootSourceGroup = NULL; } void XcodeProvider::addResourceFiles(const BuildSetup &setup, StringList &includeList, StringList &excludeList) { includeList.push_back(setup.srcDir + "/dists/ios7/Info.plist"); ValueList &resources = getResourceFiles(); for (ValueList::iterator it = resources.begin(); it != resources.end(); ++it) { includeList.push_back(setup.srcDir + "/" + *it); } StringList td; createModuleList(setup.srcDir + "/backends/platform/ios7", setup.defines, td, includeList, excludeList); } void XcodeProvider::createWorkspace(const BuildSetup &setup) { // Create project folder std::string workspace = setup.outputDir + '/' + PROJECT_NAME ".xcodeproj"; createDirectory(workspace); _projectRoot = setup.srcDir; touchGroupsForPath(_projectRoot); // Setup global objects setupDefines(setup); _targets.push_back(PROJECT_DESCRIPTION "-iOS"); _targets.push_back(PROJECT_DESCRIPTION "-macOS"); setupCopyFilesBuildPhase(); setupFrameworksBuildPhase(setup); setupNativeTarget(); setupProject(); setupResourcesBuildPhase(); setupBuildConfiguration(setup); setupImageAssetCatalog(setup); } // 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) { // This needs to be done at the end when all build files have been accounted for setupSourcesBuildPhase(); outputMainProjectFile(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) { 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::outputMainProjectFile(const BuildSetup &setup) { std::ofstream project((setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj").c_str()); if (!project) error("Could not open \"" + setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj\" for writing"); ////////////////////////////////////////////////////////////////////////// // Header project << "// !$*UTF8*$!\n" "{\n" "\t" << writeSetting("archiveVersion", "1", "", kSettingsNoQuote) << ";\n" "\tclasses = {\n" "\t};\n" "\t" << writeSetting("objectVersion", "46", "", kSettingsNoQuote) << ";\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", kSettingsNoQuote) << ";\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) { // Ensure that top-level groups are generated for i.e. engines/ Group *group = touchGroupsForPath(filePrefix); for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) { const FileNode *node = *i; // Iff it is a file, then add (build) file references. Since we're using Groups and not File References // for folders, we shouldn't add folders as file references, obviously. if (node->children.empty()) { group->addChildFile(node->name); // HACK: Also add browser_osx.mm, since browser.cpp is added for // iOS and browser_osx.mm for macOS, and create_project cannot // deal with two competing exclusive ifdefs in module.mk going // into one project if (filePrefix.find("/gui/") == filePrefix.size() - 5 && node->name == "browser.cpp") { group->addChildFile("browser_osx.mm"); } } // Process child nodes if (!node->children.empty()) writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/'); } } ////////////////////////////////////////////////////////////////////////// // Setup functions ////////////////////////////////////////////////////////////////////////// void XcodeProvider::setupCopyFilesBuildPhase() { // Nothing to do here } #define DEF_SYSFRAMEWORK(framework) properties[framework".framework"] = FileProperty("wrapper.framework", framework".framework", "System/Library/Frameworks/" framework ".framework", "SDKROOT"); \ ADD_SETTING_ORDER_NOVALUE(children, getHash(framework".framework"), framework".framework", fwOrder++); #define DEF_SYSTBD(lib) properties[lib".tbd"] = FileProperty("sourcecode.text-based-dylib-definition", lib".tbd", "usr/lib/" lib ".tbd", "SDKROOT"); \ ADD_SETTING_ORDER_NOVALUE(children, getHash(lib".tbd"), lib".tbd", fwOrder++); #define DEF_LOCALLIB_STATIC_PATH(path,lib,absolute) properties[lib".a"] = FileProperty("archive.ar", lib ".a", path, (absolute ? "\"\"" : "\"\"")); \ ADD_SETTING_ORDER_NOVALUE(children, getHash(lib".a"), lib".a", fwOrder++); #define DEF_LOCALLIB_STATIC(lib) DEF_LOCALLIB_STATIC_PATH("/usr/local/lib/" lib ".a", lib, true) /** * Sets up the frameworks build phase. * * (each native target has different build rules) */ void XcodeProvider::setupFrameworksBuildPhase(const BuildSetup &setup) { _frameworksBuildPhase._comment = "PBXFrameworksBuildPhase"; // Just use a hardcoded id for the Frameworks-group Group *frameworksGroup = new Group(this, "Frameworks", "PBXGroup_CustomTemplate_Frameworks_", ""); Property children; children._hasOrder = true; children._flags = kSettingsAsList; // Setup framework file properties std::map properties; int fwOrder = 0; // Frameworks DEF_SYSFRAMEWORK("ApplicationServices"); DEF_SYSFRAMEWORK("AudioToolbox"); DEF_SYSFRAMEWORK("AudioUnit"); DEF_SYSFRAMEWORK("Carbon"); DEF_SYSFRAMEWORK("Cocoa"); DEF_SYSFRAMEWORK("CoreAudio"); DEF_SYSFRAMEWORK("CoreMIDI"); DEF_SYSFRAMEWORK("CoreGraphics"); DEF_SYSFRAMEWORK("CoreFoundation"); DEF_SYSFRAMEWORK("CoreMIDI"); DEF_SYSFRAMEWORK("Foundation"); DEF_SYSFRAMEWORK("IOKit"); DEF_SYSFRAMEWORK("OpenGLES"); DEF_SYSFRAMEWORK("QuartzCore"); DEF_SYSFRAMEWORK("UIKit"); DEF_SYSTBD("libiconv"); // Local libraries if (CONTAINS_DEFINE(setup.defines, "USE_FLAC")) { DEF_LOCALLIB_STATIC("libFLAC"); } if (CONTAINS_DEFINE(setup.defines, "USE_FLUIDSYNTH")) { DEF_LOCALLIB_STATIC("libfluidsynth"); DEF_LOCALLIB_STATIC("libglib-2.0"); DEF_SYSTBD("libffi"); } if (CONTAINS_DEFINE(setup.defines, "USE_FREETYPE2")) { DEF_LOCALLIB_STATIC("libfreetype"); } if (CONTAINS_DEFINE(setup.defines, "USE_JPEG")) { DEF_LOCALLIB_STATIC("libjpeg"); } if (CONTAINS_DEFINE(setup.defines, "USE_LIBCURL")) { DEF_LOCALLIB_STATIC("libcurl"); } if (CONTAINS_DEFINE(setup.defines, "USE_MAD")) { DEF_LOCALLIB_STATIC("libmad"); } if (CONTAINS_DEFINE(setup.defines, "USE_PNG")) { DEF_LOCALLIB_STATIC("libpng"); } if (CONTAINS_DEFINE(setup.defines, "USE_VORBIS")) { DEF_LOCALLIB_STATIC("libogg"); DEF_LOCALLIB_STATIC("libvorbis"); DEF_LOCALLIB_STATIC("libvorbisfile"); } if (CONTAINS_DEFINE(setup.defines, "USE_ZLIB")) { DEF_SYSTBD("libz"); } if (setup.useSDL2) { DEF_LOCALLIB_STATIC("libSDL2main"); DEF_LOCALLIB_STATIC("libSDL2"); } else { DEF_LOCALLIB_STATIC("libSDLmain"); DEF_LOCALLIB_STATIC("libSDL"); } std::string absoluteOutputDir; #ifdef POSIX char *c_path = realpath(setup.outputDir.c_str(), NULL); absoluteOutputDir = c_path; absoluteOutputDir += "/lib"; free(c_path); #else absoluteOutputDir = "lib"; #endif frameworksGroup->_properties["children"] = children; _groups.add(frameworksGroup); // Force this to be added as a sub-group in the root. _rootSourceGroup->addChildGroup(frameworksGroup); // Declare this here, as it's used across all the targets int order = 0; ////////////////////////////////////////////////////////////////////////// // ScummVM-iOS Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[IOS_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_iPhone->addProperty("buildActionMask", "2147483647", "", kSettingsNoValue); framework_iPhone->addProperty("runOnlyForDeploymentPostprocessing", "0", "", kSettingsNoValue); // List of frameworks Property iOS_files; iOS_files._hasOrder = true; iOS_files._flags = kSettingsAsList; ValueList frameworks_iOS; frameworks_iOS.push_back("CoreAudio.framework"); frameworks_iOS.push_back("CoreGraphics.framework"); frameworks_iOS.push_back("CoreFoundation.framework"); frameworks_iOS.push_back("Foundation.framework"); frameworks_iOS.push_back("UIKit.framework"); frameworks_iOS.push_back("AudioToolbox.framework"); frameworks_iOS.push_back("QuartzCore.framework"); frameworks_iOS.push_back("OpenGLES.framework"); if (CONTAINS_DEFINE(setup.defines, "USE_FLAC")) { frameworks_iOS.push_back("libFLAC.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_FREETYPE2")) { frameworks_iOS.push_back("libfreetype.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_PNG")) { frameworks_iOS.push_back("libpng.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_VORBIS")) { frameworks_iOS.push_back("libogg.a"); frameworks_iOS.push_back("libvorbis.a"); frameworks_iOS.push_back("libvorbisfile.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_MAD")) { frameworks_iOS.push_back("libmad.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_FLUIDSYNTH")) { frameworks_iOS.push_back("libfluidsynth.a"); frameworks_iOS.push_back("libglib-2.0.a"); frameworks_iOS.push_back("libffi.tbd"); frameworks_iOS.push_back("CoreMIDI.framework"); frameworks_iOS.push_back("libiconv.tbd"); } if (CONTAINS_DEFINE(setup.defines, "USE_ZLIB")) { frameworks_iOS.push_back("libz.tbd"); } for (ValueList::iterator framework = frameworks_iOS.begin(); framework != frameworks_iOS.end(); framework++) { std::string id = "Frameworks_" + *framework + "_iphone"; std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(iOS_files, getHash(id), comment, order++); ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_iPhone->_properties["files"] = iOS_files; _frameworksBuildPhase.add(framework_iPhone); ////////////////////////////////////////////////////////////////////////// // ScummVM-macOS Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[OSX_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_OSX->addProperty("buildActionMask", "2147483647", "", kSettingsNoValue); framework_OSX->addProperty("runOnlyForDeploymentPostprocessing", "0", "", kSettingsNoValue); // List of frameworks Property osx_files; osx_files._hasOrder = true; osx_files._flags = kSettingsAsList; ValueList frameworks_osx; frameworks_osx.push_back("CoreFoundation.framework"); frameworks_osx.push_back("Foundation.framework"); frameworks_osx.push_back("AudioToolbox.framework"); frameworks_osx.push_back("CoreMIDI.framework"); frameworks_osx.push_back("CoreAudio.framework"); frameworks_osx.push_back("QuartzCore.framework"); frameworks_osx.push_back("Carbon.framework"); frameworks_osx.push_back("ApplicationServices.framework"); frameworks_osx.push_back("IOKit.framework"); frameworks_osx.push_back("Cocoa.framework"); frameworks_osx.push_back("AudioUnit.framework"); if (CONTAINS_DEFINE(setup.defines, "USE_FLAC")) { frameworks_osx.push_back("libFLAC.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_FLUIDSYNTH")) { frameworks_osx.push_back("libfluidsynth.a"); frameworks_osx.push_back("libglib-2.0.a"); frameworks_osx.push_back("libffi.tbd"); } if (CONTAINS_DEFINE(setup.defines, "USE_FREETYPE2")) { frameworks_osx.push_back("libfreetype.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_JPEG")) { frameworks_osx.push_back("libjpeg.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_LIBCURL")) { frameworks_osx.push_back("libcurl.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_MAD")) { frameworks_osx.push_back("libmad.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_PNG")) { frameworks_osx.push_back("libpng.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_VORBIS")) { frameworks_osx.push_back("libogg.a"); frameworks_osx.push_back("libvorbis.a"); frameworks_osx.push_back("libvorbisfile.a"); } if (CONTAINS_DEFINE(setup.defines, "USE_ZLIB")) { frameworks_osx.push_back("libz.tbd"); } if (setup.useSDL2) { frameworks_osx.push_back("libSDL2main.a"); frameworks_osx.push_back("libSDL2.a"); } else { frameworks_osx.push_back("libSDLmain.a"); frameworks_osx.push_back("libSDL.a"); } order = 0; for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) { std::string id = "Frameworks_" + *framework + "_osx"; std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(osx_files, getHash(id), comment, order++); ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_OSX->_properties["files"] = osx_files; _frameworksBuildPhase.add(framework_OSX); } void XcodeProvider::setupNativeTarget() { _nativeTarget._comment = "PBXNativeTarget"; // Just use a hardcoded id for the Products-group Group *productsGroup = new Group(this, "Products", "PBXGroup_CustomTemplate_Products_" , ""); // Output native target section for (unsigned int i = 0; i < _targets.size(); i++) { Object *target = new Object(this, "PBXNativeTarget_" + _targets[i], "PBXNativeTarget", "PBXNativeTarget", "", _targets[i]); target->addProperty("buildConfigurationList", getHash("XCConfigurationList_" + _targets[i]), "Build configuration list for PBXNativeTarget \"" + _targets[i] + "\"", kSettingsNoValue); Property buildPhases; buildPhases._hasOrder = true; buildPhases._flags = kSettingsAsList; buildPhases._settings[getHash("PBXResourcesBuildPhase_" + _targets[i])] = Setting("", "Resources", kSettingsNoValue, 0, 0); buildPhases._settings[getHash("PBXSourcesBuildPhase_" + _targets[i])] = Setting("", "Sources", kSettingsNoValue, 0, 1); buildPhases._settings[getHash("PBXFrameworksBuildPhase_" + _targets[i])] = Setting("", "Frameworks", kSettingsNoValue, 0, 2); target->_properties["buildPhases"] = buildPhases; target->addProperty("buildRules", "", "", kSettingsNoValue | kSettingsAsList); target->addProperty("dependencies", "", "", kSettingsNoValue | kSettingsAsList); target->addProperty("name", _targets[i], "", kSettingsNoValue | kSettingsQuoteVariable); target->addProperty("productName", PROJECT_NAME, "", kSettingsNoValue); addProductFileReference("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i], PROJECT_DESCRIPTION ".app"); productsGroup->addChildByHash(getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app"); target->addProperty("productReference", getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app", kSettingsNoValue); target->addProperty("productType", "com.apple.product-type.application", "", kSettingsNoValue | kSettingsQuoteVariable); _nativeTarget.add(target); } _rootSourceGroup->addChildGroup(productsGroup); _groups.add(productsGroup); } 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 \"" PROJECT_NAME "\"", kSettingsNoValue); project->addProperty("compatibilityVersion", "Xcode 3.2", "", kSettingsNoValue | kSettingsQuoteVariable); project->addProperty("developmentRegion", "English", "", kSettingsNoValue); project->addProperty("hasScannedForEncodings", "1", "", kSettingsNoValue); project->addProperty("attributes", "{ LastUpgradeCheck = " LAST_XCODE_VERSION "; }", "", kSettingsNoQuote | kSettingsNoValue); // List of known regions Property regions; regions._flags = kSettingsAsList; 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", _rootSourceGroup->getHashRef(), "CustomTemplate", kSettingsNoValue); project->addProperty("productRefGroup", getHash("PBXGroup_CustomTemplate_Products_"), "" , kSettingsNoValue); project->addProperty("projectDirPath", _projectRoot, "", kSettingsNoValue | kSettingsQuoteVariable); project->addProperty("projectRoot", "", "", kSettingsNoValue | kSettingsQuoteVariable); // List of targets Property targets; targets._flags = kSettingsAsList; targets._settings[getHash("PBXNativeTarget_" + _targets[IOS_TARGET])] = Setting("", _targets[IOS_TARGET], kSettingsNoValue, 0, 0); targets._settings[getHash("PBXNativeTarget_" + _targets[OSX_TARGET])] = Setting("", _targets[OSX_TARGET], kSettingsNoValue, 0, 1); project->_properties["targets"] = targets; // Force list even when there is only a single target project->_properties["targets"]._flags |= kSettingsSingleItem; _project.add(project); } XcodeProvider::ValueList& XcodeProvider::getResourceFiles() const { static ValueList files; if (files.empty()) { files.push_back("gui/themes/scummclassic.zip"); files.push_back("gui/themes/scummmodern.zip"); files.push_back("gui/themes/translations.dat"); files.push_back("dists/engine-data/access.dat"); files.push_back("dists/engine-data/cryo.dat"); files.push_back("dists/engine-data/drascula.dat"); files.push_back("dists/engine-data/hugo.dat"); files.push_back("dists/engine-data/kyra.dat"); files.push_back("dists/engine-data/lure.dat"); files.push_back("dists/engine-data/mort.dat"); files.push_back("dists/engine-data/neverhood.dat"); files.push_back("dists/engine-data/queen.tbl"); files.push_back("dists/engine-data/sky.cpt"); files.push_back("dists/engine-data/teenagent.dat"); files.push_back("dists/engine-data/titanic.dat"); files.push_back("dists/engine-data/tony.dat"); files.push_back("dists/engine-data/toon.dat"); files.push_back("dists/engine-data/wintermute.zip"); files.push_back("dists/engine-data/macventure.dat"); files.push_back("dists/pred.dic"); files.push_back("icons/scummvm.icns"); } return files; } void XcodeProvider::setupResourcesBuildPhase() { _resourcesBuildPhase._comment = "PBXResourcesBuildPhase"; ValueList &files_list = getResourceFiles(); // Same as for containers: a rule for each native target for (unsigned int i = 0; i < _targets.size(); i++) { Object *resource = new Object(this, "PBXResourcesBuildPhase_" + _targets[i], "PBXResourcesBuildPhase", "PBXResourcesBuildPhase", "", "Resources"); resource->addProperty("buildActionMask", "2147483647", "", kSettingsNoValue); // Add default files Property files; files._hasOrder = true; files._flags = kSettingsAsList; int order = 0; for (ValueList::iterator file = files_list.begin(); file != files_list.end(); file++) { if (shouldSkipFileForTarget(*file, _targets[i], *file)) { continue; } std::string resourceAbsolutePath = _projectRoot + "/" + *file; std::string file_id = "FileReference_" + resourceAbsolutePath; std::string base = basename(*file); std::string comment = base + " in Resources"; addBuildFile(resourceAbsolutePath, base, getHash(file_id), comment); ADD_SETTING_ORDER_NOVALUE(files, getHash(resourceAbsolutePath), comment, order++); } resource->_properties["files"] = files; resource->addProperty("runOnlyForDeploymentPostprocessing", "0", "", kSettingsNoValue); _resourcesBuildPhase.add(resource); } } void XcodeProvider::setupSourcesBuildPhase() { _sourcesBuildPhase._comment = "PBXSourcesBuildPhase"; // Same as for containers: a rule for each native target for (unsigned int i = 0; i < _targets.size(); i++) { const std::string &targetName = _targets[i]; Object *source = new Object(this, "PBXSourcesBuildPhase_" + _targets[i], "PBXSourcesBuildPhase", "PBXSourcesBuildPhase", "", "Sources"); source->addProperty("buildActionMask", "2147483647", "", kSettingsNoValue); Property files; files._hasOrder = true; files._flags = kSettingsAsList; int order = 0; for (std::vector::iterator file = _buildFile._objects.begin(); file != _buildFile._objects.end(); ++file) { const std::string &fileName = (*file)->_name; if (shouldSkipFileForTarget((*file)->_id, targetName, fileName)) { continue; } if (!producesObjectFileOnOSX(fileName)) { continue; } std::string comment = fileName + " in Sources"; ADD_SETTING_ORDER_NOVALUE(files, getHash((*file)->_id), comment, order++); } setupAdditionalSources(targetName, files, order); source->_properties["files"] = files; source->addProperty("runOnlyForDeploymentPostprocessing", "0", "", kSettingsNoValue); _sourcesBuildPhase.add(source); } } // Setup all build configurations void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) { _buildConfiguration._comment = "XCBuildConfiguration"; _buildConfiguration._flags = kSettingsAsList; std::string projectOutputDirectory; #ifdef POSIX char *rp = realpath(setup.outputDir.c_str(), NULL); projectOutputDirectory = rp; free(rp); #endif /**************************************** * ScummVM - Project Level ****************************************/ // Debug Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Debug", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Debug"); Property scummvm_Debug; ADD_SETTING(scummvm_Debug, "ALWAYS_SEARCH_USER_PATHS", "NO"); ADD_SETTING_QUOTE(scummvm_Debug, "USER_HEADER_SEARCH_PATHS", "$(SRCROOT) $(SRCROOT)/engines"); ADD_SETTING(scummvm_Debug, "CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_BOOL_CONVERSION", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_CONSTANT_CONVERSION", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_EMPTY_BODY", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_ENUM_CONVERSION", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_INFINITE_RECURSION", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_INT_CONVERSION", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_SUSPICIOUS_MOVE", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN_UNREACHABLE_CODE", "YES"); ADD_SETTING(scummvm_Debug, "CLANG_WARN__DUPLICATE_METHOD_MATCH", "YES"); ADD_SETTING_QUOTE(scummvm_Debug, "CODE_SIGN_IDENTITY", ""); ADD_SETTING_QUOTE_VAR(scummvm_Debug, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", ""); ADD_SETTING(scummvm_Debug, "ENABLE_STRICT_OBJC_MSGSEND", "YES"); ADD_SETTING_QUOTE(scummvm_Debug, "FRAMEWORK_SEARCH_PATHS", ""); ADD_SETTING(scummvm_Debug, "GCC_C_LANGUAGE_STANDARD", "c99"); ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "YES"); ADD_SETTING(scummvm_Debug, "GCC_INPUT_FILETYPE", "automatic"); ADD_SETTING(scummvm_Debug, "GCC_NO_COMMON_BLOCKS", "YES"); ADD_SETTING(scummvm_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); ADD_SETTING(scummvm_Debug, "GCC_WARN_SIGN_COMPARE", "YES"); ADD_SETTING(scummvm_Debug, "GCC_WARN_UNDECLARED_SELECTOR", "YES"); ADD_SETTING(scummvm_Debug, "GCC_WARN_UNINITIALIZED_AUTOS", "YES"); ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_FUNCTION", "YES"); ValueList scummvm_WarningCFlags; scummvm_WarningCFlags.push_back("-Wno-multichar"); scummvm_WarningCFlags.push_back("-Wno-undefined-var-template"); ADD_SETTING_LIST(scummvm_Debug, "WARNING_CFLAGS", scummvm_WarningCFlags, kSettingsQuoteVariable | kSettingsAsList, 5); ValueList scummvm_defines(_defines); REMOVE_DEFINE(scummvm_defines, "MACOSX"); REMOVE_DEFINE(scummvm_defines, "IPHONE"); REMOVE_DEFINE(scummvm_defines, "IPHONE_IOS7"); REMOVE_DEFINE(scummvm_defines, "IPHONE_SANDBOXED"); REMOVE_DEFINE(scummvm_defines, "SDL_BACKEND"); ADD_SETTING_LIST(scummvm_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvm_defines, kSettingsNoQuote | kSettingsAsList, 5); ADD_SETTING(scummvm_Debug, "GCC_WARN_ABOUT_RETURN_TYPE", "YES"); ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_VARIABLE", "YES"); ValueList scummvm_HeaderPaths; scummvm_HeaderPaths.push_back("include/"); scummvm_HeaderPaths.push_back("$(SRCROOT)/engines/"); scummvm_HeaderPaths.push_back("$(SRCROOT)"); ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, kSettingsQuoteVariable | kSettingsAsList, 5); ADD_SETTING_QUOTE(scummvm_Debug, "LIBRARY_SEARCH_PATHS", ""); ADD_SETTING(scummvm_Debug, "ONLY_ACTIVE_ARCH", "YES"); ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_CFLAGS", ""); ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_LDFLAGS", ""); ADD_SETTING(scummvm_Debug, "ENABLE_TESTABILITY", "YES"); scummvm_Debug_Object->addProperty("name", "Debug", "", kSettingsNoValue); scummvm_Debug_Object->_properties["buildSettings"] = scummvm_Debug; // Release Object *scummvm_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Release", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Release"); Property scummvm_Release(scummvm_Debug); REMOVE_SETTING(scummvm_Release, "GCC_C_LANGUAGE_STANDARD"); // Not sure why we remove that, or any of the other warnings REMOVE_SETTING(scummvm_Release, "GCC_WARN_ABOUT_RETURN_TYPE"); REMOVE_SETTING(scummvm_Release, "GCC_WARN_UNUSED_VARIABLE"); REMOVE_SETTING(scummvm_Release, "ONLY_ACTIVE_ARCH"); REMOVE_SETTING(scummvm_Release, "ENABLE_TESTABILITY"); scummvm_Release_Object->addProperty("name", "Release", "", kSettingsNoValue); scummvm_Release_Object->_properties["buildSettings"] = scummvm_Release; _buildConfiguration.add(scummvm_Debug_Object); _buildConfiguration.add(scummvm_Release_Object); ///**************************************** // * ScummVM - iOS Target // ****************************************/ // Debug Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property iPhone_Debug; ADD_SETTING_QUOTE(iPhone_Debug, "CODE_SIGN_IDENTITY", "iPhone Developer"); ADD_SETTING_QUOTE_VAR(iPhone_Debug, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "iPhone Developer"); ADD_SETTING(iPhone_Debug, "COPY_PHASE_STRIP", "NO"); ADD_SETTING_QUOTE(iPhone_Debug, "DEBUG_INFORMATION_FORMAT", "dwarf"); ValueList iPhone_FrameworkSearchPaths; iPhone_FrameworkSearchPaths.push_back("$(inherited)"); iPhone_FrameworkSearchPaths.push_back("\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\""); ADD_SETTING_LIST(iPhone_Debug, "FRAMEWORK_SEARCH_PATHS", iPhone_FrameworkSearchPaths, kSettingsAsList, 5); ADD_SETTING(iPhone_Debug, "GCC_DYNAMIC_NO_PIC", "NO"); ADD_SETTING(iPhone_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); ADD_SETTING(iPhone_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); ADD_SETTING(iPhone_Debug, "GCC_PRECOMPILE_PREFIX_HEADER", "NO"); ADD_SETTING(iPhone_Debug, "GCC_WARN_64_TO_32_BIT_CONVERSION", "NO"); ADD_SETTING_QUOTE(iPhone_Debug, "GCC_PREFIX_HEADER", ""); ADD_SETTING(iPhone_Debug, "GCC_UNROLL_LOOPS", "YES"); ValueList iPhone_HeaderSearchPaths; iPhone_HeaderSearchPaths.push_back("$(SRCROOT)/engines/"); iPhone_HeaderSearchPaths.push_back("$(SRCROOT)"); iPhone_HeaderSearchPaths.push_back("\"" + projectOutputDirectory + "\""); iPhone_HeaderSearchPaths.push_back("\"" + projectOutputDirectory + "/include\""); ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, kSettingsAsList | kSettingsQuoteVariable, 5); ADD_SETTING_QUOTE(iPhone_Debug, "INFOPLIST_FILE", "$(SRCROOT)/dists/ios7/Info.plist"); ValueList iPhone_LibPaths; iPhone_LibPaths.push_back("$(inherited)"); iPhone_LibPaths.push_back("\"" + projectOutputDirectory + "/lib\""); ADD_SETTING_LIST(iPhone_Debug, "LIBRARY_SEARCH_PATHS", iPhone_LibPaths, kSettingsAsList, 5); ADD_SETTING(iPhone_Debug, "ONLY_ACTIVE_ARCH", "YES"); ADD_SETTING(iPhone_Debug, "PRODUCT_NAME", PROJECT_NAME); ADD_SETTING(iPhone_Debug, "PRODUCT_BUNDLE_IDENTIFIER", "\"org.scummvm.${PRODUCT_NAME}\""); ADD_SETTING(iPhone_Debug, "IPHONEOS_DEPLOYMENT_TARGET", "8.0"); ADD_SETTING_QUOTE_VAR(iPhone_Debug, "PROVISIONING_PROFILE[sdk=iphoneos*]", ""); ADD_SETTING(iPhone_Debug, "SDKROOT", "iphoneos"); ADD_SETTING_QUOTE(iPhone_Debug, "TARGETED_DEVICE_FAMILY", "1,2"); ValueList scummvmIOS_defines; ADD_DEFINE(scummvmIOS_defines, "\"$(inherited)\""); ADD_DEFINE(scummvmIOS_defines, "IPHONE"); ADD_DEFINE(scummvmIOS_defines, "IPHONE_IOS7"); ADD_DEFINE(scummvmIOS_defines, "IPHONE_SANDBOXED"); ADD_SETTING_LIST(iPhone_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvmIOS_defines, kSettingsNoQuote | kSettingsAsList, 5); ADD_SETTING(iPhone_Debug, "ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon"); ADD_SETTING(iPhone_Debug, "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME", "LaunchImage"); iPhone_Debug_Object->addProperty("name", "Debug", "", kSettingsNoValue); iPhone_Debug_Object->_properties["buildSettings"] = iPhone_Debug; // Release Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property iPhone_Release(iPhone_Debug); ADD_SETTING(iPhone_Release, "GCC_OPTIMIZATION_LEVEL", "3"); ADD_SETTING(iPhone_Release, "COPY_PHASE_STRIP", "YES"); REMOVE_SETTING(iPhone_Release, "GCC_DYNAMIC_NO_PIC"); ADD_SETTING(iPhone_Release, "WRAPPER_EXTENSION", "app"); REMOVE_SETTING(iPhone_Release, "DEBUG_INFORMATION_FORMAT"); ADD_SETTING_QUOTE(iPhone_Release, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym"); iPhone_Release_Object->addProperty("name", "Release", "", kSettingsNoValue); iPhone_Release_Object->_properties["buildSettings"] = iPhone_Release; _buildConfiguration.add(iPhone_Debug_Object); _buildConfiguration.add(iPhone_Release_Object); /**************************************** * ScummVM - macOS Target ****************************************/ // Debug Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[OSX_TARGET] /* ScummVM-macOS */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property scummvmOSX_Debug; ADD_SETTING(scummvmOSX_Debug, "COMBINE_HIDPI_IMAGES", "YES"); ADD_SETTING(scummvmOSX_Debug, "SDKROOT", "macosx"); ADD_SETTING(scummvmOSX_Debug, "COPY_PHASE_STRIP", "NO"); ADD_SETTING_QUOTE(scummvmOSX_Debug, "DEBUG_INFORMATION_FORMAT", "dwarf"); ADD_SETTING_QUOTE(scummvmOSX_Debug, "FRAMEWORK_SEARCH_PATHS", ""); ADD_SETTING(scummvmOSX_Debug, "GCC_C_LANGUAGE_STANDARD", "c99"); ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "YES"); ADD_SETTING(scummvmOSX_Debug, "GCC_DYNAMIC_NO_PIC", "NO"); ADD_SETTING(scummvmOSX_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); ADD_SETTING(scummvmOSX_Debug, "GCC_PRECOMPILE_PREFIX_HEADER", "NO"); ADD_SETTING_QUOTE(scummvmOSX_Debug, "GCC_PREFIX_HEADER", ""); ValueList scummvmOSX_defines; ADD_DEFINE(scummvmOSX_defines, "\"$(inherited)\""); ADD_DEFINE(scummvmOSX_defines, "SDL_BACKEND"); ADD_DEFINE(scummvmOSX_defines, "MACOSX"); ADD_SETTING_LIST(scummvmOSX_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvmOSX_defines, kSettingsNoQuote | kSettingsAsList, 5); ADD_SETTING_QUOTE(scummvmOSX_Debug, "GCC_VERSION", ""); ValueList scummvmOSX_HeaderPaths; if (setup.useSDL2) { scummvmOSX_HeaderPaths.push_back("/usr/local/include/SDL2"); } else { scummvmOSX_HeaderPaths.push_back("/usr/local/include/SDL"); } scummvmOSX_HeaderPaths.push_back("/usr/local/include"); scummvmOSX_HeaderPaths.push_back("/usr/local/include/freetype2"); scummvmOSX_HeaderPaths.push_back("include/"); scummvmOSX_HeaderPaths.push_back("$(SRCROOT)/engines/"); scummvmOSX_HeaderPaths.push_back("$(SRCROOT)"); ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, kSettingsQuoteVariable | kSettingsAsList, 5); ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/dists/macosx/Info.plist"); ValueList scummvmOSX_LibPaths; scummvmOSX_LibPaths.push_back("/usr/local/lib"); scummvmOSX_LibPaths.push_back("\"$(inherited)\""); scummvmOSX_LibPaths.push_back("\"\\\"$(SRCROOT)/lib\\\"\""); ADD_SETTING_LIST(scummvmOSX_Debug, "LIBRARY_SEARCH_PATHS", scummvmOSX_LibPaths, kSettingsNoQuote | kSettingsAsList, 5); ADD_SETTING_QUOTE(scummvmOSX_Debug, "OTHER_CFLAGS", ""); ADD_SETTING(scummvmOSX_Debug, "PRODUCT_NAME", PROJECT_NAME); scummvmOSX_Debug_Object->addProperty("name", "Debug", "", kSettingsNoValue); scummvmOSX_Debug_Object->_properties["buildSettings"] = scummvmOSX_Debug; // Release Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[OSX_TARGET] /* ScummVM-macOS */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property scummvmOSX_Release(scummvmOSX_Debug); ADD_SETTING(scummvmOSX_Release, "COPY_PHASE_STRIP", "YES"); REMOVE_SETTING(scummvmOSX_Release, "GCC_DYNAMIC_NO_PIC"); REMOVE_SETTING(scummvmOSX_Release, "GCC_OPTIMIZATION_LEVEL"); ADD_SETTING(scummvmOSX_Release, "WRAPPER_EXTENSION", "app"); REMOVE_SETTING(scummvmOSX_Release, "DEBUG_INFORMATION_FORMAT"); ADD_SETTING_QUOTE(scummvmOSX_Release, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym"); scummvmOSX_Release_Object->addProperty("name", "Release", "", kSettingsNoValue); scummvmOSX_Release_Object->_properties["buildSettings"] = scummvmOSX_Release; _buildConfiguration.add(scummvmOSX_Debug_Object); _buildConfiguration.add(scummvmOSX_Release_Object); // Warning: This assumes we have all configurations with a Debug & Release pair for (std::vector::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 = kSettingsAsList; buildConfigs._settings[getHash((*config)->_id)] = Setting("", "Debug", kSettingsNoValue, 0, 0); buildConfigs._settings[getHash((*(++config))->_id)] = Setting("", "Release", kSettingsNoValue, 0, 1); configList->_properties["buildConfigurations"] = buildConfigs; configList->addProperty("defaultConfigurationIsVisible", "0", "", kSettingsNoValue); configList->addProperty("defaultConfigurationName", "Release", "", kSettingsNoValue); _configurationList.add(configList); } } void XcodeProvider::setupImageAssetCatalog(const BuildSetup &setup) { const std::string filename = "Images.xcassets"; const std::string absoluteCatalogPath = _projectRoot + "/dists/ios7/" + filename; const std::string id = "FileReference_" + absoluteCatalogPath; Group *group = touchGroupsForPath(absoluteCatalogPath); group->addChildFile(filename); addBuildFile(absoluteCatalogPath, filename, getHash(id), "Image Asset Catalog"); } void XcodeProvider::setupAdditionalSources(std::string targetName, Property &files, int &order) { if (targetIsIOS(targetName)) { const std::string absoluteCatalogPath = _projectRoot + "/dists/ios7/Images.xcassets"; ADD_SETTING_ORDER_NOVALUE(files, getHash(absoluteCatalogPath), "Image Asset Catalog", order++); } else { // HACK: browser_osx.mm needs to be added const std::string browserPath = "gui/browser_osx.mm"; const std::string comment = "browser_osx.mm in Sources"; ADD_SETTING_ORDER_NOVALUE(files, getHash(browserPath), comment, order++); } } ////////////////////////////////////////////////////////////////////////// // 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 == "USE_NASM") // Not supported on Mac continue; ADD_DEFINE(_defines, *i); } // Add special defines for Mac support REMOVE_DEFINE(_defines, "MACOSX"); REMOVE_DEFINE(_defines, "IPHONE"); REMOVE_DEFINE(_defines, "IPHONE_IOS7"); REMOVE_DEFINE(_defines, "IPHONE_SANDBOXED"); REMOVE_DEFINE(_defines, "SDL_BACKEND"); ADD_DEFINE(_defines, "USE_TEXT_CONSOLE_FOR_DEBUGGER"); ADD_DEFINE(_defines, "CONFIG_H"); ADD_DEFINE(_defines, "UNIX"); ADD_DEFINE(_defines, "SCUMMVM"); } ////////////////////////////////////////////////////////////////////////// // Object hash ////////////////////////////////////////////////////////////////////////// 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::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 #ifdef MACOSX std::string hash = md5(key); #else std::string hash = newHash(); #endif _hashDictionnary[key] = hash; return hash; #endif } bool isSeparator(char s) { return (s == '-'); } #ifdef MACOSX std::string XcodeProvider::md5(std::string key) { unsigned char md[CC_MD5_DIGEST_LENGTH]; CC_MD5(key.c_str(), (CC_LONG) key.length(), md); std::stringstream stream; stream << std::hex << std::setfill('0') << std::setw(2); for (int i=0; i 1 || (prop._flags & kSettingsSingleItem)) output += (flags & kSettingsSingleItem ? " " : "\t\t\t\t"); output += writeSetting(setting->first, setting->second); // The combination of kSettingsAsList, and kSettingsSingleItem should use "," and not ";" (i.e children // in PBXGroup, so we special case that case here. if ((prop._flags & kSettingsAsList) && (prop._settings.size() > 1 || (prop._flags & kSettingsSingleItem))) { output += (prop._settings.size() > 0) ? ",\n" : "\n"; } else { output += ";"; output += (flags & kSettingsSingleItem ? " " : "\n"); } } if (prop._settings.size() > 1 || (prop._flags & kSettingsSingleItem)) output += (prop._flags & kSettingsAsList) ? "\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 the QMake // XCode project generator pbuilder_pbx.cpp, writeSettings() (under LGPL 2.1) std::string XcodeProvider::writeSetting(const std::string &variable, const Setting &setting) const { std::string output; const std::string quote = (setting._flags & kSettingsNoQuote) ? "" : "\""; 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 & kSettingsQuoteVariable) ? "\"" + variable + "\"" : variable; // Output a list if (setting._flags & kSettingsAsList) { output += var + ((setting._flags & kSettingsNoValue) ? "(" : " = (") + 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 & kSettingsNoValue) ? "\t\t\t)" : "," + newline + ")"; } else { output += var; output += (setting._flags & kSettingsNoValue) ? "" : " = " + 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 & kSettingsNoValue) ? "" : quote; } return output; } } // End of CreateProjectTool namespace