From ad4a768d59bc1d224ef097ff0724b8d5bfaa8128 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Tue, 5 Jun 2012 22:39:42 +0200 Subject: [PATCH 1/6] Ninja: add response file support on Windows When MinGW is used slashes are used for dependencies because ar.exe can't read rsp files with backslashes. Many thx to Claus Klein for starting working on this. --- Source/cmGlobalNinjaGenerator.cxx | 99 +++++++++++++++++-------- Source/cmGlobalNinjaGenerator.h | 7 +- Source/cmLocalNinjaGenerator.cxx | 12 +-- Source/cmNinjaNormalTargetGenerator.cxx | 47 ++++++++++-- Source/cmNinjaNormalTargetGenerator.h | 2 +- 5 files changed, 121 insertions(+), 46 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 0e89fab746..6717c3f70e 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -89,7 +89,10 @@ std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path) { std::string result = path; #ifdef _WIN32 - cmSystemTools::ReplaceString(result, "/", "\\"); + if(UsingMinGW) + cmSystemTools::ReplaceString(result, "\\", "/"); + else + cmSystemTools::ReplaceString(result, "/", "\\"); #endif return EncodeLiteral(result); } @@ -101,7 +104,8 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps, const cmNinjaDeps& orderOnlyDeps, - const cmNinjaVars& variables) + const cmNinjaVars& variables, + int cmdLineLimit) { // Make sure there is a rule. if(rule.empty()) @@ -123,10 +127,41 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, cmGlobalNinjaGenerator::WriteComment(os, comment); - std::ostringstream builds; + std::stringstream arguments; // TODO: Better formatting for when there are multiple input/output files. + // 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 order-only dependencies. + if(!orderOnlyDeps.empty()) + { + arguments << " ||"; + for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin(); + i != orderOnlyDeps.end(); + ++i) + arguments << " " << EncodeIdent(EncodePath(*i), os); + } + + arguments << "\n"; + + + std::ostringstream builds; + // Write outputs files. builds << "build"; for(cmNinjaDeps::const_iterator i = outputs.begin(); @@ -135,38 +170,16 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, builds << " " << EncodeIdent(EncodePath(*i), os); builds << ":"; + // Write the rule. builds << " " << rule; - // Write explicit dependencies. - for(cmNinjaDeps::const_iterator i = explicitDeps.begin(); - i != explicitDeps.end(); - ++i) - builds << " " << EncodeIdent(EncodePath(*i), os); + // check if a response file rule should be used + const std::string args = arguments.str(); + if (cmdLineLimit > 0 && args.size() > (size_t)cmdLineLimit) + builds << "_RSPFILE"; - // Write implicit dependencies. - if(!implicitDeps.empty()) - { - builds << " |"; - for(cmNinjaDeps::const_iterator i = implicitDeps.begin(); - i != implicitDeps.end(); - ++i) - builds << " " << EncodeIdent(EncodePath(*i), os); - } - - // Write order-only dependencies. - if(!orderOnlyDeps.empty()) - { - builds << " ||"; - for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin(); - i != orderOnlyDeps.end(); - ++i) - builds << " " << EncodeIdent(EncodePath(*i), os); - } - - builds << "\n"; - - os << builds.str(); + os << builds.str() << args; // Write the variables bound to this build statement. for(cmNinjaVars::const_iterator i = variables.begin(); @@ -200,6 +213,7 @@ void cmGlobalNinjaGenerator::AddCustomCommandRule() "$DESC", "Rule for running custom commands.", /*depfile*/ "", + /*rspfile*/ "", /*restat*/ true); } @@ -233,6 +247,7 @@ void cmGlobalNinjaGenerator::WriteRule(std::ostream& os, const std::string& description, const std::string& comment, const std::string& depfile, + const std::string& rspfile, bool restat, bool generator) { @@ -277,6 +292,14 @@ void cmGlobalNinjaGenerator::WriteRule(std::ostream& os, os << "description = " << description << "\n"; } + if(!rspfile.empty()) + { + cmGlobalNinjaGenerator::Indent(os, 1); + os << "rspfile = " << rspfile << "\n"; + cmGlobalNinjaGenerator::Indent(os, 1); + os << "rspfile_content = $in" << "\n"; + } + if(restat) { cmGlobalNinjaGenerator::Indent(os, 1); @@ -424,8 +447,17 @@ void cmGlobalNinjaGenerator } this->ResolveLanguageCompiler(*l, mf, optional); } + + // check for mingw + std::string cc = mf->GetDefinition("CMAKE_C_COMPILER"); + if(cc.find("gcc.exe") != std::string::npos) + { + UsingMinGW = true; + } } +bool cmGlobalNinjaGenerator::UsingMinGW = false; + // Implemented by: // cmGlobalUnixMakefileGenerator3 // cmGlobalVisualStudio10Generator @@ -483,6 +515,7 @@ void cmGlobalNinjaGenerator::AddRule(const std::string& name, const std::string& description, const std::string& comment, const std::string& depfile, + const std::string& rspfile, bool restat, bool generator) { @@ -497,6 +530,7 @@ void cmGlobalNinjaGenerator::AddRule(const std::string& name, description, comment, depfile, + rspfile, restat, generator); } @@ -841,6 +875,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) "Re-running CMake...", "Rule for re-running cmake.", /*depfile=*/ "", + /*rspfile=*/ "", /*restat=*/ false, /*generator=*/ true); @@ -878,6 +913,7 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) "Cleaning all built files...", "Rule for cleaning all built files.", /*depfile=*/ "", + /*rspfile=*/ "", /*restat=*/ false, /*generator=*/ false); WriteBuild(os, @@ -898,6 +934,7 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os) "All primary targets available:", "Rule for printing all primary targets available.", /*depfile=*/ "", + /*rspfile=*/ "", /*restat=*/ false, /*generator=*/ false); WriteBuild(os, diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 7b6b9b7ee8..0c740e8dc1 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -81,7 +81,8 @@ public: const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps, const cmNinjaDeps& orderOnlyDeps, - const cmNinjaVars& variables); + const cmNinjaVars& variables, + int cmdLineLimit = -1); /** * Helper to write a build statement with the special 'phony' rule. @@ -113,6 +114,7 @@ public: const std::string& description, const std::string& comment = "", const std::string& depfile = "", + const std::string& rspfile = "" , bool restat = false, bool generator = false); @@ -226,6 +228,7 @@ public: const std::string& description, const std::string& comment = "", const std::string& depfile = "", + const std::string& rspfile = "", bool restat = false, bool generator = false); @@ -341,6 +344,8 @@ private: TargetAliasMap TargetAliases; static cmLocalGenerator* LocalGenerator; + + static bool UsingMinGW; }; #endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 425b21917e..a0141cfe2e 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -275,16 +275,16 @@ std::string cmLocalNinjaGenerator::BuildCommandLine( return ":"; #endif - // TODO: This will work only on Unix platforms. I don't - // want to use a link.txt file because I will lose the benefit of the - // $in variables. A discussion about dealing with multiple commands in - // a rule is started here: - // groups.google.com/group/ninja-build/browse_thread/thread/d515f23a78986008 std::ostringstream cmd; for (std::vector::const_iterator li = cmdLines.begin(); li != cmdLines.end(); ++li) { - if (li != cmdLines.begin()) + if (li != cmdLines.begin()) { cmd << " && "; +#ifdef _WIN32 + } else if (cmdLines.size() > 1) { + cmd << "cmd.exe /c "; +#endif + } cmd << *li; } return cmd.str(); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 8b86a98d25..a99167fa71 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -90,7 +90,10 @@ void cmNinjaNormalTargetGenerator::Generate() } else { - this->WriteLinkRule(); + this->WriteLinkRule(false); +#ifdef _WIN32 // TODO response file support only Linux + this->WriteLinkRule(true); +#endif this->WriteLinkStatement(); } @@ -144,17 +147,38 @@ cmNinjaNormalTargetGenerator void cmNinjaNormalTargetGenerator -::WriteLinkRule() +::WriteLinkRule(bool useResponseFile) { cmTarget::TargetType targetType = this->GetTarget()->GetType(); std::string ruleName = this->LanguageLinkerRule(); + // Select whether to use a response file for objects. + std::string rspfile; + if (!this->GetGlobalGenerator()->HasRule(ruleName)) { cmLocalGenerator::RuleVariables vars; vars.RuleLauncher = "RULE_LAUNCH_LINK"; vars.CMTarget = this->GetTarget(); vars.Language = this->TargetLinkLanguage; - vars.Objects = "$in"; + + std::string responseFlag; + if (!useResponseFile) { + vars.Objects = "$in"; + } else { + // handle response file + std::string cmakeLinkVar = std::string("CMAKE_") + + this->TargetLinkLanguage + "_RESPONSE_FILE_LINK_FLAG"; + const char * flag = GetMakefile()->GetDefinition(cmakeLinkVar.c_str()); + if(flag) { + responseFlag = flag; + } else { + responseFlag = "@"; + } + ruleName += "_RSPFILE"; + rspfile = "$out.rsp"; + responseFlag += rspfile; + vars.Objects = responseFlag.c_str(); + } std::string objdir = this->GetLocalGenerator()->GetHomeRelativeOutputPath(); objdir += objdir.empty() ? "" : "/"; @@ -201,7 +225,7 @@ cmNinjaNormalTargetGenerator vars.LanguageCompileFlags = langFlags.c_str(); } - // Rule for linking library. + // Rule for linking library/executable. std::vector linkCmds = this->ComputeLinkCmd(); for(std::vector::iterator i = linkCmds.begin(); i != linkCmds.end(); @@ -214,7 +238,7 @@ cmNinjaNormalTargetGenerator std::string linkCmd = this->GetLocalGenerator()->BuildCommandLine(linkCmds); - // Write the linker rule. + // Write the linker rule with response file if needed. std::ostringstream comment; comment << "Rule for linking " << this->TargetLinkLanguage << " " << this->GetVisibleTypeName() << "."; @@ -224,7 +248,9 @@ cmNinjaNormalTargetGenerator this->GetGlobalGenerator()->AddRule(ruleName, linkCmd, description.str(), - comment.str()); + comment.str(), + /*depfile*/ "", + rspfile); } if (this->TargetNameOut != this->TargetNameReal) { @@ -456,6 +482,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() symlinkVars["POST_BUILD"] = postBuildCmdLine; } + int cmdLineLimit = -1; +#ifdef _WIN32 + cmdLineLimit = 30000; +#else + // TODO +#endif // Write the build statement for this target. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), comment.str(), @@ -464,7 +496,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() explicitDeps, implicitDeps, emptyDeps, - vars); + vars, + cmdLineLimit); if (targetOutput != targetOutputReal) { if (targetType == cmTarget::EXECUTABLE) { diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 7acbe8f6f7..3be1c94e1e 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -30,7 +30,7 @@ private: std::string LanguageLinkerRule() const; const char* GetVisibleTypeName() const; void WriteLanguagesRules(); - void WriteLinkRule(); + void WriteLinkRule(bool useResponseFile); void WriteLinkStatement(); void WriteObjectLibStatement(); std::vector ComputeLinkCmd(); From 5aab7f90f1fe96611eee6b0c9d12c6bb1030d23a Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Tue, 5 Jun 2012 23:21:01 +0200 Subject: [PATCH 2/6] Ninja: 30000 is too long for windows cmd --- Source/cmNinjaNormalTargetGenerator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index a99167fa71..3e6ab767b1 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -484,7 +484,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() int cmdLineLimit = -1; #ifdef _WIN32 - cmdLineLimit = 30000; + cmdLineLimit = 8100; #else // TODO #endif From 0266c9bd1979b1136b33b4ca281eca00925e24cb Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Tue, 5 Jun 2012 23:29:29 +0200 Subject: [PATCH 3/6] Ninja: check for valid pointer --- Source/cmGlobalNinjaGenerator.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 6717c3f70e..a91aa15c3a 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -449,8 +449,8 @@ void cmGlobalNinjaGenerator } // check for mingw - std::string cc = mf->GetDefinition("CMAKE_C_COMPILER"); - if(cc.find("gcc.exe") != std::string::npos) + const char* cc = mf->GetDefinition("CMAKE_C_COMPILER"); + if(cc && std::string(cc).find("gcc.exe") != std::string::npos) { UsingMinGW = true; } From 128fe5d504196ab82dde5a7dfe0ffb031b03e590 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Tue, 5 Jun 2012 23:40:42 +0200 Subject: [PATCH 4/6] Ninja: also create rspfile rules --- Source/cmNinjaNormalTargetGenerator.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 3e6ab767b1..ddf96ebdc9 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -151,6 +151,8 @@ cmNinjaNormalTargetGenerator { cmTarget::TargetType targetType = this->GetTarget()->GetType(); std::string ruleName = this->LanguageLinkerRule(); + if (useResponseFile) + ruleName += "_RSPFILE"; // Select whether to use a response file for objects. std::string rspfile; @@ -174,7 +176,6 @@ cmNinjaNormalTargetGenerator } else { responseFlag = "@"; } - ruleName += "_RSPFILE"; rspfile = "$out.rsp"; responseFlag += rspfile; vars.Objects = responseFlag.c_str(); From c60557e973643c5d60da17911d096f7ac88996c2 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Wed, 6 Jun 2012 01:36:34 +0200 Subject: [PATCH 5/6] Ninja: don't break because of empty commands --- Source/cmGlobalNinjaGenerator.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index a91aa15c3a..5477adf4fe 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -225,10 +225,17 @@ cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command, const cmNinjaDeps& deps, const cmNinjaDeps& orderOnlyDeps) { + std::string cmd = command; +#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"] = command; + vars["COMMAND"] = cmd; vars["DESC"] = EncodeLiteral(description); cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream, From e3a1f727f75e6077e7724719c606d833e8bbd9b0 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Wed, 6 Jun 2012 13:02:30 +0200 Subject: [PATCH 6/6] Ninja: find mingw's resource compiler --- Source/cmGlobalNinjaGenerator.cxx | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 5477adf4fe..c4b1cc999d 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -434,16 +434,19 @@ void cmGlobalNinjaGenerator cmMakefile *mf, bool optional) { - this->cmGlobalGenerator::EnableLanguage(languages, mf, optional); std::string path; for(std::vector::const_iterator l = languages.begin(); l != languages.end(); ++l) { + std::vector language; + language.push_back(*l); + if(*l == "NONE") { + this->cmGlobalGenerator::EnableLanguage(language, mf, optional); continue; } - if(*l == "Fortran") + else if(*l == "Fortran") { std::string message = "The \""; message += this->GetName(); @@ -452,15 +455,22 @@ void cmGlobalNinjaGenerator message += "\" yet."; cmSystemTools::Error(message.c_str()); } + else if(*l == "RC") + { + // check if mingw is used + const char* cc = mf->GetDefinition("CMAKE_C_COMPILER"); + if(cc && std::string(cc).find("gcc.exe") != std::string::npos) + { + UsingMinGW = true; + std::string rc = cmSystemTools::FindProgram("windres"); + if(rc.empty()) + rc = "windres.exe";; + mf->AddDefinition("CMAKE_RC_COMPILER", rc.c_str()); + } + } + this->cmGlobalGenerator::EnableLanguage(language, mf, optional); this->ResolveLanguageCompiler(*l, mf, optional); - } - - // check for mingw - const char* cc = mf->GetDefinition("CMAKE_C_COMPILER"); - if(cc && std::string(cc).find("gcc.exe") != std::string::npos) - { - UsingMinGW = true; - } + } } bool cmGlobalNinjaGenerator::UsingMinGW = false;