diff options
Diffstat (limited to 'Source')
55 files changed, 3612 insertions, 568 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 0c420b9..18f9b8b 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,6 +353,36 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) +# turn on Ninja by default +set(_CMAKE_DEFAULT_NINJA_VALUE TRUE) +# turn it off for platforms where it does not pass all the +# tests +if(WIN32 OR APPLE) + SET(_CMAKE_DEFAULT_NINJA_VALUE FALSE) +endif() +SET(CMAKE_ENABLE_NINJA ${_CMAKE_DEFAULT_NINJA_VALUE} CACHE BOOL + "Enable the ninja generator for CMake. currently not fully working for Windows or OSX") +MARK_AS_ADVANCED(CMAKE_ENABLE_NINJA) +IF(CMAKE_ENABLE_NINJA) + MESSAGE(STATUS "Enable ninja generator.") + SET(SRCS ${SRCS} + cmGlobalNinjaGenerator.cxx + cmGlobalNinjaGenerator.h + cmNinjaTypes.h + cmLocalNinjaGenerator.cxx + cmLocalNinjaGenerator.h + cmNinjaTargetGenerator.cxx + cmNinjaTargetGenerator.h + cmNinjaNormalTargetGenerator.cxx + cmNinjaNormalTargetGenerator.h + cmNinjaUtilityTargetGenerator.cxx + cmNinjaUtilityTargetGenerator.h + ) + ADD_DEFINITIONS(-DCMAKE_USE_NINJA) +ELSE() + MESSAGE(STATUS "Disable ninja generator.") +ENDIF() + # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) TARGET_LINK_LIBRARIES(CMakeLib cmsys diff --git a/Source/CPack/cmCPackDocumentVariables.cxx b/Source/CPack/cmCPackDocumentVariables.cxx index 23e99f8..d2e3802 100644 --- a/Source/CPack/cmCPackDocumentVariables.cxx +++ b/Source/CPack/cmCPackDocumentVariables.cxx @@ -44,14 +44,14 @@ void cmCPackDocumentVariables::DefineVariables(cmake* cm) "Boolean toggle to make CPack use DESTDIR mechanism when" " packaging.", "DESTDIR means DESTination DIRectory." " It is commonly used by makefile " - "users in order to install software at non-default location. It a" - "basic relocation mechanism. " + "users in order to install software at non-default location. It " + "is a basic relocation mechanism. " "It is usually invoked like this:\n" " make DESTDIR=/home/john install\n" "which will install the concerned software using the" " installation prefix, e.g. \"/usr/local\" prepended with " "the DESTDIR value which finally gives \"/home/john/usr/local\"." - " When preparing a package CPack first installs the items to be " + " When preparing a package, CPack first installs the items to be " "packaged in a local (to the build tree) directory by using the " "same DESTDIR mechanism. Nevertheless, if " "CPACK_SET_DESTDIR is set then CPack will set DESTDIR before" @@ -63,7 +63,7 @@ void cmCPackDocumentVariables::DefineVariables(cmake* cm) "Manually setting CPACK_SET_DESTDIR may help (or simply be" " necessary) if some install rules uses absolute " "DESTINATION (see CMake INSTALL command)." - "However, starting with" + " However, starting with" " CPack/CMake 2.8.3 RPM and DEB installers tries to handle DESTDIR" " automatically so that it is seldom necessary for the user to set" " it.", false, diff --git a/Source/CPack/cmCPackOSXX11Generator.cxx b/Source/CPack/cmCPackOSXX11Generator.cxx index 75ad640..363ccea 100644 --- a/Source/CPack/cmCPackOSXX11Generator.cxx +++ b/Source/CPack/cmCPackOSXX11Generator.cxx @@ -170,23 +170,25 @@ int cmCPackOSXX11Generator::PackageFiles() << "\" create -ov -format UDZO -srcfolder \"" << diskImageDirectory.c_str() << "\" \"" << packageFileNames[0] << "\""; - int retVal = 1; cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Compress disk image using command: " << dmgCmd.str().c_str() << std::endl); // since we get random dashboard failures with this one // try running it more than once - int numTries = 4; + int retVal = 1; + int numTries = 10; bool res = false; while(numTries > 0) { res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, &retVal, 0, this->GeneratorVerbose, 0); - if(res && retVal) + if ( res && !retVal ) { numTries = -1; + break; } + cmSystemTools::Delay(500); numTries--; } if ( !res || retVal ) diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx index 7d00452..3a0e89b 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.cxx +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -319,17 +319,19 @@ int cmCPackPackageMakerGenerator::PackageFiles() << "\" \"" << packageFileNames[0] << "\""; std::string output; int retVal = 1; - int numTries = 4; + int numTries = 10; bool res = false; while(numTries > 0) { res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, &retVal, 0, this->GeneratorVerbose, 0); - if(res && retVal) + if ( res && !retVal ) { numTries = -1; + break; } + cmSystemTools::Delay(500); numTries--; } if ( !res || retVal ) diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 34a3e60..27bb06c 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -94,6 +94,7 @@ static const char* cmCTestErrorMatches[] = { ": Invalid argument", "^The project cannot be built\\.", "^\\[ERROR\\]", + "^Command .* failed with exit code", 0 }; diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index f0a98f9..035aaa9 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -679,7 +679,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput( " bytes in [0-9,]+ blocks are definitely lost" " in loss record [0-9,]+ of [0-9,]+"); cmsys::RegularExpression vgPAR( - "== .*Syscall param .* contains unaddressable byte\\(s\\)"); + "== .*Syscall param .* (contains|points to) unaddressable byte\\(s\\)"); cmsys::RegularExpression vgMPK1( "== .*[0-9,]+ bytes in [0-9,]+ blocks are possibly lost in" " loss record [0-9,]+ of [0-9,]+"); diff --git a/Source/cmCommandArgumentParserHelper.h b/Source/cmCommandArgumentParserHelper.h index a211e95..cdb832b 100644 --- a/Source/cmCommandArgumentParserHelper.h +++ b/Source/cmCommandArgumentParserHelper.h @@ -81,7 +81,6 @@ private: cmStdString InputBuffer; std::vector<char> OutputBuffer; int CurrentLine; - int UnionsAvailable; int Verbose; void Print(const char* place, const char* str); diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 57fd5b4..df78bf8 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -1772,6 +1772,7 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, !linking_for_install); bool use_link_rpath = outputRuntime && linking_for_install && + !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH") && this->Target->GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH"); // Construct the RPATH. diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx index 19558fa..9296d4c 100644 --- a/Source/cmDepends.cxx +++ b/Source/cmDepends.cxx @@ -260,12 +260,27 @@ bool cmDepends::CheckDependencies(std::istream& internalDepends, //---------------------------------------------------------------------------- void cmDepends::SetIncludePathFromLanguage(const char* lang) { + // Look for the new per "TARGET_" variant first: + const char * includePath = 0; std::string includePathVar = "CMAKE_"; includePathVar += lang; - includePathVar += "_INCLUDE_PATH"; + includePathVar += "_TARGET_INCLUDE_PATH"; cmMakefile* mf = this->LocalGenerator->GetMakefile(); - if(const char* includePath = mf->GetDefinition(includePathVar.c_str())) + includePath = mf->GetDefinition(includePathVar.c_str()); + if(includePath) { cmSystemTools::ExpandListArgument(includePath, this->IncludePath); } + else + { + // Fallback to the old directory level variable if no per-target var: + includePathVar = "CMAKE_"; + includePathVar += lang; + includePathVar += "_INCLUDE_PATH"; + includePath = mf->GetDefinition(includePathVar.c_str()); + if(includePath) + { + cmSystemTools::ExpandListArgument(includePath, this->IncludePath); + } + } } diff --git a/Source/cmDocumentVariables.cxx b/Source/cmDocumentVariables.cxx index a31dd01..897e516 100644 --- a/Source/cmDocumentVariables.cxx +++ b/Source/cmDocumentVariables.cxx @@ -355,7 +355,9 @@ void cmDocumentVariables::DefineVariables(cmake* cm) "If this is set to TRUE, then the rpath information " "is not added to compiled executables. The default " "is to add rpath information if the platform supports it. " - "This allows for easy running from the build tree.",false, + "This allows for easy running from the build tree. To omit RPATH" + "in the install step, but not the build step, use " + "CMAKE_SKIP_INSTALL_RPATH instead.",false, "Variables that Provide Information"); cm->DefineProperty ("CMAKE_SOURCE_DIR", cmProperty::VARIABLE, @@ -1201,6 +1203,20 @@ void cmDocumentVariables::DefineVariables(cmake* cm) "Variables that Control the Build"); cm->DefineProperty + ("CMAKE_SKIP_INSTALL_RPATH", cmProperty::VARIABLE, + "Do not include RPATHs in the install tree.", + "Normally CMake uses the build tree for the RPATH when building " + "executables etc on systems that use RPATH. When the software " + "is installed the executables etc are relinked by CMake to have " + "the install RPATH. If this variable is set to true then the software " + "is always installed without RPATH, even if RPATH is enabled when " + "building. This can be useful for example to allow running tests from " + "the build directory with RPATH enabled before the installation step. " + "To omit RPATH in both the build and install steps, use " + "CMAKE_SKIP_RPATH instead.",false, + "Variables that Control the Build"); + + cm->DefineProperty ("CMAKE_EXE_LINKER_FLAGS", cmProperty::VARIABLE, "Linker flags used to create executables.", "Flags used by the linker when creating an executable.",false, @@ -1278,6 +1294,22 @@ void cmDocumentVariables::DefineVariables(cmake* cm) "See that target property for additional information.", false, "Variables that Control the Build"); + cm->DefineProperty + ("CMAKE_WIN32_EXECUTABLE", cmProperty::VARIABLE, + "Default value for WIN32_EXECUTABLE of targets.", + "This variable is used to initialize the " + "WIN32_EXECUTABLE property on all the targets. " + "See that target property for additional information.", + false, + "Variables that Control the Build"); + cm->DefineProperty + ("CMAKE_MACOSX_BUNDLE", cmProperty::VARIABLE, + "Default value for MACOSX_BUNDLE of targets.", + "This variable is used to initialize the " + "MACOSX_BUNDLE 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/cmExprParserHelper.cxx b/Source/cmExprParserHelper.cxx index ee37352..7728d74 100644 --- a/Source/cmExprParserHelper.cxx +++ b/Source/cmExprParserHelper.cxx @@ -30,12 +30,6 @@ cmExprParserHelper::~cmExprParserHelper() this->CleanupParser(); } -void cmExprParserHelper::SetLineFile(long line, const char* file) -{ - this->FileLine = line; - this->FileName = file; -} - int cmExprParserHelper::ParseString(const char* str, int verb) { if ( !str) diff --git a/Source/cmExprParserHelper.h b/Source/cmExprParserHelper.h index 0c36b44..690426d 100644 --- a/Source/cmExprParserHelper.h +++ b/Source/cmExprParserHelper.h @@ -46,8 +46,6 @@ public: int GetResult() { return this->Result; } - void SetLineFile(long line, const char* file); - const char* GetError() { return this->ErrorString.c_str(); } private: @@ -55,7 +53,6 @@ private: cmStdString InputBuffer; std::vector<char> OutputBuffer; int CurrentLine; - int UnionsAvailable; int Verbose; void Print(const char* place, const char* str); diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index 6e246e6..ccb17f0 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -596,16 +596,17 @@ void cmExtraCodeBlocksGenerator::AppendTarget(cmGeneratedFileStream& fout, // the include directories for this target std::set<std::string> uniqIncludeDirs; - const std::vector<std::string>& incDirs = - target->GetMakefile()->GetIncludeDirectories(); - for(std::vector<std::string>::const_iterator dirIt=incDirs.begin(); - dirIt != incDirs.end(); + + std::vector<std::string> includes; + target->GetMakefile()->GetLocalGenerator()-> + GetIncludeDirectories(includes, target); + for(std::vector<std::string>::const_iterator dirIt=includes.begin(); + dirIt != includes.end(); ++dirIt) { uniqIncludeDirs.insert(*dirIt); } - std::string systemIncludeDirs = makefile->GetSafeDefinition( "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS"); if (!systemIncludeDirs.empty()) diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index c8c86c7..ebd7c7f 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -893,9 +893,13 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const it != this->GlobalGenerator->GetLocalGenerators().end(); ++it) { - const std::vector<std::string>& includeDirs - = (*it)->GetMakefile()->GetIncludeDirectories(); - this->AppendIncludeDirectories(fout, includeDirs, emmited); + cmTargets & targets = (*it)->GetMakefile()->GetTargets(); + for (cmTargets::iterator l = targets.begin(); l != targets.end(); ++l) + { + std::vector<std::string> includeDirs; + (*it)->GetIncludeDirectories(includeDirs, &l->second); + this->AppendIncludeDirectories(fout, includeDirs, emmited); + } } // now also the system include directories, in case we found them in // CMakeSystemSpecificInformation.cmake. This makes Eclipse find the diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 9177162..f17002e 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -198,7 +198,7 @@ void cmFindPackageCommand::GenerateDocumentation() "If no such version file is available then the configuration file " "is assumed to not be compatible with any requested version. " "A basic version file containing generic version matching code can be " - "created using the macro write_basic_config_version_file(), see its " + "created using the macro write_basic_package_version_file(), see its " "documentation for more details. " "When a version file is found it is loaded to check the requested " "version number. " @@ -1062,6 +1062,10 @@ bool cmFindPackageCommand::HandlePackageMode() this->Makefile->IssueMessage( this->Required? cmake::FATAL_ERROR : cmake::WARNING, e.str()); + if (this->Required) + { + cmSystemTools::SetFatalErrorOccured(); + } if (!aw.str().empty()) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 8dce053..a988844 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1067,9 +1067,9 @@ void cmGlobalGenerator::CheckLocalGenerators() { manager = this->LocalGenerators[i]->GetMakefile()->GetCacheManager(); this->LocalGenerators[i]->ConfigureFinalPass(); - const cmTargets & targets = + cmTargets & targets = this->LocalGenerators[i]->GetMakefile()->GetTargets(); - for (cmTargets::const_iterator l = targets.begin(); + for (cmTargets::iterator l = targets.begin(); l != targets.end(); l++) { const cmTarget::LinkLibraryVectorType& libs = @@ -1095,27 +1095,28 @@ void cmGlobalGenerator::CheckLocalGenerators() notFoundMap[varName] = text; } } - } - const std::vector<std::string>& incs = - this->LocalGenerators[i]->GetMakefile()->GetIncludeDirectories(); + std::vector<std::string> incs; + this->LocalGenerators[i]->GetIncludeDirectories(incs, &l->second); - for( std::vector<std::string>::const_iterator incDir = incs.begin(); - incDir != incs.end(); ++incDir) - { - if(incDir->size() > 9 && - cmSystemTools::IsNOTFOUND(incDir->c_str())) + for( std::vector<std::string>::const_iterator incDir = incs.begin(); + incDir != incs.end(); ++incDir) { - std::string varName = incDir->substr(0, incDir->size()-9); - cmCacheManager::CacheIterator it = - manager->GetCacheIterator(varName.c_str()); - if(it.GetPropertyAsBool("ADVANCED")) + if(incDir->size() > 9 && + cmSystemTools::IsNOTFOUND(incDir->c_str())) { - varName += " (ADVANCED)"; + std::string varName = incDir->substr(0, incDir->size()-9); + cmCacheManager::CacheIterator it = + manager->GetCacheIterator(varName.c_str()); + if(it.GetPropertyAsBool("ADVANCED")) + { + varName += " (ADVANCED)"; + } + std::string text = notFoundMap[varName]; + text += "\n used as include directory in directory "; + text += this->LocalGenerators[i] + ->GetMakefile()->GetCurrentDirectory(); + notFoundMap[varName] = text; } - std::string text = notFoundMap[varName]; - text += "\n used as include directory in directory "; - text += this->LocalGenerators[i]->GetMakefile()->GetCurrentDirectory(); - notFoundMap[varName] = text; } } this->CMakeInstance->UpdateProgress diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx new file mode 100644 index 0000000..7c1529b --- /dev/null +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -0,0 +1,792 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmGlobalNinjaGenerator.h" +#include "cmLocalNinjaGenerator.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmVersion.h" + +const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja"; +const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja"; +const char* cmGlobalNinjaGenerator::INDENT = " "; + +void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count) +{ + for(int i = 0; i < count; ++i) + os << cmGlobalNinjaGenerator::INDENT; +} + +void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os) +{ + os + << "# ======================================" + << "=======================================\n"; +} + +void cmGlobalNinjaGenerator::WriteComment(std::ostream& os, + const std::string& comment) +{ + if (comment.empty()) + return; + + std::string replace = comment; + std::string::size_type lpos = 0; + std::string::size_type rpos; + while((rpos = replace.find('\n', lpos)) != std::string::npos) + { + os << "# " << replace.substr(lpos, rpos - lpos) << "\n"; + lpos = rpos + 1; + } + os << "# " << replace.substr(lpos) << "\n"; +} + +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(); + } else { + std::string result = ident; + cmSystemTools::ReplaceString(result, " ", "$ "); + cmSystemTools::ReplaceString(result, ":", "$:"); + return result; + } +} + +std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit) +{ + std::string result = lit; + cmSystemTools::ReplaceString(result, "$", "$$"); + return result; +} + +std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path) +{ + std::string result = path; +#ifdef _WIN32 + cmSystemTools::ReplaceString(result, "/", "\\"); +#endif + return EncodeLiteral(result); +} + +void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, + const std::string& comment, + const std::string& rule, + const cmNinjaDeps& outputs, + const cmNinjaDeps& explicitDeps, + const cmNinjaDeps& implicitDeps, + const cmNinjaDeps& orderOnlyDeps, + const cmNinjaVars& variables) +{ + // Make sure there is a rule. + if(rule.empty()) + { + cmSystemTools::Error("No rule for WriteBuildStatement! called " + "with comment: ", + comment.c_str()); + 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()); + return; + } + + cmGlobalNinjaGenerator::WriteComment(os, comment); + + std::ostringstream builds; + + // 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); + + // 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(); + + // Write the variables bound to this build statement. + for(cmNinjaVars::const_iterator i = variables.begin(); + i != variables.end(); + ++i) + cmGlobalNinjaGenerator::WriteVariable(os, i->first, i->second, "", 1); +} + +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) +{ + cmGlobalNinjaGenerator::WriteBuild(os, + comment, + "phony", + outputs, + explicitDeps, + implicitDeps, + orderOnlyDeps, + variables); +} + +void cmGlobalNinjaGenerator::AddCustomCommandRule() +{ + this->AddRule("CUSTOM_COMMAND", + "$COMMAND", + "$DESC", + "Rule for running custom commands.", + /*depfile*/ "", + /*restat*/ true); +} + +void +cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command, + const std::string& description, + const std::string& comment, + const cmNinjaDeps& outputs, + const cmNinjaDeps& deps, + const cmNinjaDeps& orderOnlyDeps) +{ + this->AddCustomCommandRule(); + + cmNinjaVars vars; + vars["COMMAND"] = command; + vars["DESC"] = EncodeLiteral(description); + + cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream, + comment, + "CUSTOM_COMMAND", + outputs, + deps, + cmNinjaDeps(), + orderOnlyDeps, + vars); +} + +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, + bool restat, + bool generator) +{ + // Make sure the rule has a name. + if(name.empty()) + { + cmSystemTools::Error("No name given for WriteRuleStatement! called " + "with comment: ", + comment.c_str()); + return; + } + + // Make sure a command is given. + if(command.empty()) + { + cmSystemTools::Error("No command given for WriteRuleStatement! called " + "with comment: ", + comment.c_str()); + 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 command. + cmGlobalNinjaGenerator::Indent(os, 1); + os << "command = " << command << "\n"; + + // Write the description if any. + if(!description.empty()) + { + cmGlobalNinjaGenerator::Indent(os, 1); + os << "description = " << description << "\n"; + } + + if(restat) + { + cmGlobalNinjaGenerator::Indent(os, 1); + os << "restat = 1\n"; + } + + if(generator) + { + cmGlobalNinjaGenerator::Indent(os, 1); + os << "generator = 1\n"; + } +} + +void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os, + const std::string& name, + const std::string& value, + const std::string& comment, + int indent) +{ + // Make sure we have a name. + if(name.empty()) + { + cmSystemTools::Error("No name given for WriteVariable! called " + "with comment: ", + comment.c_str()); + return; + } + + // Do not add a variable if the value is empty. + std::string val = cmSystemTools::TrimWhitespace(value); + if(val.empty()) + { + return; + } + + cmGlobalNinjaGenerator::WriteComment(os, comment); + cmGlobalNinjaGenerator::Indent(os, indent); + os << name << " = " << val << "\n"; +} + +void cmGlobalNinjaGenerator::WriteInclude(std::ostream& os, + const std::string& filename, + const std::string& comment) +{ + cmGlobalNinjaGenerator::WriteComment(os, comment); + os << "include " << filename << "\n"; +} + +void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os, + const cmNinjaDeps& targets, + const std::string& comment) +{ + cmGlobalNinjaGenerator::WriteComment(os, comment); + os << "default"; + for(cmNinjaDeps::const_iterator i = targets.begin(); i != targets.end(); ++i) + os << " " << *i; + os << "\n"; +} + + +cmGlobalNinjaGenerator::cmGlobalNinjaGenerator() + : cmGlobalGenerator() + , BuildFileStream(0) + , RulesFileStream(0) + , Rules() + , AllDependencies() +{ + // // Ninja is not ported to non-Unix OS yet. + // this->ForceUnixPaths = true; + this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake"; +} + +//---------------------------------------------------------------------------- +// Virtual public methods. + +cmLocalGenerator* cmGlobalNinjaGenerator::CreateLocalGenerator() +{ + cmLocalGenerator* lg = new cmLocalNinjaGenerator; + lg->SetGlobalGenerator(this); + return lg; +} + +void cmGlobalNinjaGenerator +::GetDocumentation(cmDocumentationEntry& entry) const +{ + entry.Name = this->GetName(); + entry.Brief = "Generates build.ninja files (experimental)."; + entry.Full = + "A build.ninja file is generated into the build tree. Recent " + "versions of the ninja program can build the project through the " + "\"all\" target. An \"install\" target is also provided."; +} + +// Implemented in all cmGlobaleGenerator sub-classes. +// Used in: +// Source/cmLocalGenerator.cxx +// Source/cmake.cxx +void cmGlobalNinjaGenerator::Generate() +{ + this->OpenBuildFileStream(); + this->OpenRulesFileStream(); + + this->cmGlobalGenerator::Generate(); + + this->WriteAssumedSourceDependencies(); + this->WriteTargetAliases(*this->BuildFileStream); + this->WriteBuiltinTargets(*this->BuildFileStream); + + this->CloseRulesFileStream(); + this->CloseBuildFileStream(); +} + +// Implemented in all cmGlobaleGenerator sub-classes. +// Used in: +// Source/cmMakefile.cxx: +void cmGlobalNinjaGenerator +::EnableLanguage(std::vector<std::string>const& languages, + 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) + { + if(*l == "NONE") + { + continue; + } + if(*l == "Fortran") + { + std::string message = "The \""; + message += this->GetName(); + message += "\" generator does not support the language \""; + message += *l; + message += "\" yet."; + cmSystemTools::Error(message.c_str()); + } + this->ResolveLanguageCompiler(*l, mf, optional); + } +} + +// Implemented by: +// cmGlobalUnixMakefileGenerator3 +// cmGlobalVisualStudio10Generator +// cmGlobalVisualStudio6Generator +// cmGlobalVisualStudio7Generator +// cmGlobalXCodeGenerator +// Called by: +// cmGlobalGenerator::Build() +std::string cmGlobalNinjaGenerator +::GenerateBuildCommand(const char* makeProgram, + const char* projectName, + const char* additionalOptions, + const char* targetName, + const char* config, + bool ignoreErrors, + bool fast) +{ + // Project name and config are not used yet. + (void)projectName; + (void)config; + // Ninja does not have -i equivalent option yet. + (void)ignoreErrors; + // We do not handle fast build yet. + (void)fast; + + std::string makeCommand = + cmSystemTools::ConvertToUnixOutputPath(makeProgram); + + if(additionalOptions) + { + makeCommand += " "; + makeCommand += additionalOptions; + } + if(targetName) + { + if(strcmp(targetName, "clean") == 0) + { + makeCommand += " -t clean"; + } + else + { + makeCommand += " "; + makeCommand += targetName; + } + } + + return 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, + bool restat, + bool generator) +{ + // Do not add the same rule twice. + if (this->HasRule(name)) + return; + + this->Rules.insert(name); + cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream, + name, + command, + description, + comment, + depfile, + restat, + generator); +} + +bool cmGlobalNinjaGenerator::HasRule(const std::string &name) +{ + RulesSetType::const_iterator rule = this->Rules.find(name); + return (rule != this->Rules.end()); +} + +//---------------------------------------------------------------------------- +// Private methods + +void cmGlobalNinjaGenerator::OpenBuildFileStream() +{ + // Compute Ninja's build file path. + std::string buildFilePath = + this->GetCMakeInstance()->GetHomeOutputDirectory(); + buildFilePath += "/"; + buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE; + + // Get a stream where to generate things. + if (!this->BuildFileStream) + { + this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str()); + if (!this->BuildFileStream) + { + // An error message is generated by the constructor if it cannot + // open the file. + return; + } + } + + // Write the do not edit header. + this->WriteDisclaimer(*this->BuildFileStream); + + // Write a comment about this file. + *this->BuildFileStream + << "# This file contains all the build statements describing the\n" + << "# compilation DAG.\n\n" + ; +} + +void cmGlobalNinjaGenerator::CloseBuildFileStream() +{ + if (this->BuildFileStream) + { + delete this->BuildFileStream; + this->BuildFileStream = 0; + } + else + { + cmSystemTools::Error("Build file stream was not open."); + } +} + +void cmGlobalNinjaGenerator::OpenRulesFileStream() +{ + // Compute Ninja's build file path. + std::string rulesFilePath = + this->GetCMakeInstance()->GetHomeOutputDirectory(); + rulesFilePath += "/"; + rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE; + + // Get a stream where to generate things. + if (!this->RulesFileStream) + { + this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str()); + if (!this->RulesFileStream) + { + // An error message is generated by the constructor if it cannot + // open the file. + return; + } + } + + // Write the do not edit header. + this->WriteDisclaimer(*this->RulesFileStream); + + // Write comment about this file. + *this->RulesFileStream + << "# This file contains all the rules used to get the outputs files\n" + << "# built from the input files.\n" + << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n" + ; +} + +void cmGlobalNinjaGenerator::CloseRulesFileStream() +{ + if (this->RulesFileStream) + { + delete this->RulesFileStream; + this->RulesFileStream = 0; + } + else + { + cmSystemTools::Error("Rules file stream was not open."); + } +} + +void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) +{ + os + << "# CMAKE generated file: DO NOT EDIT!\n" + << "# Generated by \"" << this->GetName() << "\"" + << " Generator, CMake Version " + << cmVersion::GetMajorVersion() << "." + << cmVersion::GetMinorVersion() << "\n\n"; +} + +void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target) +{ + this->AppendTargetOutputs(target, this->AllDependencies); +} + +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)); + WriteCustomCommandBuild(/*command=*/"", /*description=*/"", + "Assume dependencies for generated source file.", + cmNinjaDeps(1, i->first), deps); + } +} + +void +cmGlobalNinjaGenerator +::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs) +{ + const char* configName = + target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"); + cmLocalNinjaGenerator *ng = + static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]); + + switch (target->GetType()) { + case cmTarget::EXECUTABLE: + case cmTarget::SHARED_LIBRARY: + case cmTarget::STATIC_LIBRARY: + case cmTarget::MODULE_LIBRARY: + outputs.push_back(ng->ConvertToNinjaPath( + target->GetFullPath(configName).c_str())); + break; + + case cmTarget::UTILITY: { + std::string path = ng->ConvertToNinjaPath( + target->GetMakefile()->GetStartOutputDirectory()); + if (path.empty() || path == ".") + outputs.push_back(target->GetName()); + else { + path += "/"; + path += target->GetName(); + outputs.push_back(path); + } + break; + } + + case cmTarget::GLOBAL_TARGET: + // Always use the target in HOME instead of an unused duplicate in a + // subdirectory. + outputs.push_back(target->GetName()); + break; + + default: + return; + } +} + +void +cmGlobalNinjaGenerator +::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs) +{ + if (target->GetType() == cmTarget::GLOBAL_TARGET) { + // Global targets only depend on other utilities, which may not appear in + // the TargetDepends set (e.g. "all"). + std::set<cmStdString> const& utils = target->GetUtilities(); + std::copy(utils.begin(), utils.end(), std::back_inserter(outputs)); + } else { + cmTargetDependSet const& targetDeps = + this->GetTargetDirectDepends(*target); + for (cmTargetDependSet::const_iterator i = targetDeps.begin(); + i != targetDeps.end(); ++i) { + this->AppendTargetOutputs(*i, outputs); + } + } +} + +void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, + cmTarget* target) { + cmNinjaDeps outputs; + 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] = 0; + + // 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)); + if (newAlias.second && newAlias.first->second != target) + newAlias.first->second = 0; +} + +void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) +{ + cmGlobalNinjaGenerator::WriteDivider(os); + os << "# Target aliases.\n\n"; + + for (TargetAliasMap::iterator i = TargetAliases.begin(); + i != TargetAliases.end(); ++i) { + // Don't write ambiguous aliases. + if (!i->second) + continue; + + cmNinjaDeps deps; + this->AppendTargetOutputs(i->second, deps); + + cmGlobalNinjaGenerator::WritePhonyBuild(os, + "", + cmNinjaDeps(1, i->first), + deps); + } +} + +void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os) +{ + // Write headers. + cmGlobalNinjaGenerator::WriteDivider(os); + os << "# Built-in targets\n\n"; + + this->WriteTargetAll(os); + this->WriteTargetRebuildManifest(os); +} + +void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) +{ + cmNinjaDeps outputs; + outputs.push_back("all"); + + cmGlobalNinjaGenerator::WritePhonyBuild(os, + "The main all target.", + outputs, + this->AllDependencies); + + cmGlobalNinjaGenerator::WriteDefault(os, + outputs, + "Make the all target the default."); +} + +void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) +{ + cmLocalGenerator *lg = this->LocalGenerators[0]; + cmMakefile* mfRoot = lg->GetMakefile(); + + std::ostringstream cmd; + cmd << lg->ConvertToOutputFormat( + mfRoot->GetRequiredDefinition("CMAKE_COMMAND"), + cmLocalGenerator::SHELL) + << " -H" + << lg->ConvertToOutputFormat(mfRoot->GetHomeDirectory(), + cmLocalGenerator::SHELL) + << " -B" + << lg->ConvertToOutputFormat(mfRoot->GetHomeOutputDirectory(), + cmLocalGenerator::SHELL); + WriteRule(*this->RulesFileStream, + "RERUN_CMAKE", + cmd.str(), + "Re-running CMake...", + "Rule for re-running cmake.", + /*depfile=*/ "", + /*restat=*/ false, + /*generator=*/ true); + + cmNinjaDeps implicitDeps; + for (std::vector<cmLocalGenerator *>::const_iterator i = + this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) { + const std::vector<std::string>& lf = (*i)->GetMakefile()->GetListFiles(); + implicitDeps.insert(implicitDeps.end(), lf.begin(), lf.end()); + } + std::sort(implicitDeps.begin(), implicitDeps.end()); + implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), + implicitDeps.end()); + implicitDeps.push_back("CMakeCache.txt"); + + WriteBuild(os, + "Re-run CMake if any of its inputs changed.", + "RERUN_CMAKE", + /*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE), + /*explicitDeps=*/ cmNinjaDeps(), + implicitDeps, + /*orderOnlyDeps=*/ cmNinjaDeps(), + /*variables=*/ cmNinjaVars()); + + WritePhonyBuild(os, + "A missing CMake input file is not an error.", + implicitDeps, + cmNinjaDeps()); +} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h new file mode 100644 index 0000000..39df826 --- /dev/null +++ b/Source/cmGlobalNinjaGenerator.h @@ -0,0 +1,332 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmGlobalNinjaGenerator_h +# define cmGlobalNinjaGenerator_h + +# include "cmGlobalGenerator.h" +# include "cmNinjaTypes.h" + +class cmLocalGenerator; +class cmGeneratedFileStream; + +/** + * \class cmGlobalNinjaGenerator + * \brief Write a build.ninja file. + * + * The main differences between this generator and the UnixMakefile + * generator family are: + * - We don't care about VERBOSE variable or RULE_MESSAGES property since + * it is handle by Ninja's -v option. + * - We don't care about computing any progress status since Ninja manages + * it itself. + * - We don't care about generating a clean target since Ninja already have + * a clean tool. + * - We generate one build.ninja and one rules.ninja per project. + * - We try to minimize the number of generated rules: one per target and + * language. + * - We use Ninja special variable $in and $out to produce nice output. + * - We extensively use Ninja variable overloading system to minimize the + * number of generated rules. + */ +class cmGlobalNinjaGenerator : public cmGlobalGenerator +{ +public: + /// The default name of Ninja's build file. Typically: build.ninja. + static const char* NINJA_BUILD_FILE; + + /// The default name of Ninja's rules file. Typically: rules.ninja. + /// It is included in the main build.ninja file. + static const char* NINJA_RULES_FILE; + + /// The indentation string used when generating Ninja's build file. + static const char* INDENT; + + /// Write @a count times INDENT level to output stream @a os. + static void Indent(std::ostream& os, int count); + + /// Write a divider in the given output stream @a os. + static void WriteDivider(std::ostream& os); + + static std::string EncodeIdent(const std::string &ident, std::ostream &vars); + static std::string EncodeLiteral(const std::string &lit); + static std::string EncodePath(const std::string &path); + + /** + * Write the given @a comment to the output stream @a os. It + * handles new line character properly. + */ + static void WriteComment(std::ostream& os, const std::string& comment); + + /** + * Write a build statement to @a os with the @a comment using + * the @a rule the list of @a outputs files and inputs. + * It also writes the variables bound to this build statement. + * @warning no escaping of any kind is done here. + */ + static void WriteBuild(std::ostream& os, + const std::string& comment, + const std::string& rule, + const cmNinjaDeps& outputs, + const cmNinjaDeps& explicitDeps, + const cmNinjaDeps& implicitDeps, + const cmNinjaDeps& orderOnlyDeps, + const cmNinjaVars& variables); + + /** + * Helper to write a build statement with the special 'phony' rule. + */ + static void WritePhonyBuild(std::ostream& os, + const std::string& comment, + const cmNinjaDeps& outputs, + const cmNinjaDeps& explicitDeps, + const cmNinjaDeps& implicitDeps = cmNinjaDeps(), + const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps(), + const cmNinjaVars& variables = cmNinjaVars()); + + void WriteCustomCommandBuild(const std::string& command, + const std::string& description, + const std::string& comment, + const cmNinjaDeps& outputs, + const cmNinjaDeps& deps = cmNinjaDeps(), + const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps()); + + /** + * Write a rule statement named @a name to @a os with the @a comment, + * the mandatory @a command, the @a depfile and the @a description. + * It also writes the variables bound to this rule statement. + * @warning no escaping of any kind is done here. + */ + static void WriteRule(std::ostream& os, + const std::string& name, + const std::string& command, + const std::string& description, + const std::string& comment = "", + const std::string& depfile = "", + bool restat = false, + bool generator = false); + + /** + * Write a variable named @a name to @a os with value @a value and an + * optional @a comment. An @a indent level can be specified. + * @warning no escaping of any kind is done here. + */ + static void WriteVariable(std::ostream& os, + const std::string& name, + const std::string& value, + const std::string& comment = "", + int indent = 0); + + /** + * Write an include statement including @a filename with an optional + * @a comment to the @a os stream. + */ + static void WriteInclude(std::ostream& os, + const std::string& filename, + const std::string& comment = ""); + + /** + * Write a default target statement specifying @a targets as + * the default targets. + */ + static void WriteDefault(std::ostream& os, + const cmNinjaDeps& targets, + const std::string& comment = ""); + +public: + /// Default constructor. + cmGlobalNinjaGenerator(); + + /// Convenience method for creating an instance of this class. + static cmGlobalGenerator* New() { + return new cmGlobalNinjaGenerator; } + + /// Destructor. + virtual ~cmGlobalNinjaGenerator() { } + + /// Overloaded methods. @see cmGlobalGenerator::CreateLocalGenerator() + virtual cmLocalGenerator* CreateLocalGenerator(); + + /// Overloaded methods. @see cmGlobalGenerator::GetName(). + virtual const char* GetName() const { + return cmGlobalNinjaGenerator::GetActualName(); } + + /// @return the name of this generator. + static const char* GetActualName() { return "Ninja"; } + + /// Overloaded methods. @see cmGlobalGenerator::GetDocumentation() + virtual void GetDocumentation(cmDocumentationEntry& entry) const; + + /// Overloaded methods. @see cmGlobalGenerator::Generate() + virtual void Generate(); + + /// Overloaded methods. @see cmGlobalGenerator::EnableLanguage() + virtual void EnableLanguage(std::vector<std::string>const& languages, + cmMakefile* mf, + bool optional); + + /// Overloaded methods. @see cmGlobalGenerator::GenerateBuildCommand() + virtual std::string GenerateBuildCommand(const char* makeProgram, + const char* projectName, + const char* additionalOptions, + const char* targetName, + const char* config, + bool ignoreErrors, + bool fast); + + // Setup target names + virtual const char* GetAllTargetName() const { return "all"; } + virtual const char* GetInstallTargetName() const { return "install"; } + virtual const char* GetInstallLocalTargetName() const { + return "install/local"; + } + virtual const char* GetInstallStripTargetName() const { + return "install/strip"; + } + virtual const char* GetTestTargetName() const { return "test"; } + virtual const char* GetPackageTargetName() const { return "package"; } + virtual const char* GetPackageSourceTargetName() const { + return "package_source"; + } + virtual const char* GetEditCacheTargetName() const { + return "edit_cache"; + } + virtual const char* GetRebuildCacheTargetName() const { + return "rebuild_cache"; + } + virtual const char* GetCleanTargetName() const { return "clean"; } + +public: + cmGeneratedFileStream* GetBuildFileStream() const + { return this->BuildFileStream; } + + cmGeneratedFileStream* GetRulesFileStream() const + { return this->RulesFileStream; } + + /** + * Add a rule to the generated build system. + * Call WriteRule() behind the scene but perform some check before like: + * - Do not add twice the same rule. + */ + void AddRule(const std::string& name, + const std::string& command, + const std::string& description, + const std::string& comment = "", + const std::string& depfile = "", + bool restat = false, + bool generator = false); + + bool HasRule(const std::string& name); + + void AddCustomCommandRule(); + +protected: + + /// Overloaded methods. + /// @see cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS() + virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() { return true; } + +private: + // In order to access the AddDependencyToAll() functions and co. + friend class cmLocalNinjaGenerator; + + // In order to access the SeenCustomCommand() function. + friend class cmNinjaTargetGenerator; + friend class cmNinjaNormalTargetGenerator; + friend class cmNinjaUtilityTargetGenerator; + +private: + void OpenBuildFileStream(); + void CloseBuildFileStream(); + + void OpenRulesFileStream(); + void CloseRulesFileStream(); + + /// Write the common disclaimer text at the top of each build file. + void WriteDisclaimer(std::ostream& os); + + void AddDependencyToAll(cmTarget* target); + + void WriteAssumedSourceDependencies(); + + void AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs); + void AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs); + + void AddTargetAlias(const std::string& alias, cmTarget* target); + void WriteTargetAliases(std::ostream& os); + + void WriteBuiltinTargets(std::ostream& os); + void WriteTargetAll(std::ostream& os); + void WriteTargetRebuildManifest(std::ostream& os); + + /// Called when we have seen the given custom command. Returns true + /// if we has seen it before. + bool SeenCustomCommand(cmCustomCommand const *cc) { + return !this->CustomCommands.insert(cc).second; + } + + /// Called when we have seen the given custom command output. + void SeenCustomCommandOutput(const std::string &output) { + this->CustomCommandOutputs.insert(output); + // We don't need the assumed dependencies anymore, because we have + // an output. + this->AssumedSourceDependencies.erase(output); + } + + bool HasCustomCommandOutput(const std::string &output) { + return this->CustomCommandOutputs.find(output) != + this->CustomCommandOutputs.end(); + } + + void AddAssumedSourceDependencies(const std::string &source, + const cmNinjaDeps &deps) { + std::set<std::string> &ASD = this->AssumedSourceDependencies[source]; + // Because we may see the same source file multiple times (same source + // specified in multiple targets), compute the union of any assumed + // dependencies. + ASD.insert(deps.begin(), deps.end()); + } + +private: + /// The file containing the build statement. (the relation ship of the + /// compilation DAG). + cmGeneratedFileStream* BuildFileStream; + /// The file containing the rule statements. (The action attached to each + /// edge of the compilation DAG). + cmGeneratedFileStream* RulesFileStream; + + /// The type used to store the set of rules added to the generated build + /// system. + typedef std::set<std::string> RulesSetType; + + /// The set of rules added to the generated build system. + RulesSetType Rules; + + /// The set of dependencies to add to the "all" target. + cmNinjaDeps AllDependencies; + + /// The set of custom commands we have seen. + std::set<cmCustomCommand const*> CustomCommands; + + /// The set of custom command outputs we have seen. + std::set<std::string> CustomCommandOutputs; + + /// The mapping from source file to assumed dependencies. + std::map<std::string, std::set<std::string> > AssumedSourceDependencies; + + typedef std::map<std::string, cmTarget*> TargetAliasMap; + TargetAliasMap TargetAliases; + + static cmLocalGenerator* LocalGenerator; +}; + +#endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index b764660..0bbe0a0 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -188,8 +188,6 @@ void cmGlobalXCodeGenerator::EnableLanguage(std::vector<std::string>const& mf->AddDefinition("CMAKE_GENERATOR_CC", "gcc"); mf->AddDefinition("CMAKE_GENERATOR_CXX", "g++"); mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1"); - // initialize Architectures so it can be used by - // GetTargetObjectFileDirectories this->cmGlobalGenerator::EnableLanguage(lang, mf, optional); const char* osxArch = mf->GetDefinition("CMAKE_OSX_ARCHITECTURES"); @@ -1820,7 +1818,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target, BuildObjectListOrString dirs(this, this->XcodeVersion >= 30); BuildObjectListOrString fdirs(this, this->XcodeVersion >= 30); std::vector<std::string> includes; - this->CurrentLocalGenerator->GetIncludeDirectories(includes); + this->CurrentLocalGenerator->GetIncludeDirectories(includes, &target); std::set<cmStdString> emitted; emitted.insert("/System/Library/Frameworks"); for(std::vector<std::string>::iterator i = includes.begin(); @@ -3372,37 +3370,6 @@ std::string cmGlobalXCodeGenerator::XCodeEscapePath(const char* p) } //---------------------------------------------------------------------------- -void cmGlobalXCodeGenerator:: -GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs) -{ - std::string dir = this->CurrentMakefile->GetCurrentOutputDirectory(); - dir += "/"; - dir += this->CurrentMakefile->GetProjectName(); - dir += ".build/"; - dir += this->GetCMakeCFGInitDirectory(); - dir += "/"; - dir += target->GetName(); - dir += ".build/Objects-normal/"; - std::string dirsave = dir; - if(this->Architectures.size()) - { - for(std::vector<std::string>::iterator i = this->Architectures.begin(); - i != this->Architectures.end(); ++i) - { - dir += *i; - dirs.push_back(dir); - dir = dirsave; - } - } - else - { - dirs.push_back(dir); - } -} - -//---------------------------------------------------------------------------- void cmGlobalXCodeGenerator ::AppendDirectoryForConfig(const char* prefix, diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index ed54be3..45f62eb 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -76,9 +76,6 @@ public: ///! What is the configurations directory variable called? virtual const char* GetCMakeCFGInitDirectory(); - void GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs); void SetCurrentLocalGenerator(cmLocalGenerator*); /** Return true if the generated build tree may contain multiple builds. diff --git a/Source/cmIncludeDirectoryCommand.h b/Source/cmIncludeDirectoryCommand.h index b90fe42..dcc116a 100644 --- a/Source/cmIncludeDirectoryCommand.h +++ b/Source/cmIncludeDirectoryCommand.h @@ -58,13 +58,21 @@ public: { return " include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)\n" - "Add the given directories to those searched by the compiler for " - "include files. By default the directories are appended onto " - "the current list of directories. This default behavior can be " - "changed by setting CMAKE_INCLUDE_DIRECTORIES_BEFORE to ON. " - "By using BEFORE or AFTER you can select between appending and " - "prepending, independent from the default. " - "If the SYSTEM option is given the compiler will be told that the " + "Add the given directories to those the compiler uses to search " + "for include files. " + "These directories are added to the directory property " + "INCLUDE_DIRECTORIES for the current CMakeLists file. " + "They are also added to the target property INCLUDE_DIRECTORIES " + "for each target in the current CMakeLists file. " + "The target property values are the ones used by the generators." + "\n" + "By default the directories are appended onto the current list of " + "directories. " + "This default behavior can be changed by setting " + "CMAKE_INCLUDE_DIRECTORIES_BEFORE to ON. " + "By using AFTER or BEFORE explicitly, you can select between " + "appending and prepending, independent of the default. " + "If the SYSTEM option is given, the compiler will be told the " "directories are meant as system include directories on some " "platforms."; } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index dc8d1c4..a7f201c 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -556,7 +556,7 @@ void cmLocalGenerator::GenerateTargetManifest() void cmLocalGenerator::AddCustomCommandToCreateObject(const char* ofname, const char* lang, cmSourceFile& source, - cmTarget& ) + cmTarget& target) { std::string objectDir = cmSystemTools::GetFilenamePath(std::string(ofname)); objectDir = this->Convert(objectDir.c_str(),START_OUTPUT,SHELL); @@ -574,7 +574,11 @@ void cmLocalGenerator::AddCustomCommandToCreateObject(const char* ofname, std::string flags; flags += this->Makefile->GetSafeDefinition(varString.c_str()); flags += " "; - flags += this->GetIncludeFlags(lang); + { + std::vector<std::string> includes; + this->GetIncludeDirectories(includes, &target, lang); + flags += this->GetIncludeFlags(includes, lang); + } flags += this->Makefile->GetDefineFlags(); // Construct the command lines. @@ -1192,24 +1196,16 @@ cmLocalGenerator::ConvertToIncludeReference(std::string const& path) } //---------------------------------------------------------------------------- -const char* cmLocalGenerator::GetIncludeFlags(const char* lang, - bool forResponseFile) +std::string cmLocalGenerator::GetIncludeFlags( + const std::vector<std::string> &includes, + const char* lang, bool forResponseFile) { if(!lang) { return ""; } - std::string key = lang; - key += forResponseFile? "@" : ""; - if(this->LanguageToIncludeFlags.count(key)) - { - return this->LanguageToIncludeFlags[key].c_str(); - } cmOStringStream includeFlags; - std::vector<std::string> includes; - this->GetIncludeDirectories(includes, lang); - std::vector<std::string>::iterator i; std::string flagVar = "CMAKE_INCLUDE_FLAG_"; flagVar += lang; @@ -1251,6 +1247,7 @@ const char* cmLocalGenerator::GetIncludeFlags(const char* lang, #ifdef __APPLE__ emitted.insert("/System/Library/Frameworks"); #endif + std::vector<std::string>::const_iterator i; for(i = includes.begin(); i != includes.end(); ++i) { if(this->Makefile->IsOn("APPLE") @@ -1311,16 +1308,12 @@ const char* cmLocalGenerator::GetIncludeFlags(const char* lang, { flags[flags.size()-1] = ' '; } - this->LanguageToIncludeFlags[key] = flags; - - // Use this temorary variable for the return value to work-around a - // bogus GCC 2.95 warning. - const char* ret = this->LanguageToIncludeFlags[key].c_str(); - return ret; + return flags; } //---------------------------------------------------------------------------- void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, + cmTarget* target, const char* lang) { // Need to decide whether to automatically include the source and @@ -1375,8 +1368,12 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, // Store the automatic include paths. if(includeBinaryDir) { - dirs.push_back(this->Makefile->GetStartOutputDirectory()); - emitted.insert(this->Makefile->GetStartOutputDirectory()); + if(emitted.find( + this->Makefile->GetStartOutputDirectory()) == emitted.end()) + { + dirs.push_back(this->Makefile->GetStartOutputDirectory()); + emitted.insert(this->Makefile->GetStartOutputDirectory()); + } } if(includeSourceDir) { @@ -1402,9 +1399,12 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, } } - // Get the project-specified include directories. - std::vector<std::string>& includes = - this->Makefile->GetIncludeDirectories(); + // Get the target-specific include directories. + std::vector<std::string> includes; + if(target) + { + includes = target->GetIncludeDirectories(); + } // Support putting all the in-project include directories first if // it is requested by the project. @@ -1412,7 +1412,7 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, { const char* topSourceDir = this->Makefile->GetHomeDirectory(); const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory(); - for(std::vector<std::string>::iterator i = includes.begin(); + for(std::vector<std::string>::const_iterator i = includes.begin(); i != includes.end(); ++i) { // Emit this directory only if it is a subdirectory of the @@ -1431,7 +1431,7 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, } // Construct the final ordered include directory list. - for(std::vector<std::string>::iterator i = includes.begin(); + for(std::vector<std::string>::const_iterator i = includes.begin(); i != includes.end(); ++i) { if(emitted.insert(*i).second) @@ -1503,7 +1503,7 @@ void cmLocalGenerator::GetTargetFlags(std::string& linkLibs, linkFlags += this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG"); linkFlags += this->Convert(sf->GetFullPath().c_str(), - START_OUTPUT, SHELL); + FULL, SHELL); linkFlags += " "; } } @@ -1583,6 +1583,16 @@ void cmLocalGenerator::GetTargetFlags(std::string& linkLibs, this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE"); linkFlags += " "; } + if (target.IsExecutableWithExports()) + { + std::string exportFlagVar = "CMAKE_EXE_EXPORTS_"; + exportFlagVar += linkLanguage; + exportFlagVar += "_FLAG"; + + linkFlags += + this->Makefile->GetSafeDefinition(exportFlagVar.c_str()); + linkFlags += " "; + } const char* targetLinkFlags = target.GetProperty("LINK_FLAGS"); if(targetLinkFlags) { @@ -2963,17 +2973,6 @@ cmLocalGenerator::GetTargetDirectory(cmTarget const&) const return ""; } - -//---------------------------------------------------------------------------- -void -cmLocalGenerator::GetTargetObjectFileDirectories(cmTarget* , - std::vector<std::string>& - ) -{ - cmSystemTools::Error("GetTargetObjectFileDirectories" - " called on cmLocalGenerator"); -} - //---------------------------------------------------------------------------- unsigned int cmLocalGenerator::GetBackwardsCompatibility() { diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index c3d057f..124747b 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -146,8 +146,8 @@ public: ///! Append flags to a string. virtual void AppendFlags(std::string& flags, const char* newFlags); ///! Get the include flags for the current makefile and language - const char* GetIncludeFlags(const char* lang, - bool forResponseFile = false); + std::string GetIncludeFlags(const std::vector<std::string> &includes, + const char* lang, bool forResponseFile = false); /** * Encode a list of preprocessor definitions for the compiler @@ -198,6 +198,7 @@ public: /** Get the include flags for the current makefile and language. */ void GetIncludeDirectories(std::vector<std::string>& dirs, + cmTarget* target, const char* lang = "C"); /** Compute the language used to compile the given source file. */ @@ -260,14 +261,6 @@ public: }; FortranFormat GetFortranFormat(const char* value); - /** Return the directories into which object files will be put. - * There maybe more than one for fat binary systems like OSX. - */ - virtual void - GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs); - /** * Convert the given remote path to a relative path with respect to * the given local path. The local path must be given in component @@ -395,7 +388,6 @@ protected: std::vector<std::string> StartOutputDirectoryComponents; cmLocalGenerator* Parent; std::vector<cmLocalGenerator*> Children; - std::map<cmStdString, cmStdString> LanguageToIncludeFlags; std::map<cmStdString, cmStdString> UniqueObjectNamesMap; std::string::size_type ObjectPathMax; std::set<cmStdString> ObjectMaxPathViolations; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx new file mode 100644 index 0000000..78072b5 --- /dev/null +++ b/Source/cmLocalNinjaGenerator.cxx @@ -0,0 +1,424 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmLocalNinjaGenerator.h" +#include "cmCustomCommandGenerator.h" +#include "cmMakefile.h" +#include "cmGlobalNinjaGenerator.h" +#include "cmNinjaTargetGenerator.h" +#include "cmGeneratedFileStream.h" +#include "cmSourceFile.h" +#include "cmComputeLinkInformation.h" +#include "cmake.h" + +#include <assert.h> + +cmLocalNinjaGenerator::cmLocalNinjaGenerator() + : cmLocalGenerator() + , ConfigName("") + , HomeRelativeOutputPath("") +{ + this->IsMakefileGenerator = true; +#ifdef _WIN32 + this->WindowsShell = true; +#endif + this->TargetImplib = "$TARGET_IMPLIB"; +} + +//---------------------------------------------------------------------------- +// Virtual public methods. + +cmLocalNinjaGenerator::~cmLocalNinjaGenerator() +{ +} + +void cmLocalNinjaGenerator::Generate() +{ + this->SetConfigName(); + + this->WriteProcessedMakefile(this->GetBuildFileStream()); + this->WriteProcessedMakefile(this->GetRulesFileStream()); + + this->WriteBuildFileTop(); + + cmTargets& targets = this->GetMakefile()->GetTargets(); + for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t) + { + cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(&t->second); + if(tg) + { + tg->Generate(); + // Add the target to "all" if required. + if (!this->GetGlobalNinjaGenerator()->IsExcluded( + this->GetGlobalNinjaGenerator()->LocalGenerators[0], + t->second)) + this->GetGlobalNinjaGenerator()->AddDependencyToAll(&t->second); + delete tg; + } + } + + this->WriteCustomCommandBuildStatements(); +} + +// Implemented in: +// cmLocalUnixMakefileGenerator3. +// Used in: +// Source/cmMakefile.cxx +// Source/cmGlobalGenerator.cxx +void cmLocalNinjaGenerator::Configure() +{ + // Compute the path to use when referencing the current output + // directory from the top output directory. + this->HomeRelativeOutputPath = + this->Convert(this->Makefile->GetStartOutputDirectory(), HOME_OUTPUT); + if(this->HomeRelativeOutputPath == ".") + { + this->HomeRelativeOutputPath = ""; + } + this->cmLocalGenerator::Configure(); + +} + +// TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it. +std::string cmLocalNinjaGenerator +::GetTargetDirectory(cmTarget const& target) const +{ + std::string dir = cmake::GetCMakeFilesDirectoryPostSlash(); + dir += target.GetName(); +#if defined(__VMS) + dir += "_dir"; +#else + dir += ".dir"; +#endif + return dir; +} + +//---------------------------------------------------------------------------- +// Non-virtual public methods. + +const cmGlobalNinjaGenerator* +cmLocalNinjaGenerator::GetGlobalNinjaGenerator() const +{ + return + static_cast<const cmGlobalNinjaGenerator*>(this->GetGlobalGenerator()); +} + +cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator() +{ + return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator()); +} + +// TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it. +std::string +cmLocalNinjaGenerator +::GetObjectFileName(const cmTarget& target, + const cmSourceFile& source) +{ + // Make sure we never hit this old case. + if(source.GetProperty("MACOSX_PACKAGE_LOCATION")) + { + std::string msg = "MACOSX_PACKAGE_LOCATION set on source file: "; + msg += source.GetFullPath(); + this->GetMakefile()->IssueMessage(cmake::INTERNAL_ERROR, + msg.c_str()); + } + + // Start with the target directory. + std::string obj = this->GetTargetDirectory(target); + obj += "/"; + + // Get the object file name without the target directory. + std::string dir_max; + dir_max += this->Makefile->GetCurrentOutputDirectory(); + dir_max += "/"; + dir_max += obj; + std::string objectName = + this->GetObjectFileNameWithoutTarget(source, dir_max, 0); + // Append the object name to the target directory. + obj += objectName; + return obj; +} + +//---------------------------------------------------------------------------- +// Virtual protected methods. + +std::string +cmLocalNinjaGenerator::ConvertToLinkReference(std::string const& lib) +{ + return this->Convert(lib.c_str(), HOME_OUTPUT, SHELL); +} + +std::string +cmLocalNinjaGenerator::ConvertToIncludeReference(std::string const& path) +{ + return this->Convert(path.c_str(), HOME_OUTPUT, SHELL); +} + +//---------------------------------------------------------------------------- +// Private methods. + +cmGeneratedFileStream& cmLocalNinjaGenerator::GetBuildFileStream() const +{ + return *this->GetGlobalNinjaGenerator()->GetBuildFileStream(); +} + +cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const +{ + return *this->GetGlobalNinjaGenerator()->GetRulesFileStream(); +} + +const cmake* cmLocalNinjaGenerator::GetCMakeInstance() const +{ + return this->GetGlobalGenerator()->GetCMakeInstance(); +} + +cmake* cmLocalNinjaGenerator::GetCMakeInstance() +{ + return this->GetGlobalGenerator()->GetCMakeInstance(); +} + +bool cmLocalNinjaGenerator::isRootMakefile() const +{ + return (strcmp(this->Makefile->GetCurrentDirectory(), + this->GetCMakeInstance()->GetHomeDirectory()) == 0); +} + +void cmLocalNinjaGenerator::WriteBuildFileTop() +{ + // We do that only once for the top CMakeLists.txt file. + if(!this->isRootMakefile()) + return; + + // For the build file. + this->WriteProjectHeader(this->GetBuildFileStream()); + this->WriteNinjaFilesInclusion(this->GetBuildFileStream()); + + // For the rule file. + this->WriteProjectHeader(this->GetRulesFileStream()); +} + +void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os) +{ + cmGlobalNinjaGenerator::WriteDivider(os); + os + << "# Project: " << this->GetMakefile()->GetProjectName() << std::endl + << "# Configuration: " << this->ConfigName << std::endl + ; + cmGlobalNinjaGenerator::WriteDivider(os); +} + +void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os) +{ + cmGlobalNinjaGenerator::WriteDivider(os); + os + << "# Include auxiliary files.\n" + << "\n" + ; + cmGlobalNinjaGenerator::WriteInclude(os, + cmGlobalNinjaGenerator::NINJA_RULES_FILE, + "Include rules file."); + os << "\n"; +} + +void cmLocalNinjaGenerator::SetConfigName() +{ + // Store the configuration name that will be generated. + if(const char* config = + this->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE")) + { + // Use the build type given by the user. + this->ConfigName = config; + } + else + { + // No configuration type given. + this->ConfigName = ""; + } +} + +void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os) +{ + cmGlobalNinjaGenerator::WriteDivider(os); + os + << "# Write statements declared in CMakeLists.txt:" << std::endl + << "# " << this->Makefile->GetCurrentListFile() << std::endl + ; + if(this->isRootMakefile()) + os << "# Which is the root file." << std::endl; + cmGlobalNinjaGenerator::WriteDivider(os); + os << std::endl; +} + +std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path) +{ + std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT); +#ifdef _WIN32 + cmSystemTools::ReplaceString(convPath, "/", "\\"); +#endif + return convPath; +} + +void +cmLocalNinjaGenerator +::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs) +{ + this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs); +} + +void +cmLocalNinjaGenerator +::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs) +{ + this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs); +} + +void cmLocalNinjaGenerator::AppendCustomCommandDeps(const cmCustomCommand *cc, + cmNinjaDeps &ninjaDeps) +{ + const std::vector<std::string> &deps = cc->GetDepends(); + for (std::vector<std::string>::const_iterator i = deps.begin(); + i != deps.end(); ++i) { + std::string dep; + if (this->GetRealDependency(i->c_str(), this->GetConfigName(), dep)) + ninjaDeps.push_back(ConvertToNinjaPath(dep.c_str())); + } +} + +std::string cmLocalNinjaGenerator::BuildCommandLine( + const std::vector<std::string> &cmdLines) +{ + // If we have no commands but we need to build a command anyway, use ":". + // This happens when building a POST_BUILD value for link targets that + // don't use POST_BUILD. + if (cmdLines.empty()) +#ifdef _WIN32 + return "cd."; +#else + 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<std::string>::const_iterator li = cmdLines.begin(); + li != cmdLines.end(); ++li) { + if (li != cmdLines.begin()) + cmd << " && "; + cmd << *li; + } + return cmd.str(); +} + +void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, + std::vector<std::string> &cmdLines) +{ + cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile); + if (ccg.GetNumberOfCommands() > 0) { + const char* wd = cc->GetWorkingDirectory(); + if (!wd) + wd = this->GetMakefile()->GetStartOutputDirectory(); + + std::ostringstream cdCmd; + cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL); + cmdLines.push_back(cdCmd.str()); + } + for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) { + cmdLines.push_back(this->ConvertToOutputFormat(ccg.GetCommand(i).c_str(), + SHELL)); + std::string& cmd = cmdLines.back(); + ccg.AppendArguments(i, cmd); + } +} + +void +cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( + cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps) +{ + if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc)) + return; + + const std::vector<std::string> &outputs = cc->GetOutputs(); + cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps; + + std::transform(outputs.begin(), outputs.end(), + ninjaOutputs.begin(), MapToNinjaPath()); + this->AppendCustomCommandDeps(cc, ninjaDeps); + + for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end(); + ++i) + this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i); + + std::vector<std::string> cmdLines; + this->AppendCustomCommandLines(cc, cmdLines); + + if (cmdLines.empty()) { + cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), + "Phony custom command for " + + ninjaOutputs[0], + ninjaOutputs, + ninjaDeps, + cmNinjaDeps(), + orderOnlyDeps, + cmNinjaVars()); + } else { + this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild( + this->BuildCommandLine(cmdLines), + this->ConstructComment(*cc), + "Custom command for " + ninjaOutputs[0], + ninjaOutputs, + ninjaDeps, + orderOnlyDeps); + } +} + +void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc, + cmTarget* target) +{ + this->CustomCommandTargets[cc].insert(target); +} + +void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements() +{ + for (CustomCommandTargetMap::iterator i = this->CustomCommandTargets.begin(); + i != this->CustomCommandTargets.end(); ++i) { + // A custom command may appear on multiple targets. However, some build + // systems exist where the target dependencies on some of the targets are + // overspecified, leading to a dependency cycle. If we assume all target + // dependencies are a superset of the true target dependencies for this + // custom command, we can take the set intersection of all target + // dependencies to obtain a correct dependency list. + // + // FIXME: This won't work in certain obscure scenarios involving indirect + // dependencies. + std::set<cmTarget*>::iterator j = i->second.begin(); + assert(j != i->second.end()); + std::vector<std::string> ccTargetDeps; + this->AppendTargetDepends(*j, ccTargetDeps); + std::sort(ccTargetDeps.begin(), ccTargetDeps.end()); + ++j; + + for (; j != i->second.end(); ++j) { + std::vector<std::string> jDeps, depsIntersection; + this->AppendTargetDepends(*j, jDeps); + std::sort(jDeps.begin(), jDeps.end()); + std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(), + jDeps.begin(), jDeps.end(), + std::back_inserter(depsIntersection)); + ccTargetDeps = depsIntersection; + } + + this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps); + } +} diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h new file mode 100644 index 0000000..28b431d --- /dev/null +++ b/Source/cmLocalNinjaGenerator.h @@ -0,0 +1,136 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmLocalNinjaGenerator_h +# define cmLocalNinjaGenerator_h + +# include "cmLocalGenerator.h" +# include "cmNinjaTypes.h" + +class cmGlobalNinjaGenerator; +class cmGeneratedFileStream; +class cmake; + +/** + * \class cmLocalNinjaGenerator + * \brief Write a local build.ninja file. + * + * cmLocalNinjaGenerator produces a local build.ninja file from its + * member Makefile. + */ +class cmLocalNinjaGenerator : public cmLocalGenerator +{ +public: + /// Default constructor. + cmLocalNinjaGenerator(); + + /// Destructor. + virtual ~cmLocalNinjaGenerator(); + + /// Overloaded methods. @see cmLocalGenerator::Generate() + virtual void Generate(); + + /// Overloaded methods. @see cmLocalGenerator::Configure() + virtual void Configure(); + + /// Overloaded methods. @see cmLocalGenerator::GetTargetDirectory() + virtual std::string GetTargetDirectory(cmTarget const& target) const; + +public: + const cmGlobalNinjaGenerator* GetGlobalNinjaGenerator() const; + cmGlobalNinjaGenerator* GetGlobalNinjaGenerator(); + + /** + * Shortcut to get the cmake instance throw the global generator. + * @return an instance of the cmake object. + */ + const cmake* GetCMakeInstance() const; + cmake* GetCMakeInstance(); + + const char* GetConfigName() const + { return this->ConfigName.c_str(); } + + std::string GetObjectFileName(const cmTarget& target, + const cmSourceFile& source); + + /// @return whether we are processing the top CMakeLists.txt file. + bool isRootMakefile() const; + + /// @returns the relative path between the HomeOutputDirectory and this + /// local generators StartOutputDirectory. + std::string GetHomeRelativeOutputPath() const + { return this->HomeRelativeOutputPath; } + +protected: + virtual std::string ConvertToLinkReference(std::string const& lib); + virtual std::string ConvertToIncludeReference(std::string const& path); + +private: + friend class cmGlobalNinjaGenerator; + + // In order to access to protected member of the local generator. + friend class cmNinjaTargetGenerator; + friend class cmNinjaNormalTargetGenerator; + friend class cmNinjaUtilityTargetGenerator; + +private: + cmGeneratedFileStream& GetBuildFileStream() const; + cmGeneratedFileStream& GetRulesFileStream() const; + + void WriteBuildFileTop(); + void WriteProjectHeader(std::ostream& os); + void WriteNinjaFilesInclusion(std::ostream& os); + void WriteProcessedMakefile(std::ostream& os); + + void SetConfigName(); + + std::string ConvertToNinjaPath(const char *path); + + struct map_to_ninja_path; + friend struct map_to_ninja_path; + struct map_to_ninja_path { + cmLocalNinjaGenerator *LocalGenerator; + map_to_ninja_path(cmLocalNinjaGenerator *LocalGen) + : LocalGenerator(LocalGen) {} + std::string operator()(const std::string &path) { + return LocalGenerator->ConvertToNinjaPath(path.c_str()); + } + }; + map_to_ninja_path MapToNinjaPath() { + return map_to_ninja_path(this); + } + + void AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs); + void AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs); + + void AppendCustomCommandDeps(const cmCustomCommand *cc, + cmNinjaDeps &ninjaDeps); + std::string BuildCommandLine(const std::vector<std::string> &cmdLines); + void AppendCustomCommandLines(const cmCustomCommand *cc, + std::vector<std::string> &cmdLines); + void WriteCustomCommandRule(); + void WriteCustomCommandBuildStatement(cmCustomCommand const *cc, + const cmNinjaDeps& orderOnlyDeps); + + void AddCustomCommandTarget(cmCustomCommand const* cc, cmTarget* target); + void WriteCustomCommandBuildStatements(); + +private: + std::string ConfigName; + std::string HomeRelativeOutputPath; + + typedef std::map<cmCustomCommand const*, std::set<cmTarget*> > + CustomCommandTargetMap; + CustomCommandTargetMap CustomCommandTargets; +}; + +#endif // ! cmLocalNinjaGenerator_h diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index dd313ca..fdf59b2 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -145,6 +145,20 @@ void cmLocalUnixMakefileGenerator3::Generate() } //---------------------------------------------------------------------------- +void cmLocalUnixMakefileGenerator3::AddLocalObjectFile( + cmTarget* target, cmSourceFile* sf, std::string objNoTargetDir, + bool hasSourceExtension) +{ + if(cmSystemTools::FileIsFullPath(objNoTargetDir.c_str())) + { + objNoTargetDir = cmSystemTools::GetFilenameName(objNoTargetDir); + } + LocalObjectInfo& info = this->LocalObjectFiles[objNoTargetDir]; + info.HasSourceExtension = hasSourceExtension; + info.push_back(LocalObjectEntry(target, sf->GetLanguage())); +} + +//---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator3::GetIndividualFileTargets (std::vector<std::string>& targets) { @@ -452,28 +466,6 @@ void cmLocalUnixMakefileGenerator3::WriteDirectoryInformationFile() << "\n"; } - // Store the include search path for this directory. - infoFileStream - << "# The C and CXX include file search paths:\n"; - infoFileStream - << "SET(CMAKE_C_INCLUDE_PATH\n"; - std::vector<std::string> includeDirs; - this->GetIncludeDirectories(includeDirs); - for(std::vector<std::string>::iterator i = includeDirs.begin(); - i != includeDirs.end(); ++i) - { - infoFileStream - << " \"" << this->Convert(i->c_str(),HOME_OUTPUT).c_str() << "\"\n"; - } - infoFileStream - << " )\n"; - infoFileStream - << "SET(CMAKE_CXX_INCLUDE_PATH ${CMAKE_C_INCLUDE_PATH})\n"; - infoFileStream - << "SET(CMAKE_Fortran_INCLUDE_PATH ${CMAKE_C_INCLUDE_PATH})\n"; - infoFileStream - << "SET(CMAKE_ASM_INCLUDE_PATH ${CMAKE_C_INCLUDE_PATH})\n"; - // Store the include regular expressions for this directory. infoFileStream << "\n" @@ -2289,14 +2281,3 @@ void cmLocalUnixMakefileGenerator3 } } } - - -void cmLocalUnixMakefileGenerator3 -::GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& dirs) -{ - std::string dir = this->Makefile->GetCurrentOutputDirectory(); - dir += "/"; - dir += this->GetTargetDirectory(*target); - dirs.push_back(dir); -} diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index 45ac21d..4bde082 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -225,24 +225,9 @@ public: // write the target rules for the local Makefile into the stream void WriteLocalAllRules(std::ostream& ruleFileStream); - struct LocalObjectEntry - { - cmTarget* Target; - std::string Language; - LocalObjectEntry(): Target(0), Language() {} - LocalObjectEntry(cmTarget* t, const char* lang): - Target(t), Language(lang) {} - }; - struct LocalObjectInfo: public std::vector<LocalObjectEntry> - { - bool HasSourceExtension; - bool HasPreprocessRule; - bool HasAssembleRule; - LocalObjectInfo():HasSourceExtension(false), HasPreprocessRule(false), - HasAssembleRule(false) {} - }; - std::map<cmStdString, LocalObjectInfo> const& GetLocalObjectFiles() - { return this->LocalObjectFiles;} + void AddLocalObjectFile(cmTarget* target, cmSourceFile* sf, + std::string objNoTargetDir, + bool hasSourceExtension); std::vector<cmStdString> const& GetLocalHelp() { return this->LocalHelp; } @@ -257,9 +242,6 @@ public: { return !this->SkipAssemblySourceRules; } - // Get the directories into which the .o files will go for this target - void GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& dirs); // Fill the vector with the target names for the object files, // preprocessed files and assembly files. Currently only used by the @@ -301,9 +283,6 @@ protected: void WriteTargetRequiresRule(std::ostream& ruleFileStream, cmTarget& target, const std::vector<std::string>& objects); - void WriteObjectConvenienceRule(std::ostream& ruleFileStream, - const char* comment, const char* output, - LocalObjectInfo const& info); std::string GetObjectFileName(cmTarget& target, const cmSourceFile& source, @@ -378,7 +357,27 @@ private: bool SkipPreprocessedSourceRules; bool SkipAssemblySourceRules; + struct LocalObjectEntry + { + cmTarget* Target; + std::string Language; + LocalObjectEntry(): Target(0), Language() {} + LocalObjectEntry(cmTarget* t, const char* lang): + Target(t), Language(lang) {} + }; + struct LocalObjectInfo: public std::vector<LocalObjectEntry> + { + bool HasSourceExtension; + bool HasPreprocessRule; + bool HasAssembleRule; + LocalObjectInfo():HasSourceExtension(false), HasPreprocessRule(false), + HasAssembleRule(false) {} + }; std::map<cmStdString, LocalObjectInfo> LocalObjectFiles; + void WriteObjectConvenienceRule(std::ostream& ruleFileStream, + const char* comment, const char* output, + LocalObjectInfo const& info); + std::vector<cmStdString> LocalHelp; /* does the work for each target */ diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx index 1dfcbea..678c5bf 100644 --- a/Source/cmLocalVisualStudio6Generator.cxx +++ b/Source/cmLocalVisualStudio6Generator.cxx @@ -103,52 +103,9 @@ void cmLocalVisualStudio6Generator::OutputDSPFile() } } - // Setup /I and /LIBPATH options for the resulting DSP file. VS 6 - // truncates long include paths so make it as short as possible if - // the length threatens this problem. - unsigned int maxIncludeLength = 3000; - bool useShortPath = false; - for(int j=0; j < 2; ++j) - { - std::vector<std::string> includes; - this->GetIncludeDirectories(includes); - std::vector<std::string>::iterator i; - for(i = includes.begin(); i != includes.end(); ++i) - { - std::string tmp = - this->ConvertToOptionallyRelativeOutputPath(i->c_str()); - if(useShortPath) - { - cmSystemTools::GetShortPath(tmp.c_str(), tmp); - } - this->IncludeOptions += " /I "; - - // quote if not already quoted - if (tmp[0] != '"') - { - this->IncludeOptions += "\""; - this->IncludeOptions += tmp; - this->IncludeOptions += "\""; - } - else - { - this->IncludeOptions += tmp; - } - } - if(j == 0 && this->IncludeOptions.size() > maxIncludeLength) - { - this->IncludeOptions = ""; - useShortPath = true; - } - else - { - break; - } - } - // Create the DSP or set of DSP's for libraries and executables - cmTargets &tgts = this->Makefile->GetTargets(); + cmTargets &tgts = this->Makefile->GetTargets(); for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++) { @@ -380,7 +337,7 @@ void cmLocalVisualStudio6Generator::WriteDSPFile(std::ostream& fout, } // Compute which sources need unique object computation. - this->ComputeObjectNameRequirements(sourceGroups); + this->ComputeObjectNameRequirements(classes); // Write the DSP file's header. this->WriteDSPHeader(fout, libName, target, sourceGroups); @@ -895,6 +852,61 @@ inline std::string removeQuotes(const std::string& s) return s; } + +std::string +cmLocalVisualStudio6Generator::GetTargetIncludeOptions(cmTarget &target) +{ + std::string includeOptions; + + // Setup /I and /LIBPATH options for the resulting DSP file. VS 6 + // truncates long include paths so make it as short as possible if + // the length threatens this problem. + unsigned int maxIncludeLength = 3000; + bool useShortPath = false; + for(int j=0; j < 2; ++j) + { + std::vector<std::string> includes; + this->GetIncludeDirectories(includes, &target); + + std::vector<std::string>::iterator i; + for(i = includes.begin(); i != includes.end(); ++i) + { + std::string tmp = + this->ConvertToOptionallyRelativeOutputPath(i->c_str()); + if(useShortPath) + { + cmSystemTools::GetShortPath(tmp.c_str(), tmp); + } + includeOptions += " /I "; + + // quote if not already quoted + if (tmp[0] != '"') + { + includeOptions += "\""; + includeOptions += tmp; + includeOptions += "\""; + } + else + { + includeOptions += tmp; + } + } + + if(j == 0 && includeOptions.size() > maxIncludeLength) + { + includeOptions = ""; + useShortPath = true; + } + else + { + break; + } + } + + return includeOptions; +} + + // Code in blocks surrounded by a test for this definition is needed // only for compatibility with user project's replacement DSP // templates. The CMake templates no longer use them. @@ -1132,6 +1144,9 @@ void cmLocalVisualStudio6Generator } #endif + // Get include options for this target. + std::string includeOptions = this->GetTargetIncludeOptions(target); + // Get extra linker options for this target type. std::string extraLinkOptions; std::string extraLinkOptionsDebug; @@ -1510,7 +1525,7 @@ void cmLocalVisualStudio6Generator optionsRelWithDebInfo.c_str()); cmSystemTools::ReplaceString(line, "BUILD_INCLUDES", - this->IncludeOptions.c_str()); + includeOptions.c_str()); cmSystemTools::ReplaceString(line, "TARGET_VERSION_FLAG", targetVersionFlag.c_str()); cmSystemTools::ReplaceString(line, "TARGET_IMPLIB_FLAG_DEBUG", @@ -1605,11 +1620,13 @@ void cmLocalVisualStudio6Generator flagsDebugRel = this->Makefile->GetSafeDefinition(flagVar.c_str()); flagsDebugRel += " -DCMAKE_INTDIR=\\\"RelWithDebInfo\\\" "; } - - // if unicode is not found, then add -D_MBCS + + // if _UNICODE and _SBCS are not found, then add -D_MBCS std::string defs = this->Makefile->GetDefineFlags(); if(flags.find("D_UNICODE") == flags.npos && - defs.find("D_UNICODE") == flags.npos) + defs.find("D_UNICODE") == flags.npos && + flags.find("D_SBCS") == flags.npos && + defs.find("D_SBCS") == flags.npos) { flags += " /D \"_MBCS\""; } @@ -1778,17 +1795,6 @@ cmLocalVisualStudio6Generator return ""; } -void cmLocalVisualStudio6Generator -::GetTargetObjectFileDirectories(cmTarget* , - std::vector<std::string>& - dirs) -{ - std::string dir = this->Makefile->GetCurrentOutputDirectory(); - dir += "/"; - dir += this->GetGlobalGenerator()->GetCMakeCFGInitDirectory(); - dirs.push_back(dir); -} - std::string cmLocalVisualStudio6Generator ::GetConfigName(std::string const& configuration) const diff --git a/Source/cmLocalVisualStudio6Generator.h b/Source/cmLocalVisualStudio6Generator.h index 195d654..4e588c3 100644 --- a/Source/cmLocalVisualStudio6Generator.h +++ b/Source/cmLocalVisualStudio6Generator.h @@ -50,9 +50,6 @@ public: void SetBuildType(BuildType, const char* libName, cmTarget&); virtual std::string GetTargetDirectory(cmTarget const& target) const; - void GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs); private: std::string DSPHeaderTemplate; std::string DSPFooterTemplate; @@ -89,7 +86,7 @@ private: void ComputeLinkOptions(cmTarget& target, const char* configName, const std::string extraOptions, std::string& options); - std::string IncludeOptions; + std::string GetTargetIncludeOptions(cmTarget &target); std::vector<std::string> Configurations; std::string GetConfigName(std::string const& configuration) const; diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 11a0387..c5714cc 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -774,6 +774,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout, { fout << "\t\t\tCharacterSet=\"1\">\n"; } + else if(targetOptions.UsingSBCS()) + { + fout << "\t\t\tCharacterSet=\"0\">\n"; + } else { fout << "\t\t\tCharacterSet=\"2\">\n"; @@ -807,7 +811,7 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout, targetOptions.OutputAdditionalOptions(fout, "\t\t\t\t", "\n"); fout << "\t\t\t\tAdditionalIncludeDirectories=\""; std::vector<std::string> includes; - this->GetIncludeDirectories(includes); + this->GetIncludeDirectories(includes, &target); std::vector<std::string>::iterator i = includes.begin(); for(;i != includes.end(); ++i) { @@ -1307,7 +1311,7 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout, } // Compute which sources need unique object computation. - this->ComputeObjectNameRequirements(sourceGroups); + this->ComputeObjectNameRequirements(classes); // open the project this->WriteProjectStart(fout, libName, target, sourceGroups); @@ -2114,19 +2118,6 @@ std::string cmLocalVisualStudio7Generator return dir; } -void cmLocalVisualStudio7Generator:: -GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs) -{ - std::string dir = this->Makefile->GetCurrentOutputDirectory(); - dir += "/"; - dir += this->GetTargetDirectory(*target); - dir += "/"; - dir += this->GetGlobalGenerator()->GetCMakeCFGInitDirectory(); - dirs.push_back(dir); -} - //---------------------------------------------------------------------------- #include <windows.h> static bool cmLVS6G_IsFAT(const char* dir) diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index 5b634b8..6ddf82a 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -54,9 +54,6 @@ public: void SetBuildType(BuildType,const char *name); void SetPlatformName(const char* n) { this->PlatformName = n;} - void GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs); void SetExtraFlagTable(cmVS7FlagTable const* table) { this->ExtraFlagTable = table; } diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx index de1ac30..f389b35 100644 --- a/Source/cmLocalVisualStudioGenerator.cxx +++ b/Source/cmLocalVisualStudioGenerator.cxx @@ -83,77 +83,50 @@ bool cmLocalVisualStudioGenerator::SourceFileCompiles(const cmSourceFile* sf) } //---------------------------------------------------------------------------- -void cmLocalVisualStudioGenerator::CountObjectNames( - const std::vector<cmSourceGroup>& groups, - std::map<cmStdString, int>& counts) +void +cmLocalVisualStudioGenerator::ComputeObjectNameRequirements( + std::vector<cmSourceFile*> const& sources + ) { - for(unsigned int i = 0; i < groups.size(); ++i) + // Clear the current set of requirements. + this->NeedObjectName.clear(); + + // Count the number of object files with each name. Note that + // windows file names are not case sensitive. + std::map<cmStdString, int> counts; + for(std::vector<cmSourceFile*>::const_iterator s = sources.begin(); + s != sources.end(); ++s) { - cmSourceGroup sg = groups[i]; - std::vector<const cmSourceFile*> const& srcs = sg.GetSourceFiles(); - for(std::vector<const cmSourceFile*>::const_iterator s = srcs.begin(); - s != srcs.end(); ++s) + const cmSourceFile* sf = *s; + if(this->SourceFileCompiles(sf)) { - const cmSourceFile* sf = *s; - if(this->SourceFileCompiles(sf)) - { - std::string objectName = cmSystemTools::LowerCase( - cmSystemTools::GetFilenameWithoutLastExtension( - sf->GetFullPath())); - objectName += ".obj"; - counts[objectName] += 1; - } + std::string objectName = cmSystemTools::LowerCase( + cmSystemTools::GetFilenameWithoutLastExtension( + sf->GetFullPath())); + objectName += ".obj"; + counts[objectName] += 1; } - this->CountObjectNames(sg.GetGroupChildren(), counts); } -} -//---------------------------------------------------------------------------- -void cmLocalVisualStudioGenerator::InsertNeedObjectNames( - const std::vector<cmSourceGroup>& groups, - std::map<cmStdString, int>& count) -{ - for(unsigned int i = 0; i < groups.size(); ++i) + // For all source files producing duplicate names we need unique + // object name computation. + for(std::vector<cmSourceFile*>::const_iterator s = sources.begin(); + s != sources.end(); ++s) { - cmSourceGroup sg = groups[i]; - std::vector<const cmSourceFile*> const& srcs = sg.GetSourceFiles(); - for(std::vector<const cmSourceFile*>::const_iterator s = srcs.begin(); - s != srcs.end(); ++s) + const cmSourceFile* sf = *s; + if(this->SourceFileCompiles(sf)) { - const cmSourceFile* sf = *s; - if(this->SourceFileCompiles(sf)) + std::string objectName = cmSystemTools::LowerCase( + cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath())); + objectName += ".obj"; + if(counts[objectName] > 1) { - std::string objectName = cmSystemTools::LowerCase( - cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath())); - objectName += ".obj"; - if(count[objectName] > 1) - { - this->NeedObjectName.insert(sf); - } + this->NeedObjectName.insert(sf); } } - this->InsertNeedObjectNames(sg.GetGroupChildren(), count); } } - -//---------------------------------------------------------------------------- -void cmLocalVisualStudioGenerator::ComputeObjectNameRequirements -(std::vector<cmSourceGroup> const& sourceGroups) -{ - // Clear the current set of requirements. - this->NeedObjectName.clear(); - - // Count the number of object files with each name. Note that - // windows file names are not case sensitive. - std::map<cmStdString, int> objectNameCounts; - this->CountObjectNames(sourceGroups, objectNameCounts); - - // For all source files producing duplicate names we need unique - // object name computation. - this->InsertNeedObjectNames(sourceGroups, objectNameCounts); -} - //---------------------------------------------------------------------------- const char* cmLocalVisualStudioGenerator::ReportErrorLabel() const { diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h index fcf1f21..e58c757 100644 --- a/Source/cmLocalVisualStudioGenerator.h +++ b/Source/cmLocalVisualStudioGenerator.h @@ -65,12 +65,8 @@ protected: MaybeCreateImplibDir(cmTarget& target, const char* config, bool isFortran); // Safe object file name generation. - void ComputeObjectNameRequirements(std::vector<cmSourceGroup> const&); + void ComputeObjectNameRequirements(std::vector<cmSourceFile*> const&); bool SourceFileCompiles(const cmSourceFile* sf); - void CountObjectNames(const std::vector<cmSourceGroup>& groups, - std::map<cmStdString, int>& count); - void InsertNeedObjectNames(const std::vector<cmSourceGroup>& groups, - std::map<cmStdString, int>& count); std::set<const cmSourceFile*> NeedObjectName; friend class cmVisualStudio10TargetGenerator; diff --git a/Source/cmLocalXCodeGenerator.cxx b/Source/cmLocalXCodeGenerator.cxx index b989870..551ebd3 100644 --- a/Source/cmLocalXCodeGenerator.cxx +++ b/Source/cmLocalXCodeGenerator.cxx @@ -33,16 +33,3 @@ cmLocalXCodeGenerator::GetTargetDirectory(cmTarget const&) const // No per-target directory for this generator (yet). return ""; } - -//---------------------------------------------------------------------------- -void cmLocalXCodeGenerator:: -GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs) -{ - cmGlobalXCodeGenerator* g = - (cmGlobalXCodeGenerator*)this->GetGlobalGenerator(); - g->SetCurrentLocalGenerator(this); - g->GetTargetObjectFileDirectories(target, - dirs); -} diff --git a/Source/cmLocalXCodeGenerator.h b/Source/cmLocalXCodeGenerator.h index 1ab805d..eab228f 100644 --- a/Source/cmLocalXCodeGenerator.h +++ b/Source/cmLocalXCodeGenerator.h @@ -27,9 +27,6 @@ public: cmLocalXCodeGenerator(); virtual ~cmLocalXCodeGenerator(); - void GetTargetObjectFileDirectories(cmTarget* target, - std::vector<std::string>& - dirs); virtual std::string GetTargetDirectory(cmTarget const& target) const; private: diff --git a/Source/cmMakeDepend.cxx b/Source/cmMakeDepend.cxx index 0b4eea5..6055c55 100644 --- a/Source/cmMakeDepend.cxx +++ b/Source/cmMakeDepend.cxx @@ -54,16 +54,33 @@ void cmMakeDepend::SetMakefile(cmMakefile* makefile) this->Makefile->IncludeFileRegularExpression.c_str()); this->ComplainFileRegularExpression.compile( this->Makefile->ComplainFileRegularExpression.c_str()); - - // Now extract any include paths from the makefile flags - const std::vector<std::string>& includes = - this->Makefile->GetIncludeDirectories(); - for(std::vector<std::string>::const_iterator j = includes.begin(); - j != includes.end(); ++j) + + // Now extract any include paths from the targets + std::set<std::string> uniqueIncludes; + std::vector<std::string> orderedAndUniqueIncludes; + cmTargets & targets = this->Makefile->GetTargets(); + for (cmTargets::iterator l = targets.begin(); l != targets.end(); ++l) + { + const std::vector<std::string>& includes = + l->second.GetIncludeDirectories(); + for(std::vector<std::string>::const_iterator j = includes.begin(); + j != includes.end(); ++j) + { + std::string path = *j; + this->Makefile->ExpandVariablesInString(path); + if(uniqueIncludes.insert(path).second) + { + orderedAndUniqueIncludes.push_back(path); + } + } + } + + for(std::vector<std::string>::const_iterator + it = orderedAndUniqueIncludes.begin(); + it != orderedAndUniqueIncludes.end(); + ++it) { - std::string path = *j; - this->Makefile->ExpandVariablesInString(path); - this->AddSearchPath(path.c_str()); + this->AddSearchPath(it->c_str()); } } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index fdf5b31..f90c35c 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -116,7 +116,6 @@ cmMakefile::cmMakefile(const cmMakefile& mf): Internal(new Internals) this->Targets = mf.Targets; this->SourceFiles = mf.SourceFiles; this->Tests = mf.Tests; - this->IncludeDirectories = mf.IncludeDirectories; this->LinkDirectories = mf.LinkDirectories; this->SystemIncludeDirectories = mf.SystemIncludeDirectories; this->ListFiles = mf.ListFiles; @@ -210,9 +209,9 @@ cmMakefile::~cmMakefile() { delete *i; } - for(unsigned int i=0; i < this->UsedCommands.size(); i++) + for(unsigned int i=0; i < this->FinalPassCommands.size(); i++) { - delete this->UsedCommands[i]; + delete this->FinalPassCommands[i]; } std::vector<cmFunctionBlocker*>::iterator pos; for (pos = this->FunctionBlockers.begin(); @@ -278,8 +277,6 @@ void cmMakefile::Print() this->cmHomeDirectory.c_str() << std::endl; std::cout << " this->ProjectName; " << this->ProjectName.c_str() << std::endl; - this->PrintStringVector("this->IncludeDirectories;", - this->IncludeDirectories); this->PrintStringVector("this->LinkDirectories", this->LinkDirectories); #if defined(CMAKE_BUILD_WITH_CMAKE) for( std::vector<cmSourceGroup>::const_iterator i = @@ -421,7 +418,7 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, else if(pcmd->HasFinalPass()) { // use the command - this->UsedCommands.push_back(pcmd.release()); + this->FinalPassCommands.push_back(pcmd.release()); } } else if ( this->GetCMakeInstance()->GetWorkingMode() == cmake::SCRIPT_MODE @@ -813,8 +810,8 @@ void cmMakefile::FinalPass() // give all the commands a chance to do something // after the file has been parsed before generation - for(std::vector<cmCommand*>::iterator i = this->UsedCommands.begin(); - i != this->UsedCommands.end(); ++i) + for(std::vector<cmCommand*>::iterator i = this->FinalPassCommands.begin(); + i != this->FinalPassCommands.end(); ++i) { (*i)->FinalPass(); } @@ -1478,7 +1475,8 @@ void cmMakefile::InitializeFromParent() this->Internal->VarStack.top() = parent->Internal->VarStack.top().Closure(); // copy include paths - this->IncludeDirectories = parent->IncludeDirectories; + this->SetProperty("INCLUDE_DIRECTORIES", + parent->GetProperty("INCLUDE_DIRECTORIES")); this->SystemIncludeDirectories = parent->SystemIncludeDirectories; // define flags @@ -1603,42 +1601,61 @@ void cmMakefile::AddSubDirectory(const char* srcPath, const char *binPath, } } -void cmMakefile::AddIncludeDirectory(const char* inc, bool before) +//---------------------------------------------------------------------------- +void AddStringToProperty(cmProperty *prop, const char* name, const char* s, + bool before) { - // if there is a newline then break it into multiple arguments - if (!inc) + if (!prop) { return; } - // Don't add an include directory that is already present. Yes, - // this linear search results in n^2 behavior, but n won't be - // getting much bigger than 20. We cannot use a set because of - // order dependency of the include path. - std::vector<std::string>::iterator i = - std::find(this->IncludeDirectories.begin(), - this->IncludeDirectories.end(), inc); - if(i == this->IncludeDirectories.end()) + // Don't worry about duplicates at this point. We eliminate them when + // we convert the property to a vector in GetIncludeDirectories. + + if (before) { - if (before) + const char *val = prop->GetValue(); + cmOStringStream oss; + + if(val && *val) { - // WARNING: this *is* expensive (linear time) since it's a vector - this->IncludeDirectories.insert(this->IncludeDirectories.begin(), inc); + oss << s << ";" << val; } else { - this->IncludeDirectories.push_back(inc); + oss << s; } + + std::string newVal = oss.str(); + prop->Set(name, newVal.c_str()); } else { - if(before) - { - // if this before and already in the path then remove it - this->IncludeDirectories.erase(i); - // WARNING: this *is* expensive (linear time) since it's a vector - this->IncludeDirectories.insert(this->IncludeDirectories.begin(), inc); - } + prop->Append(name, s); + } +} + +//---------------------------------------------------------------------------- +void cmMakefile::AddIncludeDirectory(const char* inc, bool before) +{ + if (!inc) + { + return; + } + + // Directory property: + cmProperty *prop = + this->GetProperties().GetOrCreateProperty("INCLUDE_DIRECTORIES"); + AddStringToProperty(prop, "INCLUDE_DIRECTORIES", inc, before); + + // Property on each target: + for (cmTargets::iterator l = this->Targets.begin(); + l != this->Targets.end(); ++l) + { + cmTarget &t = l->second; + prop = t.GetProperties().GetOrCreateProperty("INCLUDE_DIRECTORIES"); + AddStringToProperty(prop, "INCLUDE_DIRECTORIES", inc, before); } } @@ -2093,17 +2110,37 @@ void cmMakefile::AddExtraDirectory(const char* dir) } -// expance CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR in the +// expand CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR in the // include and library directories. void cmMakefile::ExpandVariables() { // Now expand variables in the include and link strings - for(std::vector<std::string>::iterator d = this->IncludeDirectories.begin(); - d != this->IncludeDirectories.end(); ++d) + + // May not be necessary anymore... But may need a policy for strict + // backwards compatibility + const char *includeDirs = this->GetProperty("INCLUDE_DIRECTORIES"); + if (includeDirs) { - this->ExpandVariablesInString(*d, true, true); + std::string dirs = includeDirs; + this->ExpandVariablesInString(dirs, true, true); + this->SetProperty("INCLUDE_DIRECTORIES", dirs.c_str()); + } + + // Also for each target's INCLUDE_DIRECTORIES property: + for (cmTargets::iterator l = this->Targets.begin(); + l != this->Targets.end(); ++l) + { + cmTarget &t = l->second; + includeDirs = t.GetProperty("INCLUDE_DIRECTORIES"); + if (includeDirs) + { + std::string dirs = includeDirs; + this->ExpandVariablesInString(dirs, true, true); + t.SetProperty("INCLUDE_DIRECTORIES", dirs.c_str()); + } } + for(std::vector<std::string>::iterator d = this->LinkDirectories.begin(); d != this->LinkDirectories.end(); ++d) { @@ -3317,16 +3354,6 @@ void cmMakefile::SetProperty(const char* prop, const char* value) // handle special props std::string propname = prop; - if ( propname == "INCLUDE_DIRECTORIES" ) - { - std::vector<std::string> varArgsExpanded; - if(value) - { - cmSystemTools::ExpandListArgument(value, varArgsExpanded); - } - this->SetIncludeDirectories(varArgsExpanded); - return; - } if ( propname == "LINK_DIRECTORIES" ) { @@ -3368,17 +3395,6 @@ void cmMakefile::AppendProperty(const char* prop, const char* value, // handle special props std::string propname = prop; - if ( propname == "INCLUDE_DIRECTORIES" ) - { - std::vector<std::string> varArgsExpanded; - cmSystemTools::ExpandListArgument(value, varArgsExpanded); - for(std::vector<std::string>::const_iterator vi = varArgsExpanded.begin(); - vi != varArgsExpanded.end(); ++vi) - { - this->AddIncludeDirectory(vi->c_str()); - } - return; - } if ( propname == "LINK_DIRECTORIES" ) { @@ -3474,23 +3490,6 @@ const char *cmMakefile::GetProperty(const char* prop, output += this->DefineFlagsOrig; return output.c_str(); } - else if (!strcmp("INCLUDE_DIRECTORIES",prop) ) - { - cmOStringStream str; - for (std::vector<std::string>::const_iterator - it = this->GetIncludeDirectories().begin(); - it != this->GetIncludeDirectories().end(); - ++ it ) - { - if ( it != this->GetIncludeDirectories().begin()) - { - str << ";"; - } - str << it->c_str(); - } - output = str.str(); - return output.c_str(); - } else if (!strcmp("LINK_DIRECTORIES",prop)) { cmOStringStream str; @@ -3861,9 +3860,22 @@ void cmMakefile::DefineProperties(cmake *cm) cm->DefineProperty ("INCLUDE_DIRECTORIES", cmProperty::DIRECTORY, "List of preprocessor include file search directories.", - "This read-only property specifies the list of directories given " - "so far to the include_directories command. " - "It is intended for debugging purposes.", false); + "This property specifies the list of directories given " + "so far to the include_directories command. " + "This property exists on directories and targets. " + "In addition to accepting values from the include_directories " + "command, values may be set directly on any directory or any " + "target using the set_property command. " + "A target gets its initial value for this property from the value " + "of the directory property. " + "A directory gets its initial value from its parent directory if " + "it has one. " + "Both directory and target property values are adjusted by calls " + "to the include_directories command." + "\n" + "The target property values are used by the generators to set " + "the include paths for the compiler. " + "See also the include_directories command."); cm->DefineProperty ("LINK_DIRECTORIES", cmProperty::DIRECTORY, diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 1c46a73..960ba39 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -522,22 +522,6 @@ public: cmTarget* FindTargetToUse(const char* name); /** - * Get a list of include directories in the build. - */ - std::vector<std::string>& GetIncludeDirectories() - { - return this->IncludeDirectories; - } - const std::vector<std::string>& GetIncludeDirectories() const - { - return this->IncludeDirectories; - } - void SetIncludeDirectories(const std::vector<std::string>& vec) - { - this->IncludeDirectories = vec; - } - - /** * Mark include directories as system directories. */ void AddSystemIncludeDirectory(const char* dir); @@ -620,12 +604,6 @@ public: */ bool CanIWriteThisFile(const char* fileName); - /** - * Get the vector of used command instances. - */ - const std::vector<cmCommand*>& GetUsedCommands() const - {return this->UsedCommands;} - #if defined(CMAKE_BUILD_WITH_CMAKE) /** * Get the vector source groups. @@ -880,9 +858,7 @@ protected: // Tests std::map<cmStdString, cmTest*> Tests; - // The include and link-library paths. These may have order - // dependency, so they must be vectors (not set). - std::vector<std::string> IncludeDirectories; + // The link-library paths. Order matters, use std::vector (not std::set). std::vector<std::string> LinkDirectories; // The set of include directories that are marked as system include @@ -913,7 +889,7 @@ protected: std::vector<cmSourceGroup> SourceGroups; #endif - std::vector<cmCommand*> UsedCommands; + std::vector<cmCommand*> FinalPassCommands; cmLocalGenerator* LocalGenerator; bool IsFunctionBlocked(const cmListFileFunction& lff, cmExecutionStatus &status); diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index a3a832b..b9120c4 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -489,16 +489,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(cmSourceFile& source) srcFullPath.c_str()); // add this to the list of objects for this local generator - if(cmSystemTools::FileIsFullPath(objNoTargetDir.c_str())) - { - objNoTargetDir = cmSystemTools::GetFilenameName(objNoTargetDir); - } - cmLocalUnixMakefileGenerator3::LocalObjectInfo& info = - this->LocalGenerator->LocalObjectFiles[objNoTargetDir]; - info.HasSourceExtension = hasSourceExtension; - info.push_back( - cmLocalUnixMakefileGenerator3::LocalObjectEntry(this->Target, lang) - ); + this->LocalGenerator->AddLocalObjectFile( + this->Target, &source, objNoTargetDir, hasSourceExtension); } //---------------------------------------------------------------------------- @@ -1069,6 +1061,35 @@ void cmMakefileTargetGenerator::WriteTargetDependRules() << "SET(CMAKE_Fortran_TARGET_MODULE_DIR \"" << mdir << "\")\n"; } + // Target-specific include directories: + *this->InfoFileStream + << "\n" + << "# The include file search paths:\n"; + *this->InfoFileStream + << "SET(CMAKE_C_TARGET_INCLUDE_PATH\n"; + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->Target); + for(std::vector<std::string>::iterator i = includes.begin(); + i != includes.end(); ++i) + { + *this->InfoFileStream + << " \"" + << this->LocalGenerator->Convert(i->c_str(), + cmLocalGenerator::HOME_OUTPUT) + << "\"\n"; + } + *this->InfoFileStream + << " )\n"; + *this->InfoFileStream + << "SET(CMAKE_CXX_TARGET_INCLUDE_PATH " + << "${CMAKE_C_TARGET_INCLUDE_PATH})\n"; + *this->InfoFileStream + << "SET(CMAKE_Fortran_TARGET_INCLUDE_PATH " + << "${CMAKE_C_TARGET_INCLUDE_PATH})\n"; + *this->InfoFileStream + << "SET(CMAKE_ASM_TARGET_INCLUDE_PATH " + << "${CMAKE_C_TARGET_INCLUDE_PATH})\n"; + // and now write the rule to use it std::vector<std::string> depends; std::vector<std::string> commands; @@ -1534,7 +1555,7 @@ std::string cmMakefileTargetGenerator::GetFrameworkFlags() emitted.insert("/System/Library/Frameworks"); #endif std::vector<std::string> includes; - this->LocalGenerator->GetIncludeDirectories(includes); + this->LocalGenerator->GetIncludeDirectories(includes, this->Target); std::vector<std::string>::iterator i; // check all include directories for frameworks as this // will already have added a -F for the framework @@ -1829,8 +1850,12 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, responseVar += "_USE_RESPONSE_FILE_FOR_INCLUDES"; bool useResponseFile = this->Makefile->IsOn(responseVar.c_str()); + + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->Target, lang); + std::string includeFlags = - this->LocalGenerator->GetIncludeFlags(lang, useResponseFile); + this->LocalGenerator->GetIncludeFlags(includes, lang, useResponseFile); if(includeFlags.empty()) { return; @@ -1930,7 +1955,7 @@ void cmMakefileTargetGenerator::AddFortranFlags(std::string& flags) this->Makefile->GetDefinition("CMAKE_Fortran_MODPATH_FLAG")) { std::vector<std::string> includes; - this->LocalGenerator->GetIncludeDirectories(includes); + this->LocalGenerator->GetIncludeDirectories(includes, this->Target); for(std::vector<std::string>::const_iterator idi = includes.begin(); idi != includes.end(); ++idi) { diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx new file mode 100644 index 0000000..9242181 --- /dev/null +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -0,0 +1,469 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmNinjaNormalTargetGenerator.h" +#include "cmLocalNinjaGenerator.h" +#include "cmGlobalNinjaGenerator.h" +#include "cmSourceFile.h" +#include "cmGeneratedFileStream.h" +#include "cmMakefile.h" + +#include <assert.h> + +cmNinjaNormalTargetGenerator:: +cmNinjaNormalTargetGenerator(cmTarget* target) + : cmNinjaTargetGenerator(target) + , TargetNameOut() + , TargetNameSO() + , TargetNameReal() + , TargetNameImport() + , TargetNamePDB() +{ + this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName()); + if (target->GetType() == cmTarget::EXECUTABLE) + target->GetExecutableNames(this->TargetNameOut, + this->TargetNameReal, + this->TargetNameImport, + this->TargetNamePDB, + GetLocalGenerator()->GetConfigName()); + else + target->GetLibraryNames(this->TargetNameOut, + this->TargetNameSO, + this->TargetNameReal, + this->TargetNameImport, + this->TargetNamePDB, + GetLocalGenerator()->GetConfigName()); + + // on Windows the output dir is already needed at compile time + // ensure the directory exists (OutDir test) + std::string outpath = target->GetDirectory(this->GetConfigName()); + cmSystemTools::MakeDirectory(outpath.c_str()); +} + +cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() +{ +} + +void cmNinjaNormalTargetGenerator::Generate() +{ + if (!this->TargetLinkLanguage) { + cmSystemTools::Error("CMake can not determine linker language for target:", + this->GetTarget()->GetName()); + return; + } + + // Write the rules for each language. + this->WriteLanguagesRules(); + + // Write the build statements + this->WriteObjectBuildStatements(); + + this->WriteLinkRule(); + this->WriteLinkStatement(); + + this->GetBuildFileStream() << "\n"; + this->GetRulesFileStream() << "\n"; +} + +void cmNinjaNormalTargetGenerator::WriteLanguagesRules() +{ + cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream()); + this->GetRulesFileStream() + << "# Rules for each languages for " + << cmTarget::GetTargetTypeName(this->GetTarget()->GetType()) + << " target " + << this->GetTargetName() + << "\n\n"; + + std::set<cmStdString> languages; + this->GetTarget()->GetLanguages(languages); + for(std::set<cmStdString>::const_iterator l = languages.begin(); + l != languages.end(); + ++l) + this->WriteLanguageRules(*l); +} + +const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const +{ + switch (this->GetTarget()->GetType()) { + case cmTarget::STATIC_LIBRARY: + return "static library"; + case cmTarget::SHARED_LIBRARY: + return "shared library"; + case cmTarget::MODULE_LIBRARY: + return "shared module"; + case cmTarget::EXECUTABLE: + return "executable"; + default: + return 0; + } +} + +std::string +cmNinjaNormalTargetGenerator +::LanguageLinkerRule() const +{ + return std::string(this->TargetLinkLanguage) + + "_" + + cmTarget::GetTargetTypeName(this->GetTarget()->GetType()) + + "_LINKER"; +} + +void +cmNinjaNormalTargetGenerator +::WriteLinkRule() +{ + cmTarget::TargetType targetType = this->GetTarget()->GetType(); + std::string ruleName = this->LanguageLinkerRule(); + + 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 = cmake::GetCMakeFilesDirectoryPostSlash(); + objdir += this->GetTargetName(); + objdir += ".dir"; + objdir = this->GetLocalGenerator()->Convert(objdir.c_str(), + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + vars.ObjectDir = objdir.c_str(); + vars.Target = "$out"; + vars.TargetSOName = "$SONAME"; + vars.TargetInstallNameDir = "$INSTALLNAME_DIR"; + vars.TargetPDB = "$TARGET_PDB"; + + // Setup the target version. + std::string targetVersionMajor; + std::string targetVersionMinor; + { + cmOStringStream majorStream; + cmOStringStream minorStream; + int major; + int minor; + this->GetTarget()->GetTargetVersion(major, minor); + majorStream << major; + minorStream << minor; + targetVersionMajor = majorStream.str(); + targetVersionMinor = minorStream.str(); + } + vars.TargetVersionMajor = targetVersionMajor.c_str(); + vars.TargetVersionMinor = targetVersionMinor.c_str(); + + vars.LinkLibraries = "$LINK_LIBRARIES"; + vars.Flags = "$FLAGS"; + vars.LinkFlags = "$LINK_FLAGS"; + + std::string langFlags; + this->GetLocalGenerator()->AddLanguageFlags(langFlags, + this->TargetLinkLanguage, + this->GetConfigName()); + if (targetType != cmTarget::EXECUTABLE) + langFlags += " $ARCH_FLAGS"; + vars.LanguageCompileFlags = langFlags.c_str(); + + // Rule for linking library. + std::vector<std::string> linkCmds = this->ComputeLinkCmd(); + for(std::vector<std::string>::iterator i = linkCmds.begin(); + i != linkCmds.end(); + ++i) + { + this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); + } + linkCmds.insert(linkCmds.begin(), "$PRE_LINK"); + linkCmds.push_back("$POST_BUILD"); + std::string linkCmd = + this->GetLocalGenerator()->BuildCommandLine(linkCmds); + + // Write the linker rule. + std::ostringstream comment; + comment << "Rule for linking " << this->TargetLinkLanguage << " " + << this->GetVisibleTypeName() << "."; + std::ostringstream description; + description << "Linking " << this->TargetLinkLanguage << " " + << this->GetVisibleTypeName() << " $out"; + this->GetGlobalGenerator()->AddRule(ruleName, + linkCmd, + description.str(), + comment.str()); + } + + if (this->TargetNameOut != this->TargetNameReal) { + std::string cmakeCommand = + this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"), + cmLocalGenerator::SHELL); + if (targetType == cmTarget::EXECUTABLE) + this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE", + cmakeCommand + + " -E cmake_symlink_executable" + " $in $out && $POST_BUILD", + "Creating executable symlink $out", + "Rule for creating executable symlink."); + else + this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY", + cmakeCommand + + " -E cmake_symlink_library" + " $in $SONAME $out && $POST_BUILD", + "Creating library symlink $out", + "Rule for creating library symlink."); + } +} + +std::vector<std::string> +cmNinjaNormalTargetGenerator +::ComputeLinkCmd() +{ + std::vector<std::string> linkCmds; + cmTarget::TargetType targetType = this->GetTarget()->GetType(); + switch (targetType) { + case cmTarget::STATIC_LIBRARY: { + // Check if you have a non archive way to create the static library. + { + std::string linkCmdVar = "CMAKE_"; + linkCmdVar += this->TargetLinkLanguage; + linkCmdVar += "_CREATE_STATIC_LIBRARY"; + if (const char *linkCmd = + this->GetMakefile()->GetDefinition(linkCmdVar.c_str())) + { + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); + return linkCmds; + } + } + + // We have archive link commands set. First, delete the existing archive. + std::string cmakeCommand = + this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"), + cmLocalGenerator::SHELL); + linkCmds.push_back(cmakeCommand + " -E remove $out"); + + // TODO: Use ARCHIVE_APPEND for archives over a certain size. + { + std::string linkCmdVar = "CMAKE_"; + linkCmdVar += this->TargetLinkLanguage; + linkCmdVar += "_ARCHIVE_CREATE"; + const char *linkCmd = + this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); + } + { + std::string linkCmdVar = "CMAKE_"; + linkCmdVar += this->TargetLinkLanguage; + linkCmdVar += "_ARCHIVE_FINISH"; + const char *linkCmd = + this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); + } + return linkCmds; + } + case cmTarget::SHARED_LIBRARY: + case cmTarget::MODULE_LIBRARY: + case cmTarget::EXECUTABLE: { + std::string linkCmdVar = "CMAKE_"; + linkCmdVar += this->TargetLinkLanguage; + switch (targetType) { + case cmTarget::SHARED_LIBRARY: + linkCmdVar += "_CREATE_SHARED_LIBRARY"; + break; + case cmTarget::MODULE_LIBRARY: + linkCmdVar += "_CREATE_SHARED_MODULE"; + break; + case cmTarget::EXECUTABLE: + linkCmdVar += "_LINK_EXECUTABLE"; + break; + default: + assert(0 && "Unexpected target type"); + } + + const char *linkCmd = + this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); + return linkCmds; + } + default: + assert(0 && "Unexpected target type"); + } + return std::vector<std::string>(); +} + +void cmNinjaNormalTargetGenerator::WriteLinkStatement() +{ + cmTarget::TargetType targetType = this->GetTarget()->GetType(); + + // Write comments. + cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream()); + this->GetBuildFileStream() + << "# Link build statements for " + << cmTarget::GetTargetTypeName(targetType) + << " target " + << this->GetTargetName() + << "\n\n"; + + cmNinjaDeps emptyDeps; + cmNinjaVars vars; + + std::string targetOutput = ConvertToNinjaPath( + this->GetTarget()->GetFullPath(this->GetConfigName()).c_str()); + std::string targetOutputReal = ConvertToNinjaPath( + this->GetTarget()->GetFullPath(this->GetConfigName(), + /*implib=*/false, + /*realpath=*/true).c_str()); + std::string targetOutputImplib = ConvertToNinjaPath( + this->GetTarget()->GetFullPath(this->GetConfigName(), + /*implib=*/true).c_str()); + + // Compute the comment. + std::ostringstream comment; + comment << "Link the " << this->GetVisibleTypeName() << " " + << targetOutputReal; + + // Compute outputs. + cmNinjaDeps outputs; + outputs.push_back(targetOutputReal); + + // Compute specific libraries to link with. + cmNinjaDeps explicitDeps = this->GetObjects(), + implicitDeps = this->ComputeLinkDeps(); + + this->GetLocalGenerator()->GetTargetFlags(vars["LINK_LIBRARIES"], + vars["FLAGS"], + vars["LINK_FLAGS"], + *this->GetTarget()); + + this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]); + + // 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 + ? vars["FLAGS"] + : vars["ARCH_FLAGS"], + this->GetTarget(), + this->TargetLinkLanguage, + this->GetConfigName()); + vars["SONAME"] = this->TargetNameSO; + + if (targetType == cmTarget::SHARED_LIBRARY) { + std::string install_name_dir = + this->GetTarget()->GetInstallNameDirForBuildTree(this->GetConfigName()); + + if (!install_name_dir.empty()) { + vars["INSTALLNAME_DIR"] = + this->GetLocalGenerator()->Convert(install_name_dir.c_str(), + cmLocalGenerator::NONE, + cmLocalGenerator::SHELL, false); + } + } + + if (!this->TargetNameImport.empty()) { + vars["TARGET_IMPLIB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + targetOutputImplib.c_str(), cmLocalGenerator::SHELL); + } + + vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL); + + std::vector<cmCustomCommand> *cmdLists[3] = { + &this->GetTarget()->GetPreBuildCommands(), + &this->GetTarget()->GetPreLinkCommands(), + &this->GetTarget()->GetPostBuildCommands() + }; + + std::vector<std::string> preLinkCmdLines, postBuildCmdLines; + std::vector<std::string> *cmdLineLists[3] = { + &preLinkCmdLines, + &preLinkCmdLines, + &postBuildCmdLines + }; + + for (unsigned i = 0; i != 3; ++i) { + for (std::vector<cmCustomCommand>::const_iterator + ci = cmdLists[i]->begin(); + ci != cmdLists[i]->end(); ++ci) { + this->GetLocalGenerator()->AppendCustomCommandLines(&*ci, + *cmdLineLists[i]); + } + } + + // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for + // the link commands. + if (!preLinkCmdLines.empty()) { + std::string path = this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetMakefile()->GetHomeOutputDirectory(), + cmLocalGenerator::SHELL); + preLinkCmdLines.push_back("cd " + path); + } + + vars["PRE_LINK"] = + this->GetLocalGenerator()->BuildCommandLine(preLinkCmdLines); + std::string postBuildCmdLine = + this->GetLocalGenerator()->BuildCommandLine(postBuildCmdLines); + + cmNinjaVars symlinkVars; + if (targetOutput == targetOutputReal) { + vars["POST_BUILD"] = postBuildCmdLine; + } else { + vars["POST_BUILD"] = ":"; + symlinkVars["POST_BUILD"] = postBuildCmdLine; + } + + // Write the build statement for this target. + cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), + comment.str(), + this->LanguageLinkerRule(), + outputs, + explicitDeps, + implicitDeps, + emptyDeps, + vars); + + if (targetOutput != targetOutputReal) { + if (targetType == cmTarget::EXECUTABLE) { + cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), + "Create executable symlink " + targetOutput, + "CMAKE_SYMLINK_EXECUTABLE", + cmNinjaDeps(1, targetOutput), + cmNinjaDeps(1, targetOutputReal), + emptyDeps, + emptyDeps, + symlinkVars); + } else { + symlinkVars["SONAME"] = this->GetTargetFilePath(this->TargetNameSO); + cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), + "Create library symlink " + targetOutput, + "CMAKE_SYMLINK_LIBRARY", + cmNinjaDeps(1, targetOutput), + cmNinjaDeps(1, targetOutputReal), + emptyDeps, + emptyDeps, + symlinkVars); + } + } + + if (!this->TargetNameImport.empty()) { + // Since using multiple outputs would mess up the $out variable, use an + // alias for the import library. + cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), + "Alias for import library.", + cmNinjaDeps(1, targetOutputImplib), + cmNinjaDeps(1, targetOutputReal)); + } + + // Add aliases for the file name and the target name. + this->GetGlobalGenerator()->AddTargetAlias(this->TargetNameOut, + this->GetTarget()); + this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), + this->GetTarget()); +} diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h new file mode 100644 index 0000000..99f5a13 --- /dev/null +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -0,0 +1,47 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmNinjaNormalTargetGenerator_h +# define cmNinjaNormalTargetGenerator_h + +# include "cmNinjaTargetGenerator.h" +# include "cmNinjaTypes.h" + +class cmSourceFile; + +class cmNinjaNormalTargetGenerator : public cmNinjaTargetGenerator +{ +public: + cmNinjaNormalTargetGenerator(cmTarget* target); + ~cmNinjaNormalTargetGenerator(); + + void Generate(); + +private: + std::string LanguageLinkerRule() const; + const char* GetVisibleTypeName() const; + void WriteLanguagesRules(); + void WriteLinkRule(); + void WriteLinkStatement(); + std::vector<std::string> ComputeLinkCmd(); + +private: + // Target name info. + std::string TargetNameOut; + std::string TargetNameSO; + std::string TargetNameReal; + std::string TargetNameImport; + std::string TargetNamePDB; + const char *TargetLinkLanguage; +}; + +#endif // ! cmNinjaNormalTargetGenerator_h diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx new file mode 100644 index 0000000..9acbc67 --- /dev/null +++ b/Source/cmNinjaTargetGenerator.cxx @@ -0,0 +1,515 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmNinjaTargetGenerator.h" +#include "cmGlobalNinjaGenerator.h" +#include "cmLocalNinjaGenerator.h" +#include "cmGeneratedFileStream.h" +#include "cmNinjaNormalTargetGenerator.h" +#include "cmNinjaUtilityTargetGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmComputeLinkInformation.h" +#include "cmSourceFile.h" +#include "cmCustomCommandGenerator.h" + +#include <algorithm> + +cmNinjaTargetGenerator * +cmNinjaTargetGenerator::New(cmTarget* target) +{ + switch (target->GetType()) + { + case cmTarget::EXECUTABLE: + case cmTarget::SHARED_LIBRARY: + case cmTarget::STATIC_LIBRARY: + case cmTarget::MODULE_LIBRARY: + return new cmNinjaNormalTargetGenerator(target); + + case cmTarget::UTILITY: + return new cmNinjaUtilityTargetGenerator(target);; + + case cmTarget::GLOBAL_TARGET: { + // We only want to process global targets that live in the home + // (i.e. top-level) directory. CMake creates copies of these targets + // in every directory, which we don't need. + cmMakefile *mf = target->GetMakefile(); + if (strcmp(mf->GetStartDirectory(), mf->GetHomeDirectory()) == 0) + return new cmNinjaUtilityTargetGenerator(target); + // else fallthrough + } + + default: + return 0; + } +} + +cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmTarget* target) + : Target(target), + Makefile(target->GetMakefile()), + LocalGenerator( + static_cast<cmLocalNinjaGenerator*>(Makefile->GetLocalGenerator())), + Objects() +{ +} + +cmNinjaTargetGenerator::~cmNinjaTargetGenerator() +{ +} + +cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const +{ + return *this->GetGlobalGenerator()->GetBuildFileStream(); +} + +cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const +{ + return *this->GetGlobalGenerator()->GetRulesFileStream(); +} + +cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const +{ + return this->LocalGenerator->GetGlobalNinjaGenerator(); +} + +const char* cmNinjaTargetGenerator::GetConfigName() const +{ + return this->LocalGenerator->ConfigName.c_str(); +} + +// TODO: Picked up from cmMakefileTargetGenerator. Refactor it. +const char* cmNinjaTargetGenerator::GetFeature(const char* feature) +{ + return this->Target->GetFeature(feature, this->GetConfigName()); +} + +// TODO: Picked up from cmMakefileTargetGenerator. Refactor it. +bool cmNinjaTargetGenerator::GetFeatureAsBool(const char* feature) +{ + return cmSystemTools::IsOn(this->GetFeature(feature)); +} + +// TODO: Picked up from cmMakefileTargetGenerator. Refactor it. +void cmNinjaTargetGenerator::AddFeatureFlags(std::string& flags, + const char* lang) +{ + // Add language-specific flags. + this->LocalGenerator->AddLanguageFlags(flags, lang, this->GetConfigName()); + + if(this->GetFeatureAsBool("INTERPROCEDURAL_OPTIMIZATION")) + { + this->LocalGenerator->AppendFeatureOptions(flags, lang, "IPO"); + } +} + +// TODO: Most of the code is picked up from +// void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink), +// void cmMakefileTargetGenerator::WriteTargetLanguageFlags() +// Refactor it. +std::string +cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source, + const std::string& language) +{ + std::string flags; + + this->AddFeatureFlags(flags, language.c_str()); + + this->GetLocalGenerator()->AddArchitectureFlags(flags, + this->GetTarget(), + language.c_str(), + this->GetConfigName()); + + // TODO: Fortran support. + // // Fortran-specific flags computed for this target. + // if(*l == "Fortran") + // { + // this->AddFortranFlags(flags); + // } + + // 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); + } + + // TODO: Handle response file. + // Add include directory flags. + { + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->Target, + language.c_str()); + std::string includeFlags = + this->LocalGenerator->GetIncludeFlags(includes, language.c_str(), false); + this->LocalGenerator->AppendFlags(flags, includeFlags.c_str()); + } + + // Append old-style preprocessor definition flags. + this->LocalGenerator->AppendFlags(flags, this->Makefile->GetDefineFlags()); + + // Add target-specific and source-specific flags. + this->LocalGenerator->AppendFlags(flags, + this->Target->GetProperty("COMPILE_FLAGS")); + this->LocalGenerator->AppendFlags(flags, + source->GetProperty("COMPILE_FLAGS")); + + // TODO: Handle Apple frameworks. + + return flags; +} + +// TODO: Refactor with +// void cmMakefileTargetGenerator::WriteTargetLanguageFlags(). +std::string +cmNinjaTargetGenerator:: +ComputeDefines(cmSourceFile *source, const std::string& language) +{ + std::string defines; + + // Add the export symbol definition for shared library objects. + if(const char* exportMacro = this->Target->GetExportMacro()) + { + this->LocalGenerator->AppendDefines(defines, exportMacro, + language.c_str()); + } + + // Add preprocessor definitions for this target and configuration. + this->LocalGenerator->AppendDefines + (defines, + this->Makefile->GetProperty("COMPILE_DEFINITIONS"), + language.c_str()); + this->LocalGenerator->AppendDefines + (defines, + this->Target->GetProperty("COMPILE_DEFINITIONS"), + language.c_str()); + this->LocalGenerator->AppendDefines + (defines, + source->GetProperty("COMPILE_DEFINITIONS"), + language.c_str()); + { + std::string defPropName = "COMPILE_DEFINITIONS_"; + defPropName += cmSystemTools::UpperCase(this->GetConfigName()); + this->LocalGenerator->AppendDefines + (defines, + this->Makefile->GetProperty(defPropName.c_str()), + language.c_str()); + this->LocalGenerator->AppendDefines + (defines, + this->Target->GetProperty(defPropName.c_str()), + language.c_str()); + this->LocalGenerator->AppendDefines + (defines, + source->GetProperty(defPropName.c_str()), + language.c_str()); + } + + return defines; +} + +cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const +{ + // Static libraries never depend on other targets for linking. + if (this->Target->GetType() == cmTarget::STATIC_LIBRARY) + return cmNinjaDeps(); + + cmComputeLinkInformation* cli = + this->Target->GetLinkInformation(this->GetConfigName()); + if(!cli) + return cmNinjaDeps(); + + const std::vector<std::string> &deps = cli->GetDepends(); + cmNinjaDeps result(deps.size()); + std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath()); + + // Add a dependency on the link definitions file, if any. + if(!this->ModuleDefinitionFile.empty()) + { + result.push_back(this->ModuleDefinitionFile); + } + + return result; +} + +std::string +cmNinjaTargetGenerator +::GetSourceFilePath(cmSourceFile* source) const +{ + return ConvertToNinjaPath(source->GetFullPath().c_str()); +} + +std::string +cmNinjaTargetGenerator +::GetObjectFilePath(cmSourceFile* source) const +{ + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if(!path.empty()) + path += "/"; + path += this->LocalGenerator->GetObjectFileName(*this->Target, *source); + return path; +} + +std::string cmNinjaTargetGenerator::GetTargetOutputDir() const +{ + std::string dir = this->Target->GetDirectory(this->GetConfigName()); + return ConvertToNinjaPath(dir.c_str()); +} + +std::string +cmNinjaTargetGenerator +::GetTargetFilePath(const std::string& name) const +{ + std::string path = this->GetTargetOutputDir(); + if (path.empty() || path == ".") + return name; + path += "/"; + path += name; + return path; +} + +std::string cmNinjaTargetGenerator::GetTargetName() const +{ + return this->Target->GetName(); +} + +std::string cmNinjaTargetGenerator::GetTargetPDB() const +{ + std::string targetFullPathPDB; + if(this->Target->GetType() == cmTarget::EXECUTABLE || + this->Target->GetType() == cmTarget::STATIC_LIBRARY || + this->Target->GetType() == cmTarget::SHARED_LIBRARY || + this->Target->GetType() == cmTarget::MODULE_LIBRARY) + { + targetFullPathPDB = this->Target->GetDirectory(this->GetConfigName()); + targetFullPathPDB += "/"; + targetFullPathPDB += this->Target->GetPDBName(this->GetConfigName()); + } + + return ConvertToNinjaPath(targetFullPathPDB.c_str()); +} + + +void +cmNinjaTargetGenerator +::WriteLanguageRules(const std::string& language) +{ + this->GetRulesFileStream() + << "# Rules for language " << language << "\n\n"; + this->WriteCompileRule(language); + this->GetRulesFileStream() << "\n"; +} + +void +cmNinjaTargetGenerator +::WriteCompileRule(const std::string& language) +{ + cmLocalGenerator::RuleVariables vars; + vars.RuleLauncher = "RULE_LAUNCH_COMPILE"; + vars.CMTarget = this->GetTarget(); + std::string lang = language; + vars.Language = lang.c_str(); + vars.Source = "$in"; + vars.Object = "$out"; + std::string flags = "$FLAGS"; + vars.Defines = "$DEFINES"; + vars.TargetPDB = "$TARGET_PDB"; + + 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()); + flags += " " + depfileFlagsStr; + } + vars.Flags = flags.c_str(); + + + // Rule for compiling object file. + std::string compileCmdVar = "CMAKE_"; + compileCmdVar += language; + compileCmdVar += "_COMPILE_OBJECT"; + std::string compileCmd = + this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str()); + std::vector<std::string> compileCmds; + cmSystemTools::ExpandListArgument(compileCmd, compileCmds); + + for (std::vector<std::string>::iterator i = compileCmds.begin(); + i != compileCmds.end(); ++i) + this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); + + std::string cmdLine = + this->GetLocalGenerator()->BuildCommandLine(compileCmds); + + // Write the rule for compiling file of the given language. + std::ostringstream comment; + comment << "Rule for compiling " << language << " files."; + std::ostringstream description; + description << "Building " << language << " object $out"; + this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language), + cmdLine, + description.str(), + comment.str(), + depfile); +} + +void +cmNinjaTargetGenerator +::WriteObjectBuildStatements() +{ + // Write comments. + cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream()); + this->GetBuildFileStream() + << "# Object build statements for " + << cmTarget::GetTargetTypeName(this->GetTarget()->GetType()) + << " target " + << this->GetTargetName() + << "\n\n"; + + // For each source files of this target. + for(std::vector<cmSourceFile*>::const_iterator i = + this->GetTarget()->GetSourceFiles().begin(); + i != this->GetTarget()->GetSourceFiles().end(); + ++i) + this->WriteObjectBuildStatement(*i); + + this->GetBuildFileStream() << "\n"; +} + +void +cmNinjaTargetGenerator +::WriteObjectBuildStatement(cmSourceFile* source) +{ + if (cmCustomCommand *cc = source->GetCustomCommand()) + this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget()); + + cmNinjaDeps emptyDeps; + + std::string comment; + const char* language = source->GetLanguage(); + // If we cannot get the language this is probably a non-source file provided + // in the list (typically an header file). + if (!language) { + if (source->GetPropertyAsBool("EXTERNAL_OBJECT")) + this->Objects.push_back(this->GetSourceFilePath(source)); + if(cmSystemTools::UpperCase(source->GetExtension()) == "DEF") + this->ModuleDefinitionFile = GetSourceFilePath(source); + return; + } + + if (source->GetPropertyAsBool("HEADER_FILE_ONLY")) + return; + + std::string rule = this->LanguageCompilerRule(language); + + cmNinjaDeps outputs; + std::string objectFileName = this->GetObjectFilePath(source); + outputs.push_back(objectFileName); + // Add this object to the list of object files. + this->Objects.push_back(objectFileName); + + cmNinjaDeps explicitDeps; + std::string sourceFileName = this->GetSourceFilePath(source); + explicitDeps.push_back(sourceFileName); + + // Ensure that the target dependencies are built before any source file in + // the target, using order-only dependencies. + cmNinjaDeps orderOnlyDeps; + this->GetLocalGenerator()->AppendTargetDepends(this->Target, orderOnlyDeps); + + if(const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) { + std::vector<std::string> depList; + cmSystemTools::ExpandListArgument(objectDeps, depList); + std::transform(depList.begin(), depList.end(), + std::back_inserter(orderOnlyDeps), MapToNinjaPath()); + } + + // Add order-only dependency on any header file with a custom command. + { + const std::vector<cmSourceFile*>& sources = + this->GetTarget()->GetSourceFiles(); + for(std::vector<cmSourceFile*>::const_iterator si = sources.begin(); + si != sources.end(); ++si) { + if (!(*si)->GetLanguage()) { + if (cmCustomCommand* cc = (*si)->GetCustomCommand()) { + const std::vector<std::string>& ccoutputs = cc->GetOutputs(); + std::transform(ccoutputs.begin(), ccoutputs.end(), + std::back_inserter(orderOnlyDeps), MapToNinjaPath()); + } + } + } + } + + // If the source file is GENERATED and does not have a custom command + // (either attached to this source file or another one), assume that one of + // the target dependencies, OBJECT_DEPENDS or header file custom commands + // will rebuild the file. + if (source->GetPropertyAsBool("GENERATED") && !source->GetCustomCommand() && + !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) { + this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName, + orderOnlyDeps); + } + + 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); + + cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), + comment, + rule, + outputs, + explicitDeps, + emptyDeps, + orderOnlyDeps, + vars); + + if(const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) { + std::vector<std::string> outputList; + cmSystemTools::ExpandListArgument(objectOutputs, outputList); + std::transform(outputList.begin(), outputList.end(), outputList.begin(), + MapToNinjaPath()); + cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), + "Additional output files.", + outputList, + outputs); + } +} + +//---------------------------------------------------------------------------- +void +cmNinjaTargetGenerator +::AddModuleDefinitionFlag(std::string& flags) +{ + if(this->ModuleDefinitionFile.empty()) + { + return; + } + + // TODO: Create a per-language flag variable. + const char* defFileFlag = + this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG"); + if(!defFileFlag) + { + return; + } + + // Append the flag and value. Use ConvertToLinkReference to help + // vs6's "cl -link" pass it to the linker. + std::string flag = defFileFlag; + flag += (this->LocalGenerator->ConvertToLinkReference( + this->ModuleDefinitionFile.c_str())); + this->LocalGenerator->AppendFlags(flags, flag.c_str()); +} diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h new file mode 100644 index 0000000..3e70a2c --- /dev/null +++ b/Source/cmNinjaTargetGenerator.h @@ -0,0 +1,124 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmNinjaTargetGenerator_h +#define cmNinjaTargetGenerator_h + +#include "cmStandardIncludes.h" +#include "cmNinjaTypes.h" +#include "cmLocalNinjaGenerator.h" + +class cmTarget; +class cmGlobalNinjaGenerator; +class cmGeneratedFileStream; +class cmMakefile; +class cmSourceFile; +class cmCustomCommand; + +class cmNinjaTargetGenerator +{ +public: + /// Create a cmNinjaTargetGenerator according to the @a target's type. + static cmNinjaTargetGenerator* New(cmTarget* target); + + /// Build a NinjaTargetGenerator. + cmNinjaTargetGenerator(cmTarget* target); + + /// Destructor. + virtual ~cmNinjaTargetGenerator(); + + virtual void Generate() = 0; + + std::string GetTargetPDB() const; + std::string GetTargetName() const; + +protected: + cmGeneratedFileStream& GetBuildFileStream() const; + cmGeneratedFileStream& GetRulesFileStream() const; + + cmTarget* GetTarget() const + { return this->Target; } + + cmLocalNinjaGenerator* GetLocalGenerator() const + { return this->LocalGenerator; } + + cmGlobalNinjaGenerator* GetGlobalGenerator() const; + + cmMakefile* GetMakefile() const + { return this->Makefile; } + + const char* GetConfigName() const; + + std::string LanguageCompilerRule(const std::string& lang) const + { return lang + "_COMPILER"; } + + const char* GetFeature(const char* feature); + bool GetFeatureAsBool(const char* feature); + void AddFeatureFlags(std::string& flags, const char* lang); + + /** + * Compute the flags for compilation of object files for a given @a language. + * @note Generally it is the value of the variable whose name is computed + * by LanguageFlagsVarName(). + */ + std::string ComputeFlagsForObject(cmSourceFile *source, + const std::string& language); + + std::string ComputeDefines(cmSourceFile *source, + const std::string& language); + + std::string ConvertToNinjaPath(const char *path) const { + return this->GetLocalGenerator()->ConvertToNinjaPath(path); + } + cmLocalNinjaGenerator::map_to_ninja_path MapToNinjaPath() const { + return this->GetLocalGenerator()->MapToNinjaPath(); + } + + /// @return the list of link dependency for the given target @a target. + cmNinjaDeps ComputeLinkDeps() const; + + /// @return the source file path for the given @a source. + std::string GetSourceFilePath(cmSourceFile* source) const; + + /// @return the object file path for the given @a source. + std::string GetObjectFilePath(cmSourceFile* source) const; + + /// @return the file path where the target named @a name is generated. + std::string GetTargetFilePath(const std::string& name) const; + + /// @return the output path for the target. + virtual std::string GetTargetOutputDir() const; + + void WriteLanguageRules(const std::string& language); + void WriteCompileRule(const std::string& language); + void WriteObjectBuildStatements(); + void WriteObjectBuildStatement(cmSourceFile* source); + void WriteCustomCommandBuildStatement(cmCustomCommand *cc); + + cmNinjaDeps GetObjects() const + { return this->Objects; } + + // Helper to add flag for windows .def file. + void AddModuleDefinitionFlag(std::string& flags); + +private: + cmTarget* Target; + cmMakefile* Makefile; + cmLocalNinjaGenerator* LocalGenerator; + /// List of object files for this target. + cmNinjaDeps Objects; + + // The windows module definition source file (.def), if any. + std::string ModuleDefinitionFile; +}; + +#endif // ! cmNinjaTargetGenerator_h diff --git a/Source/cmNinjaTypes.h b/Source/cmNinjaTypes.h new file mode 100644 index 0000000..498f6b6 --- /dev/null +++ b/Source/cmNinjaTypes.h @@ -0,0 +1,19 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmNinjaTypes_h +# define cmNinjaTypes_h + +typedef std::vector<std::string> cmNinjaDeps; +typedef std::map<std::string, std::string> cmNinjaVars; + +#endif // ! cmNinjaTypes_h diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx new file mode 100644 index 0000000..9c2fd13 --- /dev/null +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -0,0 +1,116 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmNinjaUtilityTargetGenerator.h" +#include "cmCustomCommand.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalNinjaGenerator.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmTarget.h" + +cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator(cmTarget *target) + : cmNinjaTargetGenerator(target) {} + +cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator() {} + +void cmNinjaUtilityTargetGenerator::Generate() +{ + std::vector<std::string> commands; + cmNinjaDeps deps, outputs; + + const std::vector<cmCustomCommand> *cmdLists[2] = { + &this->GetTarget()->GetPreBuildCommands(), + &this->GetTarget()->GetPostBuildCommands() + }; + + for (unsigned i = 0; i != 2; ++i) { + for (std::vector<cmCustomCommand>::const_iterator + ci = cmdLists[i]->begin(); ci != cmdLists[i]->end(); ++ci) { + this->GetLocalGenerator()->AppendCustomCommandDeps(&*ci, deps); + this->GetLocalGenerator()->AppendCustomCommandLines(&*ci, commands); + } + } + + const std::vector<cmSourceFile*>& sources = + this->GetTarget()->GetSourceFiles(); + for(std::vector<cmSourceFile*>::const_iterator source = sources.begin(); + source != sources.end(); ++source) + { + if(cmCustomCommand* cc = (*source)->GetCustomCommand()) + { + this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget()); + + // Depend on all custom command outputs. + const std::vector<std::string>& ccOutputs = cc->GetOutputs(); + std::transform(ccOutputs.begin(), ccOutputs.end(), + std::back_inserter(deps), MapToNinjaPath()); + } + } + + this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs); + this->GetLocalGenerator()->AppendTargetDepends(this->GetTarget(), deps); + + if (commands.empty()) { + cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), + "Utility command for " + + this->GetTargetName(), + outputs, + deps); + } else { + std::string command = + this->GetLocalGenerator()->BuildCommandLine(commands); + const char *echoStr = this->GetTarget()->GetProperty("EchoString"); + std::string desc; + if (echoStr) + desc = echoStr; + else + desc = "Running utility command for " + this->GetTargetName(); + + // TODO: fix problematic global targets. For now, search and replace the + // makefile vars. + cmSystemTools::ReplaceString( + command, + "$(CMAKE_SOURCE_DIR)", + this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetTarget()->GetMakefile()->GetHomeDirectory(), + cmLocalGenerator::SHELL).c_str()); + cmSystemTools::ReplaceString( + command, + "$(CMAKE_BINARY_DIR)", + this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetTarget()->GetMakefile()->GetHomeOutputDirectory(), + cmLocalGenerator::SHELL).c_str()); + cmSystemTools::ReplaceString(command, "$(ARGS)", ""); + + if (command.find('$') != std::string::npos) + return; + + std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash(); + utilCommandName += this->GetTargetName() + ".util"; + + this->GetGlobalGenerator()->WriteCustomCommandBuild( + command, + desc, + "Utility command for " + this->GetTargetName(), + cmNinjaDeps(1, utilCommandName), + deps); + + cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), + "", + outputs, + cmNinjaDeps(1, utilCommandName)); + } + + this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), + this->GetTarget()); +} diff --git a/Source/cmNinjaUtilityTargetGenerator.h b/Source/cmNinjaUtilityTargetGenerator.h new file mode 100644 index 0000000..8b82ce4 --- /dev/null +++ b/Source/cmNinjaUtilityTargetGenerator.h @@ -0,0 +1,30 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne <peter@pcc.me.uk> + Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmNinjaUtilityTargetGenerator_h +# define cmNinjaUtilityTargetGenerator_h + +# include "cmNinjaTargetGenerator.h" +# include "cmNinjaTypes.h" + +class cmSourceFile; + +class cmNinjaUtilityTargetGenerator : public cmNinjaTargetGenerator +{ +public: + cmNinjaUtilityTargetGenerator(cmTarget* target); + ~cmNinjaUtilityTargetGenerator(); + + void Generate(); +}; + +#endif // ! cmNinjaUtilityTargetGenerator_h diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 15e689b..1376a48 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -198,6 +198,20 @@ std::string cmSystemTools::EscapeQuotes(const char* str) return result; } +std::string cmSystemTools::TrimWhitespace(const std::string& s) +{ + std::string::const_iterator start = s.begin(); + while(start != s.end() && *start == ' ') + ++start; + if (start == s.end()) + return ""; + + std::string::const_iterator stop = s.end()-1; + while(*stop == ' ') + --stop; + return std::string(start, stop+1); +} + void cmSystemTools::Error(const char* m1, const char* m2, const char* m3, const char* m4) { diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 7afc701..5f21de2 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -49,6 +49,11 @@ public: ///! Escape quotes in a string. static std::string EscapeQuotes(const char* str); + /** + * Returns a string that has whitespace removed from the start and the end. + */ + static std::string TrimWhitespace(const std::string& s); + typedef void (*ErrorCallback)(const char*, const char*, bool&, void*); /** * Set the function used by GUI's to display error messages diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index da99eb9..2fbca80 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -480,6 +480,26 @@ void cmTarget::DefineProperties(cmake *cm) "undefined behavior."); cm->DefineProperty + ("INCLUDE_DIRECTORIES", cmProperty::TARGET, + "List of preprocessor include file search directories.", + "This property specifies the list of directories given " + "so far to the include_directories command. " + "This property exists on directories and targets. " + "In addition to accepting values from the include_directories " + "command, values may be set directly on any directory or any " + "target using the set_property command. " + "A target gets its initial value for this property from the value " + "of the directory property. " + "A directory gets its initial value from its parent directory if " + "it has one. " + "Both directory and target property values are adjusted by calls " + "to the include_directories command." + "\n" + "The target property values are used by the generators to set " + "the include paths for the compiler. " + "See also the include_directories command."); + + cm->DefineProperty ("INSTALL_NAME_DIR", cmProperty::TARGET, "Mac OSX directory name for installed targets.", "INSTALL_NAME_DIR is a string specifying the " @@ -861,7 +881,9 @@ void cmTarget::DefineProperties(cmake *cm) "of of just main()." "This makes it a GUI executable instead of a console application. " "See the CMAKE_MFC_FLAG variable documentation to configure use " - "of MFC for WinMain executables."); + "of MFC for WinMain executables. " + "This property is initialized by the value of the variable " + "CMAKE_WIN32_EXECUTABLE if it is set when a target is created."); cm->DefineProperty ("MACOSX_BUNDLE", cmProperty::TARGET, @@ -871,7 +893,9 @@ void cmTarget::DefineProperties(cmake *cm) "This makes it a GUI executable that can be launched from " "the Finder. " "See the MACOSX_BUNDLE_INFO_PLIST target property for information " - "about creation of the Info.plist file for the application bundle."); + "about creation of the Info.plist file for the application bundle. " + "This property is initialized by the value of the variable " + "CMAKE_MACOSX_BUNDLE if it is set when a target is created."); cm->DefineProperty ("MACOSX_BUNDLE_INFO_PLIST", cmProperty::TARGET, @@ -958,7 +982,12 @@ void cmTarget::DefineProperties(cmake *cm) "When this property is not set the modules will be placed in the " "build directory corresponding to the target's source directory. " "If the variable CMAKE_Fortran_MODULE_DIRECTORY is set when a target " - "is created its value is used to initialize this property."); + "is created its value is used to initialize this property." + "\n" + "Note that some compilers will automatically search the module output " + "directory for modules USEd during compilation but others will not. " + "If your sources USE modules their location must be specified by " + "INCLUDE_DIRECTORIES regardless of this property."); cm->DefineProperty ("GNUtoMS", cmProperty::TARGET, @@ -1077,17 +1106,6 @@ void cmTarget::DefineProperties(cmake *cm) "better if VS_GLOBAL_QtVersion is set to the version " "FindQt4.cmake found. For example, \"4.7.3\""); -#if 0 - cm->DefineProperty - ("OBJECT_FILES", cmProperty::TARGET, - "Used to get the resulting list of object files that make up a " - "target.", - "This can be used to put object files from one library " - "into another library. It is a read only property. It " - "converts the source list for the target into a list of full " - "paths to object names that will be produced by the target."); -#endif - #define CM_TARGET_FILE_TYPES_DOC \ "There are three kinds of target files that may be built: " \ "archive, library, and runtime. " \ @@ -1224,6 +1242,8 @@ void cmTarget::SetMakefile(cmMakefile* mf) this->SetPropertyDefault("AUTOMOC", 0); this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", 0); this->SetPropertyDefault("LINK_INTERFACE_LIBRARIES", 0); + this->SetPropertyDefault("WIN32_EXECUTABLE", 0); + this->SetPropertyDefault("MACOSX_BUNDLE", 0); // Collect the set of configuration types. std::vector<std::string> configNames; @@ -1263,6 +1283,11 @@ void cmTarget::SetMakefile(cmMakefile* mf) // Save the backtrace of target construction. this->Makefile->GetBacktrace(this->Internal->Backtrace); + // Initialize the INCLUDE_DIRECTORIES property based on the current value + // of the same directory property: + this->SetProperty("INCLUDE_DIRECTORIES", + this->Makefile->GetProperty("INCLUDE_DIRECTORIES")); + // Record current policies for later use. this->PolicyStatusCMP0003 = this->Makefile->GetPolicyStatus(cmPolicies::CMP0003); @@ -2582,54 +2607,6 @@ const char *cmTarget::GetProperty(const char* prop) } //---------------------------------------------------------------------------- -void cmTarget::ComputeObjectFiles() -{ - if (this->IsImported()) - { - return; - } -#if 0 - std::vector<std::string> dirs; - this->Makefile->GetLocalGenerator()-> - GetTargetObjectFileDirectories(this, - dirs); - std::string objectFiles; - std::string objExtensionLookup1 = "CMAKE_"; - std::string objExtensionLookup2 = "_OUTPUT_EXTENSION"; - - for(std::vector<std::string>::iterator d = dirs.begin(); - d != dirs.end(); ++d) - { - for(std::vector<cmSourceFile*>::iterator s = this->SourceFiles.begin(); - s != this->SourceFiles.end(); ++s) - { - cmSourceFile* sf = *s; - if(const char* lang = sf->GetLanguage()) - { - std::string lookupObj = objExtensionLookup1 + lang; - lookupObj += objExtensionLookup2; - const char* obj = this->Makefile->GetDefinition(lookupObj.c_str()); - if(obj) - { - if(objectFiles.size()) - { - objectFiles += ";"; - } - std::string objFile = *d; - objFile += "/"; - objFile += this->Makefile->GetLocalGenerator()-> - GetSourceObjectName(*sf); - objFile += obj; - objectFiles += objFile; - } - } - } - } - this->SetProperty("OBJECT_FILES", objectFiles.c_str()); -#endif -} - -//---------------------------------------------------------------------------- const char *cmTarget::GetProperty(const char* prop, cmProperty::ScopeType scope) { @@ -3586,7 +3563,8 @@ bool cmTarget::HaveBuildTreeRPATH() bool cmTarget::HaveInstallTreeRPATH() { const char* install_rpath = this->GetProperty("INSTALL_RPATH"); - return install_rpath && *install_rpath; + return (install_rpath && *install_rpath) && + !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH"); } //---------------------------------------------------------------------------- @@ -3693,7 +3671,8 @@ std::string cmTarget::GetInstallNameDirForInstallTree(const char* config, { std::string dir; - if(!this->Makefile->IsOn("CMAKE_SKIP_RPATH")) + if(!this->Makefile->IsOn("CMAKE_SKIP_RPATH") && + !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH")) { const char* install_name_dir = this->GetProperty("INSTALL_NAME_DIR"); if(install_name_dir && *install_name_dir) @@ -4660,6 +4639,30 @@ cmTarget::GetLinkInformation(const char* config) } //---------------------------------------------------------------------------- +std::vector<std::string> cmTarget::GetIncludeDirectories() +{ + std::vector<std::string> includes; + const char *prop = this->GetProperty("INCLUDE_DIRECTORIES"); + if(prop) + { + cmSystemTools::ExpandListArgument(prop, includes); + } + + std::set<std::string> uniqueIncludes; + std::vector<std::string> orderedAndUniqueIncludes; + for(std::vector<std::string>::const_iterator + li = includes.begin(); li != includes.end(); ++li) + { + if(uniqueIncludes.insert(*li).second) + { + orderedAndUniqueIncludes.push_back(*li); + } + } + + return orderedAndUniqueIncludes; +} + +//---------------------------------------------------------------------------- cmTargetLinkInformationMap ::cmTargetLinkInformationMap(cmTargetLinkInformationMap const& r): derived() { diff --git a/Source/cmTarget.h b/Source/cmTarget.h index f4b6955..ff05cd3 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -403,9 +403,6 @@ public: // Define the properties static void DefineProperties(cmake *cm); - // Compute the OBJECT_FILES property only when requested - void ComputeObjectFiles(); - /** Get the macro to define when building sources in this target. If no macro should be defined null is returned. */ const char* GetExportMacro(); @@ -457,6 +454,9 @@ public: directory. */ bool UsesDefaultOutputDir(const char* config, bool implib); + /** Get the include directories for this target. */ + std::vector<std::string> GetIncludeDirectories(); + private: /** * A list of direct dependencies. Use in conjunction with DependencyMap. diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9418761..66f9a36 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -394,6 +394,11 @@ void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues() { this->WriteString("<CharacterSet>Unicode</CharacterSet>\n", 2); } + else if (this->Target->GetType() <= cmTarget::MODULE_LIBRARY && + this->ClOptions[*i]->UsingSBCS()) + { + this->WriteString("<CharacterSet>NotSet</CharacterSet>\n", 2); + } else { this->WriteString("<CharacterSet>MultiByte</CharacterSet>\n", 2); @@ -872,9 +877,6 @@ void cmVisualStudio10TargetGenerator::WriteCLSources() void cmVisualStudio10TargetGenerator::ComputeObjectNames() { - // We may be modifying the source groups temporarily, so make a copy. - std::vector<cmSourceGroup> sourceGroups = this->Makefile->GetSourceGroups(); - // get the classes from the source lists then add them to the groups std::vector<cmSourceFile*>const & classes = this->Target->GetSourceFiles(); for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); @@ -886,13 +888,10 @@ void cmVisualStudio10TargetGenerator::ComputeObjectNames() { this->ModuleDefinitionFile = (*i)->GetFullPath(); } - cmSourceGroup& sourceGroup = - this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); - sourceGroup.AssignSource(*i); } // Compute which sources need unique object computation. - this->LocalGenerator->ComputeObjectNameRequirements(sourceGroups); + this->LocalGenerator->ComputeObjectNameRequirements(classes); } bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( @@ -1586,7 +1585,7 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() static_cast<cmGlobalVisualStudio7Generator *> (this->GlobalGenerator)->GetConfigurations(); std::vector<std::string> includes; - this->LocalGenerator->GetIncludeDirectories(includes); + this->LocalGenerator->GetIncludeDirectories(includes, this->Target); for(std::vector<std::string>::iterator i = configs->begin(); i != configs->end(); ++i) { diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 41230e7..9369af6 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -117,6 +117,20 @@ bool cmVisualStudioGeneratorOptions::UsingUnicode() } return false; } +//---------------------------------------------------------------------------- +bool cmVisualStudioGeneratorOptions::UsingSBCS() +{ + // Look for the a _SBCS definition. + for(std::vector<std::string>::const_iterator di = this->Defines.begin(); + di != this->Defines.end(); ++di) + { + if(*di == "_SBCS") + { + return true; + } + } + return false; +} //---------------------------------------------------------------------------- void cmVisualStudioGeneratorOptions::Parse(const char* flags) diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h index 51a1362..a1a55da 100644 --- a/Source/cmVisualStudioGeneratorOptions.h +++ b/Source/cmVisualStudioGeneratorOptions.h @@ -48,6 +48,7 @@ public: // Check for specific options. bool UsingUnicode(); + bool UsingSBCS(); bool IsDebug(); // Write options to output. diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 0b7a996..846aef5 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -84,6 +84,10 @@ #endif #include "cmGlobalUnixMakefileGenerator3.h" +#ifdef CMAKE_USE_NINJA +# include "cmGlobalNinjaGenerator.h" +#endif + #if defined(CMAKE_HAVE_VS_GENERATORS) #include "cmCallVisualStudioMacro.h" #endif @@ -598,14 +602,10 @@ bool cmake::FindPackage(const std::vector<std::string>& args) std::string includes = mf->GetSafeDefinition("PACKAGE_INCLUDE_DIRS"); std::vector<std::string> includeDirs; cmSystemTools::ExpandListArgument(includes, includeDirs); - for(std::vector<std::string>::const_iterator dirIt=includeDirs.begin(); - dirIt != includeDirs.end(); - ++dirIt) - { - mf->AddIncludeDirectory(dirIt->c_str(), false); - } - std::string includeFlags = lg->GetIncludeFlags(language.c_str(), false); + std::string includeFlags = lg->GetIncludeFlags(includeDirs, + language.c_str(), false); + std::string definitions = mf->GetSafeDefinition("PACKAGE_DEFINITIONS"); printf("%s %s\n", includeFlags.c_str(), definitions.c_str()); } @@ -2597,6 +2597,10 @@ void cmake::AddDefaultGenerators() #endif this->Generators[cmGlobalUnixMakefileGenerator3::GetActualName()] = &cmGlobalUnixMakefileGenerator3::New; +#ifdef CMAKE_USE_NINJA + this->Generators[cmGlobalNinjaGenerator::GetActualName()] = + &cmGlobalNinjaGenerator::New; +#endif #ifdef CMAKE_USE_XCODE this->Generators[cmGlobalXCodeGenerator::GetActualName()] = &cmGlobalXCodeGenerator::New; diff --git a/Source/kwsys/kwsysDateStamp.cmake b/Source/kwsys/kwsysDateStamp.cmake index e424fec..6ee6e5b 100644 --- a/Source/kwsys/kwsysDateStamp.cmake +++ b/Source/kwsys/kwsysDateStamp.cmake @@ -18,4 +18,4 @@ SET(KWSYS_DATE_STAMP_YEAR 2012) SET(KWSYS_DATE_STAMP_MONTH 03) # KWSys version date day component. Format is DD. -SET(KWSYS_DATE_STAMP_DAY 03) +SET(KWSYS_DATE_STAMP_DAY 19) |