[Scummvm-git-logs] scummvm master -> 667cb638234b554eb293c54a350ecf721c0a1da7

SupSuper noreply at scummvm.org
Fri Sep 29 20:37:14 UTC 2023


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
667cb63823 DEVTOOLS: Add precompiled header support to MSBuild/MSVC.


Commit: 667cb638234b554eb293c54a350ecf721c0a1da7
    https://github.com/scummvm/scummvm/commit/667cb638234b554eb293c54a350ecf721c0a1da7
Author: elasota (ejlasota at gmail.com)
Date: 2023-09-29T21:37:10+01:00

Commit Message:
DEVTOOLS: Add precompiled header support to MSBuild/MSVC.

Changed paths:
    .gitignore
    devtools/create_project/cmake.cpp
    devtools/create_project/cmake.h
    devtools/create_project/codeblocks.cpp
    devtools/create_project/codeblocks.h
    devtools/create_project/create_project.cpp
    devtools/create_project/create_project.h
    devtools/create_project/msbuild.cpp
    devtools/create_project/msbuild.h
    devtools/create_project/xcode.cpp
    devtools/create_project/xcode.h


diff --git a/.gitignore b/.gitignore
index 1dadcfd7e5b..b998a66e579 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
 *.dwp
 lib*.a
 .deps
+*_pch.cpp
 
 /config.log
 /scummvm
diff --git a/devtools/create_project/cmake.cpp b/devtools/create_project/cmake.cpp
index dfaf53b4d00..9929d1fc80d 100644
--- a/devtools/create_project/cmake.cpp
+++ b/devtools/create_project/cmake.cpp
@@ -270,7 +270,7 @@ static std::string filePrefix(const BuildSetup &setup, const std::string &module
 }
 
 void CMakeProvider::createProjectFile(const std::string &name, const std::string &, const BuildSetup &setup, const std::string &moduleDir,
-										   const StringList &includeList, const StringList &excludeList) {
+									  const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 
 	const std::string projectFile = setup.outputDir + "/CMakeLists.txt";
 	std::ofstream project(projectFile.c_str(), std::ofstream::out | std::ofstream::app);
@@ -284,12 +284,12 @@ void CMakeProvider::createProjectFile(const std::string &name, const std::string
 		project << "add_library(" << name << "\n";
 	} else {
 		enginesStr << "add_engine(" << name << "\n";
-		addFilesToProject(moduleDir, enginesStr, includeList, excludeList, filePrefix(setup, moduleDir));
+		addFilesToProject(moduleDir, enginesStr, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, filePrefix(setup, moduleDir));
 		enginesStr << ")\n\n";
 		return;
 	}
 
-	addFilesToProject(moduleDir, project, includeList, excludeList, filePrefix(setup, moduleDir));
+	addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, filePrefix(setup, moduleDir));
 
 	project << ")\n";
 	project << "\n";
@@ -356,12 +356,13 @@ void CMakeProvider::writeDefines(const BuildSetup &setup, std::ofstream &output)
 }
 
 void CMakeProvider::writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-												const std::string &objPrefix, const std::string &filePrefix) {
+										   const std::string &objPrefix, const std::string &filePrefix,
+										   const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 
 	std::string lastName;
 	for (const FileNode *node : dir.children) {
 		if (!node->children.empty()) {
-			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/');
+			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/', pchIncludeRoot, pchDirs, pchExclude);
 		} else {
 			std::string name, ext;
 			splitFilename(node->name, name, ext);
diff --git a/devtools/create_project/cmake.h b/devtools/create_project/cmake.h
index 9020d2a7394..7bb38bf0b1b 100644
--- a/devtools/create_project/cmake.h
+++ b/devtools/create_project/cmake.h
@@ -45,10 +45,11 @@ protected:
 	void addResourceFiles(const BuildSetup &setup, StringList &includeList, StringList &excludeList) final {}
 
 	void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
-						   const StringList &includeList, const StringList &excludeList) final;
+						   const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) final;
 
 	void writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-								const std::string &objPrefix, const std::string &filePrefix) final;
+								const std::string &objPrefix, const std::string &filePrefix,
+								const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) final;
 
 	const char *getProjectExtension() final;
 
