diff options
Diffstat (limited to 'Source')
25 files changed, 1399 insertions, 142 deletions
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 27ca0ca..9eb99ea 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 6) -set(CMake_VERSION_PATCH 20160923) +set(CMake_VERSION_PATCH 20160927) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 2db94f1..b49da7f 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -71,14 +71,26 @@ int cmCPackNSISGenerator::PackageFiles() std::ostringstream str; std::vector<std::string>::const_iterator it; for (it = files.begin(); it != files.end(); ++it) { + std::string outputDir = "$INSTDIR"; std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(), it->c_str()); if (!this->Components.empty()) { + const std::string::size_type pos = fileN.find('/'); + + // Use the custom component install directory if we have one + if (pos != std::string::npos) { + const std::string componentName = fileN.substr(0, pos); + outputDir = CustomComponentInstallDirectory(componentName); + } else { + outputDir = CustomComponentInstallDirectory(fileN); + } + // Strip off the component part of the path. - fileN = fileN.substr(fileN.find('/') + 1, std::string::npos); + fileN = fileN.substr(pos + 1, std::string::npos); } std::replace(fileN.begin(), fileN.end(), '/', '\\'); - str << " Delete \"$INSTDIR\\" << fileN << "\"" << std::endl; + + str << " Delete \"" << outputDir << "\\" << fileN << "\"" << std::endl; } cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: " << str.str() << std::endl); @@ -108,7 +120,12 @@ int cmCPackNSISGenerator::PackageFiles() } } std::replace(fileN.begin(), fileN.end(), '/', '\\'); - dstr << " RMDir \"$INSTDIR\\" << fileN << "\"" << std::endl; + + const std::string componentOutputDir = + CustomComponentInstallDirectory(componentName); + + dstr << " RMDir \"" << componentOutputDir << "\\" << fileN << "\"" + << std::endl; if (!componentName.empty()) { this->Components[componentName].Directories.push_back(fileN); } @@ -650,7 +667,10 @@ std::string cmCPackNSISGenerator::CreateComponentDescription( } componentCode += " SectionIn" + out.str() + "\n"; } - componentCode += " SetOutPath \"$INSTDIR\"\n"; + + const std::string componentOutputDir = + CustomComponentInstallDirectory(component->Name); + componentCode += " SetOutPath \"" + componentOutputDir + "\"\n"; // Create the actual installation commands if (component->IsDownloaded) { @@ -796,13 +816,13 @@ std::string cmCPackNSISGenerator::CreateComponentDescription( ++pathIt) { path = *pathIt; std::replace(path.begin(), path.end(), '/', '\\'); - macrosOut << " Delete \"$INSTDIR\\" << path << "\"\n"; + macrosOut << " Delete \"" << componentOutputDir << "\\" << path << "\"\n"; } for (pathIt = component->Directories.begin(); pathIt != component->Directories.end(); ++pathIt) { path = *pathIt; std::replace(path.begin(), path.end(), '/', '\\'); - macrosOut << " RMDir \"$INSTDIR\\" << path << "\"\n"; + macrosOut << " RMDir \"" << componentOutputDir << "\\" << path << "\"\n"; } macrosOut << " noremove_" << component->Name << ":\n"; macrosOut << "!macroend\n"; @@ -914,6 +934,15 @@ std::string cmCPackNSISGenerator::CreateComponentGroupDescription( return code; } +std::string cmCPackNSISGenerator::CustomComponentInstallDirectory( + const std::string& componentName) +{ + const char* outputDir = + this->GetOption("CPACK_NSIS_" + componentName + "_INSTALL_DIRECTORY"); + const std::string componentOutputDir = (outputDir ? outputDir : "$INSTDIR"); + return componentOutputDir; +} + std::string cmCPackNSISGenerator::TranslateNewlines(std::string str) { cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n"); diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h index ae03e6b..bd7d752 100644 --- a/Source/CPack/cmCPackNSISGenerator.h +++ b/Source/CPack/cmCPackNSISGenerator.h @@ -84,6 +84,11 @@ protected: std::string CreateComponentGroupDescription(cmCPackComponentGroup* group, std::ostream& macrosOut); + /// Returns the custom install directory if available for the specified + /// component, otherwise $INSTDIR is returned. + std::string CustomComponentInstallDirectory( + const std::string& componentName); + /// Translations any newlines found in the string into \\r\\n, so that the /// resulting string can be used within NSIS. static std::string TranslateNewlines(std::string str); diff --git a/Source/Checks/cm_cxx_auto_ptr.cxx b/Source/Checks/cm_cxx_auto_ptr.cxx new file mode 100644 index 0000000..d3100fd --- /dev/null +++ b/Source/Checks/cm_cxx_auto_ptr.cxx @@ -0,0 +1,18 @@ +#include <memory> + +std::auto_ptr<int> get_auto_ptr() +{ + std::auto_ptr<int> ptr; + ptr = std::auto_ptr<int>(new int(0)); + return ptr; +} + +int use_auto_ptr(std::auto_ptr<int> ptr) +{ + return *ptr; +} + +int main() +{ + return use_auto_ptr(get_auto_ptr()); +} diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index c6a532f..80c9f3b 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -32,6 +32,7 @@ function(cm_check_cxx_feature name) endfunction() if(CMAKE_CXX_STANDARD) + cm_check_cxx_feature(auto_ptr) cm_check_cxx_feature(make_unique) if(CMake_HAVE_CXX_MAKE_UNIQUE) set(CMake_HAVE_CXX_UNIQUE_PTR 1) diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 4de4bef..52809b0 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -109,11 +109,13 @@ int main(int argc, char** argv) QTextCodec::setCodecForLocale(utf8_codec); #endif +#if QT_VERSION < 0x050000 // clean out standard Qt paths for plugins, which we don't use anyway // when creating Mac bundles, it potentially causes problems foreach (QString p, QApplication::libraryPaths()) { QApplication::removeLibraryPath(p); } +#endif // tell the cmake library where cmake is QDir cmExecDir(QApplication::applicationDirPath()); diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6523e3e..9b5248e 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -1506,22 +1506,17 @@ std::string cmCTest::Base64EncodeFile(std::string const& file) | std::ios::binary #endif ); - unsigned char* file_buffer = new unsigned char[len + 1]; - ifs.read(reinterpret_cast<char*>(file_buffer), len); + std::vector<char> file_buffer(len + 1); + ifs.read(&file_buffer[0], len); ifs.close(); - unsigned char* encoded_buffer = new unsigned char[(len * 3) / 2 + 5]; + std::vector<char> encoded_buffer((len * 3) / 2 + 5); - size_t const rlen = cmsysBase64_Encode(file_buffer, len, encoded_buffer, 1); + size_t const rlen = cmsysBase64_Encode( + reinterpret_cast<unsigned char*>(&file_buffer[0]), len, + reinterpret_cast<unsigned char*>(&encoded_buffer[0]), 1); - std::string base64 = ""; - for (size_t i = 0; i < rlen; i++) { - base64 += encoded_buffer[i]; - } - delete[] file_buffer; - delete[] encoded_buffer; - - return base64; + return std::string(&encoded_buffer[0], rlen); } bool cmCTest::SubmitExtraFiles(const VectorOfStrings& files) @@ -2795,48 +2790,42 @@ bool cmCTest::CompressString(std::string& str) int ret; z_stream strm; - unsigned char* in = - reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str())); - // zlib makes the guarantee that this is the maximum output size - int outSize = - static_cast<int>(static_cast<double>(str.size()) * 1.001 + 13.0); - unsigned char* out = new unsigned char[outSize]; - strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, -1); // default compression level if (ret != Z_OK) { - delete[] out; return false; } + unsigned char* in = + reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str())); + // zlib makes the guarantee that this is the maximum output size + int outSize = + static_cast<int>(static_cast<double>(str.size()) * 1.001 + 13.0); + std::vector<unsigned char> out(outSize); + strm.avail_in = static_cast<uInt>(str.size()); strm.next_in = in; strm.avail_out = outSize; - strm.next_out = out; + strm.next_out = &out[0]; ret = deflate(&strm, Z_FINISH); if (ret == Z_STREAM_ERROR || ret != Z_STREAM_END) { cmCTestLog(this, ERROR_MESSAGE, "Error during gzip compression." << std::endl); - delete[] out; return false; } (void)deflateEnd(&strm); // Now base64 encode the resulting binary string - unsigned char* base64EncodedBuffer = new unsigned char[(outSize * 3) / 2]; + std::vector<unsigned char> base64EncodedBuffer((outSize * 3) / 2); size_t rlen = - cmsysBase64_Encode(out, strm.total_out, base64EncodedBuffer, 1); - - str = ""; - str.append(reinterpret_cast<char*>(base64EncodedBuffer), rlen); + cmsysBase64_Encode(&out[0], strm.total_out, &base64EncodedBuffer[0], 1); - delete[] base64EncodedBuffer; - delete[] out; + str.assign(reinterpret_cast<char*>(&base64EncodedBuffer[0]), rlen); return true; } diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in index 8365367..8a1e81f 100644 --- a/Source/cmConfigure.cmake.h.in +++ b/Source/cmConfigure.cmake.h.in @@ -30,6 +30,7 @@ #cmakedefine CMAKE_USE_MACH_PARSER #cmakedefine CMAKE_USE_LIBUV #cmakedefine CMAKE_ENCODING_UTF8 +#cmakedefine CMake_HAVE_CXX_AUTO_PTR #cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE #cmakedefine CMake_HAVE_CXX_NULLPTR #cmakedefine CMake_HAVE_CXX_OVERRIDE diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4772474..95747f2 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -319,6 +319,12 @@ void cmGlobalGenerator::FindMakeProgram(cmMakefile* mf) } } +bool cmGlobalGenerator::CheckLanguages( + std::vector<std::string> const& /* languages */, cmMakefile* /* mf */) const +{ + return true; +} + // enable the given language // // The following files are loaded in this order: @@ -428,6 +434,10 @@ void cmGlobalGenerator::EnableLanguage( // find and make sure CMAKE_MAKE_PROGRAM is defined this->FindMakeProgram(mf); + if (!this->CheckLanguages(languages, mf)) { + return; + } + // try and load the CMakeSystem.cmake if it is there std::string fpath = rootBin; bool const readCMakeSystem = !mf->GetDefinition("CMAKE_SYSTEM_LOADED"); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index f7b2e59..7bc389d 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -381,6 +381,8 @@ protected: void SetLanguageEnabledFlag(const std::string& l, cmMakefile* mf); void SetLanguageEnabledMaps(const std::string& l, cmMakefile* mf); void FillExtensionToLanguageMap(const std::string& l, cmMakefile* mf); + virtual bool CheckLanguages(std::vector<std::string> const& languages, + cmMakefile* mf) const; virtual void PrintCompilerAdvice(std::ostream& os, std::string const& lang, const char* envVar) const; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index b913621..81690e7 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -14,6 +14,7 @@ #include "cmAlgorithms.h" #include "cmDocumentationEntry.h" +#include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" #include "cmGeneratorTarget.h" @@ -26,8 +27,12 @@ #include "cmTarget.h" #include "cmTargetDepend.h" #include "cmVersion.h" +#include "cm_auto_ptr.hxx" #include "cmake.h" +#include "cm_jsoncpp_reader.h" +#include "cm_jsoncpp_writer.h" + #include <algorithm> #include <ctype.h> #include <functional> @@ -473,6 +478,9 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) , UsingGCCOnWindows(false) , ComputingUnknownDependencies(false) , PolicyCMP0058(cmPolicies::WARN) + , NinjaSupportsConsolePool(false) + , NinjaSupportsImplicitOuts(false) + , NinjaSupportsDyndeps(0) { #ifdef _WIN32 cm->GetState()->SetWindowsShell(true); @@ -558,15 +566,81 @@ void cmGlobalNinjaGenerator::FindMakeProgram(cmMakefile* mf) cmSystemTools::RunSingleCommand(command, &version, CM_NULLPTR, CM_NULLPTR, CM_NULLPTR, cmSystemTools::OUTPUT_NONE); this->NinjaVersion = cmSystemTools::TrimWhitespace(version); + this->CheckNinjaFeatures(); } } +void cmGlobalNinjaGenerator::CheckNinjaFeatures() +{ + this->NinjaSupportsConsolePool = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForConsolePool().c_str()); + this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + this->RequiredNinjaVersionForImplicitOuts().c_str()); + { + // Our ninja branch adds ".dyndep-#" to its version number, + // where '#' is a feature-specific version number. Extract it. + static std::string const k_DYNDEP_ = ".dyndep-"; + std::string::size_type pos = this->NinjaVersion.find(k_DYNDEP_); + if (pos != std::string::npos) { + const char* fv = this->NinjaVersion.c_str() + pos + k_DYNDEP_.size(); + cmSystemTools::StringToULong(fv, &this->NinjaSupportsDyndeps); + } + } +} + +bool cmGlobalNinjaGenerator::CheckLanguages( + std::vector<std::string> const& languages, cmMakefile* mf) const +{ + if (std::find(languages.begin(), languages.end(), "Fortran") != + languages.end()) { + return this->CheckFortran(mf); + } + return true; +} + +bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const +{ + if (this->NinjaSupportsDyndeps == 1) { + return true; + } + + std::ostringstream e; + if (this->NinjaSupportsDyndeps == 0) { + /* clang-format off */ + e << + "The Ninja generator does not support Fortran using Ninja version\n" + " " + this->NinjaVersion + "\n" + "due to lack of required features. " + "Kitware has implemented the required features but as of this version " + "of CMake they have not been integrated to upstream ninja. " + "Pending integration, Kitware maintains a branch at:\n" + " https://github.com/Kitware/ninja/tree/features-for-fortran#readme\n" + "with the required features. " + "One may build ninja from that branch to get support for Fortran." + ; + /* clang-format on */ + } else { + /* clang-format off */ + e << + "The Ninja generator in this version of CMake does not support Fortran " + "using Ninja version\n" + " " + this->NinjaVersion + "\n" + "because its 'dyndep' feature version is " << + this->NinjaSupportsDyndeps << ". " + "This version of CMake is aware only of 'dyndep' feature version 1." + ; + /* clang-format on */ + } + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; +} + void cmGlobalNinjaGenerator::EnableLanguage( std::vector<std::string> const& langs, cmMakefile* mf, bool optional) { - if (std::find(langs.begin(), langs.end(), "Fortran") != langs.end()) { - cmSystemTools::Error("The Ninja generator does not support Fortran yet."); - } this->cmGlobalGenerator::EnableLanguage(langs, mf, optional); for (std::vector<std::string>::const_iterator l = langs.begin(); l != langs.end(); ++l) { @@ -1301,16 +1375,12 @@ std::string cmGlobalNinjaGenerator::ninjaCmd() const bool cmGlobalNinjaGenerator::SupportsConsolePool() const { - return !cmSystemTools::VersionCompare( - cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), - RequiredNinjaVersionForConsolePool().c_str()); + return this->NinjaSupportsConsolePool; } bool cmGlobalNinjaGenerator::SupportsImplicitOuts() const { - return !cmSystemTools::VersionCompare( - cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), - this->RequiredNinjaVersionForImplicitOuts().c_str()); + return this->NinjaSupportsImplicitOuts; } void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) @@ -1378,3 +1448,421 @@ void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix( EnsureTrailingSlash(path); cmStripSuffixIfExists(path, this->OutputPathPrefix); } + +/* + +We use the following approach to support Fortran. Each target already +has a <target>.dir/ directory used to hold intermediate files for CMake. +For each target, a FortranDependInfo.json file is generated by CMake with +information about include directories, module directories, and the locations +the per-target directories for target dependencies. + +Compilation of source files within a target is split into the following steps: + +1. Preprocess all sources, scan preprocessed output for module dependencies. + This step is done with independent build statements for each source, + and can therefore be done in parallel. + + rule Fortran_PREPROCESS + depfile = $DEP_FILE + command = gfortran -cpp $DEFINES $INCLUDES $FLAGS -E $in -o $out && + cmake -E cmake_ninja_depends \ + --tdi=FortranDependInfo.json --pp=$out --dep=$DEP_FILE \ + --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE + + build src.f90-pp.f90 | src.f90-pp.f90.ddi: Fortran_PREPROCESS src.f90 + OBJ_FILE = src.f90.o + DEP_FILE = src.f90-pp.f90.d + DYNDEP_INTERMEDIATE_FILE = src.f90-pp.f90.ddi + + The ``cmake -E cmake_ninja_depends`` tool reads the preprocessed output + and generates the ninja depfile for preprocessor dependencies. It also + generates a "ddi" file (in a format private to CMake) that lists the + object file that compilation will produce along with the module names + it provides and/or requires. The "ddi" file is an implicit output + because it should not appear in "$out" but is generated by the rule. + +2. Consolidate the per-source module dependencies saved in the "ddi" + files from all sources to produce a ninja "dyndep" file, ``Fortran.dd``. + + rule Fortran_DYNDEP + command = cmake -E cmake_ninja_dyndep \ + --tdi=FortranDependInfo.json --dd=$out $in + + build Fortran.dd: Fortran_DYNDEP src1.f90-pp.f90.ddi src2.f90-pp.f90.ddi + + The ``cmake -E cmake_ninja_dyndep`` tool reads the "ddi" files from all + sources in the target and the ``FortranModules.json`` files from targets + on which the target depends. It computes dependency edges on compilations + that require modules to those that provide the modules. This information + is placed in the ``Fortran.dd`` file for ninja to load later. It also + writes the expected location of modules provided by this target into + ``FortranModules.json`` for use by dependent targets. + +3. Compile all sources after loading dynamically discovered dependencies + of the compilation build statements from their ``dyndep`` bindings. + + rule Fortran_COMPILE + command = gfortran $INCLUDES $FLAGS -c $in -o $out + + build src1.f90.o: Fortran_COMPILE src1.f90-pp.f90 || Fortran.dd + dyndep = Fortran.dd + + The "dyndep" binding tells ninja to load dynamically discovered + dependency information from ``Fortran.dd``. This adds information + such as: + + build src1.f90.o | mod1.mod: dyndep + restat = 1 + + This tells ninja that ``mod1.mod`` is an implicit output of compiling + the object file ``src1.f90.o``. The ``restat`` binding tells it that + the timestamp of the output may not always change. Additionally: + + build src2.f90.o: dyndep | mod1.mod + + This tells ninja that ``mod1.mod`` is a dependency of compiling the + object file ``src2.f90.o``. This ensures that ``src1.f90.o`` and + ``mod1.mod`` will always be up to date before ``src2.f90.o`` is built + (because the latter consumes the module). +*/ + +int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd) +{ + std::string arg_tdi; + std::string arg_pp; + std::string arg_dep; + std::string arg_obj; + std::string arg_ddi; + for (std::vector<std::string>::const_iterator a = argBeg; a != argEnd; ++a) { + std::string const& arg = *a; + if (cmHasLiteralPrefix(arg, "--tdi=")) { + arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--pp=")) { + arg_pp = arg.substr(5); + } else if (cmHasLiteralPrefix(arg, "--dep=")) { + arg_dep = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--obj=")) { + arg_obj = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--ddi=")) { + arg_ddi = arg.substr(6); + } else { + cmSystemTools::Error("-E cmake_ninja_depends unknown argument: ", + arg.c_str()); + return 1; + } + } + if (arg_tdi.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --tdi="); + return 1; + } + if (arg_pp.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --pp="); + return 1; + } + if (arg_dep.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --dep="); + return 1; + } + if (arg_obj.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --obj="); + return 1; + } + if (arg_ddi.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --ddi="); + return 1; + } + + std::vector<std::string> includes; + { + Json::Value tdio; + Json::Value const& tdi = tdio; + { + cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(tdif, tdio, false)) { + cmSystemTools::Error("-E cmake_ninja_depends failed to parse ", + arg_tdi.c_str(), + reader.getFormattedErrorMessages().c_str()); + return 1; + } + } + + Json::Value const& tdi_include_dirs = tdi["include-dirs"]; + if (tdi_include_dirs.isArray()) { + for (Json::Value::const_iterator i = tdi_include_dirs.begin(); + i != tdi_include_dirs.end(); ++i) { + includes.push_back(i->asString()); + } + } + } + + cmFortranSourceInfo info; + std::set<std::string> defines; + cmFortranParser parser(includes, defines, info); + if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) { + cmSystemTools::Error("-E cmake_ninja_depends failed to open ", + arg_pp.c_str()); + return 1; + } + if (cmFortran_yyparse(parser.Scanner) != 0) { + // Failed to parse the file. + return 1; + } + + { + cmGeneratedFileStream depfile(arg_dep.c_str()); + depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":"; + for (std::set<std::string>::iterator i = info.Includes.begin(); + i != info.Includes.end(); ++i) { + depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(*i); + } + depfile << "\n"; + } + + Json::Value ddi(Json::objectValue); + ddi["object"] = arg_obj; + + Json::Value& ddi_provides = ddi["provides"] = Json::arrayValue; + for (std::set<std::string>::iterator i = info.Provides.begin(); + i != info.Provides.end(); ++i) { + ddi_provides.append(*i); + } + Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue; + for (std::set<std::string>::iterator i = info.Requires.begin(); + i != info.Requires.end(); ++i) { + // Require modules not provided in the same source. + if (!info.Provides.count(*i)) { + ddi_requires.append(*i); + } + } + + cmGeneratedFileStream ddif(arg_ddi.c_str()); + ddif << ddi; + if (!ddif) { + cmSystemTools::Error("-E cmake_ninja_depends failed to write ", + arg_ddi.c_str()); + return 1; + } + return 0; +} + +struct cmFortranObjectInfo +{ + std::string Object; + std::vector<std::string> Provides; + std::vector<std::string> Requires; +}; + +bool cmGlobalNinjaGenerator::WriteDyndepFile( + std::string const& dir_top_src, std::string const& dir_top_bld, + std::string const& dir_cur_src, std::string const& dir_cur_bld, + std::string const& arg_dd, std::vector<std::string> const& arg_ddis, + std::string const& module_dir, + std::vector<std::string> const& linked_target_dirs) +{ + // Setup path conversions. + { + cmState::Snapshot snapshot = + this->GetCMakeInstance()->GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentSource(dir_cur_src); + snapshot.GetDirectory().SetCurrentBinary(dir_cur_bld); + snapshot.GetDirectory().SetRelativePathTopSource(dir_top_src.c_str()); + snapshot.GetDirectory().SetRelativePathTopBinary(dir_top_bld.c_str()); + CM_AUTO_PTR<cmMakefile> mfd(new cmMakefile(this, snapshot)); + CM_AUTO_PTR<cmLocalNinjaGenerator> lgd(static_cast<cmLocalNinjaGenerator*>( + this->CreateLocalGenerator(mfd.get()))); + this->Makefiles.push_back(mfd.release()); + this->LocalGenerators.push_back(lgd.release()); + } + + std::vector<cmFortranObjectInfo> objects; + for (std::vector<std::string>::const_iterator ddii = arg_ddis.begin(); + ddii != arg_ddis.end(); ++ddii) { + // Load the ddi file and compute the module file paths it provides. + Json::Value ddio; + Json::Value const& ddi = ddio; + cmsys::ifstream ddif(ddii->c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(ddif, ddio, false)) { + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", + ddii->c_str(), + reader.getFormattedErrorMessages().c_str()); + return false; + } + + cmFortranObjectInfo info; + info.Object = ddi["object"].asString(); + Json::Value const& ddi_provides = ddi["provides"]; + if (ddi_provides.isArray()) { + for (Json::Value::const_iterator i = ddi_provides.begin(); + i != ddi_provides.end(); ++i) { + info.Provides.push_back(i->asString()); + } + } + Json::Value const& ddi_requires = ddi["requires"]; + if (ddi_requires.isArray()) { + for (Json::Value::const_iterator i = ddi_requires.begin(); + i != ddi_requires.end(); ++i) { + info.Requires.push_back(i->asString()); + } + } + objects.push_back(info); + } + + // Map from module name to module file path, if known. + std::map<std::string, std::string> mod_files; + + // Populate the module map with those provided by linked targets first. + for (std::vector<std::string>::const_iterator di = + linked_target_dirs.begin(); + di != linked_target_dirs.end(); ++di) { + std::string const ltmn = *di + "/FortranModules.json"; + Json::Value ltm; + cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (ltmf && !reader.parse(ltmf, ltm, false)) { + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", + di->c_str(), + reader.getFormattedErrorMessages().c_str()); + return false; + } + if (ltm.isObject()) { + for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) { + mod_files[i.key().asString()] = i->asString(); + } + } + } + + // Extend the module map with those provided by this target. + // We do this after loading the modules provided by linked targets + // in case we have one of the same name that must be preferred. + Json::Value tm = Json::objectValue; + for (std::vector<cmFortranObjectInfo>::iterator oi = objects.begin(); + oi != objects.end(); ++oi) { + for (std::vector<std::string>::iterator i = oi->Provides.begin(); + i != oi->Provides.end(); ++i) { + std::string const mod = module_dir + *i + ".mod"; + mod_files[*i] = mod; + tm[*i] = mod; + } + } + + cmGeneratedFileStream ddf(arg_dd.c_str()); + ddf << "ninja_dyndep_version = 1.0\n"; + + for (std::vector<cmFortranObjectInfo>::iterator oi = objects.begin(); + oi != objects.end(); ++oi) { + std::string const ddComment; + std::string const ddRule = "dyndep"; + cmNinjaDeps ddOutputs; + cmNinjaDeps ddImplicitOuts; + cmNinjaDeps ddExplicitDeps; + cmNinjaDeps ddImplicitDeps; + cmNinjaDeps ddOrderOnlyDeps; + cmNinjaVars ddVars; + + ddOutputs.push_back(oi->Object); + for (std::vector<std::string>::iterator i = oi->Provides.begin(); + i != oi->Provides.end(); ++i) { + ddImplicitOuts.push_back(this->ConvertToNinjaPath(mod_files[*i])); + } + for (std::vector<std::string>::iterator i = oi->Requires.begin(); + i != oi->Requires.end(); ++i) { + std::map<std::string, std::string>::iterator m = mod_files.find(*i); + if (m != mod_files.end()) { + ddImplicitDeps.push_back(this->ConvertToNinjaPath(m->second)); + } + } + if (!oi->Provides.empty()) { + ddVars["restat"] = "1"; + } + + this->WriteBuild(ddf, ddComment, ddRule, ddOutputs, ddImplicitOuts, + ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + } + + // Store the map of modules provided by this target in a file for + // use by dependents that reference this target in linked-target-dirs. + std::string const target_mods_file = + cmSystemTools::GetFilenamePath(arg_dd) + "/FortranModules.json"; + cmGeneratedFileStream tmf(target_mods_file.c_str()); + tmf << tm; + + return true; +} + +int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd) +{ + std::string arg_dd; + std::string arg_tdi; + std::vector<std::string> arg_ddis; + for (std::vector<std::string>::const_iterator a = argBeg; a != argEnd; ++a) { + std::string const& arg = *a; + if (cmHasLiteralPrefix(arg, "--tdi=")) { + arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--dd=")) { + arg_dd = arg.substr(5); + } else if (!cmHasLiteralPrefix(arg, "--") && + cmHasLiteralSuffix(arg, ".ddi")) { + arg_ddis.push_back(arg); + } else { + cmSystemTools::Error("-E cmake_ninja_dyndep unknown argument: ", + arg.c_str()); + return 1; + } + } + if (arg_tdi.empty()) { + cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --tdi="); + return 1; + } + if (arg_dd.empty()) { + cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --dd="); + return 1; + } + + Json::Value tdio; + Json::Value const& tdi = tdio; + { + cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(tdif, tdio, false)) { + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", + arg_tdi.c_str(), + reader.getFormattedErrorMessages().c_str()); + return 1; + } + } + + std::string const dir_cur_bld = tdi["dir-cur-bld"].asString(); + std::string const dir_cur_src = tdi["dir-cur-src"].asString(); + std::string const dir_top_bld = tdi["dir-top-bld"].asString(); + std::string const dir_top_src = tdi["dir-top-src"].asString(); + std::string module_dir = tdi["module-dir"].asString(); + if (!module_dir.empty()) { + module_dir += "/"; + } + std::vector<std::string> linked_target_dirs; + Json::Value const& tdi_linked_target_dirs = tdi["linked-target-dirs"]; + if (tdi_linked_target_dirs.isArray()) { + for (Json::Value::const_iterator i = tdi_linked_target_dirs.begin(); + i != tdi_linked_target_dirs.end(); ++i) { + linked_target_dirs.push_back(i->asString()); + } + } + + cmake cm; + cm.SetHomeDirectory(dir_top_src); + cm.SetHomeOutputDirectory(dir_top_bld); + CM_AUTO_PTR<cmGlobalNinjaGenerator> ggd( + static_cast<cmGlobalNinjaGenerator*>(cm.CreateGlobalGenerator("Ninja"))); + if (!ggd.get() || + !ggd->WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, + arg_dd, arg_ddis, module_dir, + linked_target_dirs)) { + return 1; + } + return 0; +} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 0201685..76430a0 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -347,6 +347,15 @@ public: bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } void StripNinjaOutputPathPrefixAsSuffix(std::string& path); + bool WriteDyndepFile(std::string const& dir_top_src, + std::string const& dir_top_bld, + std::string const& dir_cur_src, + std::string const& dir_cur_bld, + std::string const& arg_dd, + std::vector<std::string> const& arg_ddis, + std::string const& module_dir, + std::vector<std::string> const& linked_target_dirs); + protected: void Generate() CM_OVERRIDE; @@ -355,6 +364,10 @@ protected: private: std::string GetEditCacheCommand() const CM_OVERRIDE; void FindMakeProgram(cmMakefile* mf) CM_OVERRIDE; + void CheckNinjaFeatures(); + bool CheckLanguages(std::vector<std::string> const& languages, + cmMakefile* mf) const CM_OVERRIDE; + bool CheckFortran(cmMakefile* mf) const; void OpenBuildFileStream(); void CloseBuildFileStream(); @@ -439,6 +452,9 @@ private: std::string NinjaCommand; std::string NinjaVersion; + bool NinjaSupportsConsolePool; + bool NinjaSupportsImplicitOuts; + unsigned long NinjaSupportsDyndeps; private: void InitOutputPathPrefix(); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 997f46c..0d5de06 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -2996,6 +2996,15 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( buildSettings->AddAttribute("GCC_VERSION", this->CreateString(this->GeneratorToolset)); } + if (this->GetLanguageEnabled("Swift")) { + std::string swiftVersion = "2.3"; + if (const char* vers = this->CurrentMakefile->GetDefinition( + "CMAKE_Swift_LANGUAGE_VERSION")) { + swiftVersion = vers; + } + buildSettings->AddAttribute("SWIFT_VERSION", + this->CreateString(swiftVersion)); + } std::string symroot = root->GetCurrentBinaryDirectory(); symroot += "/build"; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index fb09bfe..f88eb7b 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -29,6 +29,8 @@ #include "cmSystemTools.h" #include "cmake.h" +#include "cm_jsoncpp_writer.h" + #include <algorithm> #include <iterator> #include <map> @@ -93,6 +95,31 @@ std::string cmNinjaTargetGenerator::LanguageCompilerRule( cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); } +std::string cmNinjaTargetGenerator::LanguagePreprocessRule( + std::string const& lang) const +{ + return lang + "_PREPROCESS__" + + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); +} + +bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( + std::string const& lang) const +{ + return lang == "Fortran"; +} + +std::string cmNinjaTargetGenerator::LanguageDyndepRule( + const std::string& lang) const +{ + return lang + "_DYNDEP__" + + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); +} + +bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang) const +{ + return lang == "Fortran"; +} + std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget() { return "cmake_order_depends_target_" + this->GetTargetName(); @@ -229,6 +256,64 @@ std::string cmNinjaTargetGenerator::GetObjectFilePath( return path; } +std::string cmNinjaTargetGenerator::GetPreprocessedFilePath( + cmSourceFile const* source) const +{ + // Choose an extension to compile already-preprocessed source. + std::string ppExt = source->GetExtension(); + if (cmHasLiteralPrefix(ppExt, "F")) { + // Some Fortran compilers automatically enable preprocessing for + // upper-case extensions. Since the source is already preprocessed, + // use a lower-case extension. + ppExt = cmSystemTools::LowerCase(ppExt); + } + if (ppExt == "fpp") { + // Some Fortran compilers automatically enable preprocessing for + // the ".fpp" extension. Since the source is already preprocessed, + // use the ".f" extension. + ppExt = "f"; + } + + // Take the object file name and replace the extension. + std::string const& objName = this->GeneratorTarget->GetObjectName(source); + std::string const& objExt = + this->GetGlobalGenerator()->GetLanguageOutputExtension(*source); + assert(objName.size() >= objExt.size()); + std::string const ppName = + objName.substr(0, objName.size() - objExt.size()) + "-pp." + ppExt; + + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/"; + path += ppName; + return path; +} + +std::string cmNinjaTargetGenerator::GetDyndepFilePath( + std::string const& lang) const +{ + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/"; + path += lang; + path += ".dd"; + return path; +} + +std::string cmNinjaTargetGenerator::GetTargetDependInfoPath( + std::string const& lang) const +{ + std::string path = this->Makefile->GetCurrentBinaryDirectory(); + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/" + lang + "DependInfo.json"; + return path; +} + std::string cmNinjaTargetGenerator::GetTargetOutputDir() const { std::string dir = this->GeneratorTarget->GetDirectory(this->GetConfigName()); @@ -311,6 +396,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.ObjectDir = "$OBJECT_DIR"; vars.ObjectFileDir = "$OBJECT_FILE_DIR"; + // For some cases we do an explicit preprocessor invocation. + bool const explicitPP = this->NeedExplicitPreprocessing(lang); + bool const needDyndep = this->NeedDyndep(lang); + cmMakefile* mf = this->GetMakefile(); std::string flags = "$FLAGS"; @@ -331,7 +420,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string deptype; std::string depfile; std::string cldeps; - if (this->NeedDepTypeMSVC(lang)) { + if (explicitPP) { + // The explicit preprocessing step will handle dependency scanning. + } else if (this->NeedDepTypeMSVC(lang)) { deptype = "msvc"; depfile = ""; flags += " /showIncludes"; @@ -371,6 +462,109 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.Flags = flags.c_str(); vars.DependencyFile = depfile.c_str(); + std::string const tdi = this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(this->GetTargetDependInfoPath(lang)), + cmLocalGenerator::SHELL); + + if (explicitPP) { + // Lookup the explicit preprocessing rule. + std::string const ppVar = "CMAKE_" + lang + "_PREPROCESS_SOURCE"; + std::string const ppCmd = + this->GetMakefile()->GetRequiredDefinition(ppVar); + + // Explicit preprocessing always uses a depfile. + std::string const ppDeptype = ""; // no deps= for multiple outputs + std::string const ppDepfile = "$DEP_FILE"; + + cmLocalGenerator::RuleVariables ppVars; + ppVars.RuleLauncher = vars.RuleLauncher; + ppVars.CMTarget = vars.CMTarget; + ppVars.Language = vars.Language; + ppVars.Object = "$out"; // for RULE_LAUNCH_COMPILE + ppVars.PreprocessedSource = "$out"; + ppVars.DependencyFile = ppDepfile.c_str(); + + // Preprocessing uses the original source, + // compilation uses preprocessed output. + ppVars.Source = vars.Source; + vars.Source = "$in"; + + // Preprocessing and compilation use the same flags. + ppVars.Flags = vars.Flags; + + // Move preprocessor definitions to the preprocessor rule. + ppVars.Defines = vars.Defines; + vars.Defines = ""; + + // Copy include directories to the preprocessor rule. The Fortran + // compilation rule still needs them for the INCLUDE directive. + ppVars.Includes = vars.Includes; + + // Rule for preprocessing source file. + std::vector<std::string> ppCmds; + cmSystemTools::ExpandListArgument(ppCmd, ppCmds); + + for (std::vector<std::string>::iterator i = ppCmds.begin(); + i != ppCmds.end(); ++i) { + this->GetLocalGenerator()->ExpandRuleVariables(*i, ppVars); + } + + // Run CMake dependency scanner on preprocessed output. + std::string const cmake = this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); + ppCmds.push_back( + cmake + " -E cmake_ninja_depends" + " --tdi=" + + tdi + " --pp=$out" + " --dep=$DEP_FILE" + + (needDyndep ? " --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE" : "")); + + std::string const ppCmdLine = + this->GetLocalGenerator()->BuildCommandLine(ppCmds); + + // Write the rule for preprocessing file of the given language. + std::ostringstream ppComment; + ppComment << "Rule for preprocessing " << lang << " files."; + std::ostringstream ppDesc; + ppDesc << "Building " << lang << " preprocessed $out"; + this->GetGlobalGenerator()->AddRule(this->LanguagePreprocessRule(lang), + ppCmdLine, ppDesc.str(), + ppComment.str(), ppDepfile, ppDeptype, + /*rspfile*/ "", + /*rspcontent*/ "", + /*restat*/ "", + /*generator*/ false); + } + + if (needDyndep) { + // Write the rule for ninja dyndep file generation. + std::vector<std::string> ddCmds; + + // Run CMake dependency scanner on preprocessed output. + std::string const cmake = this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); + ddCmds.push_back(cmake + " -E cmake_ninja_dyndep" + " --tdi=" + + tdi + " --dd=$out" + " $in"); + + std::string const ddCmdLine = + this->GetLocalGenerator()->BuildCommandLine(ddCmds); + + std::ostringstream ddComment; + ddComment << "Rule to generate ninja dyndep files for " << lang << "."; + std::ostringstream ddDesc; + ddDesc << "Generating " << lang << " dyndep file $out"; + this->GetGlobalGenerator()->AddRule( + this->LanguageDyndepRule(lang), ddCmdLine, ddDesc.str(), ddComment.str(), + /*depfile*/ "", + /*deps*/ "", + /*rspfile*/ "", + /*rspcontent*/ "", + /*restat*/ "", + /*generator*/ false); + } + // Rule for compiling object file. const std::string cmdVar = std::string("CMAKE_") + lang + "_COMPILE_OBJECT"; std::string compileCmd = mf->GetRequiredDefinition(cmdVar); @@ -519,6 +713,25 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() this->WriteObjectBuildStatement(*si, !orderOnlyDeps.empty()); } + if (!this->DDIFiles.empty()) { + std::string const ddComment; + std::string const ddRule = this->LanguageDyndepRule("Fortran"); + cmNinjaDeps ddOutputs; + cmNinjaDeps ddImplicitOuts; + cmNinjaDeps const& ddExplicitDeps = this->DDIFiles; + cmNinjaDeps ddImplicitDeps; + cmNinjaDeps ddOrderOnlyDeps; + cmNinjaVars ddVars; + + this->WriteTargetDependInfo("Fortran"); + + ddOutputs.push_back(this->GetDyndepFilePath("Fortran")); + + this->GetGlobalGenerator()->WriteBuild( + this->GetBuildFileStream(), ddComment, ddRule, ddOutputs, ddImplicitOuts, + ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + } + this->GetBuildFileStream() << "\n"; } @@ -589,6 +802,89 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( orderOnlyDeps); } + // For some cases we need to generate a ninja dyndep file. + bool const needDyndep = this->NeedDyndep(language); + + // For some cases we do an explicit preprocessor invocation. + bool const explicitPP = this->NeedExplicitPreprocessing(language); + if (explicitPP) { + std::string const ppComment; + std::string const ppRule = this->LanguagePreprocessRule(language); + cmNinjaDeps ppOutputs; + cmNinjaDeps ppImplicitOuts; + cmNinjaDeps ppExplicitDeps; + cmNinjaDeps ppImplicitDeps; + cmNinjaDeps ppOrderOnlyDeps; + cmNinjaVars ppVars; + + std::string const ppFileName = + this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source)); + ppOutputs.push_back(ppFileName); + + // Move compilation dependencies to the preprocessing build statement. + std::swap(ppExplicitDeps, explicitDeps); + std::swap(ppImplicitDeps, implicitDeps); + std::swap(ppOrderOnlyDeps, orderOnlyDeps); + std::swap(ppVars["IN_ABS"], vars["IN_ABS"]); + + // The actual compilation will now use the preprocessed source. + explicitDeps.push_back(ppFileName); + + // Preprocessing and compilation use the same flags. + ppVars["FLAGS"] = vars["FLAGS"]; + + // Move preprocessor definitions to the preprocessor build statement. + std::swap(ppVars["DEFINES"], vars["DEFINES"]); + + // Copy include directories to the preprocessor build statement. The + // Fortran compilation build statement still needs them for the INCLUDE + // directive. + ppVars["INCLUDES"] = vars["INCLUDES"]; + + // Prepend source file's original directory as an include directory + // so e.g. Fortran INCLUDE statements can look for files in it. + std::vector<std::string> sourceDirectory; + sourceDirectory.push_back( + cmSystemTools::GetParentDirectory(source->GetFullPath())); + + std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags( + sourceDirectory, this->GeneratorTarget, language, false, false, + this->GetConfigName()); + + vars["INCLUDES"] = sourceDirectoryFlag + " " + vars["INCLUDES"]; + + // Explicit preprocessing always uses a depfile. + ppVars["DEP_FILE"] = + cmGlobalNinjaGenerator::EncodeDepfileSpace(ppFileName + ".d"); + // The actual compilation does not need a depfile because it + // depends on the already-preprocessed source. + vars.erase("DEP_FILE"); + + if (needDyndep) { + // Tell dependency scanner the object file that will result from + // compiling the preprocessed source. + ppVars["OBJ_FILE"] = objectFileName; + + // Tell dependency scanner where to store dyndep intermediate results. + std::string const ddiFile = ppFileName + ".ddi"; + ppVars["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + ppImplicitOuts.push_back(ddiFile); + this->DDIFiles.push_back(ddiFile); + } + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + ppVars); + + this->GetGlobalGenerator()->WriteBuild( + this->GetBuildFileStream(), ppComment, ppRule, ppOutputs, ppImplicitOuts, + ppExplicitDeps, ppImplicitDeps, ppOrderOnlyDeps, ppVars); + } + if (needDyndep) { + std::string const dyndep = this->GetDyndepFilePath(language); + orderOnlyDeps.push_back(dyndep); + vars["dyndep"] = dyndep; + } + EnsureParentDirectoryExists(objectFileName); vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( @@ -622,6 +918,49 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } +void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang) +{ + Json::Value tdi(Json::objectValue); + tdi["language"] = lang; + tdi["compiler-id"] = + this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID"); + + if (lang == "Fortran") { + std::string mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( + this->Makefile->GetHomeOutputDirectory()); + if (mod_dir.empty()) { + mod_dir = this->Makefile->GetCurrentBinaryDirectory(); + } + tdi["module-dir"] = mod_dir; + } + + tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory(); + tdi["dir-cur-src"] = this->Makefile->GetCurrentSourceDirectory(); + tdi["dir-top-bld"] = this->Makefile->GetHomeOutputDirectory(); + tdi["dir-top-src"] = this->Makefile->GetHomeDirectory(); + + Json::Value& tdi_include_dirs = tdi["include-dirs"] = Json::arrayValue; + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, + lang, this->GetConfigName()); + for (std::vector<std::string>::iterator i = includes.begin(); + i != includes.end(); ++i) { + tdi_include_dirs.append(*i); + } + + Json::Value& tdi_linked_target_dirs = tdi["linked-target-dirs"] = + Json::arrayValue; + std::vector<std::string> linked = this->GetLinkedTargetDirectories(); + for (std::vector<std::string>::iterator i = linked.begin(); + i != linked.end(); ++i) { + tdi_linked_target_dirs.append(*i); + } + + std::string const tdin = this->GetTargetDependInfoPath(lang); + cmGeneratedFileStream tdif(tdin.c_str()); + tdif << tdi; +} + void cmNinjaTargetGenerator::ExportObjectCompileCommand( std::string const& language, std::string const& sourceFileName, std::string const& objectDir, std::string const& objectFileName, diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 2b26788..f5e6511 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -70,6 +70,10 @@ protected: cmMakefile* GetMakefile() const { return this->Makefile; } std::string LanguageCompilerRule(const std::string& lang) const; + std::string LanguagePreprocessRule(std::string const& lang) const; + bool NeedExplicitPreprocessing(std::string const& lang) const; + std::string LanguageDyndepRule(std::string const& lang) const; + bool NeedDyndep(std::string const& lang) const; std::string OrderDependsTargetForTarget(); @@ -107,6 +111,15 @@ protected: /// @return the object file path for the given @a source. std::string GetObjectFilePath(cmSourceFile const* source) const; + /// @return the preprocessed source file path for the given @a source. + std::string GetPreprocessedFilePath(cmSourceFile const* source) const; + + /// @return the dyndep file path for this target. + std::string GetDyndepFilePath(std::string const& lang) const; + + /// @return the target dependency scanner info file path + std::string GetTargetDependInfoPath(std::string const& lang) const; + /// @return the file path where the target named @a name is generated. std::string GetTargetFilePath(const std::string& name) const; @@ -118,6 +131,7 @@ protected: void WriteObjectBuildStatements(); void WriteObjectBuildStatement(cmSourceFile const* source, bool writeOrderDependsTargetForTarget); + void WriteTargetDependInfo(std::string const& lang); void ExportObjectCompileCommand( std::string const& language, std::string const& sourceFileName, @@ -161,6 +175,7 @@ private: cmLocalNinjaGenerator* LocalGenerator; /// List of object files for this target. cmNinjaDeps Objects; + cmNinjaDeps DDIFiles; // TODO: Make per-language. std::vector<cmCustomCommand const*> CustomCommands; cmNinjaDeps ExtraFiles; }; diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 56cd7ba..46c1946 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -14,6 +14,7 @@ #include "cmServer.h" #include "cmServerConnection.h" +#include "cmServerDictionary.h" #include "cmServerProtocol.h" #include "cmSystemTools.h" #include "cmVersionMacros.h" @@ -28,19 +29,6 @@ #include <iostream> #include <memory> -static const std::string kTYPE_KEY = "type"; -static const std::string kCOOKIE_KEY = "cookie"; -static const std::string kREPLY_TO_KEY = "inReplyTo"; -static const std::string kERROR_MESSAGE_KEY = "errorMessage"; - -static const std::string kERROR_TYPE = "error"; -static const std::string kREPLY_TYPE = "reply"; -static const std::string kPROGRESS_TYPE = "progress"; -static const std::string kMESSAGE_TYPE = "message"; - -static const std::string kSTART_MAGIC = "[== CMake Server ==["; -static const std::string kEND_MAGIC = "]== CMake Server ==]"; - class cmServer::DebugInfo { public: @@ -135,8 +123,9 @@ void cmServer::RegisterProtocol(cmServerProtocol* protocol) [version](cmServerProtocol* p) { return p->ProtocolVersion() == version; }); - if (it == this->SupportedProtocols.end()) + if (it == this->SupportedProtocols.end()) { this->SupportedProtocols.push_back(protocol); + } } void cmServer::PrintHello() const @@ -144,16 +133,16 @@ void cmServer::PrintHello() const Json::Value hello = Json::objectValue; hello[kTYPE_KEY] = "hello"; - Json::Value& protocolVersions = hello["supportedProtocolVersions"] = + Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] = Json::arrayValue; for (auto const& proto : this->SupportedProtocols) { auto version = proto->ProtocolVersion(); Json::Value tmp = Json::objectValue; - tmp["major"] = version.first; - tmp["minor"] = version.second; + tmp[kMAJOR_KEY] = version.first; + tmp[kMINOR_KEY] = version.second; if (proto->IsExperimental()) { - tmp["experimental"] = true; + tmp[kIS_EXPERIMENTAL_KEY] = true; } protocolVersions.append(tmp); } @@ -193,31 +182,44 @@ void cmServer::reportMessage(const char* msg, const char* title, cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) { - if (request.Type != "handshake") - return request.ReportError("Waiting for type \"handshake\"."); + if (request.Type != kHANDSHAKE_TYPE) { + return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE + + "\"."); + } - Json::Value requestedProtocolVersion = request.Data["protocolVersion"]; - if (requestedProtocolVersion.isNull()) - return request.ReportError( - "\"protocolVersion\" is required for \"handshake\"."); + Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY]; + if (requestedProtocolVersion.isNull()) { + return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + + "\" is required for \"" + kHANDSHAKE_TYPE + + "\"."); + } - if (!requestedProtocolVersion.isObject()) - return request.ReportError("\"protocolVersion\" must be a JSON object."); + if (!requestedProtocolVersion.isObject()) { + return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + + "\" must be a JSON object."); + } - Json::Value majorValue = requestedProtocolVersion["major"]; - if (!majorValue.isInt()) - return request.ReportError("\"major\" must be set and an integer."); + Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY]; + if (!majorValue.isInt()) { + return request.ReportError("\"" + kMAJOR_KEY + + "\" must be set and an integer."); + } - Json::Value minorValue = requestedProtocolVersion["minor"]; - if (!minorValue.isNull() && !minorValue.isInt()) - return request.ReportError("\"minor\" must be unset or an integer."); + Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY]; + if (!minorValue.isNull() && !minorValue.isInt()) { + return request.ReportError("\"" + kMINOR_KEY + + "\" must be unset or an integer."); + } const int major = majorValue.asInt(); const int minor = minorValue.isNull() ? -1 : minorValue.asInt(); - if (major < 0) - return request.ReportError("\"major\" must be >= 0."); - if (!minorValue.isNull() && minor < 0) - return request.ReportError("\"minor\" must be >= 0 when set."); + if (major < 0) { + return request.ReportError("\"" + kMAJOR_KEY + "\" must be >= 0."); + } + if (!minorValue.isNull() && minor < 0) { + return request.ReportError("\"" + kMINOR_KEY + + "\" must be >= 0 when set."); + } this->Protocol = this->FindMatchingProtocol(this->SupportedProtocols, major, minor); @@ -226,7 +228,7 @@ cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) } std::string errorMessage; - if (!this->Protocol->Activate(request, &errorMessage)) { + if (!this->Protocol->Activate(this, request, &errorMessage)) { this->Protocol = CM_NULLPTR; return request.ReportError("Failed to activate protocol version: " + errorMessage); @@ -290,12 +292,15 @@ cmServerProtocol* cmServer::FindMatchingProtocol( cmServerProtocol* bestMatch = nullptr; for (auto protocol : protocols) { auto version = protocol->ProtocolVersion(); - if (major != version.first) + if (major != version.first) { continue; - if (minor == version.second) + } + if (minor == version.second) { return protocol; - if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) + } + if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) { bestMatch = protocol; + } } return minor < 0 ? bestMatch : nullptr; } @@ -311,10 +316,10 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min, obj[kTYPE_KEY] = kPROGRESS_TYPE; obj[kREPLY_TO_KEY] = request.Type; obj[kCOOKIE_KEY] = request.Cookie; - obj["progressMessage"] = message; - obj["progressMinimum"] = min; - obj["progressMaximum"] = max; - obj["progressCurrent"] = current; + obj[kPROGRESS_MESSAGE_KEY] = message; + obj[kPROGRESS_MINIMUM_KEY] = min; + obj[kPROGRESS_MAXIMUM_KEY] = max; + obj[kPROGRESS_CURRENT_KEY] = current; this->WriteJsonObject(obj, nullptr); } @@ -323,16 +328,17 @@ void cmServer::WriteMessage(const cmServerRequest& request, const std::string& message, const std::string& title) const { - if (message.empty()) + if (message.empty()) { return; + } Json::Value obj = Json::objectValue; obj[kTYPE_KEY] = kMESSAGE_TYPE; obj[kREPLY_TO_KEY] = request.Type; obj[kCOOKIE_KEY] = request.Cookie; - obj["message"] = message; + obj[kMESSAGE_KEY] = message; if (!title.empty()) { - obj["title"] = title; + obj[kTITLE_KEY] = title; } WriteJsonObject(obj, nullptr); @@ -349,6 +355,19 @@ void cmServer::WriteParseError(const std::string& message) const this->WriteJsonObject(obj, nullptr); } +void cmServer::WriteSignal(const std::string& name, + const Json::Value& data) const +{ + assert(data.isObject()); + Json::Value obj = data; + obj[kTYPE_KEY] = kSIGNAL_TYPE; + obj[kREPLY_TO_KEY] = ""; + obj[kCOOKIE_KEY] = ""; + obj[kNAME_KEY] = name; + + WriteJsonObject(obj, nullptr); +} + void cmServer::WriteResponse(const cmServerResponse& response, const DebugInfo* debug) const { diff --git a/Source/cmServer.h b/Source/cmServer.h index dde5333..849e5c5 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -63,6 +63,7 @@ private: void WriteResponse(const cmServerResponse& response, const DebugInfo* debug) const; void WriteParseError(const std::string& message) const; + void WriteSignal(const std::string& name, const Json::Value& obj) const; void WriteJsonObject(Json::Value const& jsonValue, const DebugInfo* debug) const; @@ -95,6 +96,7 @@ private: mutable bool Writing = false; - friend class cmServerRequest; friend class cmServerConnection; + friend class cmServerProtocol; + friend class cmServerRequest; }; diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx index 398e250..0b3db9f 100644 --- a/Source/cmServerConnection.cxx +++ b/Source/cmServerConnection.cxx @@ -13,15 +13,14 @@ #include "cmServerConnection.h" +#include "cmServerDictionary.h" + #include <cmServer.h> #include <assert.h> namespace { -static const std::string kSTART_MAGIC = "[== CMake Server ==["; -static const std::string kEND_MAGIC = "]== CMake Server ==]"; - struct write_req_t { uv_write_t req; @@ -147,8 +146,9 @@ void cmServerConnection::ReadData(const std::string& data) } std::string line = this->RawReadBuffer.substr(0, needle); const auto ls = line.size(); - if (ls > 1 && line.at(ls - 1) == '\r') + if (ls > 1 && line.at(ls - 1) == '\r') { line.erase(ls - 1, 1); + } this->RawReadBuffer.erase(this->RawReadBuffer.begin(), this->RawReadBuffer.begin() + static_cast<long>(needle) + 1); diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h new file mode 100644 index 0000000..fc1b44d --- /dev/null +++ b/Source/cmServerDictionary.h @@ -0,0 +1,62 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2016 Tobias Hunger <tobias.hunger@qt.io> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#pragma once + +#include <string> + +// Vocabulary: + +static const std::string kCOMPUTE_TYPE = "compute"; +static const std::string kCONFIGURE_TYPE = "configure"; +static const std::string kERROR_TYPE = "error"; +static const std::string kGLOBAL_SETTINGS_TYPE = "globalSettings"; +static const std::string kHANDSHAKE_TYPE = "handshake"; +static const std::string kMESSAGE_TYPE = "message"; +static const std::string kPROGRESS_TYPE = "progress"; +static const std::string kREPLY_TYPE = "reply"; +static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings"; +static const std::string kSIGNAL_TYPE = "signal"; + +static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; +static const std::string kCACHE_ARGUMENTS_KEY = "cacheArguments"; +static const std::string kCAPABILITIES_KEY = "capabilities"; +static const std::string kCHECK_SYSTEM_VARS_KEY = "checkSystemVars"; +static const std::string kCOOKIE_KEY = "cookie"; +static const std::string kDEBUG_OUTPUT_KEY = "debugOutput"; +static const std::string kERROR_MESSAGE_KEY = "errorMessage"; +static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; +static const std::string kGENERATOR_KEY = "generator"; +static const std::string kIS_EXPERIMENTAL_KEY = "isExperimental"; +static const std::string kMAJOR_KEY = "major"; +static const std::string kMESSAGE_KEY = "message"; +static const std::string kMINOR_KEY = "minor"; +static const std::string kNAME_KEY = "name"; +static const std::string kPROGRESS_CURRENT_KEY = "progressCurrent"; +static const std::string kPROGRESS_MAXIMUM_KEY = "progressMaximum"; +static const std::string kPROGRESS_MESSAGE_KEY = "progressMessage"; +static const std::string kPROGRESS_MINIMUM_KEY = "progressMinimum"; +static const std::string kPROTOCOL_VERSION_KEY = "protocolVersion"; +static const std::string kREPLY_TO_KEY = "inReplyTo"; +static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; +static const std::string kSUPPORTED_PROTOCOL_VERSIONS = + "supportedProtocolVersions"; +static const std::string kTITLE_KEY = "title"; +static const std::string kTRACE_EXPAND_KEY = "traceExpand"; +static const std::string kTRACE_KEY = "trace"; +static const std::string kTYPE_KEY = "type"; +static const std::string kWARN_UNINITIALIZED_KEY = "warnUninitialized"; +static const std::string kWARN_UNUSED_CLI_KEY = "warnUnusedCli"; +static const std::string kWARN_UNUSED_KEY = "warnUnused"; + +static const std::string kSTART_MAGIC = "[== CMake Server ==["; +static const std::string kEND_MAGIC = "]== CMake Server ==]"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 26942d3..134edf3 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -13,24 +13,19 @@ #include "cmServerProtocol.h" #include "cmExternalMakefileProjectGenerator.h" +#include "cmGlobalGenerator.h" #include "cmServer.h" +#include "cmServerDictionary.h" #include "cmSystemTools.h" #include "cmake.h" +#include "cmServerDictionary.h" + #if defined(CMAKE_BUILD_WITH_CMAKE) #include "cm_jsoncpp_reader.h" #include "cm_jsoncpp_value.h" #endif -// Vocabulary: - -static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; -static const std::string kCOOKIE_KEY = "cookie"; -static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; -static const std::string kGENERATOR_KEY = "generator"; -static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; -static const std::string kTYPE_KEY = "type"; - cmServerRequest::cmServerRequest(cmServer* server, const std::string& t, const std::string& c, const Json::Value& d) : Type(t) @@ -103,10 +98,10 @@ bool cmServerResponse::IsError() const std::string cmServerResponse::ErrorMessage() const { - if (this->m_Payload == PAYLOAD_ERROR) + if (this->m_Payload == PAYLOAD_ERROR) { return this->m_ErrorMessage; - else - return std::string(); + } + return std::string(); } Json::Value cmServerResponse::Data() const @@ -115,16 +110,28 @@ Json::Value cmServerResponse::Data() const return this->m_Data; } -bool cmServerProtocol::Activate(const cmServerRequest& request, +bool cmServerProtocol::Activate(cmServer* server, + const cmServerRequest& request, std::string* errorMessage) { + assert(server); + this->m_Server = server; this->m_CMakeInstance = std::make_unique<cmake>(); const bool result = this->DoActivate(request, errorMessage); - if (!result) + if (!result) { this->m_CMakeInstance = CM_NULLPTR; + } return result; } +void cmServerProtocol::SendSignal(const std::string& name, + const Json::Value& data) const +{ + if (this->m_Server) { + this->m_Server->WriteSignal(name, data); + } +} + cmake* cmServerProtocol::CMakeInstance() const { return this->m_CMakeInstance.get(); @@ -151,17 +158,19 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, std::string extraGenerator = request.Data[kEXTRA_GENERATOR_KEY].asString(); if (buildDirectory.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kBUILD_DIRECTORY_KEY + "\" is missing."; + } return false; } cmake* cm = CMakeInstance(); if (cmSystemTools::PathExists(buildDirectory)) { if (!cmSystemTools::FileIsDirectory(buildDirectory)) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kBUILD_DIRECTORY_KEY + "\" exists but is not a directory."; + } return false; } @@ -173,18 +182,20 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, const std::string cachedGenerator = std::string(state->GetCacheEntryValue("CMAKE_GENERATOR")); if (cachedGenerator.empty() && generator.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kGENERATOR_KEY + "\" is required but unset."; + } return false; } if (generator.empty()) { generator = cachedGenerator; } if (generator != cachedGenerator) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kGENERATOR_KEY + "\" set but incompatible with configured generator."; + } return false; } @@ -193,9 +204,10 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, std::string(state->GetCacheEntryValue("CMAKE_EXTRA_GENERATOR")); if (!cachedExtraGenerator.empty() && !extraGenerator.empty() && cachedExtraGenerator != extraGenerator) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kEXTRA_GENERATOR_KEY + "\" is set but incompatible with configured extra generator."; + } return false; } if (extraGenerator.empty()) { @@ -207,9 +219,10 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, std::string(state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY")); if (!cachedSourceDirectory.empty() && !sourceDirectory.empty() && cachedSourceDirectory != sourceDirectory) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kSOURCE_DIRECTORY_KEY + "\" is set but incompatible with configured source directory."; + } return false; } if (sourceDirectory.empty()) { @@ -219,21 +232,24 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, } if (sourceDirectory.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kSOURCE_DIRECTORY_KEY + "\" is unset but required."; + } return false; } if (!cmSystemTools::FileIsDirectory(sourceDirectory)) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kSOURCE_DIRECTORY_KEY + "\" is not a directory."; + } return false; } if (generator.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kGENERATOR_KEY + "\" is unset but required."; + } return false; } @@ -243,10 +259,11 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, cmGlobalGenerator* gg = cm->CreateGlobalGenerator(fullGeneratorName); if (!gg) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("Could not set up the requested combination of \"") + kGENERATOR_KEY + "\" and \"" + kEXTRA_GENERATOR_KEY + "\""; + } return false; } @@ -263,6 +280,19 @@ const cmServerResponse cmServerProtocol1_0::Process( { assert(this->m_State >= STATE_ACTIVE); + if (request.Type == kCOMPUTE_TYPE) { + return this->ProcessCompute(request); + } + if (request.Type == kCONFIGURE_TYPE) { + return this->ProcessConfigure(request); + } + if (request.Type == kGLOBAL_SETTINGS_TYPE) { + return this->ProcessGlobalSettings(request); + } + if (request.Type == kSET_GLOBAL_SETTINGS_TYPE) { + return this->ProcessSetGlobalSettings(request); + } + return request.ReportError("Unknown command!"); } @@ -270,3 +300,179 @@ bool cmServerProtocol1_0::IsExperimental() const { return true; } + +cmServerResponse cmServerProtocol1_0::ProcessCompute( + const cmServerRequest& request) +{ + if (this->m_State > STATE_CONFIGURED) { + return request.ReportError("This build system was already generated."); + } + if (this->m_State < STATE_CONFIGURED) { + return request.ReportError("This project was not configured yet."); + } + + cmake* cm = this->CMakeInstance(); + int ret = cm->Generate(); + + if (ret < 0) { + return request.ReportError("Failed to compute build system."); + } + m_State = STATE_COMPUTED; + return request.Reply(Json::Value()); +} + +cmServerResponse cmServerProtocol1_0::ProcessConfigure( + const cmServerRequest& request) +{ + if (this->m_State == STATE_INACTIVE) { + return request.ReportError("This instance is inactive."); + } + + // Make sure the types of cacheArguments matches (if given): + std::vector<std::string> cacheArgs; + bool cacheArgumentsError = false; + const Json::Value passedArgs = request.Data[kCACHE_ARGUMENTS_KEY]; + if (!passedArgs.isNull()) { + if (passedArgs.isString()) { + cacheArgs.push_back(passedArgs.asString()); + } else if (passedArgs.isArray()) { + for (auto i = passedArgs.begin(); i != passedArgs.end(); ++i) { + if (!i->isString()) { + cacheArgumentsError = true; + break; + } + cacheArgs.push_back(i->asString()); + } + } else { + cacheArgumentsError = true; + } + } + if (cacheArgumentsError) { + request.ReportError( + "cacheArguments must be unset, a string or an array of strings."); + } + + cmake* cm = this->CMakeInstance(); + std::string sourceDir = cm->GetHomeDirectory(); + const std::string buildDir = cm->GetHomeOutputDirectory(); + + if (buildDir.empty()) { + return request.ReportError( + "No build directory set via setGlobalSettings."); + } + + if (cm->LoadCache(buildDir)) { + // build directory has been set up before + const char* cachedSourceDir = + cm->GetState()->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"); + if (!cachedSourceDir) { + return request.ReportError("No CMAKE_HOME_DIRECTORY found in cache."); + } + if (sourceDir.empty()) { + sourceDir = std::string(cachedSourceDir); + cm->SetHomeDirectory(sourceDir); + } + + const char* cachedGenerator = + cm->GetState()->GetInitializedCacheValue("CMAKE_GENERATOR"); + if (cachedGenerator) { + cmGlobalGenerator* gen = cm->GetGlobalGenerator(); + if (gen && gen->GetName() != cachedGenerator) { + return request.ReportError("Configured generator does not match with " + "CMAKE_GENERATOR found in cache."); + } + } + } else { + // build directory has not been set up before + if (sourceDir.empty()) { + return request.ReportError("No sourceDirectory set via " + "setGlobalSettings and no cache found in " + "buildDirectory."); + } + } + + if (cm->AddCMakePaths() != 1) { + return request.ReportError("Failed to set CMake paths."); + } + + if (!cm->SetCacheArgs(cacheArgs)) { + return request.ReportError("cacheArguments could not be set."); + } + + int ret = cm->Configure(); + if (ret < 0) { + return request.ReportError("Configuration failed."); + } + m_State = STATE_CONFIGURED; + return request.Reply(Json::Value()); +} + +cmServerResponse cmServerProtocol1_0::ProcessGlobalSettings( + const cmServerRequest& request) +{ + cmake* cm = this->CMakeInstance(); + Json::Value obj = Json::objectValue; + + // Capabilities information: + obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson(true); + + obj[kDEBUG_OUTPUT_KEY] = cm->GetDebugOutput(); + obj[kTRACE_KEY] = cm->GetTrace(); + obj[kTRACE_EXPAND_KEY] = cm->GetTraceExpand(); + obj[kWARN_UNINITIALIZED_KEY] = cm->GetWarnUninitialized(); + obj[kWARN_UNUSED_KEY] = cm->GetWarnUnused(); + obj[kWARN_UNUSED_CLI_KEY] = cm->GetWarnUnusedCli(); + obj[kCHECK_SYSTEM_VARS_KEY] = cm->GetCheckSystemVars(); + + obj[kSOURCE_DIRECTORY_KEY] = cm->GetHomeDirectory(); + obj[kBUILD_DIRECTORY_KEY] = cm->GetHomeOutputDirectory(); + + // Currently used generator: + cmGlobalGenerator* gen = cm->GetGlobalGenerator(); + obj[kGENERATOR_KEY] = gen ? gen->GetName() : std::string(); + obj[kEXTRA_GENERATOR_KEY] = + gen ? gen->GetExtraGeneratorName() : std::string(); + + return request.Reply(obj); +} + +static void setBool(const cmServerRequest& request, const std::string& key, + std::function<void(bool)> setter) +{ + if (request.Data[key].isNull()) { + return; + } + setter(request.Data[key].asBool()); +} + +cmServerResponse cmServerProtocol1_0::ProcessSetGlobalSettings( + const cmServerRequest& request) +{ + const std::vector<std::string> boolValues = { + kDEBUG_OUTPUT_KEY, kTRACE_KEY, kTRACE_EXPAND_KEY, + kWARN_UNINITIALIZED_KEY, kWARN_UNUSED_KEY, kWARN_UNUSED_CLI_KEY, + kCHECK_SYSTEM_VARS_KEY + }; + for (auto i : boolValues) { + if (!request.Data[i].isNull() && !request.Data[i].isBool()) { + return request.ReportError("\"" + i + + "\" must be unset or a bool value."); + } + } + + cmake* cm = this->CMakeInstance(); + + setBool(request, kDEBUG_OUTPUT_KEY, + [cm](bool e) { cm->SetDebugOutputOn(e); }); + setBool(request, kTRACE_KEY, [cm](bool e) { cm->SetTrace(e); }); + setBool(request, kTRACE_EXPAND_KEY, [cm](bool e) { cm->SetTraceExpand(e); }); + setBool(request, kWARN_UNINITIALIZED_KEY, + [cm](bool e) { cm->SetWarnUninitialized(e); }); + setBool(request, kWARN_UNUSED_KEY, [cm](bool e) { cm->SetWarnUnused(e); }); + setBool(request, kWARN_UNUSED_CLI_KEY, + [cm](bool e) { cm->SetWarnUnusedCli(e); }); + setBool(request, kCHECK_SYSTEM_VARS_KEY, + [cm](bool e) { cm->SetCheckSystemVars(e); }); + + return request.Reply(Json::Value()); +} diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index bab949b..e772eca 100644 --- a/Source/cmServerProtocol.h +++ b/Source/cmServerProtocol.h @@ -13,6 +13,7 @@ #pragma once #include "cmListFileCache.h" +#include "cmake.h" #if defined(CMAKE_BUILD_WITH_CMAKE) #include "cm_jsoncpp_writer.h" @@ -87,7 +88,10 @@ public: virtual bool IsExperimental() const = 0; virtual const cmServerResponse Process(const cmServerRequest& request) = 0; - bool Activate(const cmServerRequest& request, std::string* errorMessage); + bool Activate(cmServer* server, const cmServerRequest& request, + std::string* errorMessage); + + void SendSignal(const std::string& name, const Json::Value& data) const; protected: cmake* CMakeInstance() const; @@ -97,6 +101,7 @@ protected: private: std::unique_ptr<cmake> m_CMakeInstance; + cmServer* m_Server = nullptr; // not owned! friend class cmServer; }; @@ -112,10 +117,18 @@ private: bool DoActivate(const cmServerRequest& request, std::string* errorMessage) override; + // Handle requests: + cmServerResponse ProcessCompute(const cmServerRequest& request); + cmServerResponse ProcessConfigure(const cmServerRequest& request); + cmServerResponse ProcessGlobalSettings(const cmServerRequest& request); + cmServerResponse ProcessSetGlobalSettings(const cmServerRequest& request); + enum State { STATE_INACTIVE, - STATE_ACTIVE + STATE_ACTIVE, + STATE_CONFIGURED, + STATE_COMPUTED }; State m_State = STATE_INACTIVE; }; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 1dad742..eb17561 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1164,7 +1164,8 @@ const char* cmTarget::GetProperty(const std::string& prop, } } // Support "<CONFIG>_LOCATION". - else if (cmHasLiteralSuffix(prop, "_LOCATION")) { + else if (cmHasLiteralSuffix(prop, "_LOCATION") && + !cmHasLiteralPrefix(prop, "XCODE_ATTRIBUTE_")) { std::string configName(prop.c_str(), prop.size() - 9); if (configName != "IMPORTED") { if (!this->HandleLocationPropertyPolicy(context)) { diff --git a/Source/cm_auto_ptr.hxx b/Source/cm_auto_ptr.hxx index f6c4362..f38eda5 100644 --- a/Source/cm_auto_ptr.hxx +++ b/Source/cm_auto_ptr.hxx @@ -14,7 +14,13 @@ #include <cmConfigure.h> -// FIXME: Use std::auto_ptr on compilers that do not warn about it. +#ifdef CMake_HAVE_CXX_AUTO_PTR + +#include <memory> +#define CM_AUTO_PTR std::auto_ptr + +#else + #define CM_AUTO_PTR cm::auto_ptr // The HP compiler cannot handle the conversions necessary to use @@ -219,3 +225,5 @@ public: #endif #endif + +#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9daed4b..2b4a830 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -52,6 +52,11 @@ #include <stdlib.h> #include <time.h> +int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd); +int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd); + void CMakeCommandUsage(const char* program) { std::ostringstream errorStream; @@ -782,6 +787,18 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return cmcmd::ExecuteLinkScript(args); } +#ifdef CMAKE_BUILD_WITH_CMAKE + // Internal CMake ninja dependency scanning support. + else if (args[1] == "cmake_ninja_depends") { + return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end()); + } + + // Internal CMake ninja dyndep support. + else if (args[1] == "cmake_ninja_dyndep") { + return cmcmd_cmake_ninja_dyndep(args.begin() + 2, args.end()); + } +#endif + // Internal CMake unimplemented feature notification. else if (args[1] == "cmake_unimplemented_variable") { std::cerr << "Feature not implemented for this platform."; diff --git a/Source/kwsys/testConsoleBuf.cxx b/Source/kwsys/testConsoleBuf.cxx index 3dc3337..d7775e6 100644 --- a/Source/kwsys/testConsoleBuf.cxx +++ b/Source/kwsys/testConsoleBuf.cxx @@ -103,7 +103,7 @@ static void dumpBuffers(const T *expected, const T *received, size_t size) { } std::cerr << std::endl; } - std::cerr << std::endl << std::flush; + std::cerr << std::endl; } //---------------------------------------------------------------------------- @@ -331,21 +331,23 @@ static int testPipe() didFail = encodedTestString.compare(buffer2) == 0 ? 0 : 1; } if (didFail != 0) { - std::cerr << "Pipe's output didn't match expected output!" << std::endl << std::flush; + std::cerr << "Pipe's output didn't match expected output!" << std::endl; dumpBuffers<char>(encodedTestString.c_str(), buffer, encodedTestString.size()); dumpBuffers<char>(encodedInputTestString.c_str(), buffer + encodedTestString.size() + 1, encodedInputTestString.size()); dumpBuffers<char>(encodedTestString.c_str(), buffer2, encodedTestString.size()); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testPipe, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishProcess(didFail == 0); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testPipe, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishPipe(inPipeRead, inPipeWrite); @@ -368,11 +370,11 @@ static int testFile() (errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) { throw std::runtime_error("createFile failed!"); } - int length = 0; DWORD bytesWritten = 0; char buffer[200]; char buffer2[200]; + int length; if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, -1, buffer, sizeof(buffer), NULL, NULL)) == 0) { @@ -424,21 +426,23 @@ static int testFile() didFail = encodedTestString.compare(buffer2) == 0 ? 0 : 1; } if (didFail != 0) { - std::cerr << "File's output didn't match expected output!" << std::endl << std::flush; + std::cerr << "File's output didn't match expected output!" << std::endl; dumpBuffers<char>(encodedTestString.c_str(), buffer, encodedTestString.size()); dumpBuffers<char>(encodedInputTestString.c_str(), buffer + encodedTestString.size() + 1, encodedInputTestString.size() - 1); dumpBuffers<char>(encodedTestString.c_str(), buffer2, encodedTestString.size()); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testFile, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishProcess(didFail == 0); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testFile, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishFile(inFile); @@ -508,18 +512,18 @@ static int testConsole() forceNewConsole = true; } } else { - std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl << std::flush; + std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl; } RegCloseKey(hConsoleKey); } else { - std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" << std::endl << std::flush; + std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" << std::endl; } } if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) { // Not a real console, let's create new one. FreeConsole(); if (!AllocConsole()) { - std::cerr << "AllocConsole failed!" << std::endl << std::flush; + std::cerr << "AllocConsole failed!" << std::endl; return didFail; } SECURITY_ATTRIBUTES securityAttributes; @@ -561,11 +565,11 @@ static int testConsole() consoleFont.FontFamily = TestFontFamily; wcscpy(consoleFont.FaceName, TestFaceName); if (!setConsoleFont(hOut, FALSE, &consoleFont)) { - std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl << std::flush; + std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl; } } } else { - std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl << std::flush; + std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl; } } else { #endif @@ -599,7 +603,7 @@ static int testConsole() INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) / sizeof(UnicodeInputTestString[0])) * 2]; memset(&inputBuffer, 0, sizeof(inputBuffer)); - unsigned int i = 0; + unsigned int i; for (i = 0; i < (sizeof(UnicodeInputTestString) / sizeof(UnicodeInputTestString[0]) - 1); i++) { writeInputKeyEvent(&inputBuffer[i*2], UnicodeInputTestString[i]); @@ -650,7 +654,7 @@ static int testConsole() ) { didFail = 0; } else { - std::cerr << "Console's output didn't match expected output!" << std::endl << std::flush; + std::cerr << "Console's output didn't match expected output!" << std::endl; dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer, wideTestString.size()); dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer + screenBufferInfo.dwSize.X * 1, wideTestString.size()); dumpBuffers<wchar_t>(UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2, (sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR)); @@ -659,7 +663,8 @@ static int testConsole() delete[] outputBuffer; } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testConsole, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishProcess(didFail == 0); |