From 667cb638234b554eb293c54a350ecf721c0a1da7 Mon Sep 17 00:00:00 2001 From: elasota Date: Sat, 24 Dec 2022 09:54:18 -0500 Subject: [PATCH] DEVTOOLS: Add precompiled header support to MSBuild/MSVC. --- .gitignore | 1 + devtools/create_project/cmake.cpp | 11 +- devtools/create_project/cmake.h | 5 +- devtools/create_project/codeblocks.cpp | 11 +- devtools/create_project/codeblocks.h | 5 +- devtools/create_project/create_project.cpp | 165 ++++++++++++++--- devtools/create_project/create_project.h | 25 ++- devtools/create_project/msbuild.cpp | 195 ++++++++++++++++++++- devtools/create_project/msbuild.h | 14 +- devtools/create_project/xcode.cpp | 15 +- devtools/create_project/xcode.h | 6 +- 11 files changed, 396 insertions(+), 57 deletions(-) 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\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::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\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 pchMap; + + if (!files.empty()) { + projectFile << "\t\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\n"; + projectFile << "\t\t\tUse\n"; + projectFile << "\t\t\t" << pchIncludePath << "\n"; + projectFile << "\t\t\t" << pchOutputFileName << "\n"; + projectFile << "\t\t\n"; + } else { + projectFile << "\t\t\n"; + } + } + + // Flush PCH files + for (std::map::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\n"; + projectFile << "\t\t\tCreate\n"; + projectFile << "\t\t\t" << pchInfo.file << "\n"; + projectFile << "\t\t\t" << pchInfo.outputFile << "\n"; + projectFile << "\t\t\n"; + + outPCHFiles.push_back(cppFilePath); + } + + projectFile << "\t\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 FileEntries; + struct PCHInfo { + std::string file; + std::string outputFile; + }; + std::list _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,