diff --git a/devtools/create_project/codeblocks.cpp b/devtools/create_project/codeblocks.cpp
index bc72ba332cb..a5e88d6b364 100644
--- a/devtools/create_project/codeblocks.cpp
+++ b/devtools/create_project/codeblocks.cpp
@@ -97,7 +97,7 @@ StringList getFeatureLibraries(const BuildSetup &setup) {
 }
 
 void CodeBlocksProvider::createProjectFile(const std::string &name, const std::string &, const BuildSetup &setup, const std::string &moduleDir,
-										   const StringList &includeList, const StringList &excludeList) {
+										   const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 
 	const std::string projectFile = setup.outputDir + '/' + name + getProjectExtension();
 	std::ofstream project(projectFile.c_str());
@@ -210,9 +210,9 @@ void CodeBlocksProvider::createProjectFile(const std::string &name, const std::s
 	}
 
 	if (!modulePath.empty())
-		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath);
+		addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, setup.filePrefix + '/' + modulePath);
 	else
-		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix);
+		addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, setup.filePrefix);
 
 
 	project << "\t\t<Extensions>\n"
@@ -249,13 +249,14 @@ void CodeBlocksProvider::writeDefines(const StringList &defines, std::ofstream &
 }
 
 void CodeBlocksProvider::writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-												const std::string &objPrefix, const std::string &filePrefix) {
+												const std::string &objPrefix, const std::string &filePrefix,
+												const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 
 	for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) {
 		const FileNode *node = *i;
 
 		if (!node->children.empty()) {
-			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/');
+			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/', pchIncludeRoot, pchDirs, pchExclude);
 		} else {
 			std::string name, ext;
 			splitFilename(node->name, name, ext);
diff --git a/devtools/create_project/codeblocks.h b/devtools/create_project/codeblocks.h
index 9ef68e63ccc..8718d47041a 100644
--- a/devtools/create_project/codeblocks.h
+++ b/devtools/create_project/codeblocks.h
@@ -39,10 +39,11 @@ protected:
 	void addResourceFiles(const BuildSetup &setup, StringList &includeList, StringList &excludeList) final;
 
 	void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
-						   const StringList &includeList, const StringList &excludeList) final;
+						   const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) final;
 
 	void writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-								const std::string &objPrefix, const std::string &filePrefix) final;
+								const std::string &objPrefix, const std::string &filePrefix,
+								const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) final;
 
 	void writeReferences(const BuildSetup &setup, std::ofstream &output) final;
 
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index b64cef443cb..39310293b0d 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1319,6 +1319,55 @@ void splitPath(const std::string &path, std::string &dir, std::string &file) {
 	file = (sep == std::string::npos) ? std::string() : path.substr(sep + 1);
 }
 
