diff options
| author | Julien | 2011-06-02 14:09:46 -0400 | 
|---|---|---|
| committer | Julien | 2011-06-02 17:29:36 -0400 | 
| commit | 9854f1b48667b082f9ee1c377dcba5b44e92827b (patch) | |
| tree | 7cc3d7814f123395d93a261781e31916651b88a4 | |
| parent | 76c1cdb3c26756ff78495683d2acb98e3a07a495 (diff) | |
| download | scummvm-rg350-9854f1b48667b082f9ee1c377dcba5b44e92827b.tar.gz scummvm-rg350-9854f1b48667b082f9ee1c377dcba5b44e92827b.tar.bz2 scummvm-rg350-9854f1b48667b082f9ee1c377dcba5b44e92827b.zip  | |
CREATE_PROJECT: Implement basic XCode provider
This only outputs a skeleton project with no files and targets yet
| -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 = (  			);  | 
