diff options
Diffstat (limited to 'Source/cmGlobalNinjaGenerator.cxx')
-rw-r--r-- | Source/cmGlobalNinjaGenerator.cxx | 1640 |
1 files changed, 863 insertions, 777 deletions
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 2eef9e492..da21d6c2a 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -2,16 +2,19 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGlobalNinjaGenerator.h" -#include "cm_jsoncpp_reader.h" -#include "cm_jsoncpp_value.h" -#include "cm_jsoncpp_writer.h" -#include "cmsys/FStream.hxx" #include <algorithm> -#include <ctype.h> -#include <functional> +#include <cctype> +#include <cstdio> #include <iterator> #include <sstream> -#include <stdio.h> + +#include <cm/memory> + +#include "cmsys/FStream.hxx" + +#include "cm_jsoncpp_reader.h" +#include "cm_jsoncpp_value.h" +#include "cm_jsoncpp_writer.h" #include "cmAlgorithms.h" #include "cmDocumentationEntry.h" @@ -19,20 +22,24 @@ #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" #include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmNinjaLinkLineComputer.h" #include "cmOutputConverter.h" +#include "cmRange.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" #include "cmVersion.h" -#include "cm_auto_ptr.hxx" #include "cmake.h" class cmLinkLineComputer; @@ -90,43 +97,18 @@ std::string cmGlobalNinjaGenerator::EncodeRuleName(std::string const& name) // Ninja rule names must match "[a-zA-Z0-9_.-]+". Use ".xx" to encode // "." and all invalid characters as hexadecimal. std::string encoded; - for (std::string::const_iterator i = name.begin(); i != name.end(); ++i) { - if (isalnum(*i) || *i == '_' || *i == '-') { - encoded += *i; + for (char i : name) { + if (isalnum(i) || i == '_' || i == '-') { + encoded += i; } else { char buf[16]; - sprintf(buf, ".%02x", static_cast<unsigned int>(*i)); + sprintf(buf, ".%02x", static_cast<unsigned int>(i)); encoded += buf; } } return encoded; } -static bool IsIdentChar(char c) -{ - return ('a' <= c && c <= 'z') || - ('+' <= c && c <= '9') || // +,-./ and numbers - ('A' <= c && c <= 'Z') || (c == '_') || (c == '$') || (c == '\\') || - (c == ' ') || (c == ':'); -} - -std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string& ident, - std::ostream& vars) -{ - if (std::find_if(ident.begin(), ident.end(), - std::not1(std::ptr_fun(IsIdentChar))) != ident.end()) { - static unsigned VarNum = 0; - std::ostringstream names; - names << "ident" << VarNum++; - vars << names.str() << " = " << ident << "\n"; - return "$" + names.str(); - } - std::string result = ident; - cmSystemTools::ReplaceString(result, " ", "$ "); - cmSystemTools::ReplaceString(result, ":", "$:"); - return result; -} - std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit) { std::string result = lit; @@ -137,293 +119,256 @@ std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit) std::string cmGlobalNinjaGenerator::EncodePath(const std::string& path) { - std::string result = path; // NOLINT(clang-tidy) + std::string result = path; #ifdef _WIN32 if (this->IsGCCOnWindows()) std::replace(result.begin(), result.end(), '\\', '/'); else std::replace(result.begin(), result.end(), '/', '\\'); #endif - return EncodeLiteral(result); + result = EncodeLiteral(result); + cmSystemTools::ReplaceString(result, " ", "$ "); + cmSystemTools::ReplaceString(result, ":", "$:"); + return result; } -void cmGlobalNinjaGenerator::WriteBuild( - std::ostream& os, const std::string& comment, const std::string& rule, - const cmNinjaDeps& outputs, const cmNinjaDeps& implicitOuts, - const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps, - const cmNinjaDeps& orderOnlyDeps, const cmNinjaVars& variables, - const std::string& rspfile, int cmdLineLimit, bool* usedResponseFile) +void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, + cmNinjaBuild const& build, + int cmdLineLimit, + bool* usedResponseFile) { // Make sure there is a rule. - if (rule.empty()) { - cmSystemTools::Error("No rule for WriteBuildStatement! called " - "with comment: ", - comment.c_str()); + if (build.Rule.empty()) { + cmSystemTools::Error("No rule for WriteBuild! called with comment: " + + build.Comment); return; } // Make sure there is at least one output file. - if (outputs.empty()) { - cmSystemTools::Error("No output files for WriteBuildStatement! called " - "with comment: ", - comment.c_str()); + if (build.Outputs.empty()) { + cmSystemTools::Error( + "No output files for WriteBuild! called with comment: " + build.Comment); return; } - cmGlobalNinjaGenerator::WriteComment(os, comment); - - std::string arguments; - - // TODO: Better formatting for when there are multiple input/output files. + cmGlobalNinjaGenerator::WriteComment(os, build.Comment); - // Write explicit dependencies. - for (cmNinjaDeps::const_iterator i = explicitDeps.begin(); - i != explicitDeps.end(); ++i) { - arguments += " " + EncodeIdent(EncodePath(*i), os); - } - - // Write implicit dependencies. - if (!implicitDeps.empty()) { - arguments += " |"; - for (cmNinjaDeps::const_iterator i = implicitDeps.begin(); - i != implicitDeps.end(); ++i) { - arguments += " " + EncodeIdent(EncodePath(*i), os); + // Write output files. + std::string buildStr("build"); + { + // Write explicit outputs + for (std::string const& output : build.Outputs) { + buildStr += " " + EncodePath(output); + if (this->ComputingUnknownDependencies) { + this->CombinedBuildOutputs.insert(output); + } } - } - - // Write order-only dependencies. - if (!orderOnlyDeps.empty()) { - arguments += " ||"; - for (cmNinjaDeps::const_iterator i = orderOnlyDeps.begin(); - i != orderOnlyDeps.end(); ++i) { - arguments += " " + EncodeIdent(EncodePath(*i), os); + // Write implicit outputs + if (!build.ImplicitOuts.empty()) { + buildStr += " |"; + for (std::string const& implicitOut : build.ImplicitOuts) { + buildStr += " " + EncodePath(implicitOut); + } } + buildStr += ":"; + + // Write the rule. + buildStr += " "; + buildStr += build.Rule; } - arguments += "\n"; + std::string arguments; + { + // TODO: Better formatting for when there are multiple input/output files. - std::string build; + // Write explicit dependencies. + for (std::string const& explicitDep : build.ExplicitDeps) { + arguments += " " + EncodePath(explicitDep); + } - // Write outputs files. - build += "build"; - for (cmNinjaDeps::const_iterator i = outputs.begin(); i != outputs.end(); - ++i) { - build += " " + EncodeIdent(EncodePath(*i), os); - if (this->ComputingUnknownDependencies) { - this->CombinedBuildOutputs.insert(*i); + // Write implicit dependencies. + if (!build.ImplicitDeps.empty()) { + arguments += " |"; + for (std::string const& implicitDep : build.ImplicitDeps) { + arguments += " " + EncodePath(implicitDep); + } } - } - if (!implicitOuts.empty()) { - build += " |"; - for (cmNinjaDeps::const_iterator i = implicitOuts.begin(); - i != implicitOuts.end(); ++i) { - build += " " + EncodeIdent(EncodePath(*i), os); + + // Write order-only dependencies. + if (!build.OrderOnlyDeps.empty()) { + arguments += " ||"; + for (std::string const& orderOnlyDep : build.OrderOnlyDeps) { + arguments += " " + EncodePath(orderOnlyDep); + } } - } - build += ":"; - // Write the rule. - build += " " + rule; + arguments += "\n"; + } // Write the variables bound to this build statement. - std::ostringstream variable_assignments; - for (cmNinjaVars::const_iterator i = variables.begin(); i != variables.end(); - ++i) { - cmGlobalNinjaGenerator::WriteVariable(variable_assignments, i->first, - i->second, "", 1); - } + std::string assignments; + { + std::ostringstream variable_assignments; + for (auto const& variable : build.Variables) { + cmGlobalNinjaGenerator::WriteVariable( + variable_assignments, variable.first, variable.second, "", 1); + } - // check if a response file rule should be used - std::string buildstr = build; - std::string assignments = variable_assignments.str(); - const std::string& args = arguments; - bool useResponseFile = false; - if (cmdLineLimit < 0 || - (cmdLineLimit > 0 && - (args.size() + buildstr.size() + assignments.size() + 1000) > - static_cast<size_t>(cmdLineLimit))) { - variable_assignments.str(std::string()); - cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE", - rspfile, "", 1); - assignments += variable_assignments.str(); - useResponseFile = true; - } - if (usedResponseFile) { - *usedResponseFile = useResponseFile; + // check if a response file rule should be used + assignments = variable_assignments.str(); + bool useResponseFile = false; + if (cmdLineLimit < 0 || + (cmdLineLimit > 0 && + (arguments.size() + buildStr.size() + assignments.size() + 1000) > + static_cast<size_t>(cmdLineLimit))) { + variable_assignments.str(std::string()); + cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE", + build.RspFile, "", 1); + assignments += variable_assignments.str(); + useResponseFile = true; + } + if (usedResponseFile) { + *usedResponseFile = useResponseFile; + } } - os << buildstr << args << assignments; -} - -void cmGlobalNinjaGenerator::WritePhonyBuild( - std::ostream& os, const std::string& comment, const cmNinjaDeps& outputs, - const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps, - const cmNinjaDeps& orderOnlyDeps, const cmNinjaVars& variables) -{ - this->WriteBuild(os, comment, "phony", outputs, - /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, - orderOnlyDeps, variables); + os << buildStr << arguments << assignments << "\n"; } void cmGlobalNinjaGenerator::AddCustomCommandRule() { - this->AddRule("CUSTOM_COMMAND", "$COMMAND", "$DESC", - "Rule for running custom commands.", - /*depfile*/ "", - /*deptype*/ "", - /*rspfile*/ "", - /*rspcontent*/ "", - /*restat*/ "", // bound on each build statement as needed - /*generator*/ false); + cmNinjaRule rule("CUSTOM_COMMAND"); + rule.Command = "$COMMAND"; + rule.Description = "$DESC"; + rule.Comment = "Rule for running custom commands."; + this->AddRule(rule); } void cmGlobalNinjaGenerator::WriteCustomCommandBuild( const std::string& command, const std::string& description, - const std::string& comment, const std::string& depfile, bool uses_terminal, - bool restat, const cmNinjaDeps& outputs, const cmNinjaDeps& deps, - const cmNinjaDeps& orderOnly) + const std::string& comment, const std::string& depfile, + const std::string& job_pool, bool uses_terminal, bool restat, + const cmNinjaDeps& outputs, const cmNinjaDeps& explicitDeps, + const cmNinjaDeps& orderOnlyDeps) { - std::string cmd = command; // NOLINT(clang-tidy) -#ifdef _WIN32 - if (cmd.empty()) - // TODO Shouldn't an empty command be handled by ninja? - cmd = "cmd.exe /c"; -#endif - this->AddCustomCommandRule(); - cmNinjaVars vars; - vars["COMMAND"] = cmd; - vars["DESC"] = EncodeLiteral(description); - if (restat) { - vars["restat"] = "1"; - } - if (uses_terminal && SupportsConsolePool()) { - vars["pool"] = "console"; - } - if (!depfile.empty()) { - vars["depfile"] = depfile; + { + cmNinjaBuild build("CUSTOM_COMMAND"); + build.Comment = comment; + build.Outputs = outputs; + build.ExplicitDeps = explicitDeps; + build.OrderOnlyDeps = orderOnlyDeps; + + cmNinjaVars& vars = build.Variables; + { + std::string cmd = command; // NOLINT(*) +#ifdef _WIN32 + if (cmd.empty()) + // TODO Shouldn't an empty command be handled by ninja? + cmd = "cmd.exe /c"; +#endif + vars["COMMAND"] = std::move(cmd); + } + vars["DESC"] = EncodeLiteral(description); + if (restat) { + vars["restat"] = "1"; + } + if (uses_terminal && SupportsConsolePool()) { + vars["pool"] = "console"; + } else if (!job_pool.empty()) { + vars["pool"] = job_pool; + } + if (!depfile.empty()) { + vars["depfile"] = depfile; + } + this->WriteBuild(*this->BuildFileStream, build); } - this->WriteBuild(*this->BuildFileStream, comment, "CUSTOM_COMMAND", outputs, - /*implicitOuts=*/cmNinjaDeps(), deps, cmNinjaDeps(), - orderOnly, vars); if (this->ComputingUnknownDependencies) { // we need to track every dependency that comes in, since we are trying // to find dependencies that are side effects of build commands - for (cmNinjaDeps::const_iterator i = deps.begin(); i != deps.end(); ++i) { - this->CombinedCustomCommandExplicitDependencies.insert(*i); + for (std::string const& dep : explicitDeps) { + this->CombinedCustomCommandExplicitDependencies.insert(dep); } } } void cmGlobalNinjaGenerator::AddMacOSXContentRule() { - cmLocalGenerator* lg = this->LocalGenerators[0]; - - std::ostringstream cmd; - cmd << lg->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(), - cmOutputConverter::SHELL) - << " -E copy $in $out"; - - this->AddRule("COPY_OSX_CONTENT", cmd.str(), "Copying OS X Content $out", - "Rule for copying OS X bundle content file.", - /*depfile*/ "", - /*deptype*/ "", - /*rspfile*/ "", - /*rspcontent*/ "", - /*restat*/ "", - /*generator*/ false); + cmNinjaRule rule("COPY_OSX_CONTENT"); + rule.Command = CMakeCmd() + " -E copy $in $out"; + rule.Description = "Copying OS X Content $out"; + rule.Comment = "Rule for copying OS X bundle content file."; + this->AddRule(rule); } -void cmGlobalNinjaGenerator::WriteMacOSXContentBuild(const std::string& input, - const std::string& output) +void cmGlobalNinjaGenerator::WriteMacOSXContentBuild(std::string input, + std::string output) { this->AddMacOSXContentRule(); - - cmNinjaDeps outputs; - outputs.push_back(output); - cmNinjaDeps deps; - deps.push_back(input); - cmNinjaVars vars; - - this->WriteBuild(*this->BuildFileStream, "", "COPY_OSX_CONTENT", outputs, - /*implicitOuts=*/cmNinjaDeps(), deps, cmNinjaDeps(), - cmNinjaDeps(), cmNinjaVars()); + { + cmNinjaBuild build("COPY_OSX_CONTENT"); + build.Outputs.push_back(std::move(output)); + build.ExplicitDeps.push_back(std::move(input)); + this->WriteBuild(*this->BuildFileStream, build); + } } -void cmGlobalNinjaGenerator::WriteRule( - std::ostream& os, const std::string& name, const std::string& command, - const std::string& description, const std::string& comment, - const std::string& depfile, const std::string& deptype, - const std::string& rspfile, const std::string& rspcontent, - const std::string& restat, bool generator) +void cmGlobalNinjaGenerator::WriteRule(std::ostream& os, + cmNinjaRule const& rule) { + // -- Parameter checks // Make sure the rule has a name. - if (name.empty()) { - cmSystemTools::Error("No name given for WriteRuleStatement! called " - "with comment: ", - comment.c_str()); + if (rule.Name.empty()) { + cmSystemTools::Error("No name given for WriteRule! called with comment: " + + rule.Comment); return; } // Make sure a command is given. - if (command.empty()) { - cmSystemTools::Error("No command given for WriteRuleStatement! called " - "with comment: ", - comment.c_str()); + if (rule.Command.empty()) { + cmSystemTools::Error( + "No command given for WriteRule! called with comment: " + rule.Comment); return; } - cmGlobalNinjaGenerator::WriteComment(os, comment); - - // Write the rule. - os << "rule " << name << "\n"; - - // Write the depfile if any. - if (!depfile.empty()) { - cmGlobalNinjaGenerator::Indent(os, 1); - os << "depfile = " << depfile << "\n"; - } - - // Write the deptype if any. - if (!deptype.empty()) { - cmGlobalNinjaGenerator::Indent(os, 1); - os << "deps = " << deptype << "\n"; + // Make sure response file content is given + if (!rule.RspFile.empty() && rule.RspContent.empty()) { + cmSystemTools::Error("rspfile but no rspfile_content given for WriteRule! " + "called with comment: " + + rule.Comment); + return; } - // Write the command. - cmGlobalNinjaGenerator::Indent(os, 1); - os << "command = " << command << "\n"; + // -- Write rule + // Write rule intro + cmGlobalNinjaGenerator::WriteComment(os, rule.Comment); + os << "rule " << rule.Name << '\n'; - // Write the description if any. - if (!description.empty()) { - cmGlobalNinjaGenerator::Indent(os, 1); - os << "description = " << description << "\n"; - } - - if (!rspfile.empty()) { - if (rspcontent.empty()) { - cmSystemTools::Error("No rspfile_content given!", comment.c_str()); - return; + // Write rule key/value pairs + auto writeKV = [&os](const char* key, std::string const& value) { + if (!value.empty()) { + cmGlobalNinjaGenerator::Indent(os, 1); + os << key << " = " << value << '\n'; } - cmGlobalNinjaGenerator::Indent(os, 1); - os << "rspfile = " << rspfile << "\n"; - cmGlobalNinjaGenerator::Indent(os, 1); - os << "rspfile_content = " << rspcontent << "\n"; - } + }; - if (!restat.empty()) { - cmGlobalNinjaGenerator::Indent(os, 1); - os << "restat = " << restat << "\n"; + writeKV("depfile", rule.DepFile); + writeKV("deps", rule.DepType); + writeKV("command", rule.Command); + writeKV("description", rule.Description); + if (!rule.RspFile.empty()) { + writeKV("rspfile", rule.RspFile); + writeKV("rspfile_content", rule.RspContent); } - - if (generator) { - cmGlobalNinjaGenerator::Indent(os, 1); - os << "generator = 1\n"; + writeKV("restat", rule.Restat); + if (rule.Generator) { + writeKV("generator", "1"); } - os << "\n"; + // Finish rule + os << '\n'; } void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os, @@ -435,13 +380,13 @@ void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os, // Make sure we have a name. if (name.empty()) { cmSystemTools::Error("No name given for WriteVariable! called " - "with comment: ", - comment.c_str()); + "with comment: " + + comment); return; } // Do not add a variable if the value is empty. - std::string val = cmSystemTools::TrimWhitespace(value); + std::string val = cmTrimWhitespace(value); if (val.empty()) { return; } @@ -465,26 +410,14 @@ void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os, { cmGlobalNinjaGenerator::WriteComment(os, comment); os << "default"; - for (cmNinjaDeps::const_iterator i = targets.begin(); i != targets.end(); - ++i) { - os << " " << *i; + for (std::string const& target : targets) { + os << " " << target; } os << "\n"; } cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) : cmGlobalCommonGenerator(cm) - , BuildFileStream(CM_NULLPTR) - , RulesFileStream(CM_NULLPTR) - , CompileCommandsStream(CM_NULLPTR) - , Rules() - , AllDependencies() - , UsingGCCOnWindows(false) - , ComputingUnknownDependencies(false) - , PolicyCMP0058(cmPolicies::WARN) - , NinjaSupportsConsolePool(false) - , NinjaSupportsImplicitOuts(false) - , NinjaSupportsDyndeps(0) { #ifdef _WIN32 cm->GetState()->SetWindowsShell(true); @@ -532,12 +465,17 @@ void cmGlobalNinjaGenerator::Generate() std::ostringstream msg; msg << "The detected version of Ninja (" << this->NinjaVersion; msg << ") is less than the version of Ninja required by CMake ("; - msg << this->RequiredNinjaVersion() << ")."; - this->GetCMakeInstance()->IssueMessage(cmake::FATAL_ERROR, msg.str()); + msg << cmGlobalNinjaGenerator::RequiredNinjaVersion() << ")."; + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, + msg.str()); + return; + } + if (!this->OpenBuildFileStream()) { + return; + } + if (!this->OpenRulesFileStream()) { return; } - this->OpenBuildFileStream(); - this->OpenRulesFileStream(); this->TargetDependsClosures.clear(); @@ -579,20 +517,21 @@ bool cmGlobalNinjaGenerator::FindMakeProgram(cmMakefile* mf) this->NinjaCommand = ninjaCommand; std::vector<std::string> command; command.push_back(this->NinjaCommand); - command.push_back("--version"); + command.emplace_back("--version"); std::string version; std::string error; - if (!cmSystemTools::RunSingleCommand(command, &version, &error, CM_NULLPTR, - CM_NULLPTR, + if (!cmSystemTools::RunSingleCommand(command, &version, &error, nullptr, + nullptr, cmSystemTools::OUTPUT_NONE)) { - mf->IssueMessage(cmake::FATAL_ERROR, "Running\n '" + - cmJoin(command, "' '") + "'\n" - "failed with:\n " + + mf->IssueMessage(MessageType::FATAL_ERROR, + "Running\n '" + cmJoin(command, "' '") + + "'\n" + "failed with:\n " + error); cmSystemTools::SetFatalErrorOccured(); return false; } - this->NinjaVersion = cmSystemTools::TrimWhitespace(version); + this->NinjaVersion = cmTrimWhitespace(version); this->CheckNinjaFeatures(); } return true; @@ -605,15 +544,29 @@ void cmGlobalNinjaGenerator::CheckNinjaFeatures() RequiredNinjaVersionForConsolePool().c_str()); this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare( cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), - this->RequiredNinjaVersionForImplicitOuts().c_str()); - { + cmGlobalNinjaGenerator::RequiredNinjaVersionForImplicitOuts().c_str()); + this->NinjaSupportsManifestRestat = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForManifestRestat().c_str()); + this->NinjaSupportsMultilineDepfile = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForMultilineDepfile().c_str()); + this->NinjaSupportsDyndeps = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForDyndeps().c_str()); + if (!this->NinjaSupportsDyndeps) { + // The ninja version number is not new enough to have upstream support. // 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); + const char* fv = &this->NinjaVersion[pos + k_DYNDEP_.size()]; + unsigned long dyndep = 0; + cmStrToULong(fv, &dyndep); + if (dyndep == 1) { + this->NinjaSupportsDyndeps = true; + } } } } @@ -621,8 +574,7 @@ void cmGlobalNinjaGenerator::CheckNinjaFeatures() bool cmGlobalNinjaGenerator::CheckLanguages( std::vector<std::string> const& languages, cmMakefile* mf) const { - if (std::find(languages.begin(), languages.end(), "Fortran") != - languages.end()) { + if (cmContains(languages, "Fortran")) { return this->CheckFortran(mf); } return true; @@ -630,38 +582,26 @@ bool cmGlobalNinjaGenerator::CheckLanguages( bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const { - if (this->NinjaSupportsDyndeps == 1) { + if (this->NinjaSupportsDyndeps) { 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()); + /* 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 and they have been " + "merged to upstream ninja for inclusion in Ninja 1.10 and higher. " + "As of this version of CMake, Ninja 1.10 has not been released. " + "Meanwhile, Kitware maintains a branch of Ninja 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 */ + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); cmSystemTools::SetFatalErrorOccured(); return false; } @@ -670,21 +610,29 @@ void cmGlobalNinjaGenerator::EnableLanguage( std::vector<std::string> const& langs, cmMakefile* mf, bool optional) { this->cmGlobalGenerator::EnableLanguage(langs, mf, optional); - for (std::vector<std::string>::const_iterator l = langs.begin(); - l != langs.end(); ++l) { - if (*l == "NONE") { + for (std::string const& l : langs) { + if (l == "NONE") { continue; } - this->ResolveLanguageCompiler(*l, mf, optional); + this->ResolveLanguageCompiler(l, mf, optional); } #ifdef _WIN32 - if (strcmp(mf->GetSafeDefinition("CMAKE_C_SIMULATE_ID"), "MSVC") != 0 && - strcmp(mf->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"), "MSVC") != 0 && - (mf->IsOn("CMAKE_COMPILER_IS_MINGW") || - strcmp(mf->GetSafeDefinition("CMAKE_C_COMPILER_ID"), "GNU") == 0 || - strcmp(mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"), "GNU") == 0 || - strcmp(mf->GetSafeDefinition("CMAKE_C_COMPILER_ID"), "Clang") == 0 || - strcmp(mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"), "Clang") == 0)) { + const bool clangGnuMode = + ((mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "Clang") && + (mf->GetSafeDefinition("CMAKE_C_COMPILER_FRONTEND_VARIANT") == "GNU")) || + ((mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "Clang") && + (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_FRONTEND_VARIANT") == "GNU")); + + if (clangGnuMode || + ((mf->GetSafeDefinition("CMAKE_C_SIMULATE_ID") != "MSVC") && + (mf->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID") != "MSVC") && + (mf->IsOn("CMAKE_COMPILER_IS_MINGW") || + (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "GNU") || + (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "GNU") || + (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "Clang") || + (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "Clang") || + (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "QCC") || + (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "QCC")))) { this->UsingGCCOnWindows = true; } #endif @@ -698,56 +646,52 @@ void cmGlobalNinjaGenerator::EnableLanguage( // cmGlobalXCodeGenerator // Called by: // cmGlobalGenerator::Build() -void cmGlobalNinjaGenerator::GenerateBuildCommand( - std::vector<std::string>& makeCommand, const std::string& makeProgram, - const std::string& /*projectName*/, const std::string& /*projectDir*/, - const std::string& targetName, const std::string& /*config*/, bool /*fast*/, - bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalNinjaGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& /*projectName*/, + const std::string& /*projectDir*/, + std::vector<std::string> const& targetNames, const std::string& /*config*/, + bool /*fast*/, int jobs, bool verbose, + std::vector<std::string> const& makeOptions) { - makeCommand.push_back(this->SelectMakeProgram(makeProgram)); + GeneratedMakeCommand makeCommand; + makeCommand.Add(this->SelectMakeProgram(makeProgram)); if (verbose) { - makeCommand.push_back("-v"); + makeCommand.Add("-v"); } - makeCommand.insert(makeCommand.end(), makeOptions.begin(), - makeOptions.end()); - if (!targetName.empty()) { - if (targetName == "clean") { - makeCommand.push_back("-t"); - makeCommand.push_back("clean"); - } else { - makeCommand.push_back(targetName); + if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) && + (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) { + makeCommand.Add("-j", std::to_string(jobs)); + } + + makeCommand.Add(makeOptions.begin(), makeOptions.end()); + for (const auto& tname : targetNames) { + if (!tname.empty()) { + makeCommand.Add(tname); } } + return { std::move(makeCommand) }; } // Non-virtual public methods. -void cmGlobalNinjaGenerator::AddRule( - const std::string& name, const std::string& command, - const std::string& description, const std::string& comment, - const std::string& depfile, const std::string& deptype, - const std::string& rspfile, const std::string& rspcontent, - const std::string& restat, bool generator) +void cmGlobalNinjaGenerator::AddRule(cmNinjaRule const& rule) { // Do not add the same rule twice. - if (this->HasRule(name)) { + if (!this->Rules.insert(rule.Name).second) { return; } - - this->Rules.insert(name); - cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream, name, command, - description, comment, depfile, deptype, - rspfile, rspcontent, restat, generator); - - this->RuleCmdLength[name] = (int)command.size(); + // Store command length + this->RuleCmdLength[rule.Name] = static_cast<int>(rule.Command.size()); + // Write rule + cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream, rule); } bool cmGlobalNinjaGenerator::HasRule(const std::string& name) { - RulesSetType::const_iterator rule = this->Rules.find(name); - return (rule != this->Rules.end()); + return (this->Rules.find(name) != this->Rules.end()); } // Private virtual overrides @@ -763,32 +707,29 @@ void cmGlobalNinjaGenerator::ComputeTargetObjectDirectory( cmGeneratorTarget* gt) const { // Compute full path to object file directory for this target. - std::string dir; - dir += gt->LocalGenerator->GetCurrentBinaryDirectory(); - dir += "/"; - dir += gt->LocalGenerator->GetTargetDirectory(gt); - dir += "/"; + std::string dir = + cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/', + gt->LocalGenerator->GetTargetDirectory(gt), '/'); gt->ObjectDirectory = dir; } // Private methods -void cmGlobalNinjaGenerator::OpenBuildFileStream() +bool cmGlobalNinjaGenerator::OpenBuildFileStream() { // Compute Ninja's build file path. std::string buildFilePath = - this->GetCMakeInstance()->GetHomeOutputDirectory(); - buildFilePath += "/"; - buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE; + cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/', + cmGlobalNinjaGenerator::NINJA_BUILD_FILE); // Get a stream where to generate things. if (!this->BuildFileStream) { - this->BuildFileStream = new cmGeneratedFileStream( - buildFilePath.c_str(), false, this->GetMakefileEncoding()); - if (!this->BuildFileStream) { + this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>( + buildFilePath, false, this->GetMakefileEncoding()); + if (!(*this->BuildFileStream)) { // An error message is generated by the constructor if it cannot // open the file. - return; + return false; } } @@ -799,34 +740,34 @@ void cmGlobalNinjaGenerator::OpenBuildFileStream() *this->BuildFileStream << "# This file contains all the build statements describing the\n" << "# compilation DAG.\n\n"; + + return true; } void cmGlobalNinjaGenerator::CloseBuildFileStream() { if (this->BuildFileStream) { - delete this->BuildFileStream; - this->BuildFileStream = CM_NULLPTR; + this->BuildFileStream.reset(); } else { cmSystemTools::Error("Build file stream was not open."); } } -void cmGlobalNinjaGenerator::OpenRulesFileStream() +bool cmGlobalNinjaGenerator::OpenRulesFileStream() { // Compute Ninja's build file path. std::string rulesFilePath = - this->GetCMakeInstance()->GetHomeOutputDirectory(); - rulesFilePath += "/"; - rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE; + cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/', + cmGlobalNinjaGenerator::NINJA_RULES_FILE); // Get a stream where to generate things. if (!this->RulesFileStream) { - this->RulesFileStream = new cmGeneratedFileStream( - rulesFilePath.c_str(), false, this->GetMakefileEncoding()); - if (!this->RulesFileStream) { + this->RulesFileStream = cm::make_unique<cmGeneratedFileStream>( + rulesFilePath, false, this->GetMakefileEncoding()); + if (!(*this->RulesFileStream)) { // An error message is generated by the constructor if it cannot // open the file. - return; + return false; } } @@ -841,13 +782,13 @@ void cmGlobalNinjaGenerator::OpenRulesFileStream() << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n" ; /* clang-format on */ + return true; } void cmGlobalNinjaGenerator::CloseRulesFileStream() { if (this->RulesFileStream) { - delete this->RulesFileStream; - this->RulesFileStream = CM_NULLPTR; + this->RulesFileStream.reset(); } else { cmSystemTools::Error("Rules file stream was not open."); } @@ -858,7 +799,7 @@ static void EnsureTrailingSlash(std::string& path) if (path.empty()) { return; } - std::string::value_type last = path[path.size() - 1]; + std::string::value_type last = path.back(); #ifdef _WIN32 if (last != '\\') { path += '\\'; @@ -870,18 +811,29 @@ static void EnsureTrailingSlash(std::string& path) #endif } -std::string cmGlobalNinjaGenerator::ConvertToNinjaPath( +std::string const& cmGlobalNinjaGenerator::ConvertToNinjaPath( const std::string& path) const { + auto const f = ConvertToNinjaPathCache.find(path); + if (f != ConvertToNinjaPathCache.end()) { + return f->second; + } + cmLocalNinjaGenerator* ng = static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]); - std::string convPath = ng->ConvertToRelativePath( - this->LocalGenerators[0]->GetState()->GetBinaryDirectory(), path); + std::string const& bin_dir = ng->GetState()->GetBinaryDirectory(); + std::string convPath = ng->MaybeConvertToRelativePath(bin_dir, path); convPath = this->NinjaOutputPath(convPath); #ifdef _WIN32 std::replace(convPath.begin(), convPath.end(), '/', '\\'); #endif - return convPath; + return ConvertToNinjaPathCache.emplace(path, std::move(convPath)) + .first->second; +} + +void cmGlobalNinjaGenerator::AddAdditionalCleanFile(std::string fileName) +{ + this->AdditionalCleanFiles.emplace(std::move(fileName)); } void cmGlobalNinjaGenerator::AddCXXCompileCommand( @@ -899,25 +851,25 @@ void cmGlobalNinjaGenerator::AddCXXCompileCommand( // Get a stream where to generate things. this->CompileCommandsStream = - new cmGeneratedFileStream(buildFilePath.c_str()); + cm::make_unique<cmGeneratedFileStream>(buildFilePath); *this->CompileCommandsStream << "["; } else { *this->CompileCommandsStream << "," << std::endl; } std::string sourceFileName = sourceFile; - if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str())) { + if (!cmSystemTools::FileIsFullPath(sourceFileName)) { sourceFileName = cmSystemTools::CollapseFullPath( sourceFileName, this->GetCMakeInstance()->GetHomeOutputDirectory()); } /* clang-format off */ *this->CompileCommandsStream << "\n{\n" - << " \"directory\": \"" + << R"( "directory": ")" << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n" - << " \"command\": \"" + << R"( "command": ")" << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n" - << " \"file\": \"" + << R"( "file": ")" << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n" << "}"; /* clang-format on */ @@ -927,8 +879,7 @@ void cmGlobalNinjaGenerator::CloseCompileCommandsStream() { if (this->CompileCommandsStream) { *this->CompileCommandsStream << "\n]"; - delete this->CompileCommandsStream; - this->CompileCommandsStream = CM_NULLPTR; + this->CompileCommandsStream.reset(); } } @@ -940,27 +891,18 @@ void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) << cmVersion::GetMinorVersion() << "\n\n"; } -void cmGlobalNinjaGenerator::AddDependencyToAll(cmGeneratorTarget* target) -{ - this->AppendTargetOutputs(target, this->AllDependencies); -} - -void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input) -{ - this->AllDependencies.push_back(input); -} - void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies() { - for (std::map<std::string, std::set<std::string> >::iterator i = - this->AssumedSourceDependencies.begin(); - i != this->AssumedSourceDependencies.end(); ++i) { - cmNinjaDeps deps; - std::copy(i->second.begin(), i->second.end(), std::back_inserter(deps)); + for (auto const& asd : this->AssumedSourceDependencies) { + cmNinjaDeps orderOnlyDeps; + std::copy(asd.second.begin(), asd.second.end(), + std::back_inserter(orderOnlyDeps)); WriteCustomCommandBuild(/*command=*/"", /*description=*/"", "Assume dependencies for generated source file.", - /*depfile*/ "", /*uses_terminal*/ false, - /*restat*/ true, cmNinjaDeps(1, i->first), deps); + /*depfile*/ "", /*job_pool*/ "", + /*uses_terminal*/ false, + /*restat*/ true, cmNinjaDeps(1, asd.first), + cmNinjaDeps(), orderOnlyDeps); } } @@ -1024,63 +966,72 @@ void cmGlobalNinjaGenerator::AppendTargetDepends( { if (target->GetType() == cmStateEnums::GLOBAL_TARGET) { // These depend only on other CMake-provided targets, e.g. "all". - std::set<std::string> const& utils = target->GetUtilities(); - for (std::set<std::string>::const_iterator i = utils.begin(); - i != utils.end(); ++i) { + for (BT<std::string> const& util : target->GetUtilities()) { std::string d = - target->GetLocalGenerator()->GetCurrentBinaryDirectory() + - std::string("/") + *i; + target->GetLocalGenerator()->GetCurrentBinaryDirectory() + "/" + + util.Value; outputs.push_back(this->ConvertToNinjaPath(d)); } } else { cmNinjaDeps outs; - cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(target); - for (cmTargetDependSet::const_iterator i = targetDeps.begin(); - i != targetDeps.end(); ++i) { - if ((*i)->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + for (cmTargetDepend const& targetDep : + this->GetTargetDirectDepends(target)) { + if (targetDep->GetType() == cmStateEnums::INTERFACE_LIBRARY) { continue; } - this->AppendTargetOutputs(*i, outs, depends); + this->AppendTargetOutputs(targetDep, outs, depends); } std::sort(outs.begin(), outs.end()); - outputs.insert(outputs.end(), outs.begin(), outs.end()); + cmAppend(outputs, outs); } } void cmGlobalNinjaGenerator::AppendTargetDependsClosure( cmGeneratorTarget const* target, cmNinjaDeps& outputs) { - TargetDependsClosureMap::iterator i = - this->TargetDependsClosures.find(target); - if (i == this->TargetDependsClosures.end()) { - TargetDependsClosureMap::value_type e( - target, std::set<cmGeneratorTarget const*>()); - i = this->TargetDependsClosures.insert(e).first; - this->ComputeTargetDependsClosure(target, i->second); - } - std::set<cmGeneratorTarget const*> const& targets = i->second; - cmNinjaDeps outs; - for (std::set<cmGeneratorTarget const*>::const_iterator ti = targets.begin(); - ti != targets.end(); ++ti) { - this->AppendTargetOutputs(*ti, outs); - } - std::sort(outs.begin(), outs.end()); - outputs.insert(outputs.end(), outs.begin(), outs.end()); + cmNinjaOuts outs; + this->AppendTargetDependsClosure(target, outs, true); + cmAppend(outputs, outs); } -void cmGlobalNinjaGenerator::ComputeTargetDependsClosure( - cmGeneratorTarget const* target, std::set<cmGeneratorTarget const*>& depends) +void cmGlobalNinjaGenerator::AppendTargetDependsClosure( + cmGeneratorTarget const* target, cmNinjaOuts& outputs, bool omit_self) { - cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(target); - for (cmTargetDependSet::const_iterator i = targetDeps.begin(); - i != targetDeps.end(); ++i) { - if ((*i)->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - if (depends.insert(*i).second) { - this->ComputeTargetDependsClosure(*i, depends); + + // try to locate the target in the cache + auto find = this->TargetDependsClosures.lower_bound(target); + + if (find == this->TargetDependsClosures.end() || find->first != target) { + // We now calculate the closure outputs by inspecting the dependent + // targets recursively. + // For that we have to distinguish between a local result set that is only + // relevant for filling the cache entries properly isolated and a global + // result set that is relevant for the result of the top level call to + // AppendTargetDependsClosure. + cmNinjaOuts this_outs; // this will be the new cache entry + + for (auto const& dep_target : this->GetTargetDirectDepends(target)) { + if (dep_target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + + // Collect the dependent targets for _this_ target + this->AppendTargetDependsClosure(dep_target, this_outs, false); } + find = this->TargetDependsClosures.emplace_hint(find, target, + std::move(this_outs)); + } + + // now fill the outputs of the final result from the newly generated cache + // entry + outputs.insert(find->second.begin(), find->second.end()); + + // finally generate the outputs of the target itself, if applicable + cmNinjaDeps outs; + if (!omit_self) { + this->AppendTargetOutputs(target, outs); } + outputs.insert(outs.begin(), outs.end()); } void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, @@ -1091,8 +1042,8 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, this->AppendTargetOutputs(target, outputs); // Mark the target's outputs as ambiguous to ensure that no other target uses // the output as an alias. - for (cmNinjaDeps::iterator i = outputs.begin(); i != outputs.end(); ++i) { - TargetAliases[*i] = CM_NULLPTR; + for (std::string const& output : outputs) { + TargetAliases[output] = nullptr; } // Insert the alias into the map. If the alias was already present in the @@ -1100,7 +1051,7 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, std::pair<TargetAliasMap::iterator, bool> newAlias = TargetAliases.insert(std::make_pair(buildAlias, target)); if (newAlias.second && newAlias.first->second != target) { - newAlias.first->second = CM_NULLPTR; + newAlias.first->second = nullptr; } } @@ -1109,17 +1060,27 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); os << "# Target aliases.\n\n"; - for (TargetAliasMap::const_iterator i = TargetAliases.begin(); - i != TargetAliases.end(); ++i) { + cmNinjaBuild build("phony"); + build.Outputs.emplace_back(""); + for (auto const& ta : TargetAliases) { // Don't write ambiguous aliases. - if (!i->second) { + if (!ta.second) { continue; } - cmNinjaDeps deps; - this->AppendTargetOutputs(i->second, deps); + // Don't write alias if there is a already a custom command with + // matching output + if (this->HasCustomCommandOutput(ta.first)) { + continue; + } - this->WritePhonyBuild(os, "", cmNinjaDeps(1, i->first), deps); + // Outputs + build.Outputs[0] = ta.first; + // Explicit depdendencies + build.ExplicitDeps.clear(); + this->AppendTargetOutputs(ta.second, build.ExplicitDeps); + // Write + this->WriteBuild(os, build); } } @@ -1128,65 +1089,32 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); os << "# Folder targets.\n\n"; - std::map<std::string, cmNinjaDeps> targetsPerFolder; - for (std::vector<cmLocalGenerator*>::const_iterator lgi = - this->LocalGenerators.begin(); - lgi != this->LocalGenerators.end(); ++lgi) { - cmLocalGenerator const* lg = *lgi; - const std::string currentBinaryFolder( - lg->GetStateSnapshot().GetDirectory().GetCurrentBinary()); - // The directory-level rule should depend on the target-level rules - // for all targets in the directory. - targetsPerFolder[currentBinaryFolder] = cmNinjaDeps(); - for (std::vector<cmGeneratorTarget*>::const_iterator ti = - lg->GetGeneratorTargets().begin(); - ti != lg->GetGeneratorTargets().end(); ++ti) { - cmGeneratorTarget const* gt = *ti; - cmStateEnums::TargetType const type = gt->GetType(); - if ((type == cmStateEnums::EXECUTABLE || - type == cmStateEnums::STATIC_LIBRARY || - type == cmStateEnums::SHARED_LIBRARY || - type == cmStateEnums::MODULE_LIBRARY || - type == cmStateEnums::OBJECT_LIBRARY || - type == cmStateEnums::UTILITY) && - !gt->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { - targetsPerFolder[currentBinaryFolder].push_back(gt->GetName()); - } - } + std::map<std::string, DirectoryTarget> dirTargets = + this->ComputeDirectoryTargets(); - // The directory-level rule should depend on the directory-level - // rules of the subdirectories. - std::vector<cmStateSnapshot> const& children = - lg->GetStateSnapshot().GetChildren(); - for (std::vector<cmStateSnapshot>::const_iterator stateIt = - children.begin(); - stateIt != children.end(); ++stateIt) { - std::string const currentBinaryDir = - stateIt->GetDirectory().GetCurrentBinary(); - - targetsPerFolder[currentBinaryFolder].push_back( - this->ConvertToNinjaPath(currentBinaryDir + "/all")); - } - } - - std::string const rootBinaryDir = - this->LocalGenerators[0]->GetBinaryDirectory(); - for (std::map<std::string, cmNinjaDeps>::const_iterator it = - targetsPerFolder.begin(); - it != targetsPerFolder.end(); ++it) { + for (auto const& it : dirTargets) { + cmNinjaBuild build("phony"); cmGlobalNinjaGenerator::WriteDivider(os); - std::string const& currentBinaryDir = it->first; - - // Do not generate a rule for the root binary dir. - if (rootBinaryDir.length() >= currentBinaryDir.length()) { - continue; + std::string const& currentBinaryDir = it.first; + DirectoryTarget const& dt = it.second; + + // Setup target + build.Comment = "Folder: " + currentBinaryDir; + build.Outputs.emplace_back( + this->ConvertToNinjaPath(currentBinaryDir + "/all")); + for (DirectoryTarget::Target const& t : dt.Targets) { + if (!t.ExcludeFromAll) { + this->AppendTargetOutputs(t.GT, build.ExplicitDeps); + } } - - std::string const comment = "Folder: " + currentBinaryDir; - cmNinjaDeps output(1); - output.push_back(this->ConvertToNinjaPath(currentBinaryDir + "/all")); - - this->WritePhonyBuild(os, comment, output, it->second); + for (DirectoryTarget::Dir const& d : dt.Children) { + if (!d.ExcludeFromAll) { + build.ExplicitDeps.emplace_back( + this->ConvertToNinjaPath(d.Path + "/all")); + } + } + // Write target + this->WriteBuild(os, build); } } @@ -1220,50 +1148,37 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) // get the list of files that cmake itself has generated as a // product of configuration. - for (std::vector<cmLocalGenerator*>::const_iterator i = - this->LocalGenerators.begin(); - i != this->LocalGenerators.end(); ++i) { + for (cmLocalGenerator* lg : this->LocalGenerators) { // get the vector of files created by this makefile and convert them // to ninja paths, which are all relative in respect to the build directory - const std::vector<std::string>& files = - (*i)->GetMakefile()->GetOutputFiles(); - typedef std::vector<std::string>::const_iterator vect_it; - for (vect_it j = files.begin(); j != files.end(); ++j) { - knownDependencies.insert(this->ConvertToNinjaPath(*j)); + for (std::string const& file : lg->GetMakefile()->GetOutputFiles()) { + knownDependencies.insert(this->ConvertToNinjaPath(file)); } - // get list files which are implicit dependencies as well and will be phony - // for rebuild manifest - std::vector<std::string> const& lf = (*i)->GetMakefile()->GetListFiles(); - typedef std::vector<std::string>::const_iterator vect_it; - for (vect_it j = lf.begin(); j != lf.end(); ++j) { - knownDependencies.insert(this->ConvertToNinjaPath(*j)); + if (!this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + // get list files which are implicit dependencies as well and will be + // phony for rebuild manifest + for (std::string const& j : lg->GetMakefile()->GetListFiles()) { + knownDependencies.insert(this->ConvertToNinjaPath(j)); + } } - std::vector<cmGeneratorExpressionEvaluationFile*> const& ef = - (*i)->GetMakefile()->GetEvaluationFiles(); - for (std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator li = - ef.begin(); - li != ef.end(); ++li) { + for (cmGeneratorExpressionEvaluationFile* li : + lg->GetMakefile()->GetEvaluationFiles()) { // get all the files created by generator expressions and convert them // to ninja paths - std::vector<std::string> evaluationFiles = (*li)->GetFiles(); - for (vect_it j = evaluationFiles.begin(); j != evaluationFiles.end(); - ++j) { - knownDependencies.insert(this->ConvertToNinjaPath(*j)); + for (std::string const& evaluationFile : li->GetFiles()) { + knownDependencies.insert(this->ConvertToNinjaPath(evaluationFile)); } } } knownDependencies.insert(this->CMakeCacheFile); - for (TargetAliasMap::const_iterator i = this->TargetAliases.begin(); - i != this->TargetAliases.end(); ++i) { - knownDependencies.insert(this->ConvertToNinjaPath(i->first)); + for (auto const& ta : this->TargetAliases) { + knownDependencies.insert(this->ConvertToNinjaPath(ta.first)); } // remove all source files we know will exist. - typedef std::map<std::string, std::set<std::string> >::const_iterator map_it; - for (map_it i = this->AssumedSourceDependencies.begin(); - i != this->AssumedSourceDependencies.end(); ++i) { - knownDependencies.insert(this->ConvertToNinjaPath(i->first)); + for (auto const& i : this->AssumedSourceDependencies) { + knownDependencies.insert(this->ConvertToNinjaPath(i.first)); } // now we difference with CombinedCustomCommandExplicitDependencies to find @@ -1280,25 +1195,26 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) knownDependencies.begin(), knownDependencies.end(), std::back_inserter(unknownExplicitDepends)); - std::string const rootBuildDirectory = - this->GetCMakeInstance()->GetHomeOutputDirectory(); - bool const inSourceBuild = - (rootBuildDirectory == this->GetCMakeInstance()->GetHomeDirectory()); std::vector<std::string> warnExplicitDepends; - for (std::vector<std::string>::const_iterator i = - unknownExplicitDepends.begin(); - i != unknownExplicitDepends.end(); ++i) { - // verify the file is in the build directory - std::string const absDepPath = - cmSystemTools::CollapseFullPath(*i, rootBuildDirectory.c_str()); - bool const inBuildDir = - cmSystemTools::IsSubDirectory(absDepPath, rootBuildDirectory); - if (inBuildDir) { - cmNinjaDeps deps(1, *i); - this->WritePhonyBuild(os, "", deps, cmNinjaDeps()); - if (this->PolicyCMP0058 == cmPolicies::WARN && !inSourceBuild && - warnExplicitDepends.size() < 10) { - warnExplicitDepends.push_back(*i); + if (!unknownExplicitDepends.empty()) { + cmake* cmk = this->GetCMakeInstance(); + std::string const& buildRoot = cmk->GetHomeOutputDirectory(); + bool const inSource = (buildRoot == cmk->GetHomeDirectory()); + bool const warn = (!inSource && (this->PolicyCMP0058 == cmPolicies::WARN)); + cmNinjaBuild build("phony"); + build.Outputs.emplace_back(""); + for (std::string const& ued : unknownExplicitDepends) { + // verify the file is in the build directory + std::string const absDepPath = + cmSystemTools::CollapseFullPath(ued, buildRoot); + if (cmSystemTools::IsSubDirectory(absDepPath, buildRoot)) { + // Generate phony build statement + build.Outputs[0] = ued; + this->WriteBuild(os, build); + // Add to warning on demand + if (warn && warnExplicitDepends.size() < 10) { + warnExplicitDepends.push_back(ued); + } } } } @@ -1320,7 +1236,8 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) "options to the custom commands that produce these files." ; /* clang-format on */ - this->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING, w.str()); + this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); } } @@ -1330,87 +1247,144 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); os << "# Built-in targets\n\n"; - this->WriteTargetAll(os); + this->WriteTargetDefault(os); this->WriteTargetRebuildManifest(os); this->WriteTargetClean(os); this->WriteTargetHelp(os); } -void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) +void cmGlobalNinjaGenerator::WriteTargetDefault(std::ostream& os) { - cmNinjaDeps outputs; - outputs.push_back(this->TargetAll); - - this->WritePhonyBuild(os, "The main all target.", outputs, - this->AllDependencies); - if (!this->HasOutputPathPrefix()) { - cmGlobalNinjaGenerator::WriteDefault(os, outputs, + cmNinjaDeps all; + all.push_back(this->TargetAll); + cmGlobalNinjaGenerator::WriteDefault(os, all, "Make the all target the default."); } } void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) { + if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { + return; + } cmLocalGenerator* lg = this->LocalGenerators[0]; - std::ostringstream cmd; - cmd << lg->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(), - cmOutputConverter::SHELL) - << " -H" - << lg->ConvertToOutputFormat(lg->GetSourceDirectory(), - cmOutputConverter::SHELL) - << " -B" - << lg->ConvertToOutputFormat(lg->GetBinaryDirectory(), - cmOutputConverter::SHELL); - WriteRule(*this->RulesFileStream, "RERUN_CMAKE", cmd.str(), - "Re-running CMake...", "Rule for re-running cmake.", - /*depfile=*/"", - /*deptype=*/"", - /*rspfile=*/"", - /*rspcontent*/ "", - /*restat=*/"", - /*generator=*/true); - - cmNinjaDeps implicitDeps; - for (std::vector<cmLocalGenerator*>::const_iterator i = - this->LocalGenerators.begin(); - i != this->LocalGenerators.end(); ++i) { - std::vector<std::string> const& lf = (*i)->GetMakefile()->GetListFiles(); - for (std::vector<std::string>::const_iterator fi = lf.begin(); - fi != lf.end(); ++fi) { - implicitDeps.push_back(this->ConvertToNinjaPath(*fi)); + { + cmNinjaRule rule("RERUN_CMAKE"); + rule.Command = + cmStrCat(CMakeCmd(), " -S", + lg->ConvertToOutputFormat(lg->GetSourceDirectory(), + cmOutputConverter::SHELL), + " -B", + lg->ConvertToOutputFormat(lg->GetBinaryDirectory(), + cmOutputConverter::SHELL)); + rule.Description = "Re-running CMake..."; + rule.Comment = "Rule for re-running cmake."; + rule.Generator = true; + WriteRule(*this->RulesFileStream, rule); + } + + cmNinjaBuild reBuild("RERUN_CMAKE"); + reBuild.Comment = "Re-run CMake if any of its inputs changed."; + reBuild.Outputs.push_back(this->NinjaOutputPath(NINJA_BUILD_FILE)); + + for (cmLocalGenerator* localGen : this->LocalGenerators) { + for (std::string const& fi : localGen->GetMakefile()->GetListFiles()) { + reBuild.ImplicitDeps.push_back(this->ConvertToNinjaPath(fi)); } } - implicitDeps.push_back(this->CMakeCacheFile); - - std::sort(implicitDeps.begin(), implicitDeps.end()); - implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), - implicitDeps.end()); + reBuild.ImplicitDeps.push_back(this->CMakeCacheFile); - cmNinjaVars variables; // Use 'console' pool to get non buffered output of the CMake re-run call // Available since Ninja 1.5 if (SupportsConsolePool()) { - variables["pool"] = "console"; + reBuild.Variables["pool"] = "console"; } - std::string const ninjaBuildFile = this->NinjaOutputPath(NINJA_BUILD_FILE); - this->WriteBuild(os, "Re-run CMake if any of its inputs changed.", - "RERUN_CMAKE", - /*outputs=*/cmNinjaDeps(1, ninjaBuildFile), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), implicitDeps, - /*orderOnlyDeps=*/cmNinjaDeps(), variables); + cmake* cm = this->GetCMakeInstance(); + if (this->SupportsManifestRestat() && cm->DoWriteGlobVerifyTarget()) { + { + cmNinjaRule rule("VERIFY_GLOBS"); + rule.Command = + cmStrCat(CMakeCmd(), " -P ", + lg->ConvertToOutputFormat(cm->GetGlobVerifyScript(), + cmOutputConverter::SHELL)); + rule.Description = "Re-checking globbed directories..."; + rule.Comment = "Rule for re-checking globbed directories."; + rule.Generator = true; + this->WriteRule(*this->RulesFileStream, rule); + } + + cmNinjaBuild phonyBuild("phony"); + phonyBuild.Comment = "Phony target to force glob verification run."; + phonyBuild.Outputs.push_back(cm->GetGlobVerifyScript() + "_force"); + this->WriteBuild(os, phonyBuild); + + reBuild.Variables["restat"] = "1"; + std::string const verifyScriptFile = + this->NinjaOutputPath(cm->GetGlobVerifyScript()); + std::string const verifyStampFile = + this->NinjaOutputPath(cm->GetGlobVerifyStamp()); + { + cmNinjaBuild vgBuild("VERIFY_GLOBS"); + vgBuild.Comment = + "Re-run CMake to check if globbed directories changed."; + vgBuild.Outputs.push_back(verifyStampFile); + vgBuild.ImplicitDeps = phonyBuild.Outputs; + vgBuild.Variables = reBuild.Variables; + this->WriteBuild(os, vgBuild); + } + reBuild.Variables.erase("restat"); + reBuild.ImplicitDeps.push_back(verifyScriptFile); + reBuild.ExplicitDeps.push_back(verifyStampFile); + } else if (!this->SupportsManifestRestat() && + cm->DoWriteGlobVerifyTarget()) { + std::ostringstream msg; + msg << "The detected version of Ninja:\n" + << " " << this->NinjaVersion << "\n" + << "is less than the version of Ninja required by CMake for adding " + "restat dependencies to the build.ninja manifest regeneration " + "target:\n" + << " " + << cmGlobalNinjaGenerator::RequiredNinjaVersionForManifestRestat() + << "\n"; + msg << "Any pre-check scripts, such as those generated for file(GLOB " + "CONFIGURE_DEPENDS), will not be run by Ninja."; + this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, + msg.str()); + } + + std::sort(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end()); + reBuild.ImplicitDeps.erase( + std::unique(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end()), + reBuild.ImplicitDeps.end()); + + this->WriteBuild(os, reBuild); - this->WritePhonyBuild(os, "A missing CMake input file is not an error.", - implicitDeps, cmNinjaDeps()); + { + cmNinjaBuild build("phony"); + build.Comment = "A missing CMake input file is not an error."; + std::set_difference(std::make_move_iterator(reBuild.ImplicitDeps.begin()), + std::make_move_iterator(reBuild.ImplicitDeps.end()), + CustomCommandOutputs.begin(), + CustomCommandOutputs.end(), + std::back_inserter(build.Outputs)); + this->WriteBuild(os, build); + } +} + +std::string cmGlobalNinjaGenerator::CMakeCmd() const +{ + cmLocalGenerator* lgen = this->LocalGenerators.at(0); + return lgen->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(), + cmOutputConverter::SHELL); } -std::string cmGlobalNinjaGenerator::ninjaCmd() const +std::string cmGlobalNinjaGenerator::NinjaCmd() const { cmLocalGenerator* lgen = this->LocalGenerators[0]; - if (lgen) { + if (lgen != nullptr) { return lgen->ConvertToOutputFormat(this->NinjaCommand, cmOutputConverter::SHELL); } @@ -1427,44 +1401,115 @@ bool cmGlobalNinjaGenerator::SupportsImplicitOuts() const return this->NinjaSupportsImplicitOuts; } +bool cmGlobalNinjaGenerator::SupportsManifestRestat() const +{ + return this->NinjaSupportsManifestRestat; +} + +bool cmGlobalNinjaGenerator::SupportsMultilineDepfile() const +{ + return this->NinjaSupportsMultilineDepfile; +} + +bool cmGlobalNinjaGenerator::WriteTargetCleanAdditional(std::ostream& os) +{ + cmLocalGenerator* lgr = this->LocalGenerators.at(0); + std::string cleanScriptRel = "CMakeFiles/clean_additional.cmake"; + std::string cleanScriptAbs = + cmStrCat(lgr->GetBinaryDirectory(), '/', cleanScriptRel); + + // Check if there are additional files to clean + if (this->AdditionalCleanFiles.empty()) { + // Remove cmake clean script file if it exists + cmSystemTools::RemoveFile(cleanScriptAbs); + return false; + } + + // Write cmake clean script file + { + cmGeneratedFileStream fout(cleanScriptAbs); + if (!fout) { + return false; + } + fout << "# Additional clean files\n\n"; + fout << "file(REMOVE_RECURSE\n"; + for (std::string const& acf : this->AdditionalCleanFiles) { + fout << " " + << cmOutputConverter::EscapeForCMake(ConvertToNinjaPath(acf)) + << '\n'; + } + fout << ")\n"; + } + // Register clean script file + lgr->GetMakefile()->AddCMakeOutputFile(cleanScriptAbs); + + // Write rule + { + cmNinjaRule rule("CLEAN_ADDITIONAL"); + rule.Command = cmStrCat( + CMakeCmd(), " -P ", + lgr->ConvertToOutputFormat(this->NinjaOutputPath(cleanScriptRel), + cmOutputConverter::SHELL)); + rule.Description = "Cleaning additional files..."; + rule.Comment = "Rule for cleaning additional files."; + WriteRule(*this->RulesFileStream, rule); + } + + // Write build + { + cmNinjaBuild build("CLEAN_ADDITIONAL"); + build.Comment = "Clean additional files."; + build.Outputs.push_back( + this->NinjaOutputPath(this->GetAdditionalCleanTargetName())); + WriteBuild(os, build); + } + // Return success + return true; +} + void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) { - WriteRule(*this->RulesFileStream, "CLEAN", ninjaCmd() + " -t clean", - "Cleaning all built files...", - "Rule for cleaning all built files.", - /*depfile=*/"", - /*deptype=*/"", - /*rspfile=*/"", - /*rspcontent*/ "", - /*restat=*/"", - /*generator=*/false); - WriteBuild(os, "Clean all the built files.", "CLEAN", - /*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("clean")), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), - /*implicitDeps=*/cmNinjaDeps(), - /*orderOnlyDeps=*/cmNinjaDeps(), - /*variables=*/cmNinjaVars()); + // -- Additional clean target + bool additionalFiles = WriteTargetCleanAdditional(os); + + // -- Default clean target + // Write rule + { + cmNinjaRule rule("CLEAN"); + rule.Command = NinjaCmd() + " -t clean"; + rule.Description = "Cleaning all built files..."; + rule.Comment = "Rule for cleaning all built files."; + WriteRule(*this->RulesFileStream, rule); + } + + // Write build + { + cmNinjaBuild build("CLEAN"); + build.Comment = "Clean all the built files."; + build.Outputs.push_back(this->NinjaOutputPath(this->GetCleanTargetName())); + if (additionalFiles) { + build.ExplicitDeps.push_back( + this->NinjaOutputPath(this->GetAdditionalCleanTargetName())); + } + WriteBuild(os, build); + } } void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os) { - WriteRule(*this->RulesFileStream, "HELP", ninjaCmd() + " -t targets", - "All primary targets available:", - "Rule for printing all primary targets available.", - /*depfile=*/"", - /*deptype=*/"", - /*rspfile=*/"", - /*rspcontent*/ "", - /*restat=*/"", - /*generator=*/false); - WriteBuild(os, "Print all primary targets available.", "HELP", - /*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("help")), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), - /*implicitDeps=*/cmNinjaDeps(), - /*orderOnlyDeps=*/cmNinjaDeps(), - /*variables=*/cmNinjaVars()); + { + cmNinjaRule rule("HELP"); + rule.Command = NinjaCmd() + " -t targets"; + rule.Description = "All primary targets available:"; + rule.Comment = "Rule for printing all primary targets available."; + WriteRule(*this->RulesFileStream, rule); + } + { + cmNinjaBuild build("HELP"); + build.Comment = "Print all primary targets available."; + build.Outputs.push_back(this->NinjaOutputPath("help")); + WriteBuild(os, build); + } } void cmGlobalNinjaGenerator::InitOutputPathPrefix() @@ -1513,12 +1558,13 @@ Compilation of source files within a target is split into the following steps: 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 + --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE \ + --lang=Fortran - build src.f90-pp.f90 | src.f90-pp.f90.ddi: Fortran_PREPROCESS src.f90 + build src.f90-pp.f90 | src.f90.o.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 + DEP_FILE = src.f90.o.d + DYNDEP_INTERMEDIATE_FILE = src.f90.o.ddi The ``cmake -E cmake_ninja_depends`` tool reads the preprocessed output and generates the ninja depfile for preprocessor dependencies. It also @@ -1532,9 +1578,9 @@ Compilation of source files within a target is split into the following steps: rule Fortran_DYNDEP command = cmake -E cmake_ninja_dyndep \ - --tdi=FortranDependInfo.json --dd=$out $in + --tdi=FortranDependInfo.json --lang=Fortran --dd=$out $in - build Fortran.dd: Fortran_DYNDEP src1.f90-pp.f90.ddi src2.f90-pp.f90.ddi + build Fortran.dd: Fortran_DYNDEP src1.f90.o.ddi src2.f90.o.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 @@ -1572,6 +1618,19 @@ Compilation of source files within a target is split into the following steps: (because the latter consumes the module). */ +struct cmSourceInfo +{ + // Set of provided and required modules. + std::set<std::string> Provides; + std::set<std::string> Requires; + + // Set of files included in the translation unit. + std::set<std::string> Includes; +}; + +static std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( + std::string const& arg_tdi, std::string const& arg_pp); + int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd) { @@ -1580,8 +1639,8 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, 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; + std::string arg_lang; + for (std::string const& arg : cmMakeRange(argBeg, argEnd)) { if (cmHasLiteralPrefix(arg, "--tdi=")) { arg_tdi = arg.substr(6); } else if (cmHasLiteralPrefix(arg, "--pp=")) { @@ -1592,9 +1651,10 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, arg_obj = arg.substr(6); } else if (cmHasLiteralPrefix(arg, "--ddi=")) { arg_ddi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--lang=")) { + arg_lang = arg.substr(7); } else { - cmSystemTools::Error("-E cmake_ninja_depends unknown argument: ", - arg.c_str()); + cmSystemTools::Error("-E cmake_ninja_depends unknown argument: " + arg); return 1; } } @@ -1618,50 +1678,31 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, 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()); - } - } + if (arg_lang.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --lang="); + return 1; } - 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()); + std::unique_ptr<cmSourceInfo> info; + if (arg_lang == "Fortran") { + info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_pp); + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_depends does not understand the ", arg_lang, + " language")); return 1; } - if (cmFortran_yyparse(parser.Scanner) != 0) { - // Failed to parse the file. + + if (!info) { + // The error message is already expected to have been output. return 1; } { - cmGeneratedFileStream depfile(arg_dep.c_str()); + cmGeneratedFileStream depfile(arg_dep); 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); + for (std::string const& include : info->Includes) { + depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(include); } depfile << "\n"; } @@ -1670,30 +1711,82 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, 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); + for (std::string const& provide : info->Provides) { + ddi_provides.append(provide); } Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue; - for (std::set<std::string>::iterator i = info.Requires.begin(); - i != info.Requires.end(); ++i) { + for (std::string const& r : info->Requires) { // Require modules not provided in the same source. - if (!info.Provides.count(*i)) { - ddi_requires.append(*i); + if (!info->Provides.count(r)) { + ddi_requires.append(r); } } - cmGeneratedFileStream ddif(arg_ddi.c_str()); + cmGeneratedFileStream ddif(arg_ddi); ddif << ddi; if (!ddif) { - cmSystemTools::Error("-E cmake_ninja_depends failed to write ", - arg_ddi.c_str()); + cmSystemTools::Error("-E cmake_ninja_depends failed to write " + arg_ddi); return 1; } return 0; } -struct cmFortranObjectInfo +std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( + std::string const& arg_tdi, std::string const& arg_pp) +{ + cmFortranCompiler fc; + 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( + cmStrCat("-E cmake_ninja_depends failed to parse ", arg_tdi, + reader.getFormattedErrorMessages())); + return nullptr; + } + } + + Json::Value const& tdi_include_dirs = tdi["include-dirs"]; + if (tdi_include_dirs.isArray()) { + for (auto const& tdi_include_dir : tdi_include_dirs) { + includes.push_back(tdi_include_dir.asString()); + } + } + + Json::Value const& tdi_compiler_id = tdi["compiler-id"]; + fc.Id = tdi_compiler_id.asString(); + + Json::Value const& tdi_submodule_sep = tdi["submodule-sep"]; + fc.SModSep = tdi_submodule_sep.asString(); + + Json::Value const& tdi_submodule_ext = tdi["submodule-ext"]; + fc.SModExt = tdi_submodule_ext.asString(); + } + + cmFortranSourceInfo finfo; + std::set<std::string> defines; + cmFortranParser parser(fc, includes, defines, finfo); + if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) { + cmSystemTools::Error("-E cmake_ninja_depends failed to open " + arg_pp); + return nullptr; + } + if (cmFortran_yyparse(parser.Scanner) != 0) { + // Failed to parse the file. + return nullptr; + } + + auto info = cm::make_unique<cmSourceInfo>(); + info->Provides = finfo.Provides; + info->Requires = finfo.Requires; + info->Includes = finfo.Includes; + return info; +} + +struct cmDyndepObjectInfo { std::string Object; std::vector<std::string> Provides; @@ -1705,7 +1798,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( 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) + std::vector<std::string> const& linked_target_dirs, + std::string const& arg_lang) { // Setup path conversions. { @@ -1714,62 +1808,59 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( 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()))); + auto mfd = cm::make_unique<cmMakefile>(this, snapshot); + std::unique_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) { + std::vector<cmDyndepObjectInfo> objects; + for (std::string const& arg_ddi : arg_ddis) { // 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); + cmsys::ifstream ddif(arg_ddi.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()); + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", + arg_ddi, + reader.getFormattedErrorMessages())); return false; } - cmFortranObjectInfo info; + cmDyndepObjectInfo 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()); + for (auto const& ddi_provide : ddi_provides) { + info.Provides.push_back(ddi_provide.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()); + for (auto const& ddi_require : ddi_requires) { + info.Requires.push_back(ddi_require.asString()); } } - objects.push_back(info); + objects.push_back(std::move(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"; + for (std::string const& linked_target_dir : linked_target_dirs) { + std::string const ltmn = + cmStrCat(linked_target_dir, "/", arg_lang, "Modules.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()); + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", + linked_target_dir, + reader.getFormattedErrorMessages())); return false; } if (ltm.isObject()) { @@ -1783,55 +1874,47 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // 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; + for (cmDyndepObjectInfo const& object : objects) { + for (std::string const& p : object.Provides) { + std::string const mod = module_dir + p; + mod_files[p] = mod; + tm[p] = mod; } } - cmGeneratedFileStream ddf(arg_dd.c_str()); + cmGeneratedFileStream ddf(arg_dd); 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)); + { + cmNinjaBuild build("dyndep"); + build.Outputs.emplace_back(""); + for (cmDyndepObjectInfo const& object : objects) { + build.Outputs[0] = object.Object; + build.ImplicitOuts.clear(); + for (std::string const& p : object.Provides) { + build.ImplicitOuts.push_back(this->ConvertToNinjaPath(mod_files[p])); + } + build.ImplicitDeps.clear(); + for (std::string const& r : object.Requires) { + auto mit = mod_files.find(r); + if (mit != mod_files.end()) { + build.ImplicitDeps.push_back(this->ConvertToNinjaPath(mit->second)); + } + } + build.Variables.clear(); + if (!object.Provides.empty()) { + build.Variables.emplace("restat", "1"); } - } - if (!oi->Provides.empty()) { - ddVars["restat"] = "1"; - } - this->WriteBuild(ddf, ddComment, ddRule, ddOutputs, ddImplicitOuts, - ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + this->WriteBuild(ddf, build); + } } // 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()); + cmSystemTools::GetFilenamePath(arg_dd) + "/" + arg_lang + "Modules.json"; + cmGeneratedFileStream tmf(target_mods_file); tmf << tm; return true; @@ -1844,21 +1927,21 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, cmSystemTools::HandleResponseFile(argBeg, argEnd); std::string arg_dd; + std::string arg_lang; std::string arg_tdi; std::vector<std::string> arg_ddis; - for (std::vector<std::string>::const_iterator a = arg_full.begin(); - a != arg_full.end(); ++a) { - std::string const& arg = *a; + for (std::string const& arg : arg_full) { if (cmHasLiteralPrefix(arg, "--tdi=")) { arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--lang=")) { + arg_lang = arg.substr(7); } 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()); + cmSystemTools::Error("-E cmake_ninja_dyndep unknown argument: " + arg); return 1; } } @@ -1866,6 +1949,10 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --tdi="); return 1; } + if (arg_lang.empty()) { + cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --lang="); + return 1; + } if (arg_dd.empty()) { cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --dd="); return 1; @@ -1877,9 +1964,9 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, 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()); + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", + arg_tdi, + reader.getFormattedErrorMessages())); return 1; } } @@ -1889,27 +1976,26 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, 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()) { + if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) { 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()); + for (auto const& tdi_linked_target_dir : tdi_linked_target_dirs) { + linked_target_dirs.push_back(tdi_linked_target_dir.asString()); } } - cmake cm(cmake::RoleInternal); + cmake cm(cmake::RoleInternal, cmState::Unknown); cm.SetHomeDirectory(dir_top_src); cm.SetHomeOutputDirectory(dir_top_bld); - CM_AUTO_PTR<cmGlobalNinjaGenerator> ggd( + std::unique_ptr<cmGlobalNinjaGenerator> ggd( static_cast<cmGlobalNinjaGenerator*>(cm.CreateGlobalGenerator("Ninja"))); - if (!ggd.get() || + if (!ggd || !ggd->WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, - arg_dd, arg_ddis, module_dir, - linked_target_dirs)) { + arg_dd, arg_ddis, module_dir, linked_target_dirs, + arg_lang)) { return 1; } return 0; |