+bool calculatePchPaths(const std::string &sourceFilePath, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude, char separator, std::string &outPchIncludePath, std::string &outPchFilePath, std::string &outPchFileName) {
+	std::string compareName, extensionName;
+	splitFilename(sourceFilePath, compareName, extensionName);
+
+	// Is this file excluded?
+	if (std::find(pchExclude.begin(), pchExclude.end(), compareName) != pchExclude.end())
+		return false;
+
+	size_t lastDelimiter = sourceFilePath.find_last_of(separator);
+	if (lastDelimiter == std::string::npos)
+		lastDelimiter = 0;
+
+	std::string pchDirectory = sourceFilePath.substr(0, lastDelimiter);
+
+	if (std::find(pchDirs.begin(), pchDirs.end(), pchDirectory) == pchDirs.end())
+		return false;
+
+	// This file uses a PCH
+	if (pchDirectory.size() < pchIncludeRoot.size() || pchDirectory.substr(0, pchIncludeRoot.size()) != pchIncludeRoot) {
+		error("PCH prefix for file '" + sourceFilePath + "' wasn't located under PCH include root '" + pchIncludeRoot + "'");
+	}
+
+	size_t pchDirNamePos = pchDirectory.find_last_of(separator);
+	if (pchDirNamePos == std::string::npos)
+		pchDirNamePos = 0;
+	else
+		pchDirNamePos++;
+
+	std::string pchFileName = pchDirectory.substr(pchDirNamePos) + "_pch.h";
+
+	std::string pchPath = (pchDirectory + separator + pchFileName);
+
+	// Convert to the local file prefix
+	std::string includePath = pchPath.substr(pchIncludeRoot.size());
+
+	if (separator != '/') {
+		for (std::string::iterator ch = includePath.begin(), chEnd = includePath.end(); ch != chEnd; ++ch) {
+			if (*ch == separator)
+				*ch = '/';
+		}
+	}
+
+	outPchIncludePath = includePath;
+	outPchFilePath = pchPath;
+	outPchFileName = pchFileName;
+
+	return true;
+}
+
 std::string basename(const std::string &fileName) {
 	const std::string::size_type slash = fileName.find_last_of('/');
 	if (slash == std::string::npos)
@@ -1586,7 +1635,7 @@ void ProjectProvider::createProject(BuildSetup &setup) {
 
 	createWorkspace(setup);
 
-	StringList in, ex;
+	StringList in, ex, pchDirs, pchEx;
 
 	// Create project files
 	for (UUIDMap::const_iterator i = _engineUuidMap.begin(); i != _engineUuidMap.end(); ++i) {
@@ -1595,11 +1644,13 @@ void ProjectProvider::createProject(BuildSetup &setup) {
 		// Retain the files between engines if we're creating a single project
 		in.clear();
 		ex.clear();
+		pchDirs.clear();
+		pchEx.clear();
 
 		const std::string moduleDir = setup.srcDir + targetFolder + i->first;
 
-		createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex);
-		createProjectFile(i->first, i->second, setup, moduleDir, in, ex);
+		createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createProjectFile(i->first, i->second, setup, moduleDir, in, ex, setup.srcDir + targetFolder, pchDirs, pchEx);
 	}
 
 	// Create engine-detection submodules.
@@ -1623,37 +1674,40 @@ void ProjectProvider::createProject(BuildSetup &setup) {
 		}
 
 		for (std::vector<std::string>::const_iterator i = detectionModuleDirs.begin(), end = detectionModuleDirs.end(); i != end; ++i) {
-			createModuleList(*i, setup.defines, setup.testDirs, in, ex, true);
+			StringList tempPchDirs, tempSchEx;	// No PCH for detection
+			createModuleList(*i, setup.defines, setup.testDirs, in, ex, tempPchDirs, tempSchEx, true);
 		}
 
-		createProjectFile(detProject, detUUID, setup, setup.srcDir + "/engines", in, ex);
+		createProjectFile(detProject, detUUID, setup, setup.srcDir + "/engines", in, ex, "", StringList(), StringList());
 	}
 
 	if (!setup.devTools) {
 		// Last but not least create the main project file.
 		in.clear();
 		ex.clear();
+		pchDirs.clear();
+		pchEx.clear();
 		// File list for the Project file
-		createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/base", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/common", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/common/compression", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/common/formats", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/common/lua", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/engines", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/graphics", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/gui", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/audio", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/video", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/image", setup.defines, setup.testDirs, in, ex);
-		createModuleList(setup.srcDir + "/math", setup.defines, setup.testDirs, in, ex);
+		createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/base", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/common", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/common/compression", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/common/formats", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/common/lua", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/engines", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/graphics", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/gui", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/audio", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/video", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/image", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
+		createModuleList(setup.srcDir + "/math", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
 
 		if (getFeatureBuildState("mt32emu", setup.features))
-			createModuleList(setup.srcDir + "/audio/softsynth/mt32", setup.defines, setup.testDirs, in, ex);
+			createModuleList(setup.srcDir + "/audio/softsynth/mt32", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
 
 		if (setup.tests) {
-			createModuleList(setup.srcDir + "/test", setup.defines, setup.testDirs, in, ex);
+			createModuleList(setup.srcDir + "/test", setup.defines, setup.testDirs, in, ex, pchDirs, pchEx);
 		} else {
 			// Resource files
 			addResourceFiles(setup, in, ex);
@@ -1678,7 +1732,7 @@ void ProjectProvider::createProject(BuildSetup &setup) {
 		}
 
 		// Create the main project file.
-		createProjectFile(setup.projectName, svmUUID, setup, setup.srcDir, in, ex);
+		createProjectFile(setup.projectName, svmUUID, setup, setup.srcDir, in, ex, setup.srcDir + '/', pchDirs, pchEx);
 	}
 
 	// Create other misc. build files
@@ -1827,15 +1881,16 @@ std::string ProjectProvider::getLastPathComponent(const std::string &path) {
 
 void ProjectProvider::addFilesToProject(const std::string &dir, std::ostream &projectFile,
 										const StringList &includeList, const StringList &excludeList,
+										const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude,
 										const std::string &filePrefix) {
 	FileNode *files = scanFiles(dir, includeList, excludeList);
 
-	writeFileListToProject(*files, projectFile, 0, std::string(), filePrefix + '/');
+	writeFileListToProject(*files, projectFile, 0, std::string(), filePrefix + '/', pchIncludeRoot, pchDirs, pchExclude);
 
 	delete files;
 }
 
-void ProjectProvider::createModuleList(const std::string &moduleDir, const StringList &defines, StringList &testDirs, StringList &includeList, StringList &excludeList, bool forDetection) const {
+void ProjectProvider::createModuleList(const std::string &moduleDir, const StringList &defines, StringList &testDirs, StringList &includeList, StringList &excludeList, StringList &pchDirs, StringList &pchExclude, bool forDetection) const {
 	const std::string moduleMkFile = moduleDir + "/module.mk";
 	std::ifstream moduleMk(moduleMkFile.c_str());
 	if (!moduleMk)
@@ -1948,6 +2003,68 @@ void ProjectProvider::createModuleList(const std::string &moduleDir, const Strin
 					++i;
 				}
 			}
+		} else if (*i == "MODULE_PCH_DIRS") {
+			if (tokens.size() < 3)
+				error("Malformed MODULE_PCH_DIRS definition in " + moduleMkFile);
+			++i;
+
+			// This is not exactly correct, for example an ":=" would usually overwrite
+			// all already added files, but since we do only save the files inside
+			// includeList or excludeList currently, we couldn't handle such a case easily.
+			// (includeList and excludeList should always preserve their entries, not added
+			// by this function, thus we can't just clear them on ":=" or "=").
+			// But hopefully our module.mk files will never do such things anyway.
+			if (*i != ":=" && *i != "+=" && *i != "=")
+				error("Malformed MODULE_PCH_DIRS definition in " + moduleMkFile);
+
+			++i;
+
+			while (i != tokens.end()) {
+				if (*i == "\\") {
+					std::getline(moduleMk, line);
+					tokens = tokenize(line);
+					i = tokens.begin();
+				} else {
+					std::string filename = moduleDir;
+					if ((*i) != ".")
+						filename += "/" + unifyPath(*i);
+
+					if (shouldInclude.top())
+						pchDirs.push_back(filename);
+
+					++i;
+				}
+			}
+		} else if (*i == "MODULE_PCH_EXCLUDE") {
+			if (tokens.size() < 3)
+				error("Malformed MODULE_PCH_EXCLUDE definition in " + moduleMkFile);
+			++i;
+
+			// This is not exactly correct, for example an ":=" would usually overwrite
+			// all already added files, but since we do only save the files inside
+			// includeList or excludeList currently, we couldn't handle such a case easily.
+			// (includeList and excludeList should always preserve their entries, not added
+			// by this function, thus we can't just clear them on ":=" or "=").
+			// But hopefully our module.mk files will never do such things anyway.
+			if (*i != ":=" && *i != "+=" && *i != "=")
+				error("Malformed MODULE_PCH_EXCLUDE definition in " + moduleMkFile);
+
+			++i;
+
+			while (i != tokens.end()) {
+				if (*i == "\\") {
+					std::getline(moduleMk, line);
+					tokens = tokenize(line);
+					i = tokens.begin();
+				} else {
+					const std::string filename = moduleDir + "/" + unifyPath(*i);
+
+					if (shouldInclude.top())
+						pchExclude.push_back(filename);
+
+					++i;
+				}
+			}
 		} else if (*i == "KYRARPG_COMMON_OBJ") {
 			// HACK to fix EOB/LOL compilation in the kyra engine: add the
 			// files defined in the KYRARPG_COMMON_OBJ variable in a list
diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h
index 8c263b5695d..ce08bdf9053 100644
--- a/devtools/create_project/create_project.h
+++ b/devtools/create_project/create_project.h
@@ -409,6 +409,21 @@ void splitFilename(const std::string &fileName, std::string &name, std::string &
  */
 void splitPath(const std::string &path, std::string &dir, std::string &file);
 
+/**
+ * Calculates the include path and PCH file path (without the base directory).
+ *
+ * @param filePath Path to the source file.
+ * @param pchIncludeRoot Path to the PCH inclusion root directory (ending with separator).
+ * @param pchDirs List of PCH directories.
+ * @param pchExclude List of PCH exclusions.
+ * @param separator Path separator
+ * @param outPchIncludePath Output path to be used by #include directives.
+ * @param outPchFilePath Output file path.
+ * @param outPchFileName Output file name.
+ * @return True if the file path uses PCH, false if not.
+ */
+bool calculatePchPaths(const std::string &sourceFilePath, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude, char separator, std::string &outPchIncludePath, std::string &outPchFilePath, std::string &outPchFileName);
+
 /**
  * Returns the basename of a path.
  * examples:
@@ -551,7 +566,7 @@ protected:
 	 * @param excludeList Files to exclude (must have "moduleDir" as prefix).
 	 */
 	virtual void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
-	                               const StringList &includeList, const StringList &excludeList) = 0;
+								   const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) = 0;
 
 	/**
 	 * Writes file entries for the specified directory node into
@@ -564,7 +579,8 @@ protected:
 	 * @param filePrefix Generic prefix to all files of the node.
 	 */
 	virtual void writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-	                                    const std::string &objPrefix, const std::string &filePrefix) = 0;
+										const std::string &objPrefix, const std::string &filePrefix,
+										const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) = 0;
 
 	/**
 	 * Output a list of project references to the file stream
@@ -588,7 +604,8 @@ protected:
 	 * @param filePrefix Prefix to use for relative path arguments.
 	 */
 	void addFilesToProject(const std::string &dir, std::ostream &projectFile,
-	                       const StringList &includeList, const StringList &excludeList,
+						   const StringList &includeList, const StringList &excludeList,
+						   const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude,
 	                       const std::string &filePrefix);
 
 	/**
@@ -602,7 +619,7 @@ protected:
 	 * @param includeList Reference to a list, where included files should be added.
 	 * @param excludeList Reference to a list, where excluded files should be added.
 	 */
-	void createModuleList(const std::string &moduleDir, const StringList &defines, StringList &testDirs, StringList &includeList, StringList &excludeList, bool forDetection = false) const;
+	void createModuleList(const std::string &moduleDir, const StringList &defines, StringList &testDirs, StringList &includeList, StringList &excludeList, StringList &pchDirs, StringList &pchExclude, bool forDetection = false) const;
 
 	/**
 	 * Creates an UUID for every enabled engine of the
diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp
index 44fdce9ae10..07a165554e0 100644
--- a/devtools/create_project/msbuild.cpp
+++ b/devtools/create_project/msbuild.cpp
@@ -81,7 +81,7 @@ inline void outputProperties(const BuildSetup &setup, std::ostream &project, con
 } // End of anonymous namespace
 
 void MSBuildProvider::createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
-										const StringList &includeList, const StringList &excludeList) {
+										const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 	const std::string projectFile = setup.outputDir + '/' + name + getProjectExtension();
 	std::ofstream project(projectFile.c_str());
 	if (!project || !project.is_open()) {
@@ -156,9 +156,9 @@ void MSBuildProvider::createProjectFile(const std::string &name, const std::stri
 	}
 
 	if (!modulePath.empty())
-		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath);
+		addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, setup.filePrefix + '/' + modulePath);
 	else
-		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix);
+		addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, setup.filePrefix);
 
 	// Output references for the main project
 	if (name == setup.projectName)
@@ -529,8 +529,52 @@ void MSBuildProvider::outputNasmCommand(std::ostream &projectFile, const std::st
 	}
 }
 
+void MSBuildProvider::insertPathIntoDirectory(FileNode &dir, const std::string &path) {
+	size_t separatorLoc = path.find('\\');
+	if (separatorLoc != std::string::npos) {
+		// Inside of a subdirectory
+
+		std::string subdirName = path.substr(0, separatorLoc);
+
+		FileNode::NodeList::iterator dirIt = dir.children.begin();
+		FileNode::NodeList::iterator dirItEnd = dir.children.end();
+		while (dirIt != dirItEnd) {
+			if ((*dirIt)->name == subdirName)
+				break;
+
+			++dirIt;
+		}
+
+		FileNode *dirNode = nullptr;
+		if (dirIt == dirItEnd) {
+			dirNode = new FileNode(subdirName);
+			dir.children.push_back(dirNode);
+		} else {
+			dirNode = *dirIt;
+		}
+
+		insertPathIntoDirectory(*dirNode, path.substr(separatorLoc + 1));
+	} else {
+		FileNode *fileNode = new FileNode(path);
+		dir.children.push_back(fileNode);
+	}
+}
+
+void MSBuildProvider::createFileNodesFromPCHList(FileNode &dir, const std::string &pathBase, const StringList &pchCompileFiles) {
+	for (StringList::const_iterator it = pchCompileFiles.begin(), itEnd = pchCompileFiles.end(); it != itEnd; ++it) {
+		const std::string &pchPath = *it;
+
+		if (pchPath.size() > pathBase.size() && pchPath.substr(0, pathBase.size()) == pathBase) {
+			std::string internalPath = pchPath.substr(pathBase.size());
+
+			insertPathIntoDirectory(dir, internalPath);
+		}
+	}
+}
+
 void MSBuildProvider::writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int,
-											 const std::string &objPrefix, const std::string &filePrefix) {
+											 const std::string &objPrefix, const std::string &filePrefix,
+											 const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 	// Reset lists
 	_filters.clear();
 	_compileFiles.clear();
@@ -544,12 +588,33 @@ void MSBuildProvider::writeFileListToProject(const FileNode &dir, std::ostream &
 	computeFileList(dir, objPrefix, filePrefix);
 	_filters.pop_back(); // remove last empty filter
 
+	StringList pchCompileFiles;
+
 	// Output compile, include, other and resource files
-	outputFiles(projectFile, _compileFiles, "ClCompile");
+	outputCompileFiles(projectFile, pchIncludeRoot, pchDirs, pchExclude, pchCompileFiles);
 	outputFiles(projectFile, _includeFiles, "ClInclude");
 	outputFiles(projectFile, _otherFiles, "None");
 	outputFiles(projectFile, _resourceFiles, "ResourceCompile");
 
+	if (pchCompileFiles.size() > 0) {
+		// Generate filters and additional compile files for PCH files
+		FileNode pchDir(dir.name);
+		createFileNodesFromPCHList(pchDir, convertPathToWin(dir.name) + '\\', pchCompileFiles);
+
+		StringList backupFilters = _filters;
+		_filters.clear();
+
+		_filters.push_back(""); // init filters
+		computeFileList(pchDir, objPrefix, filePrefix);
+		_filters.pop_back(); // remove last empty filter
+
+		// Combine lists, removing duplicates
+		for (StringList::const_iterator it = backupFilters.begin(), itEnd = backupFilters.end(); it != itEnd; ++it) {
+			if (std::find(_filters.begin(), _filters.end(), *it) != _filters.end())
+				_filters.push_back(*it);
+		}
+	}
+
 	// Output asm files
 	if (!_asmFiles.empty()) {
 		projectFile << "\t<ItemGroup>\n";
@@ -579,6 +644,126 @@ void MSBuildProvider::outputFiles(std::ostream &projectFile, const FileEntries &
 	}
 }
 
+void MSBuildProvider::outputCompileFiles(std::ostream &projectFile, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude, StringList &outPCHFiles) {
+	const FileEntries &files = _compileFiles;
+
+	const bool hasPCH = (pchDirs.size() > 0);
+
+	std::string pchIncludeRootWin;
+	StringList pchDirsWin;
+	StringList pchExcludeWin;
+
+	if (hasPCH) {
+		pchIncludeRootWin = convertPathToWin(pchIncludeRoot);
+
+		// Convert PCH paths to Win
+		for (StringList::const_iterator entry = pchDirs.begin(), end = pchDirs.end(); entry != end; ++entry) {
+			std::string convertedPath = convertPathToWin(*entry);
+			if (convertedPath.size() < pchIncludeRootWin.size() || convertedPath.substr(0, pchIncludeRootWin.size()) != pchIncludeRootWin) {
+				error("PCH path '" + convertedPath + "' wasn't located under PCH include root '" + pchIncludeRootWin + "'");
+			}
+
+			pchDirsWin.push_back(convertPathToWin(*entry));
+		}
+		for (StringList::const_iterator entry = pchExclude.begin(), end = pchExclude.end(); entry != end; ++entry) {
+			const std::string path = *entry;
+
+			if (path.size() >= 2 && path[path.size() - 1] == 'o' && path[path.size() - 2] == '.')
+				pchExcludeWin.push_back(convertPathToWin(path.substr(0, path.size() - 2)));
+		}
+	}
+
+	std::map<std::string, PCHInfo> pchMap;
+
+	if (!files.empty()) {
+		projectFile << "\t<ItemGroup>\n";
+		for (FileEntries::const_iterator entry = files.begin(), end = files.end(); entry != end; ++entry) {
+			std::string pchIncludePath, pchFilePath, pchFileName;
+
+			bool fileHasPCH = false;
+			if (hasPCH)
+				fileHasPCH = calculatePchPaths(entry->path, pchIncludeRootWin, pchDirsWin, pchExcludeWin, '\\', pchIncludePath, pchFilePath, pchFileName);
+
+			if (fileHasPCH) {
+				std::string pchOutputFileName = "$(IntDir)dists\\msvc\\%(RelativeDir)" + pchFileName.substr(0, pchFileName.size() - 2) + ".pch";
+
+				PCHInfo &pchInfo = pchMap[pchFilePath];
+				pchInfo.file = pchIncludePath;
+				pchInfo.outputFile = pchOutputFileName;
+
+				projectFile << "\t\t<ClCompile Include=\"" << (*entry).path << "\">\n";
+				projectFile << "\t\t\t<PrecompiledHeader>Use</PrecompiledHeader>\n";
+				projectFile << "\t\t\t<PrecompiledHeaderFile>" << pchIncludePath << "</PrecompiledHeaderFile>\n";
+				projectFile << "\t\t\t<PrecompiledHeaderOutputFile>" << pchOutputFileName << "</PrecompiledHeaderOutputFile>\n";
+				projectFile << "\t\t</ClCompile>\n";
+			} else {
+				projectFile << "\t\t<ClCompile Include=\"" << (*entry).path << "\" />\n";
+			}
+		}
+
+		// Flush PCH files
+		for (std::map<std::string, PCHInfo>::const_iterator pchIt = pchMap.begin(), pchItEnd = pchMap.end(); pchIt != pchItEnd; ++pchIt) {
+			const PCHInfo &pchInfo = pchIt->second;
+
+			const std::string &filePath = pchIt->first;
+			assert(filePath.size() >= 2 && filePath.substr(filePath.size() - 2) == ".h");
+
+			std::string cppFilePath = filePath.substr(0, filePath.size() - 2) + ".cpp";
+
+			std::string expectedContents = "/* This file is automatically generated by create_project */\n"
+										   "/* DO NOT EDIT MANUALLY */\n"
+										   "#include \"" + pchInfo.file + "\"\n";
+
+			// Try to avoid touching the generated .cpp if it's identical to the expected output.
+			// If we touch the file, then every file that includes PCH needs to be recompiled.
+			std::ifstream pchInputFile(cppFilePath.c_str());
+			bool needToEmit = true;
+			if (pchInputFile && pchInputFile.is_open()) {
+				std::string fileContents;
+				for (;;) {
+					char buffer[1024];
+					size_t numRead = sizeof(buffer) - 1;
+					pchInputFile.read(buffer, numRead);
+
+					buffer[pchInputFile.gcount()] = '\0';
+
+					fileContents += buffer;
+
+					if (pchInputFile.eof() || pchInputFile.fail())
+						break;
+
+					if (fileContents.size() > expectedContents.size())
+						break;
+				}
+
+				needToEmit = (fileContents != expectedContents);
+				pchInputFile.close();
+			}
+
+			if (needToEmit) {
+				std::ofstream pchOutputFile(cppFilePath.c_str());
+				if (!pchOutputFile || !pchOutputFile.is_open()) {
+					error("Could not open \"" + cppFilePath + "\" for writing");
+					return;
+				}
+
+				pchOutputFile << expectedContents;
+				pchOutputFile.close();
+			}
+
+			projectFile << "\t\t<ClCompile Include=\"" << cppFilePath << "\">\n";
+			projectFile << "\t\t\t<PrecompiledHeader>Create</PrecompiledHeader>\n";
+			projectFile << "\t\t\t<PrecompiledHeaderFile>" << pchInfo.file << "</PrecompiledHeaderFile>\n";
+			projectFile << "\t\t\t<PrecompiledHeaderOutputFile>" << pchInfo.outputFile << "</PrecompiledHeaderOutputFile>\n";
+			projectFile << "\t\t</ClCompile>\n";
+
+			outPCHFiles.push_back(cppFilePath);
+		}
+
+		projectFile << "\t</ItemGroup>\n";
+	}
+}
+
 void MSBuildProvider::computeFileList(const FileNode &dir, const std::string &objPrefix, const std::string &filePrefix) {
 	for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) {
 		const FileNode *node = *i;
diff --git a/devtools/create_project/msbuild.h b/devtools/create_project/msbuild.h
index d5d25de2240..254ffef0592 100644
--- a/devtools/create_project/msbuild.h
+++ b/devtools/create_project/msbuild.h
@@ -32,12 +32,13 @@ public:
 
 protected:
 	void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
-	                       const StringList &includeList, const StringList &excludeList) override;
+						   const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) override;
 
 	void outputProjectSettings(std::ofstream &project, const std::string &name, const BuildSetup &setup, bool isRelease, MSVC_Architecture arch, const std::string &configuration);
 
 	void writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-	                            const std::string &objPrefix, const std::string &filePrefix) override;
