diff options
Diffstat (limited to 'Source')
31 files changed, 1310 insertions, 173 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 46bdec6..2c6bc76 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -361,11 +361,11 @@ ENDIF (WIN32) # on platforms where it does not pass all tests. # Enforce Ninja support by setting CMAKE_USE_NINJA set(_CMAKE_DEFAULT_NINJA_VALUE TRUE) -if(WIN32 OR APPLE) +if(APPLE) SET(_CMAKE_DEFAULT_NINJA_VALUE FALSE) endif() SET(CMAKE_ENABLE_NINJA ${_CMAKE_DEFAULT_NINJA_VALUE} CACHE BOOL - "Enable the ninja generator for CMake. On Windows and OSX broken") + "Enable the ninja generator for CMake. When enabled, some CMake tests still fail on OSX") MARK_AS_ADVANCED(CMAKE_ENABLE_NINJA) IF(CMAKE_ENABLE_NINJA) MESSAGE(STATUS "Ninja generator enabled.") @@ -383,8 +383,13 @@ IF(CMAKE_ENABLE_NINJA) cmNinjaUtilityTargetGenerator.h ) ADD_DEFINITIONS(-DCMAKE_USE_NINJA) + IF(WIN32 AND NOT CYGWIN AND NOT BORLAND) + SET_SOURCE_FILES_PROPERTIES(cmcldeps.cxx PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0501) + ADD_EXECUTABLE(cmcldeps cmcldeps.cxx) + INSTALL_TARGETS(/bin cmcldeps) + ENDIF() ELSE() - MESSAGE(STATUS "Ninja generator disabled, enforce with -DCMAKE_ENABLE_NINJA=ON") + MESSAGE(STATUS "Ninja generator disabled, enable it with -DCMAKE_ENABLE_NINJA=ON") ENDIF() # create a library used by the command line and the GUI diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 174a317..3f48c27 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -2,5 +2,5 @@ SET(CMake_VERSION_MAJOR 2) SET(CMake_VERSION_MINOR 8) SET(CMake_VERSION_PATCH 8) -SET(CMake_VERSION_TWEAK 20120610) +SET(CMake_VERSION_TWEAK 20120619) #SET(CMake_VERSION_RC 1) diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index c1d0e9d..fd39eec 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx @@ -102,16 +102,17 @@ bool cmAddLibraryCommand STATIC. But at this point we know only the name of the target, but not yet its linker language. */ if ((type != cmTarget::STATIC_LIBRARY) && + (type != cmTarget::OBJECT_LIBRARY) && (this->Makefile->GetCMakeInstance()->GetPropertyAsBool( "TARGET_SUPPORTS_SHARED_LIBS") == false)) { - std::string msg = "ADD_LIBRARY for library "; - msg += args[0]; - msg += " is used with the "; - msg += type==cmTarget::SHARED_LIBRARY ? "SHARED" : "MODULE"; - msg += " option, but the target platform supports only STATIC libraries. " - "Building it STATIC instead. This may lead to problems."; - cmSystemTools::Message(msg.c_str() ,"Warning"); + cmOStringStream w; + w << + "ADD_LIBRARY called with " << + (type==cmTarget::SHARED_LIBRARY ? "SHARED" : "MODULE") << + " option but the target platform does not support dynamic linking. " + "Building a STATIC library instead. This may lead to problems."; + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str()); type = cmTarget::STATIC_LIBRARY; } diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index dc6b749..b410e44 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx @@ -240,6 +240,7 @@ bool cmArchiveWrite::AddFile(const char* file, // Clear acl and xattr fields not useful for distribution. archive_entry_acl_clear(e); archive_entry_xattr_clear(e); + archive_entry_set_fflags(e, 0, 0); if(archive_write_header(this->Archive, e) != ARCHIVE_OK) { this->Error = "archive_write_header: "; diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index ed485e3..1ae7035 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -281,6 +281,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv) flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); cmakeFlags.push_back(flag); } + if(this->Makefile->GetDefinition("CMAKE_POSITION_INDEPENDENT_CODE")!=0) + { + fprintf(fout, "SET(CMAKE_POSITION_INDEPENDENT_CODE \"ON\")\n"); + } /* Use a random file name to avoid rapid creation and deletion of the same executable name (some filesystems fail on that). */ diff --git a/Source/cmDocumentVariables.cxx b/Source/cmDocumentVariables.cxx index 9e33d75..592d931 100644 --- a/Source/cmDocumentVariables.cxx +++ b/Source/cmDocumentVariables.cxx @@ -1350,6 +1350,14 @@ void cmDocumentVariables::DefineVariables(cmake* cm) "See that target property for additional information.", false, "Variables that Control the Build"); + cm->DefineProperty + ("CMAKE_POSITION_INDEPENDENT_FLAGS", cmProperty::VARIABLE, + "Default value for POSITION_INDEPENDENT_CODE of targets.", + "This variable is used to initialize the " + "POSITION_INDEPENDENT_CODE property on all the targets. " + "See that target property for additional information.", + false, + "Variables that Control the Build"); // Variables defined when the a language is enabled These variables will // also be defined whenever CMake has loaded its support for compiling (LANG) diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index 65077b3..ab11307 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -34,6 +34,9 @@ cmExtraEclipseCDT4Generator this->SupportedGlobalGenerators.push_back("MinGW Makefiles"); // this->SupportedGlobalGenerators.push_back("MSYS Makefiles"); #endif +#ifdef CMAKE_USE_NINJA + this->SupportedGlobalGenerators.push_back("Ninja"); +#endif this->SupportedGlobalGenerators.push_back("Unix Makefiles"); this->SupportsVirtualFolders = true; @@ -286,6 +289,9 @@ void cmExtraEclipseCDT4Generator::CreateProjectFile() // set the make command std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string makeArgs = mf->GetSafeDefinition( + "CMAKE_ECLIPSE_MAKE_ARGUMENTS"); + fout << "\t\t\t\t<dictionary>\n" "\t\t\t\t\t<key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>\n" @@ -293,7 +299,7 @@ void cmExtraEclipseCDT4Generator::CreateProjectFile() "\t\t\t\t</dictionary>\n" "\t\t\t\t<dictionary>\n" "\t\t\t\t\t<key>org.eclipse.cdt.make.core.build.command</key>\n" - "\t\t\t\t\t<value>" + this->GetEclipsePath(make) + "</value>\n" + "\t\t\t\t\t<value>" << this->GetEclipsePath(make) << "</value>\n" "\t\t\t\t</dictionary>\n" "\t\t\t\t<dictionary>\n" "\t\t\t\t\t<key>org.eclipse.cdt.make.core.contents</key>\n" @@ -305,7 +311,7 @@ void cmExtraEclipseCDT4Generator::CreateProjectFile() "\t\t\t\t</dictionary>\n" "\t\t\t\t<dictionary>\n" "\t\t\t\t\t<key>org.eclipse.cdt.make.core.build.arguments</key>\n" - "\t\t\t\t\t<value></value>\n" + "\t\t\t\t\t<value>" << makeArgs << "</value>\n" "\t\t\t\t</dictionary>\n" "\t\t\t\t<dictionary>\n" "\t\t\t\t\t<key>org.eclipse.cdt.make.core.buildLocation</key>\n" @@ -1067,9 +1073,8 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } //insert rules for compiling, preprocessing and assembling individual files - cmLocalUnixMakefileGenerator3* lumg=(cmLocalUnixMakefileGenerator3*)*it; std::vector<std::string> objectFileTargets; - lumg->GetIndividualFileTargets(objectFileTargets); + (*it)->GetIndividualFileTargets(objectFileTargets); for(std::vector<std::string>::const_iterator fit=objectFileTargets.begin(); fit != objectFileTargets.end(); ++fit) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index d69431e..5da5a01 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -1003,7 +1003,9 @@ protected: // Match rules are case-insensitive on some platforms. #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) std::string lower = cmSystemTools::LowerCase(file); - file = lower.c_str(); + const char* file_to_match = lower.c_str(); +#else + const char* file_to_match = file; #endif // Collect properties from all matching rules. @@ -1012,7 +1014,7 @@ protected: for(std::vector<MatchRule>::iterator mr = this->MatchRules.begin(); mr != this->MatchRules.end(); ++mr) { - if(mr->Regex.find(file)) + if(mr->Regex.find(file_to_match)) { matched = true; result.Exclude |= mr->Properties.Exclude; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index f883041..a47ca36 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -586,6 +586,16 @@ cmGlobalGenerator::EnableLanguage(std::vector<std::string>const& languages, } } // end if in try compile } // end need test language + // Store the shared library flags so that we can satisfy CMP0018 + std::string sharedLibFlagsVar = "CMAKE_SHARED_LIBRARY_"; + sharedLibFlagsVar += lang; + sharedLibFlagsVar += "_FLAGS"; + const char* sharedLibFlags = + mf->GetSafeDefinition(sharedLibFlagsVar.c_str()); + if (sharedLibFlags) + { + this->LanguageToOriginalSharedLibFlags[lang] = sharedLibFlags; + } } // end for each language // Now load files that can override any settings on the platform or for @@ -2107,6 +2117,17 @@ cmGlobalGenerator::GenerateRuleFile(std::string const& output) const } //---------------------------------------------------------------------------- +std::string cmGlobalGenerator::GetSharedLibFlagsForLanguage( + std::string const& l) +{ + if(this->LanguageToOriginalSharedLibFlags.count(l) > 0) + { + return this->LanguageToOriginalSharedLibFlags[l]; + } + return ""; +} + +//---------------------------------------------------------------------------- void cmGlobalGenerator::AppendDirectoryForConfig(const char*, const char*, const char*, std::string&) { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 8535edc..ce91793 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -277,6 +277,8 @@ public: i.e. "Can I build Debug and Release in the same tree?" */ virtual bool IsMultiConfig() { return false; } + std::string GetSharedLibFlagsForLanguage(std::string const& lang); + /** Generate an <output>.rule file path for a given command output. */ virtual std::string GenerateRuleFile(std::string const& output) const; @@ -359,6 +361,7 @@ private: std::map<cmStdString, cmStdString> LanguageToOutputExtension; std::map<cmStdString, cmStdString> ExtensionToLanguage; std::map<cmStdString, int> LanguageToLinkerPreference; + std::map<cmStdString, cmStdString> LanguageToOriginalSharedLibFlags; // Record hashes for rules and outputs. struct RuleHash { char Data[32]; }; diff --git a/Source/cmGlobalKdevelopGenerator.cxx b/Source/cmGlobalKdevelopGenerator.cxx index 56b9e9c..f699448 100644 --- a/Source/cmGlobalKdevelopGenerator.cxx +++ b/Source/cmGlobalKdevelopGenerator.cxx @@ -44,6 +44,9 @@ cmGlobalKdevelopGenerator::cmGlobalKdevelopGenerator() :cmExternalMakefileProjectGenerator() { this->SupportedGlobalGenerators.push_back("Unix Makefiles"); +#ifdef CMAKE_USE_NINJA + this->SupportedGlobalGenerators.push_back("Ninja"); +#endif } void cmGlobalKdevelopGenerator::Generate() diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 0e89fab..c3989c0 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -43,12 +43,13 @@ void cmGlobalNinjaGenerator::WriteComment(std::ostream& os, std::string replace = comment; std::string::size_type lpos = 0; std::string::size_type rpos; + os << "\n#############################################\n"; while((rpos = replace.find('\n', lpos)) != std::string::npos) { os << "# " << replace.substr(lpos, rpos - lpos) << "\n"; lpos = rpos + 1; } - os << "# " << replace.substr(lpos) << "\n"; + os << "# " << replace.substr(lpos) << "\n\n"; } static bool IsIdentChar(char c) @@ -66,7 +67,7 @@ std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, if (std::find_if(ident.begin(), ident.end(), std::not1(std::ptr_fun(IsIdentChar))) != ident.end()) { static unsigned VarNum = 0; - std::ostringstream names; + cmOStringStream names; names << "ident" << VarNum++; vars << names.str() << " = " << ident << "\n"; return "$" + names.str(); @@ -89,7 +90,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 +105,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,50 +128,60 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, cmGlobalNinjaGenerator::WriteComment(os, comment); - std::ostringstream builds; + cmOStringStream arguments; // TODO: Better formatting for when there are multiple input/output files. - // Write outputs files. - builds << "build"; - for(cmNinjaDeps::const_iterator i = outputs.begin(); - i != outputs.end(); - ++i) - 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); + arguments << " " << EncodeIdent(EncodePath(*i), os); // Write implicit dependencies. if(!implicitDeps.empty()) { - builds << " |"; + arguments << " |"; for(cmNinjaDeps::const_iterator i = implicitDeps.begin(); i != implicitDeps.end(); ++i) - builds << " " << EncodeIdent(EncodePath(*i), os); + arguments << " " << EncodeIdent(EncodePath(*i), os); } // Write order-only dependencies. if(!orderOnlyDeps.empty()) { - builds << " ||"; + arguments << " ||"; for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin(); i != orderOnlyDeps.end(); ++i) - builds << " " << EncodeIdent(EncodePath(*i), os); + arguments << " " << EncodeIdent(EncodePath(*i), os); } - builds << "\n"; + arguments << "\n"; + + + cmOStringStream builds; + + // Write outputs files. + builds << "build"; + for(cmNinjaDeps::const_iterator i = outputs.begin(); + i != outputs.end(); + ++i) + builds << " " << EncodeIdent(EncodePath(*i), os); + builds << ":"; + - os << builds.str(); + // Write the rule. + builds << " " << rule; + + // check if a response file rule should be used + const std::string args = arguments.str(); + if (cmdLineLimit > 0 && + (args.size() + + builds.str().size()) > (size_t)cmdLineLimit) + builds << "_RSPFILE"; + + os << builds.str() << args; // Write the variables bound to this build statement. for(cmNinjaVars::const_iterator i = variables.begin(); @@ -200,6 +215,7 @@ void cmGlobalNinjaGenerator::AddCustomCommandRule() "$DESC", "Rule for running custom commands.", /*depfile*/ "", + /*rspfile*/ "", /*restat*/ true); } @@ -211,10 +227,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, @@ -233,6 +256,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 +301,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); @@ -288,6 +320,8 @@ void cmGlobalNinjaGenerator::WriteRule(std::ostream& os, cmGlobalNinjaGenerator::Indent(os, 1); os << "generator = 1\n"; } + + os << "\n"; } void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os, @@ -350,6 +384,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator() this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake"; } + //---------------------------------------------------------------------------- // Virtual public methods. @@ -404,16 +439,19 @@ void cmGlobalNinjaGenerator cmMakefile *mf, bool optional) { - this->cmGlobalGenerator::EnableLanguage(languages, mf, optional); std::string path; for(std::vector<std::string>::const_iterator l = languages.begin(); l != languages.end(); ++l) { + std::vector<std::string> 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(); @@ -422,10 +460,25 @@ void cmGlobalNinjaGenerator message += "\" yet."; cmSystemTools::Error(message.c_str()); } + else if(*l == "RC") + { + // check if mingw is used + if(mf->IsOn("CMAKE_COMPILER_IS_MINGW")) + { + 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); } } +bool cmGlobalNinjaGenerator::UsingMinGW = false; + // Implemented by: // cmGlobalUnixMakefileGenerator3 // cmGlobalVisualStudio10Generator @@ -483,12 +536,15 @@ 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) { // Do not add the same rule twice. if (this->HasRule(name)) + { return; + } this->Rules.insert(name); cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream, @@ -497,6 +553,7 @@ void cmGlobalNinjaGenerator::AddRule(const std::string& name, description, comment, depfile, + rspfile, restat, generator); } @@ -767,7 +824,7 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, // Insert the alias into the map. If the alias was already present in the // map and referred to another target, mark it as ambiguous. std::pair<TargetAliasMap::iterator, bool> newAlias = - TargetAliases.insert(make_pair(alias, target)); + TargetAliases.insert(std::make_pair(alias, target)); if (newAlias.second && newAlias.first->second != target) newAlias.first->second = 0; } @@ -825,7 +882,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) cmLocalGenerator *lg = this->LocalGenerators[0]; cmMakefile* mfRoot = lg->GetMakefile(); - std::ostringstream cmd; + cmOStringStream cmd; cmd << lg->ConvertToOutputFormat( mfRoot->GetRequiredDefinition("CMAKE_COMMAND"), cmLocalGenerator::SHELL) @@ -841,6 +898,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) "Re-running CMake...", "Rule for re-running cmake.", /*depfile=*/ "", + /*rspfile=*/ "", /*restat=*/ false, /*generator=*/ true); @@ -870,14 +928,26 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) cmNinjaDeps()); } +std::string cmGlobalNinjaGenerator::ninjaCmd() const +{ + cmLocalGenerator* lgen = this->LocalGenerators[0]; + if (lgen) { + return lgen->ConvertToOutputFormat( + lgen->GetMakefile()->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"), + cmLocalGenerator::SHELL); + } + return "ninja"; +} + void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) { WriteRule(*this->RulesFileStream, "CLEAN", - "ninja -t clean", + (ninjaCmd() + " -t clean").c_str(), "Cleaning all built files...", "Rule for cleaning all built files.", /*depfile=*/ "", + /*rspfile=*/ "", /*restat=*/ false, /*generator=*/ false); WriteBuild(os, @@ -894,10 +964,11 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os) { WriteRule(*this->RulesFileStream, "HELP", - "ninja -t targets", + (ninjaCmd() + " -t tagets").c_str(), "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 7b6b9b7..e939f61 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -16,6 +16,8 @@ # include "cmGlobalGenerator.h" # include "cmNinjaTypes.h" +//#define NINJA_GEN_VERBOSE_FILES + class cmLocalGenerator; class cmGeneratedFileStream; class cmGeneratorTarget; @@ -81,7 +83,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 +116,7 @@ public: const std::string& description, const std::string& comment = "", const std::string& depfile = "", + const std::string& rspfile = "" , bool restat = false, bool generator = false); @@ -143,6 +147,9 @@ public: const cmNinjaDeps& targets, const std::string& comment = ""); + + static bool IsMinGW() { return UsingMinGW; } + public: /// Default constructor. cmGlobalNinjaGenerator(); @@ -226,6 +233,7 @@ public: const std::string& description, const std::string& comment = "", const std::string& depfile = "", + const std::string& rspfile = "", bool restat = false, bool generator = false); @@ -309,6 +317,8 @@ private: ASD.insert(deps.begin(), deps.end()); } + std::string ninjaCmd() const; + private: /// The file containing the build statement. (the relation ship of the /// compilation DAG). @@ -341,6 +351,9 @@ private: TargetAliasMap TargetAliases; static cmLocalGenerator* LocalGenerator; + + static bool UsingMinGW; + }; #endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 522f3da..938977b 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1594,14 +1594,14 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target, if(strcmp(lang, "CXX") == 0) { this->CurrentLocalGenerator->AddLanguageFlags(cflags, "C", configName); - this->CurrentLocalGenerator->AddSharedFlags(cflags, lang, shared); + this->CurrentLocalGenerator->AddCMP0018Flags(cflags, &target, "C"); } // Add language-specific flags. this->CurrentLocalGenerator->AddLanguageFlags(flags, lang, configName); // Add shared-library flags if needed. - this->CurrentLocalGenerator->AddSharedFlags(flags, lang, shared); + this->CurrentLocalGenerator->AddCMP0018Flags(flags, &target, lang); } else if(binary) { diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 8265d72..46c92cb 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -872,6 +872,13 @@ cmLocalGenerator::ExpandRuleVariable(std::string const& variable, return replaceValues.TargetPDB; } } + if(replaceValues.DependencyFile ) + { + if(variable == "DEP_FILE") + { + return replaceValues.DependencyFile; + } + } if(replaceValues.Target) { @@ -1549,12 +1556,6 @@ void cmLocalGenerator::GetTargetFlags(std::string& linkLibs, return; } this->AddLanguageFlags(flags, linkLanguage, buildType.c_str()); - std::string sharedFlagsVar = "CMAKE_SHARED_LIBRARY_"; - sharedFlagsVar += linkLanguage; - sharedFlagsVar += "_FLAGS"; - flags += " "; - flags += this->Makefile->GetSafeDefinition(sharedFlagsVar.c_str()); - flags += " "; cmOStringStream linklibs; this->OutputLinkLibraries(linklibs, target, false); linkLibs = linklibs.str(); @@ -1963,6 +1964,111 @@ void cmLocalGenerator::AddSharedFlags(std::string& flags, } //---------------------------------------------------------------------------- +void cmLocalGenerator::AddCMP0018Flags(std::string &flags, cmTarget* target, + std::string const& lang) +{ + int targetType = target->GetType(); + + bool shared = ((targetType == cmTarget::SHARED_LIBRARY) || + (targetType == cmTarget::MODULE_LIBRARY)); + + if (this->GetShouldUseOldFlags(shared, lang)) + { + this->AddSharedFlags(flags, lang.c_str(), shared); + } + else + { + // Add position independendent flags, if needed. + if (target->GetPropertyAsBool("POSITION_INDEPENDENT_CODE")) + { + this->AddPositionIndependentFlags(flags, lang, targetType); + } + if (shared) + { + this->AppendFeatureOptions(flags, lang.c_str(), "DLL"); + } + } +} + +//---------------------------------------------------------------------------- +bool cmLocalGenerator::GetShouldUseOldFlags(bool shared, + const std::string &lang) const +{ + std::string originalFlags = + this->GlobalGenerator->GetSharedLibFlagsForLanguage(lang); + if (shared) + { + std::string flagsVar = "CMAKE_SHARED_LIBRARY_"; + flagsVar += lang; + flagsVar += "_FLAGS"; + const char* flags = + this->Makefile->GetSafeDefinition(flagsVar.c_str()); + + if (flags && flags != originalFlags) + { + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0018)) + { + case cmPolicies::WARN: + { + cmOStringStream e; + e << "Variable " << flagsVar << " has been modified. CMake " + "will ignore the POSITION_INDEPENDENT_CODE target property for " + "shared libraries and will use the " << flagsVar << " variable " + "instead. This may cause errors if the original content of " + << flagsVar << " was removed.\n" + << this->Makefile->GetPolicies()->GetPolicyWarning( + cmPolicies::CMP0018); + + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str()); + // fall through to OLD behaviour + } + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + default: + return false; + } + } + } + return false; +} + +//---------------------------------------------------------------------------- +void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags, + std::string const& lang, + int targetType) +{ + const char* picFlags = 0; + + if(targetType == cmTarget::EXECUTABLE) + { + std::string flagsVar = "CMAKE_"; + flagsVar += lang; + flagsVar += "_COMPILE_OPTIONS_PIE"; + picFlags = this->Makefile->GetSafeDefinition(flagsVar.c_str()); + } + if (!picFlags) + { + std::string flagsVar = "CMAKE_"; + flagsVar += lang; + flagsVar += "_COMPILE_OPTIONS_PIC"; + picFlags = this->Makefile->GetSafeDefinition(flagsVar.c_str()); + } + if (picFlags) + { + std::vector<std::string> options; + cmSystemTools::ExpandListArgument(picFlags, options); + for(std::vector<std::string>::const_iterator oi = options.begin(); + oi != options.end(); ++oi) + { + this->AppendFlags(flags, this->EscapeForShell(oi->c_str()).c_str()); + } + } +} + +//---------------------------------------------------------------------------- void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, const char* var, const char* config) diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index cb84a30..39b493f 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -140,7 +140,8 @@ public: void AddLanguageFlags(std::string& flags, const char* lang, const char* config); - void AddSharedFlags(std::string& flags, const char* lang, bool shared); + void AddCMP0018Flags(std::string &flags, cmTarget* target, + std::string const& lang); void AddConfigVariableFlags(std::string& flags, const char* var, const char* config); ///! Append flags to a string. @@ -204,6 +205,10 @@ public: /** Compute the language used to compile the given source file. */ const char* GetSourceFileLanguage(const cmSourceFile& source); + // Fill the vector with the target names for the object files, + // preprocessed files and assembly files. + virtual void GetIndividualFileTargets(std::vector<std::string>&) {} + // Create a struct to hold the varibles passed into // ExpandRuleVariables struct RuleVariables @@ -235,6 +240,7 @@ public: const char* LanguageCompileFlags; const char* Defines; const char* RuleLauncher; + const char* DependencyFile; }; /** Set whether to treat conversions to SHELL as a link script shell. */ @@ -425,6 +431,11 @@ protected: private: std::string ConvertToOutputForExistingCommon(const char* remote, std::string const& result); + + void AddSharedFlags(std::string& flags, const char* lang, bool shared); + bool GetShouldUseOldFlags(bool shared, const std::string &lang) const; + void AddPositionIndependentFlags(std::string& flags, std::string const& l, + int targetType); }; #endif diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 425b219..9a496f2 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -46,7 +46,9 @@ void cmLocalNinjaGenerator::Generate() this->SetConfigName(); this->WriteProcessedMakefile(this->GetBuildFileStream()); +#ifdef NINJA_GEN_VERBOSE_FILES this->WriteProcessedMakefile(this->GetRulesFileStream()); +#endif this->WriteBuildFileTop(); @@ -275,16 +277,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; + cmOStringStream cmd; for (std::vector<std::string>::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(); @@ -299,7 +301,7 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, if (!wd) wd = this->GetMakefile()->GetStartOutputDirectory(); - std::ostringstream cdCmd; + cmOStringStream cdCmd; cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL); cmdLines.push_back(cdCmd.str()); } diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index a645303..db93529 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -36,6 +36,30 @@ #include <queue> //---------------------------------------------------------------------------- +// Escape special characters in Makefile dependency lines +class cmMakeSafe +{ +public: + cmMakeSafe(const char* s): Data(s) {} + cmMakeSafe(std::string const& s): Data(s.c_str()) {} +private: + const char* Data; + friend std::ostream& operator<<(std::ostream& os, + cmMakeSafe const& self) + { + for(const char* c = self.Data; *c; ++c) + { + switch (*c) + { + case '=': os << "$(EQUALS)"; break; + default: os << *c; break; + } + } + return os; + } +}; + +//---------------------------------------------------------------------------- // Helper function used below. static std::string cmSplitExtension(std::string const& in, std::string& base) { @@ -555,28 +579,13 @@ cmLocalUnixMakefileGenerator3 space = " "; } - // Warn about paths not supported by Make tools. - std::string::size_type pos = tgt.find_first_of("="); - if(pos != std::string::npos) - { - cmOStringStream m; - m << - "Make rule for\n" - " " << tgt << "\n" - "has '=' on left hand side. " - "The make tool may not support this."; - cmListFileBacktrace bt; - this->GlobalGenerator->GetCMakeInstance() - ->IssueMessage(cmake::WARNING, m.str(), bt); - } - // Mark the rule as symbolic if requested. if(symbolic) { if(const char* sym = this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE")) { - os << tgt.c_str() << space << ": " << sym << "\n"; + os << cmMakeSafe(tgt) << space << ": " << sym << "\n"; } } @@ -584,7 +593,7 @@ cmLocalUnixMakefileGenerator3 if(depends.empty()) { // No dependencies. The commands will always run. - os << tgt.c_str() << space << ":\n"; + os << cmMakeSafe(tgt) << space << ":\n"; } else { @@ -595,7 +604,7 @@ cmLocalUnixMakefileGenerator3 { replace = *dep; replace = this->Convert(replace.c_str(),HOME_OUTPUT,MAKEFILE); - os << tgt.c_str() << space << ": " << replace.c_str() << "\n"; + os << cmMakeSafe(tgt) << space << ": " << cmMakeSafe(replace) << "\n"; } } @@ -608,7 +617,7 @@ cmLocalUnixMakefileGenerator3 } if(symbolic && !this->WatcomWMake) { - os << ".PHONY : " << tgt.c_str() << "\n"; + os << ".PHONY : " << cmMakeSafe(tgt) << "\n"; } os << "\n"; // Add the output to the local help if requested. @@ -687,6 +696,10 @@ cmLocalUnixMakefileGenerator3 << this->ConvertShellCommand(cmakecommand, FULL) << " -E remove -f\n" << "\n"; + makefileStream + << "# Escaping for special characters.\n" + << "EQUALS = =\n" + << "\n"; if(const char* edit_cmd = this->Makefile->GetDefinition("CMAKE_EDIT_COMMAND")) diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 9faf46d..f2ab79d 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -1933,9 +1933,11 @@ cmLocalVisualStudio7Generator vskey += "\\Packages\\" CM_INTEL_PLUGIN_GUID ";ProductVersion"; cmSystemTools::ReadRegistryValue(vskey.c_str(), intelVersion, cmSystemTools::KeyWOW64_32); - if (intelVersion.find("12") == 0 || (intelVersion.find("11") == 0)) + if (intelVersion.find("13") == 0 || + intelVersion.find("12") == 0 || + intelVersion.find("11") == 0) { - // Version 11.x and 12.x actually use 11.0 in project files! + // Version 11.x, 12.x, and 13.x actually use 11.0 in project files! intelVersion = "11.0" ; } else if(intelVersion.find("10") == 0) diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 408f287..df89275 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -245,9 +245,6 @@ std::string cmMakefileTargetGenerator::GetFlags(const std::string &l) std::string flags; const char *lang = l.c_str(); - bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) || - (this->Target->GetType() == cmTarget::MODULE_LIBRARY)); - // Add language feature flags. this->AddFeatureFlags(flags, lang); @@ -260,8 +257,7 @@ std::string cmMakefileTargetGenerator::GetFlags(const std::string &l) this->AddFortranFlags(flags); } - // Add shared-library flags if needed. - this->LocalGenerator->AddSharedFlags(flags, lang, shared); + this->LocalGenerator->AddCMP0018Flags(flags, this->Target, lang); // Add include directory flags. this->AddIncludeFlags(flags, lang); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 8b86a98..01e8e73 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -55,21 +55,6 @@ cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() { } -void -cmNinjaNormalTargetGenerator -::EnsureDirectoryExists(const std::string& dir) -{ - cmSystemTools::MakeDirectory(dir.c_str()); -} - -void -cmNinjaNormalTargetGenerator -::EnsureParentDirectoryExists(const std::string& path) -{ - EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path.c_str())); -} - - void cmNinjaNormalTargetGenerator::Generate() { if (!this->TargetLinkLanguage) { @@ -90,16 +75,17 @@ void cmNinjaNormalTargetGenerator::Generate() } else { - this->WriteLinkRule(); + this->WriteLinkRule(false); +#ifdef _WIN32 // TODO response file support only Linux + this->WriteLinkRule(true); +#endif this->WriteLinkStatement(); } - - this->GetBuildFileStream() << "\n"; - this->GetRulesFileStream() << "\n"; } void cmNinjaNormalTargetGenerator::WriteLanguagesRules() { +#ifdef NINJA_GEN_VERBOSE_FILES cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream()); this->GetRulesFileStream() << "# Rules for each languages for " @@ -107,6 +93,7 @@ void cmNinjaNormalTargetGenerator::WriteLanguagesRules() << " target " << this->GetTargetName() << "\n\n"; +#endif std::set<cmStdString> languages; this->GetTarget()->GetLanguages(languages); @@ -144,27 +131,41 @@ cmNinjaNormalTargetGenerator void cmNinjaNormalTargetGenerator -::WriteLinkRule() +::WriteLinkRule(bool useResponseFile) { 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; 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 objdir = - this->GetLocalGenerator()->GetHomeRelativeOutputPath(); - objdir += objdir.empty() ? "" : "/"; - objdir += cmake::GetCMakeFilesDirectoryPostSlash(); - objdir += this->GetTargetName(); - objdir += ".dir"; - objdir = this->GetLocalGenerator()->Convert(objdir.c_str(), - cmLocalGenerator::START_OUTPUT, - cmLocalGenerator::SHELL); - vars.ObjectDir = objdir.c_str(); + + 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 = "@"; + } + rspfile = "$out.rsp"; + responseFlag += rspfile; + vars.Objects = responseFlag.c_str(); + } + + vars.ObjectDir = "$OBJECT_DIR"; vars.Target = "$out"; vars.SONameFlag = "$SONAME_FLAG"; vars.TargetSOName = "$SONAME"; @@ -201,7 +202,7 @@ cmNinjaNormalTargetGenerator vars.LanguageCompileFlags = langFlags.c_str(); } - // Rule for linking library. + // Rule for linking library/executable. std::vector<std::string> linkCmds = this->ComputeLinkCmd(); for(std::vector<std::string>::iterator i = linkCmds.begin(); i != linkCmds.end(); @@ -214,17 +215,19 @@ cmNinjaNormalTargetGenerator std::string linkCmd = this->GetLocalGenerator()->BuildCommandLine(linkCmds); - // Write the linker rule. - std::ostringstream comment; + // Write the linker rule with response file if needed. + cmOStringStream comment; comment << "Rule for linking " << this->TargetLinkLanguage << " " << this->GetVisibleTypeName() << "."; - std::ostringstream description; + cmOStringStream description; description << "Linking " << this->TargetLinkLanguage << " " << this->GetVisibleTypeName() << " $out"; this->GetGlobalGenerator()->AddRule(ruleName, linkCmd, description.str(), - comment.str()); + comment.str(), + /*depfile*/ "", + rspfile); } if (this->TargetNameOut != this->TargetNameReal) { @@ -353,7 +356,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() /*implib=*/true).c_str()); // Compute the comment. - std::ostringstream comment; + cmOStringStream comment; comment << "Link the " << this->GetVisibleTypeName() << " " << targetOutputReal; @@ -375,13 +378,18 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() // Compute architecture specific link flags. Yes, these go into a different // variable for executables, probably due to a mistake made when duplicating // code between the Makefile executable and library generators. - this->GetLocalGenerator() - ->AddArchitectureFlags(targetType == cmTarget::EXECUTABLE + std::string flags = (targetType == cmTarget::EXECUTABLE ? vars["FLAGS"] - : vars["ARCH_FLAGS"], + : vars["ARCH_FLAGS"]); + this->GetLocalGenerator()->AddArchitectureFlags(flags, this->GetTarget(), this->TargetLinkLanguage, this->GetConfigName()); + if (targetType == cmTarget::EXECUTABLE) { + vars["FLAGS"] = flags; + } else { + vars["ARCH_FLAGS"] = flags; + } if (this->GetTarget()->HasSOName(this->GetConfigName())) { vars["SONAME_FLAG"] = this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage); @@ -407,10 +415,24 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() EnsureParentDirectoryExists(path); } - path = this->GetLocalGenerator()->ConvertToOutputFormat( - this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL); - vars["TARGET_PDB"] = path; - EnsureParentDirectoryExists(path); + // TODO move to GetTargetPDB + cmMakefile* mf = this->GetMakefile(); + if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || + mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID")) + { + path = this->GetTargetPDB(); + vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(path.c_str()).c_str(), + cmLocalGenerator::SHELL); + EnsureParentDirectoryExists(path); + } + + if (mf->IsOn("CMAKE_COMPILER_IS_MINGW")) + { + path = GetTarget()->GetSupportDirectory(); + vars["OBJECT_DIR"] = ConvertToNinjaPath(path.c_str()); + EnsureDirectoryExists(path); + } std::vector<cmCustomCommand> *cmdLists[3] = { &this->GetTarget()->GetPreBuildCommands(), @@ -456,6 +478,13 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() symlinkVars["POST_BUILD"] = postBuildCmdLine; } + int cmdLineLimit; +#ifdef _WIN32 + cmdLineLimit = 8000; +#else + cmdLineLimit = -1; // TODO +#endif + // Write the build statement for this target. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), comment.str(), @@ -464,7 +493,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 7acbe8f..1ef9567 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -30,14 +30,11 @@ private: std::string LanguageLinkerRule() const; const char* GetVisibleTypeName() const; void WriteLanguagesRules(); - void WriteLinkRule(); + void WriteLinkRule(bool useResponseFile); void WriteLinkStatement(); void WriteObjectLibStatement(); std::vector<std::string> ComputeLinkCmd(); - void EnsureDirectoryExists(const std::string& dir); - void EnsureParentDirectoryExists(const std::string& path); - private: // Target name info. std::string TargetNameOut; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index e419a4d..6157931 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -140,11 +140,8 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source, // } // Add shared-library flags if needed. - { - bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) || - (this->Target->GetType() == cmTarget::MODULE_LIBRARY)); - this->GetLocalGenerator()->AddSharedFlags(flags, language.c_str(), shared); - } + this->LocalGenerator->AddCMP0018Flags(flags, this->Target, + language.c_str()); // TODO: Handle response file. // Add include directory flags. @@ -154,6 +151,8 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source, language.c_str()); std::string includeFlags = this->LocalGenerator->GetIncludeFlags(includes, language.c_str(), false); + if(cmGlobalNinjaGenerator::IsMinGW()) + cmSystemTools::ReplaceString(includeFlags, "\\", "/"); this->LocalGenerator->AppendFlags(flags, includeFlags.c_str()); } @@ -301,7 +300,7 @@ std::string cmNinjaTargetGenerator::GetTargetPDB() const targetFullPathPDB += this->Target->GetPDBName(this->GetConfigName()); } - return ConvertToNinjaPath(targetFullPathPDB.c_str()); + return targetFullPathPDB.c_str(); } @@ -309,10 +308,11 @@ void cmNinjaTargetGenerator ::WriteLanguageRules(const std::string& language) { +#ifdef NINJA_GEN_VERBOSE_FILES this->GetRulesFileStream() << "# Rules for language " << language << "\n\n"; +#endif this->WriteCompileRule(language); - this->GetRulesFileStream() << "\n"; } void @@ -330,20 +330,45 @@ cmNinjaTargetGenerator vars.Defines = "$DEFINES"; vars.TargetPDB = "$TARGET_PDB"; + + cmMakefile* mf = this->GetMakefile(); + + bool useClDeps = false; + std::string clDepsBinary; + std::string clShowPrefix; + if (lang == "C" || lang == "CXX" || lang == "RC") + { + const char* depsPtr = mf->GetDefinition("CMAKE_CMCLDEPS_EXECUTABLE"); + const char* showPtr = mf->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX"); + if (depsPtr && showPtr) + { + // don't wrap for try_compile, + // TODO but why doesn't it work with cmcldeps? + const std::string projectName = mf->GetProjectName() ? + mf->GetProjectName() : ""; + if (projectName != "CMAKE_TRY_COMPILE") + { + useClDeps = true; + std::string qu = "\""; + clDepsBinary = qu + depsPtr + qu; + clShowPrefix = qu + showPtr + qu; + vars.DependencyFile = "$DEP_FILE"; + } + } + } + + std::string depfile; std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language; - const char *depfileFlags = - this->GetMakefile()->GetDefinition(depfileFlagsName.c_str()); - if (depfileFlags) { - std::string depfileFlagsStr = depfileFlags; - depfile = "$out.d"; - cmSystemTools::ReplaceString(depfileFlagsStr, "<DEPFILE>", - depfile.c_str()); - cmSystemTools::ReplaceString(depfileFlagsStr, "<OBJECT>", - "$out"); - cmSystemTools::ReplaceString(depfileFlagsStr, "<CMAKE_C_COMPILER>", - this->GetMakefile()->GetDefinition("CMAKE_C_COMPILER")); - flags += " " + depfileFlagsStr; + const char *depfileFlags = mf->GetDefinition(depfileFlagsName.c_str()); + if (depfileFlags || useClDeps) { + std::string depFlagsStr = depfileFlags ? depfileFlags : ""; + depfile = "$DEP_FILE"; + cmSystemTools::ReplaceString(depFlagsStr, "<DEPFILE>", "\"$DEP_FILE\""); + cmSystemTools::ReplaceString(depFlagsStr, "<OBJECT>", "$out"); + cmSystemTools::ReplaceString(depFlagsStr, "<CMAKE_C_COMPILER>", + mf->GetDefinition("CMAKE_C_COMPILER")); + flags += " " + depFlagsStr; } vars.Flags = flags.c_str(); @@ -352,8 +377,7 @@ cmNinjaTargetGenerator std::string compileCmdVar = "CMAKE_"; compileCmdVar += language; compileCmdVar += "_COMPILE_OBJECT"; - std::string compileCmd = - this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str()); + std::string compileCmd = mf->GetRequiredDefinition(compileCmdVar.c_str()); std::vector<std::string> compileCmds; cmSystemTools::ExpandListArgument(compileCmd, compileCmds); @@ -364,10 +388,18 @@ cmNinjaTargetGenerator std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine(compileCmds); + if(useClDeps) + { + std::string cl = mf->GetDefinition("CMAKE_C_COMPILER"); + cl = "\"" + cl + "\" "; + cmdLine = clDepsBinary + " " + lang + " $in \"$DEP_FILE\" $out " + + clShowPrefix + " " + cl + cmdLine; + } + // Write the rule for compiling file of the given language. - std::ostringstream comment; + cmOStringStream comment; comment << "Rule for compiling " << language << " files."; - std::ostringstream description; + cmOStringStream description; description << "Building " << language << " object $out"; this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language), cmdLine, @@ -484,8 +516,18 @@ cmNinjaTargetGenerator cmNinjaVars vars; vars["FLAGS"] = this->ComputeFlagsForObject(source, language); vars["DEFINES"] = this->ComputeDefines(source, language); - vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat( - this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL); + vars["DEP_FILE"] = objectFileName + ".d";; + EnsureParentDirectoryExists(objectFileName); + + // TODO move to GetTargetPDB + cmMakefile* mf = this->GetMakefile(); + if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || + mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID")) + { + vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(GetTargetPDB().c_str()).c_str(), + cmLocalGenerator::SHELL); + } if(this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS")) { @@ -566,3 +608,17 @@ cmNinjaTargetGenerator this->ModuleDefinitionFile.c_str())); this->LocalGenerator->AppendFlags(flags, flag.c_str()); } + +void +cmNinjaTargetGenerator +::EnsureDirectoryExists(const std::string& dir) +{ + cmSystemTools::MakeDirectory(dir.c_str()); +} + +void +cmNinjaTargetGenerator +::EnsureParentDirectoryExists(const std::string& path) +{ + EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path.c_str())); +} diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index b64ce1e..af43a8b 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -111,6 +111,9 @@ protected: // Helper to add flag for windows .def file. void AddModuleDefinitionFlag(std::string& flags); + void EnsureDirectoryExists(const std::string& dir); + void EnsureParentDirectoryExists(const std::string& path); + private: cmTarget* Target; cmGeneratorTarget* GeneratorTarget; diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index 37070b6..79af4d7 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -463,6 +463,34 @@ cmPolicies::cmPolicies() "The OLD behaviour is to always prefer files from CMAKE_MODULE_PATH over " "files from the CMake modules directory.", 2,8,4,0, cmPolicies::WARN); + + this->DefinePolicy( + CMP0018, "CMP0018", + "Ignore CMAKE_SHARED_LIBRARY_<Lang>_FLAGS variable.", + "CMake 2.8.8 and lower compiled sources in SHARED and MODULE libraries " + "using the value of the undocumented CMAKE_SHARED_LIBRARY_<Lang>_FLAGS " + "platform variable. The variable contained platform-specific flags " + "needed to compile objects for shared libraries. Typically it included " + "a flag such as -fPIC for position independent code but also included " + "other flags needed on certain platforms. CMake 2.8.9 and higher " + "prefer instead to use the POSITION_INDEPENDENT_CODE target property to " + "determine what targets should be position independent, and new " + "undocumented platform variables to select flags while ignoring " + "CMAKE_SHARED_LIBRARY_<Lang>_FLAGS completely." + "\n" + "The default for either approach produces identical compilation flags, " + "but if a project modifies CMAKE_SHARED_LIBRARY_<Lang>_FLAGS from its " + "original value this policy determines which approach to use." + "\n" + "The OLD behavior for this policy is to ignore the " + "POSITION_INDEPENDENT_CODE property for all targets and use the modified " + "value of CMAKE_SHARED_LIBRARY_<Lang>_FLAGS for SHARED and MODULE " + "libraries." + "\n" + "The NEW behavior for this policy is to ignore " + "CMAKE_SHARED_LIBRARY_<Lang>_FLAGS whether it is modified or not and " + "honor the POSITION_INDEPENDENT_CODE target property.", + 2,8,9,0, cmPolicies::WARN); } cmPolicies::~cmPolicies() diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 3106248..6932565 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -65,6 +65,9 @@ public: /// target_link_libraries() fails if only argument is not a target CMP0016, CMP0017, ///< Prefer files in CMAKE_ROOT when including from CMAKE_ROOT + CMP0018, ///< Ignore language flags for shared libs, and adhere to + /// POSITION_INDEPENDENT_CODE property and *_COMPILE_OPTIONS_PI{E,C} + /// instead. /** \brief Always the last entry. * diff --git a/Source/cmQtAutomoc.cxx b/Source/cmQtAutomoc.cxx index ca27530..65ecdf7 100644 --- a/Source/cmQtAutomoc.cxx +++ b/Source/cmQtAutomoc.cxx @@ -18,6 +18,7 @@ #include "cmSystemTools.h" #include <cmsys/Terminal.h> +#include <cmsys/ios/sstream> #include <string.h> #if defined(__APPLE__) @@ -244,6 +245,7 @@ void cmQtAutomoc::SetupAutomocTarget(cmTarget* target) bool cmQtAutomoc::Run(const char* targetDirectory) { + bool success = true; cmake cm; cmGlobalGenerator* gg = this->CreateGlobalGenerator(&cm, targetDirectory); cmMakefile* makefile = gg->GetCurrentLocalGenerator()->GetMakefile(); @@ -255,7 +257,7 @@ bool cmQtAutomoc::Run(const char* targetDirectory) if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") { - this->RunAutomoc(); + success = this->RunAutomoc(); } this->WriteOldMocDefinitionsFile(targetDirectory); @@ -263,7 +265,7 @@ bool cmQtAutomoc::Run(const char* targetDirectory) delete gg; gg = NULL; makefile = NULL; - return true; + return success; } @@ -549,7 +551,7 @@ bool cmQtAutomoc::RunAutomoc() this->GenerateMoc(it->first, it->second); } - std::stringstream outStream(std::stringstream::out); + cmsys_ios::stringstream outStream; outStream << "/* This file is autogenerated, do not edit*/\n"; bool automocCppChanged = false; @@ -577,7 +579,7 @@ bool cmQtAutomoc::RunAutomoc() if (this->RunMocFailed) { - std::cerr << "returning failed.."<< std::endl; + std::cerr << "moc failed..."<< std::endl; return false; } outStream.flush(); @@ -1077,7 +1079,7 @@ bool cmQtAutomoc::EndsWith(const std::string& str, const std::string& with) std::string cmQtAutomoc::ReadAll(const std::string& filename) { std::ifstream file(filename.c_str()); - std::stringstream stream; + cmsys_ios::stringstream stream; stream << file.rdbuf(); file.close(); return stream.str(); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index c5ef9ff..4f3f2c5 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -756,6 +756,14 @@ void cmTarget::DefineProperties(cmake *cm) "(such as \"lib\") on a library name."); cm->DefineProperty + ("POSITION_INDEPENDENT_CODE", cmProperty::TARGET, + "Whether to create a position-independent target", + "The POSITION_INDEPENDENT_CODE property determines whether position " + "independent executables or shared libraries will be created. " + "This property is true by default for SHARED and MODULE library " + "targets and false otherwise."); + + cm->DefineProperty ("POST_INSTALL_SCRIPT", cmProperty::TARGET, "Deprecated install support.", "The PRE_INSTALL_SCRIPT and POST_INSTALL_SCRIPT properties are the " @@ -1305,6 +1313,13 @@ void cmTarget::SetMakefile(cmMakefile* mf) this->SetProperty("INCLUDE_DIRECTORIES", this->Makefile->GetProperty("INCLUDE_DIRECTORIES")); + if(this->TargetTypeValue == cmTarget::SHARED_LIBRARY + || this->TargetTypeValue == cmTarget::MODULE_LIBRARY) + { + this->SetProperty("POSITION_INDEPENDENT_CODE", "True"); + } + this->SetPropertyDefault("POSITION_INDEPENDENT_CODE", 0); + // Record current policies for later use. this->PolicyStatusCMP0003 = this->Makefile->GetPolicyStatus(cmPolicies::CMP0003); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 2ffff42..451aec8 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1699,8 +1699,8 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args) else if (args[1] == "cmake_automoc") { cmQtAutomoc automoc; - automoc.Run(args[2].c_str()); - return 0; + bool automocSuccess = automoc.Run(args[2].c_str()); + return automocSuccess ? 0 : 1; } #endif diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx new file mode 100644 index 0000000..7d3c4bd --- /dev/null +++ b/Source/cmcldeps.cxx @@ -0,0 +1,736 @@ +/* + ninja's subprocess.h +*/ + +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_SUBPROCESS_H_ +#define NINJA_SUBPROCESS_H_ + +#include <string> +#include <vector> +#include <queue> +#include <cstdio> +#include <algorithm> + + +#ifdef _WIN32 +#include <windows.h> +#else +#include <signal.h> +#endif + + +#if defined(_WIN64) +typedef unsigned __int64 cmULONG_PTR; +#else +typedef unsigned long cmULONG_PTR; +#endif + +//#include "exit_status.h" +enum ExitStatus { + ExitSuccess, + ExitFailure, + ExitInterrupted +}; + +/// Subprocess wraps a single async subprocess. It is entirely +/// passive: it expects the caller to notify it when its fds are ready +/// for reading, as well as call Finish() to reap the child once done() +/// is true. +struct Subprocess { + ~Subprocess(); + + /// Returns ExitSuccess on successful process exit, ExitInterrupted if + /// the process was interrupted, ExitFailure if it otherwise failed. + ExitStatus Finish(); + + bool Done() const; + + const std::string& GetOutput() const; + + int ExitCode() const { return exit_code_; } + + private: + Subprocess(); + bool Start(struct SubprocessSet* set, const std::string& command, + const std::string& dir); + void OnPipeReady(); + + std::string buf_; + +#ifdef _WIN32 + /// Set up pipe_ as the parent-side pipe of the subprocess; return the + /// other end of the pipe, usable in the child process. + HANDLE SetupPipe(HANDLE ioport); + + PROCESS_INFORMATION child_; + HANDLE pipe_; + OVERLAPPED overlapped_; + char overlapped_buf_[4 << 10]; + bool is_reading_; + int exit_code_; +#else + int fd_; + pid_t pid_; +#endif + + friend struct SubprocessSet; +}; + +/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses. +/// DoWork() waits for any state change in subprocesses; finished_ +/// is a queue of subprocesses as they finish. +struct SubprocessSet { + SubprocessSet(); + ~SubprocessSet(); + + Subprocess* Add(const std::string& command, const std::string& dir); + bool DoWork(); + Subprocess* NextFinished(); + void Clear(); + + std::vector<Subprocess*> running_; + std::queue<Subprocess*> finished_; + +#ifdef _WIN32 + static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType); + static HANDLE ioport_; +#else + static void SetInterruptedFlag(int signum); + static bool interrupted_; + + struct sigaction old_act_; + sigset_t old_mask_; +#endif +}; + +#endif // NINJA_SUBPROCESS_H_ + + +/* + ninja's util functions +*/ + + +static void Fatal(const char* msg, ...) { + va_list ap; + fprintf(stderr, "ninja: FATAL: "); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); +#ifdef _WIN32 + // On Windows, some tools may inject extra threads. + // exit() may block on locks held by those threads, so forcibly exit. + fflush(stderr); + fflush(stdout); + ExitProcess(1); +#else + exit(1); +#endif +} + + +#ifdef _WIN32 +std::string GetLastErrorString() { + DWORD err = GetLastError(); + + char* msg_buf; + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&msg_buf, + 0, + NULL); + std::string msg = msg_buf; + LocalFree(msg_buf); + return msg; +} +#endif + +#define snprintf _snprintf + + +/* + ninja's subprocess-win32.cc +*/ + +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//#include "subprocess.h" + +#include <stdio.h> + +#include <algorithm> + +//#include "util.h" + +namespace { + +void Win32Fatal(const char* function) { + Fatal("%s: %s", function, GetLastErrorString().c_str()); +} + +} // anonymous namespace + +Subprocess::Subprocess() : overlapped_(), is_reading_(false), + exit_code_(1) { + child_.hProcess = NULL; +} + +Subprocess::~Subprocess() { + if (pipe_) { + if (!CloseHandle(pipe_)) + Win32Fatal("CloseHandle"); + } + // Reap child if forgotten. + if (child_.hProcess) + Finish(); +} + +HANDLE Subprocess::SetupPipe(HANDLE ioport) { + char pipe_name[100]; + snprintf(pipe_name, sizeof(pipe_name), + "\\\\.\\pipe\\ninja_pid%u_sp%p", GetCurrentProcessId(), this); + + pipe_ = ::CreateNamedPipeA(pipe_name, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 0, 0, INFINITE, NULL); + if (pipe_ == INVALID_HANDLE_VALUE) + Win32Fatal("CreateNamedPipe"); + + if (!CreateIoCompletionPort(pipe_, ioport, (cmULONG_PTR)this, 0)) + Win32Fatal("CreateIoCompletionPort"); + + memset(&overlapped_, 0, sizeof(overlapped_)); + if (!ConnectNamedPipe(pipe_, &overlapped_) && + GetLastError() != ERROR_IO_PENDING) { + Win32Fatal("ConnectNamedPipe"); + } + + // Get the write end of the pipe as a handle inheritable across processes. + HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, 0, NULL); + HANDLE output_write_child; + if (!DuplicateHandle(GetCurrentProcess(), output_write_handle, + GetCurrentProcess(), &output_write_child, + 0, TRUE, DUPLICATE_SAME_ACCESS)) { + Win32Fatal("DuplicateHandle"); + } + CloseHandle(output_write_handle); + + return output_write_child; +} + +bool Subprocess::Start(SubprocessSet* set, const std::string& command, + const std::string& dir) { + HANDLE child_pipe = SetupPipe(set->ioport_); + + SECURITY_ATTRIBUTES security_attributes; + memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES)); + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + // Must be inheritable so subprocesses can dup to children. + HANDLE nul = CreateFile("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); + if (nul == INVALID_HANDLE_VALUE) + Fatal("couldn't open nul"); + + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(STARTUPINFO); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = nul; + startup_info.hStdOutput = child_pipe; + startup_info.hStdError = child_pipe; + + PROCESS_INFORMATION process_info; + memset(&process_info, 0, sizeof(process_info)); + + // Do not prepend 'cmd /c' on Windows, this breaks command + // lines greater than 8,191 chars. + if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL, + /* inherit handles */ TRUE, CREATE_NEW_PROCESS_GROUP, + NULL, (dir.empty() ? NULL : dir.c_str()), + &startup_info, &process_info)) { + DWORD error = GetLastError(); + if (error == ERROR_FILE_NOT_FOUND) { + // file (program) not found error is treated + // as a normal build action failure + if (child_pipe) + CloseHandle(child_pipe); + CloseHandle(pipe_); + CloseHandle(nul); + pipe_ = NULL; + // child_ is already NULL; + buf_ = + "CreateProcess failed: The system cannot find the file specified.\n"; + return true; + } else { + Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal + } + } + + // Close pipe channel only used by the child. + if (child_pipe) + CloseHandle(child_pipe); + CloseHandle(nul); + + CloseHandle(process_info.hThread); + child_ = process_info; + + return true; +} + +void Subprocess::OnPipeReady() { + DWORD bytes; + if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + CloseHandle(pipe_); + pipe_ = NULL; + return; + } + Win32Fatal("GetOverlappedResult"); + } + + if (is_reading_ && bytes) + buf_.append(overlapped_buf_, bytes); + + memset(&overlapped_, 0, sizeof(overlapped_)); + is_reading_ = true; + if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_), + &bytes, &overlapped_)) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + CloseHandle(pipe_); + pipe_ = NULL; + return; + } + if (GetLastError() != ERROR_IO_PENDING) + Win32Fatal("ReadFile"); + } + + // Even if we read any bytes in the readfile call, we'll enter this + // function again later and get them at that point. +} + +ExitStatus Subprocess::Finish() { + if (!child_.hProcess) + return ExitFailure; + + // TODO: add error handling for all of these. + WaitForSingleObject(child_.hProcess, INFINITE); + + DWORD exit_code = 0; + GetExitCodeProcess(child_.hProcess, &exit_code); + + CloseHandle(child_.hProcess); + child_.hProcess = NULL; + exit_code_ = exit_code; + return exit_code == 0 ? ExitSuccess : + exit_code == CONTROL_C_EXIT ? ExitInterrupted : + ExitFailure; +} + +bool Subprocess::Done() const { + return pipe_ == NULL; +} + +const std::string& Subprocess::GetOutput() const { + return buf_; +} + +HANDLE SubprocessSet::ioport_; + +SubprocessSet::SubprocessSet() { + ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + if (!ioport_) + Win32Fatal("CreateIoCompletionPort"); + if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE)) + Win32Fatal("SetConsoleCtrlHandler"); +} + +SubprocessSet::~SubprocessSet() { + Clear(); + + SetConsoleCtrlHandler(NotifyInterrupted, FALSE); + CloseHandle(ioport_); +} + +BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) { + if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { + if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL)) + Win32Fatal("PostQueuedCompletionStatus"); + return TRUE; + } + + return FALSE; +} + +Subprocess *SubprocessSet::Add(const std::string& command, + const std::string& dir) { + Subprocess *subprocess = new Subprocess; + if (!subprocess->Start(this, command, dir)) { + delete subprocess; + return 0; + } + if (subprocess->child_.hProcess) + running_.push_back(subprocess); + else + finished_.push(subprocess); + return subprocess; +} + +bool SubprocessSet::DoWork() { + DWORD bytes_read; + Subprocess* subproc; + OVERLAPPED* overlapped; + + if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (cmULONG_PTR*)&subproc, + &overlapped, INFINITE)) { + if (GetLastError() != ERROR_BROKEN_PIPE) + Win32Fatal("GetQueuedCompletionStatus"); + } + + if (!subproc) // A NULL subproc indicates that we were interrupted and is + // delivered by NotifyInterrupted above. + return true; + + subproc->OnPipeReady(); + + if (subproc->Done()) { + std::vector<Subprocess*>::iterator end = + std::remove(running_.begin(), running_.end(), subproc); + if (running_.end() != end) { + finished_.push(subproc); + running_.resize(end - running_.begin()); + } + } + + return false; +} + +Subprocess* SubprocessSet::NextFinished() { + if (finished_.empty()) + return NULL; + Subprocess* subproc = finished_.front(); + finished_.pop(); + return subproc; +} + +void SubprocessSet::Clear() { + std::vector<Subprocess*>::iterator it = running_.begin(); + for (; it != running_.end(); ++it) { + if ((*it)->child_.hProcess) { + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, + (*it)->child_.dwProcessId)) + Win32Fatal("GenerateConsoleCtrlEvent"); + } + } + it = running_.begin(); + for (; it != running_.end(); ++it) + delete *it; + running_.clear(); +} + + +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +// Wrapper around cl that adds /showIncludes to command line, and uses that to +// generate .d files that match the style from gcc -MD. +// +// /showIncludes is equivalent to -MD, not -MMD, that is, system headers are +// included. + + +#include <windows.h> +#include <sstream> +//#include "subprocess.h" +//#include "util.h" + +// We don't want any wildcard expansion. +// See http://msdn.microsoft.com/en-us/library/zay8tzh6(v=vs.85).aspx +void _setargv() {} + +static void usage(const char* msg) { + Fatal("%s\n\nusage:\n " + "cmcldeps " + "<language C, CXX or RC> " + "<source file path> " + "<output path for *.d file> " + "<output path for *.obj file> " + "<prefix of /showIncludes> " + "<path to cl.exe> " + "<path to tool (cl or rc)> " + "<rest of command ...>\n", msg); +} + +static std::string trimLeadingSpace(const std::string& cmdline) { + int i = 0; + for (; cmdline[i] == ' '; ++i) + ; + return cmdline.substr(i); +} + +static void doEscape(std::string& str, const std::string& search, + const std::string& repl) { + std::string::size_type pos = 0; + while ((pos = str.find(search, pos)) != std::string::npos) { + str.replace(pos, search.size(), repl); + pos += repl.size(); + } +} + +// Strips one argument from the cmdline and returns it. "surrounding quotes" +// are removed from the argument if there were any. +static std::string getArg(std::string& cmdline) { + std::string ret; + bool in_quoted = false; + unsigned int i = 0; + + cmdline = trimLeadingSpace(cmdline); + + for (;; ++i) { + if (i >= cmdline.size()) + usage("Couldn't parse arguments."); + if (!in_quoted && cmdline[i] == ' ') + break; // "a b" "x y" + if (cmdline[i] == '"') + in_quoted = !in_quoted; + } + + ret = cmdline.substr(0, i); + if (ret[0] == '"' && ret[i - 1] == '"') + ret = ret.substr(1, ret.size() - 2); + cmdline = cmdline.substr(i); + return ret; +} + +static void parseCommandLine(LPTSTR wincmdline, + std::string& lang, + std::string& srcfile, + std::string& dfile, + std::string& objfile, + std::string& prefix, + std::string& clpath, + std::string& binpath, + std::string& rest) { + std::string cmdline(wincmdline); + /* self */ getArg(cmdline); + lang = getArg(cmdline); + srcfile = getArg(cmdline); + dfile = getArg(cmdline); + objfile = getArg(cmdline); + prefix = getArg(cmdline); + clpath = getArg(cmdline); + binpath = getArg(cmdline); + rest = trimLeadingSpace(cmdline); +} + +static void outputDepFile(const std::string& dfile, const std::string& objfile, + std::vector<std::string>& incs) { + + if (dfile.empty()) + return; + + // strip duplicates + std::sort(incs.begin(), incs.end()); + incs.erase(std::unique(incs.begin(), incs.end()), incs.end()); + + FILE* out = fopen(dfile.c_str(), "wb"); + + // FIXME should this be fatal or not? delete obj? delete d? + if (!out) + return; + + std::string tmp = objfile; + doEscape(tmp, " ", "\\ "); + fprintf(out, "%s: \\\n", tmp.c_str()); + + std::vector<std::string>::iterator it = incs.begin(); + for (; it != incs.end(); ++it) { + tmp = *it; + doEscape(tmp, "\\", "/"); + doEscape(tmp, " ", "\\ "); + fprintf(out, "%s \\\n", tmp.c_str()); + } + + fprintf(out, "\n"); + fclose(out); +} + + +bool startsWith(const std::string& str, const std::string& what) { + return str.compare(0, what.size(), what) == 0; +} + +bool contains(const std::string& str, const std::string& what) { + return str.find(what) != std::string::npos; +} + +std::string replace(const std::string& str, const std::string& what, + const std::string& replacement) { + size_t pos = str.find(what); + if (pos == std::string::npos) + return str; + std::string replaced = str; + return replaced.replace(pos, what.size(), replacement); +} + + + +static int process( const std::string& srcfilename, + const std::string& dfile, + const std::string& objfile, + const std::string& prefix, + const std::string& cmd, + const std::string& dir = "", + bool quiet = false) { + + SubprocessSet subprocs; + Subprocess* subproc = subprocs.Add(cmd, dir); + + if(!subproc) + return 2; + + while ((subproc = subprocs.NextFinished()) == NULL) { + subprocs.DoWork(); + } + + bool success = subproc->Finish() == ExitSuccess; + int exit_code = subproc->ExitCode(); + + std::string output = subproc->GetOutput(); + delete subproc; + + // process the include directives and output everything else + std::stringstream ss(output); + std::string line; + std::vector<std::string> includes; + bool isFirstLine = true; // cl prints always first the source filename + while (std::getline(ss, line)) { + if (startsWith(line, prefix)) { + std::string inc = trimLeadingSpace(line.substr(prefix.size()).c_str()); + if (inc[inc.size() - 1] == '\r') // blech, stupid \r\n + inc = inc.substr(0, inc.size() - 1); + includes.push_back(inc); + } else { + if (!isFirstLine || !startsWith(line, srcfilename)) { + if (!quiet) { + fprintf(stdout, "%s\n", line.c_str()); + } + } else { + isFirstLine = false; + } + } + } + + if (!success) { + return exit_code; + } + + // don't update .d until/unless we succeed compilation + outputDepFile(dfile, objfile, includes); + + return 0; +} + + +int main() { + + // Use the Win32 api instead of argc/argv so we can avoid interpreting the + // rest of command line after the .d and .obj. Custom parsing seemed + // preferable to the ugliness you get into in trying to re-escape quotes for + // subprocesses, so by avoiding argc/argv, the subprocess is called with + // the same command line verbatim. + + std::string lang, srcfile, dfile, objfile, prefix, cl, binpath, rest; + parseCommandLine(GetCommandLine(), lang, srcfile, dfile, objfile, + prefix, cl, binpath, rest); + + // needed to suppress filename output of msvc tools + std::string srcfilename; + std::string::size_type pos = srcfile.rfind("\\"); + if (pos != std::string::npos) { + srcfilename = srcfile.substr(pos + 1); + } + + std::string nol = " /nologo "; + std::string show = " /showIncludes "; + if (lang == "C" || lang == "CXX") { + return process(srcfilename, dfile, objfile, prefix, + binpath + nol + show + rest); + } else if (lang == "RC") { + // "misuse" cl.exe to get headers from .rc files + + std::string clrest = rest; + // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj + clrest = replace(clrest, "/fo", "/out:"); + clrest = replace(clrest, objfile, objfile + ".dep.obj "); + // rc: src\x\x.rc -> cl: /Tc src\x\x.rc + clrest = replace(clrest, srcfile, "/Tc " + srcfile); + + cl = "\"" + cl + "\" /P /DRC_INVOKED "; + + // call cl in object dir so the .i is generated there + std::string objdir; + std::string::size_type pos = objfile.rfind("\\"); + if (pos != std::string::npos) { + objdir = objfile.substr(0, pos); + } + + // extract dependencies with cl.exe + process(srcfilename, dfile, objfile, + prefix, cl + nol + show + clrest, objdir, true); + + // compile rc file with rc.exe + return process(srcfilename, "" , objfile, prefix, binpath + " " + rest); + } + + usage("Invalid language specified."); + return 1; +} diff --git a/Source/kwsys/hashtable.hxx.in b/Source/kwsys/hashtable.hxx.in index db52fc8..c835503 100644 --- a/Source/kwsys/hashtable.hxx.in +++ b/Source/kwsys/hashtable.hxx.in @@ -394,7 +394,7 @@ enum { _stl_num_primes = 31 }; // create a function with a static local to that function that returns // the static -inline const unsigned long* get_stl_prime_list() { +static inline const unsigned long* get_stl_prime_list() { static const unsigned long _stl_prime_list[_stl_num_primes] = { |