+								const std::string &objPrefix, const std::string &filePrefix,
+								const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) override;
 
 	void writeReferences(const BuildSetup &setup, std::ofstream &output) override;
 
@@ -61,6 +62,11 @@ private:
 	};
 	typedef std::list<FileEntry> FileEntries;
 
+	struct PCHInfo {
+		std::string file;
+		std::string outputFile;
+	};
+
 	std::list<std::string> _filters; // list of filters (we need to create a GUID for each filter id)
 	FileEntries _compileFiles;
 	FileEntries _includeFiles;
@@ -73,8 +79,12 @@ private:
 
 	void outputFilter(std::ostream &filters, const FileEntries &files, const std::string &action);
 	void outputFiles(std::ostream &projectFile, const FileEntries &files, const std::string &action);
+	void outputCompileFiles(std::ostream &projectFile, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude, StringList &outPCHFiles);
 
 	void outputNasmCommand(std::ostream &projectFile, const std::string &config, const std::string &prefix);
+
+	static void createFileNodesFromPCHList(FileNode &dir, const std::string &pathBase, const StringList &pchCompileFiles);
+	static void insertPathIntoDirectory(FileNode &dir, const std::string &path);
 };
 
 } // namespace CreateProjectTool
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index 8232aec5dd1..32375d627d9 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -307,8 +307,10 @@ void XcodeProvider::addResourceFiles(const BuildSetup &setup, StringList &includ
 		includeList.push_back(setup.srcDir + "/" + *it);
 	}
 
+	StringList pchDirs, pchEx;
+
 	StringList td;
-	createModuleList(setup.srcDir + "/backends/platform/ios7", setup.defines, td, includeList, excludeList);
+	createModuleList(setup.srcDir + "/backends/platform/ios7", setup.defines, td, includeList, excludeList, pchDirs, pchEx);
 }
 
 void XcodeProvider::createWorkspace(const BuildSetup &setup) {
@@ -343,7 +345,7 @@ void XcodeProvider::createOtherBuildFiles(const BuildSetup &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) {
+									  const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 	std::string modulePath;
 	if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) {
 		modulePath = moduleDir.substr(setup.srcDir.size());
@@ -353,9 +355,9 @@ void XcodeProvider::createProjectFile(const std::string &, const std::string &,
 
 	std::ofstream project;
 	if (!modulePath.empty())
-		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix + '/' + modulePath);
+		addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, setup.filePrefix + '/' + modulePath);
 	else
-		addFilesToProject(moduleDir, project, includeList, excludeList, setup.filePrefix);
+		addFilesToProject(moduleDir, project, includeList, excludeList, pchIncludeRoot, pchDirs, pchExclude, setup.filePrefix);
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -402,7 +404,8 @@ void XcodeProvider::outputMainProjectFile(const BuildSetup &setup) {
 // Files
 //////////////////////////////////////////////////////////////////////////
 void XcodeProvider::writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-										   const std::string &objPrefix, const std::string &filePrefix) {
+										   const std::string &objPrefix, const std::string &filePrefix,
+										   const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) {
 
 	// Ensure that top-level groups are generated for i.e. engines/
 	Group *group = touchGroupsForPath(filePrefix);
@@ -416,7 +419,7 @@ void XcodeProvider::writeFileListToProject(const FileNode &dir, std::ostream &pr
 		}
 		// Process child nodes
 		if (!node->children.empty())
-			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/');
+			writeFileListToProject(*node, projectFile, indentation + 1, objPrefix + node->name + '_', filePrefix + node->name + '/', pchIncludeRoot, pchDirs, pchExclude);
 	}
 }
 
diff --git a/devtools/create_project/xcode.h b/devtools/create_project/xcode.h
index 939c14c831e..b4dfa6cd37f 100644
--- a/devtools/create_project/xcode.h
+++ b/devtools/create_project/xcode.h
@@ -42,10 +42,12 @@ protected:
 	void addResourceFiles(const BuildSetup &setup, StringList &includeList, StringList &excludeList) final;
 
 	void createProjectFile(const std::string &name, const std::string &uuid, const BuildSetup &setup, const std::string &moduleDir,
-						   const StringList &includeList, const StringList &excludeList) final;
+						   const StringList &includeList, const StringList &excludeList, const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) final;
 
 	void writeFileListToProject(const FileNode &dir, std::ostream &projectFile, const int indentation,
-								const std::string &objPrefix, const std::string &filePrefix) final;
+								const std::string &objPrefix, const std::string &filePrefix,
+								const std::string &pchIncludeRoot, const StringList &pchDirs, const StringList &pchExclude) final;
+
 private:
 	enum {
 		kSettingsAsList        = 0x01,




More information about the Scummvm-git-logs mailing list