From 2cd36550b073fd4f559f7edc5a5170af534f1068 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 11 Nov 2011 05:00:35 +0000 Subject: [PATCH 01/37] Add cmSystemTools::TrimWhitespace function --- Source/cmSystemTools.cxx | 14 ++++++++++++++ Source/cmSystemTools.h | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 02060ca48..3e927cf62 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -196,6 +196,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 213734043..88ae1c009 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 From 4468edf12b0b87e32768fcdce7abd30afddcad7b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 11 Nov 2011 19:11:38 +0000 Subject: [PATCH 02/37] Add executable with exports flag support to cmLocalGenerator::GetTargetFlags --- Source/cmLocalGenerator.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index ffbeb4856..1cfefed6d 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -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) { From 7eb8d9036c73784f14da9d8381023c1e26df1275 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 15 Nov 2011 04:39:52 +0000 Subject: [PATCH 03/37] Provide dependency file flags to generator Causes compiler modules (currently only GNU) to set a CMAKE_DEPFILE_FLAGS_${lang} variable, which communicates to the generator the flags required to cause the compiler to create dependency files. --- Modules/Compiler/GNU.cmake | 9 +++++++++ Tests/BuildDepends/CMakeLists.txt | 2 ++ 2 files changed, 11 insertions(+) diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake index 8d6f5dfb8..bdcaf9d62 100644 --- a/Modules/Compiler/GNU.cmake +++ b/Modules/Compiler/GNU.cmake @@ -24,6 +24,15 @@ macro(__compiler_gnu lang) set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-fPIC") set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") + # Older versions of gcc (< 4.5) contain a bug causing them to report a missing + # header file as a warning if depfiles are enabled, causing check_header_file + # tests to always succeed. Work around this by disabling dependency tracking + # in try_compile mode. + GET_PROPERTY(_IN_TC GLOBAL PROPERTY IN_TRY_COMPILE) + if(NOT _IN_TC OR CMAKE_FORCE_DEPFILES) + set(CMAKE_DEPFILE_FLAGS_${lang} "-MMD -MF ") + endif() + # Initial configuration flags. set(CMAKE_${lang}_FLAGS_INIT "") set(CMAKE_${lang}_FLAGS_DEBUG_INIT "-g") diff --git a/Tests/BuildDepends/CMakeLists.txt b/Tests/BuildDepends/CMakeLists.txt index 31392b5e4..aa32d67a0 100644 --- a/Tests/BuildDepends/CMakeLists.txt +++ b/Tests/BuildDepends/CMakeLists.txt @@ -40,6 +40,8 @@ if("${CMAKE_GENERATOR}" MATCHES "Make") endif() list(APPEND _cmake_options "-DTEST_LINK_DEPENDS=${TEST_LINK_DEPENDS}") +list(APPEND _cmake_options "-DCMAKE_FORCE_DEPFILES=1") + file(MAKE_DIRECTORY ${BuildDepends_BINARY_DIR}/Project) message("Creating Project/foo.cxx") write_file(${BuildDepends_BINARY_DIR}/Project/foo.cxx From 6dd410c2b98d5152adf69b5986b5f25d4dcd9e2a Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 11 Nov 2011 05:00:49 +0000 Subject: [PATCH 04/37] Ninja: Add the Ninja generator --- Modules/CMakeNinjaFindMake.cmake | 17 + Modules/ExternalProject.cmake | 19 +- Source/CMakeLists.txt | 18 + Source/cmGlobalNinjaGenerator.cxx | 774 ++++++++++++++++++ Source/cmGlobalNinjaGenerator.h | 329 ++++++++ Source/cmLocalNinjaGenerator.cxx | 413 ++++++++++ Source/cmLocalNinjaGenerator.h | 134 +++ Source/cmNinjaNormalTargetGenerator.cxx | 430 ++++++++++ Source/cmNinjaNormalTargetGenerator.h | 47 ++ Source/cmNinjaTargetGenerator.cxx | 445 ++++++++++ Source/cmNinjaTargetGenerator.h | 115 +++ Source/cmNinjaTypes.h | 19 + Source/cmNinjaUtilityTargetGenerator.cxx | 99 +++ Source/cmNinjaUtilityTargetGenerator.h | 30 + Source/cmake.cxx | 8 + Tests/CMakeLists.txt | 14 +- .../OutOfSourceSubdir/CMakeLists.txt | 6 + 17 files changed, 2913 insertions(+), 4 deletions(-) create mode 100644 Modules/CMakeNinjaFindMake.cmake create mode 100644 Source/cmGlobalNinjaGenerator.cxx create mode 100644 Source/cmGlobalNinjaGenerator.h create mode 100644 Source/cmLocalNinjaGenerator.cxx create mode 100644 Source/cmLocalNinjaGenerator.h create mode 100644 Source/cmNinjaNormalTargetGenerator.cxx create mode 100644 Source/cmNinjaNormalTargetGenerator.h create mode 100644 Source/cmNinjaTargetGenerator.cxx create mode 100644 Source/cmNinjaTargetGenerator.h create mode 100644 Source/cmNinjaTypes.h create mode 100644 Source/cmNinjaUtilityTargetGenerator.cxx create mode 100644 Source/cmNinjaUtilityTargetGenerator.h diff --git a/Modules/CMakeNinjaFindMake.cmake b/Modules/CMakeNinjaFindMake.cmake new file mode 100644 index 000000000..f15c3e0d4 --- /dev/null +++ b/Modules/CMakeNinjaFindMake.cmake @@ -0,0 +1,17 @@ + +#============================================================================= +# Copyright 2011 Kitware, Inc. +# +# 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. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +FIND_PROGRAM(CMAKE_MAKE_PROGRAM ninja + DOC "Program used to build from build.ninja files.") +MARK_AS_ADVANCED(CMAKE_MAKE_PROGRAM) diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index fb55d3bf3..b6fe19004 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -833,6 +833,12 @@ function(ExternalProject_Add_StepTargets name) foreach(step ${steps}) add_custom_target(${name}-${step} DEPENDS ${stamp_dir}${cfgdir}/${name}-${step}) + + # Depend on other external projects (target-level). + get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS) + foreach(arg IN LISTS deps) + add_dependencies(${name}-${step} ${arg}) + endforeach() endforeach() endfunction(ExternalProject_Add_StepTargets) @@ -1451,9 +1457,18 @@ function(ExternalProject_Add name) # depends on the 'done' mark so that it rebuilds when this project # rebuilds. It is important that 'done' is not the output of any # custom command so that CMake does not propagate build rules to - # other external project targets. + # other external project targets, which may cause problems during + # parallel builds. However, the Ninja generator needs to see the entire + # dependency graph, and can cope with custom commands belonging to + # multiple targets, so we add the 'done' mark as an output for Ninja only. + set(complete_outputs ${cmf_dir}${cfgdir}/${name}-complete) + if(${CMAKE_GENERATOR} MATCHES "Ninja") + set(complete_outputs + ${complete_outputs} ${stamp_dir}${cfgdir}/${name}-done) + endif() + add_custom_command( - OUTPUT ${cmf_dir}${cfgdir}/${name}-complete + OUTPUT ${complete_outputs} COMMENT "Completed '${name}'" COMMAND ${CMAKE_COMMAND} -E make_directory ${cmf_dir}${cfgdir} COMMAND ${CMAKE_COMMAND} -E touch ${cmf_dir}${cfgdir}/${name}-complete diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index b5115b78b..7ddabbd55 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,6 +353,24 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) +# Ninja only works on UNIX. +IF(UNIX) + 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) +ENDIF(UNIX) + # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) TARGET_LINK_LIBRARIES(CMakeLib cmsys diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx new file mode 100644 index 000000000..24900c562 --- /dev/null +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -0,0 +1,774 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 == '\\'); +} + +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 { + return ident; + } +} + +std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit) +{ + std::string result = lit; + cmSystemTools::ReplaceString(result, "$", "$$"); + return 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(*i, os); + builds << ":"; + + // Write the rule. + builds << " " << rule; + + // Write explicit dependencies. + for(cmNinjaDeps::const_iterator i = explicitDeps.begin(); + i != explicitDeps.end(); + ++i) + builds << " " << EncodeIdent(*i, os); + + // Write implicit dependencies. + if(!implicitDeps.empty()) + { + builds << " |"; + for(cmNinjaDeps::const_iterator i = implicitDeps.begin(); + i != implicitDeps.end(); + ++i) + builds << " " << EncodeIdent(*i, os); + } + + // Write order-only dependencies. + if(!orderOnlyDeps.empty()) + { + builds << " ||"; + for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin(); + i != orderOnlyDeps.end(); + ++i) + builds << " " << EncodeIdent(*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->BuildFileStream); + 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::vectorconst& languages, + cmMakefile *mf, + bool optional) +{ + this->cmGlobalGenerator::EnableLanguage(languages, mf, optional); + std::string path; + for(std::vector::const_iterator l = languages.begin(); + l != languages.end(); ++l) + { + 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(std::ostream& os) +{ + for (std::map >::iterator + i = this->AssumedSourceDependencies.begin(); + i != this->AssumedSourceDependencies.end(); ++i) { + WriteCustomCommandBuild(/*command=*/"", /*description=*/"", + "Assume dependencies for generated source file.", + cmNinjaDeps(1, i->first), + cmNinjaDeps(i->second.begin(), i->second.end())); + } +} + +void +cmGlobalNinjaGenerator +::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs) +{ + const char* configName = + target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"); + cmLocalNinjaGenerator *ng = + static_cast(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 const& utils = target->GetUtilities(); + outputs.insert(outputs.end(), utils.begin(), utils.end()); + } 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 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) +{ + cmMakefile* mfRoot = this->LocalGenerators[0]->GetMakefile(); + + std::ostringstream cmd; + cmd << mfRoot->GetRequiredDefinition("CMAKE_COMMAND") + << " -H" << mfRoot->GetHomeDirectory() + << " -B" << mfRoot->GetHomeOutputDirectory(); + 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::const_iterator i = + this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) { + const std::vector& 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(), + cmNinjaDeps(), + cmNinjaDeps(), + cmNinjaVars()); +} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h new file mode 100644 index 000000000..171d14b90 --- /dev/null +++ b/Source/cmGlobalNinjaGenerator.h @@ -0,0 +1,329 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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); + + /** + * 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::vectorconst& 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(std::ostream& os); + + 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 *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 &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 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 CustomCommands; + + /// The set of custom command outputs we have seen. + std::set CustomCommandOutputs; + + /// The mapping from source file to assumed dependencies. + std::map > AssumedSourceDependencies; + + typedef std::map TargetAliasMap; + TargetAliasMap TargetAliases; +}; + +#endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx new file mode 100644 index 000000000..6ae50322f --- /dev/null +++ b/Source/cmLocalNinjaGenerator.cxx @@ -0,0 +1,413 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 + +cmLocalNinjaGenerator::cmLocalNinjaGenerator() + : cmLocalGenerator() + , ConfigName("") + , HomeRelativeOutputPath("") +{ + this->IsMakefileGenerator = true; +} + +//---------------------------------------------------------------------------- +// 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(this->GetGlobalGenerator()); +} + +cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator() +{ + return static_cast(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) +{ + return this->Convert(path, + cmLocalGenerator::HOME_OUTPUT, + cmLocalGenerator::MAKEFILE); +} + +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 &deps = cc->GetDepends(); + for (std::vector::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 &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()) + return ":"; + + // 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: + // http://groups.google.com/group/ninja-build/browse_thread/thread/d515f23a78986008 + std::ostringstream cmd; + for (std::vector::const_iterator li = cmdLines.begin(); + li != cmdLines.end(); ++li) { + if (li != cmdLines.begin()) + cmd << " && "; + cmd << *li; + } + return cmd.str(); +} + +void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, + std::vector &cmdLines) +{ + cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile); + if (ccg.GetNumberOfCommands() > 0) { + std::ostringstream cdCmd; + cdCmd << "cd "; + if (const char* wd = cc->GetWorkingDirectory()) + cdCmd << wd; + else + cdCmd << this->GetMakefile()->GetStartOutputDirectory(); + cmdLines.push_back(cdCmd.str()); + } + for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) { + cmdLines.push_back(ccg.GetCommand(i)); + std::string& cmd = cmdLines.back(); + ccg.AppendArguments(i, cmd); + } +} + +void +cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(cmCustomCommand *cc, + const cmNinjaDeps& orderOnlyDeps) +{ + if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc)) + return; + + const std::vector &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 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* 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::iterator j = i->second.begin(); + assert(j != i->second.end()); + std::vector ccTargetDeps; + this->AppendTargetDepends(*j, ccTargetDeps); + std::sort(ccTargetDeps.begin(), ccTargetDeps.end()); + ++j; + + for (; j != i->second.end(); ++j) { + std::vector 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 000000000..fee9cd0d7 --- /dev/null +++ b/Source/cmLocalNinjaGenerator.h @@ -0,0 +1,134 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 { + cmLocalNinjaGenerator *LocalGenerator; + map_to_ninja_path(cmLocalNinjaGenerator *LocalGenerator) + : LocalGenerator(LocalGenerator) {} + 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 &cmdLines); + void AppendCustomCommandLines(const cmCustomCommand *cc, + std::vector &cmdLines); + void WriteCustomCommandRule(); + void WriteCustomCommandBuildStatement(cmCustomCommand *cc, + const cmNinjaDeps& orderOnlyDeps); + + void AddCustomCommandTarget(cmCustomCommand* cc, cmTarget* target); + void WriteCustomCommandBuildStatements(); + +private: + std::string ConfigName; + std::string HomeRelativeOutputPath; + + typedef std::map > + CustomCommandTargetMap; + CustomCommandTargetMap CustomCommandTargets; +}; + +#endif // ! cmLocalNinjaGenerator_h diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx new file mode 100644 index 000000000..d7f8ad797 --- /dev/null +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -0,0 +1,430 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 + +cmNinjaNormalTargetGenerator:: +cmNinjaNormalTargetGenerator(cmTarget* target) + : cmNinjaTargetGenerator(target) + , TargetNameOut() + , TargetNameSO() + , TargetNameReal() + , TargetNameImport() + , TargetNamePDB() + , 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()); +} + +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 languages; + this->GetTarget()->GetLanguages(languages); + for(std::set::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"; + + // 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 linkCmds = this->ComputeLinkCmd(); + for(std::vector::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->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"); + 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 +cmNinjaNormalTargetGenerator +::ComputeLinkCmd() +{ + 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())) + { + return std::vector(1, linkCmd); + } + } + + // We have archive link commands set. First, delete the existing archive. + std::vector linkCmds; + std::string cmakeCommand = + this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"); + 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()); + linkCmds.push_back(linkCmd); + } + { + std::string linkCmdVar = "CMAKE_"; + linkCmdVar += this->TargetLinkLanguage; + linkCmdVar += "_ARCHIVE_FINISH"; + const char *linkCmd = + this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); + linkCmds.push_back(linkCmd); + } + 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()); + return std::vector(1, linkCmd); + } + default: + assert(0 && "Unexpected target type"); + } +} + +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()); + + // 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()); + + // 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); + } + } + + std::vector *cmdLists[3] = { + &this->GetTarget()->GetPreBuildCommands(), + &this->GetTarget()->GetPreLinkCommands(), + &this->GetTarget()->GetPostBuildCommands() + }; + + std::vector preLinkCmdLines, postBuildCmdLines; + std::vector *cmdLineLists[3] = { + &preLinkCmdLines, + &preLinkCmdLines, + &postBuildCmdLines + }; + + for (unsigned i = 0; i != 3; ++i) { + for (std::vector::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()) + preLinkCmdLines.push_back(std::string("cd ") + + this->GetMakefile()->GetHomeOutputDirectory()); + + 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); + } + } + + // 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 000000000..99f5a133b --- /dev/null +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -0,0 +1,47 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 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 000000000..1e31044b9 --- /dev/null +++ b/Source/cmNinjaTargetGenerator.cxx @@ -0,0 +1,445 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 + +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(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::string includeFlags = + this->LocalGenerator->GetIncludeFlags(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; +} + +std::string cmNinjaTargetGenerator::ConvertToNinjaPath(const char *path) const +{ + return this->LocalGenerator->Convert(path, + cmLocalGenerator::HOME_OUTPUT, + cmLocalGenerator::MAKEFILE); +} + +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 &deps = cli->GetDepends(); + cmNinjaDeps result(deps.size()); + std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath()); + 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(); +} + +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"; + + 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.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()); + + this->GetLocalGenerator()->ExpandRuleVariables(compileCmd, vars); + + // 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), + compileCmd, + 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::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)); + 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 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& sources = + this->GetTarget()->GetSourceFiles(); + for(std::vector::const_iterator si = sources.begin(); + si != sources.end(); ++si) { + if (!(*si)->GetLanguage()) { + if (cmCustomCommand* cc = (*si)->GetCustomCommand()) { + const std::vector& 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); + + cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), + comment, + rule, + outputs, + explicitDeps, + emptyDeps, + orderOnlyDeps, + vars); +} diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h new file mode 100644 index 000000000..cf47abf1d --- /dev/null +++ b/Source/cmNinjaTargetGenerator.h @@ -0,0 +1,115 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 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; + 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; } + +private: + cmTarget* Target; + cmMakefile* Makefile; + cmLocalNinjaGenerator* LocalGenerator; + /// List of object files for this target. + cmNinjaDeps Objects; +}; + +#endif // ! cmNinjaTargetGenerator_h diff --git a/Source/cmNinjaTypes.h b/Source/cmNinjaTypes.h new file mode 100644 index 000000000..498f6b639 --- /dev/null +++ b/Source/cmNinjaTypes.h @@ -0,0 +1,19 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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 cmNinjaDeps; +typedef std::map cmNinjaVars; + +#endif // ! cmNinjaTypes_h diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx new file mode 100644 index 000000000..0a15834ce --- /dev/null +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -0,0 +1,99 @@ +#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 commands; + cmNinjaDeps deps, outputs; + + const std::vector *cmdLists[2] = { + &this->GetTarget()->GetPreBuildCommands(), + &this->GetTarget()->GetPostBuildCommands() + }; + + for (unsigned i = 0; i != 2; ++i) { + for (std::vector::const_iterator + ci = cmdLists[i]->begin(); ci != cmdLists[i]->end(); ++ci) { + this->GetLocalGenerator()->AppendCustomCommandDeps(&*ci, deps); + this->GetLocalGenerator()->AppendCustomCommandLines(&*ci, commands); + } + } + + const std::vector& sources = + this->GetTarget()->GetSourceFiles(); + for(std::vector::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& outputs = cc->GetOutputs(); + std::transform(outputs.begin(), outputs.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->GetTarget()->GetMakefile()->GetHomeDirectory()); + cmSystemTools::ReplaceString(command, "$(CMAKE_BINARY_DIR)", + this->GetTarget()->GetMakefile()->GetHomeOutputDirectory()); + 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), + cmNinjaDeps(), + cmNinjaDeps(), + cmNinjaVars()); + } + + this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), + this->GetTarget()); +} diff --git a/Source/cmNinjaUtilityTargetGenerator.h b/Source/cmNinjaUtilityTargetGenerator.h new file mode 100644 index 000000000..8b82ce49a --- /dev/null +++ b/Source/cmNinjaUtilityTargetGenerator.h @@ -0,0 +1,30 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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/cmake.cxx b/Source/cmake.cxx index d691f46dc..1d793d776 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 @@ -2614,6 +2618,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/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 2c5acd963..066926a97 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -334,6 +334,15 @@ IF(BUILD_TESTING) --build-target car --test-command car ) + + IF(${CMAKE_TEST_GENERATOR} MATCHES "Ninja") + # The Ninja generator does not create a recursive build system. Start + # from the root directory. + SET(SubProject_SUBDIR) + ELSE() + SET(SubProject_SUBDIR "/foo") + ENDIF() + # For stage 2, do not run cmake again. # Then build the foo sub project which should build # the bar library which should be referenced because @@ -341,13 +350,14 @@ IF(BUILD_TESTING) # directly in the foo sub project ADD_TEST(SubProject-Stage2 ${CMAKE_CTEST_COMMAND} --build-and-test - "${CMake_SOURCE_DIR}/Tests/SubProject/foo" - "${CMake_BINARY_DIR}/Tests/SubProject/foo" + "${CMake_SOURCE_DIR}/Tests/SubProject${SubProject_SUBDIR}" + "${CMake_BINARY_DIR}/Tests/SubProject${SubProject_SUBDIR}" --build-generator ${CMAKE_TEST_GENERATOR} --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM} --build-nocmake --build-project foo --build-target foo + --build-exe-dir "${CMake_BINARY_DIR}/Tests/SubProject/foo" --test-command foo ) SET_TESTS_PROPERTIES ( SubProject-Stage2 PROPERTIES DEPENDS SubProject) diff --git a/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt b/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt index c7cc09016..c362e79ba 100644 --- a/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt +++ b/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt @@ -24,6 +24,12 @@ IF ("${PROJECT_SOURCE_DIR}" STREQUAL "${ANOTHER_PROJ_SOURCE_DIR}") MATH(EXPR MAXPATH "${MAXPATH} - 46") ENDIF() + # Ninja imposes a maximum path component count of 30. Permit more + # path components in the source path. + IF(${CMAKE_GENERATOR} MATCHES "Ninja") + MATH(EXPR MAXPATH "${MAXPATH} - 44") + ENDIF() + # MAXPATH less 25 for last /and/deeper/simple.cxx part and small safety MATH(EXPR MAXPATH "${MAXPATH} - 25") STRING(LENGTH "${DEEPDIR}" DEEPDIR_LEN) From 69984906ef41ed9549d4acd75590c67030281df9 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 5 Feb 2012 00:55:34 +0000 Subject: [PATCH 05/37] Ninja: Fix a 79-col violation --- Source/cmLocalNinjaGenerator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 6ae50322f..a4e66e49d 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -300,7 +300,7 @@ std::string cmLocalNinjaGenerator::BuildCommandLine( // 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: - // http://groups.google.com/group/ninja-build/browse_thread/thread/d515f23a78986008 + // groups.google.com/group/ninja-build/browse_thread/thread/d515f23a78986008 std::ostringstream cmd; for (std::vector::const_iterator li = cmdLines.begin(); li != cmdLines.end(); ++li) { From bfe56f6802fc7db7f8e824aab8881be6cf23ccef Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 5 Feb 2012 01:48:01 +0000 Subject: [PATCH 06/37] Ninja: Remove some default arguments --- Source/cmGlobalNinjaGenerator.cxx | 5 +---- Source/cmNinjaUtilityTargetGenerator.cxx | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 24900c562..8b633bf83 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -767,8 +767,5 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) WritePhonyBuild(os, "A missing CMake input file is not an error.", implicitDeps, - cmNinjaDeps(), - cmNinjaDeps(), - cmNinjaDeps(), - cmNinjaVars()); + cmNinjaDeps()); } diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index 0a15834ce..bfe6f0795 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -88,10 +88,7 @@ void cmNinjaUtilityTargetGenerator::Generate() cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), "", outputs, - cmNinjaDeps(1, utilCommandName), - cmNinjaDeps(), - cmNinjaDeps(), - cmNinjaVars()); + cmNinjaDeps(1, utilCommandName)); } this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), From 5d19e8aa6aac75fb08897aece8aa5545360aeeb2 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 5 Feb 2012 01:48:08 +0000 Subject: [PATCH 07/37] Ninja: Appease various compilers --- Source/cmGlobalNinjaGenerator.cxx | 11 ++++++----- Source/cmGlobalNinjaGenerator.h | 2 +- Source/cmLocalNinjaGenerator.h | 4 ++-- Source/cmNinjaNormalTargetGenerator.cxx | 1 + Source/cmNinjaUtilityTargetGenerator.cxx | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 8b633bf83..7ff33bad8 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -367,7 +367,7 @@ void cmGlobalNinjaGenerator::Generate() this->cmGlobalGenerator::Generate(); - this->WriteAssumedSourceDependencies(*this->BuildFileStream); + this->WriteAssumedSourceDependencies(); this->WriteTargetAliases(*this->BuildFileStream); this->WriteBuiltinTargets(*this->BuildFileStream); @@ -591,15 +591,16 @@ void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target) this->AppendTargetOutputs(target, this->AllDependencies); } -void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies(std::ostream& os) +void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies() { for (std::map >::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), - cmNinjaDeps(i->second.begin(), i->second.end())); + cmNinjaDeps(1, i->first), deps); } } @@ -653,7 +654,7 @@ cmGlobalNinjaGenerator // Global targets only depend on other utilities, which may not appear in // the TargetDepends set (e.g. "all"). std::set const& utils = target->GetUtilities(); - outputs.insert(outputs.end(), utils.begin(), utils.end()); + std::copy(utils.begin(), utils.end(), std::back_inserter(outputs)); } else { cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(*target); diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 171d14b90..6f3c6b2c9 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -255,7 +255,7 @@ private: void AddDependencyToAll(cmTarget* target); - void WriteAssumedSourceDependencies(std::ostream& os); + void WriteAssumedSourceDependencies(); void AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs); void AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs); diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index fee9cd0d7..8ef4d2851 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -97,8 +97,8 @@ private: struct map_to_ninja_path { cmLocalNinjaGenerator *LocalGenerator; - map_to_ninja_path(cmLocalNinjaGenerator *LocalGenerator) - : LocalGenerator(LocalGenerator) {} + map_to_ninja_path(cmLocalNinjaGenerator *LocalGen) + : LocalGenerator(LocalGen) {} std::string operator()(const std::string &path) { return LocalGenerator->ConvertToNinjaPath(path.c_str()); } diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index d7f8ad797..4dd6a9b9c 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -281,6 +281,7 @@ cmNinjaNormalTargetGenerator default: assert(0 && "Unexpected target type"); } + return std::vector(); } void cmNinjaNormalTargetGenerator::WriteLinkStatement() diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index bfe6f0795..bf8194de5 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -39,8 +39,8 @@ void cmNinjaUtilityTargetGenerator::Generate() this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget()); // Depend on all custom command outputs. - const std::vector& outputs = cc->GetOutputs(); - std::transform(outputs.begin(), outputs.end(), + const std::vector& ccOutputs = cc->GetOutputs(); + std::transform(ccOutputs.begin(), ccOutputs.end(), std::back_inserter(deps), MapToNinjaPath()); } } From 7fb2bb3e8b0dc7bc718aadc4ab9a74cb7fdb656e Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sat, 18 Feb 2012 08:07:11 +0100 Subject: [PATCH 08/37] Ninja: win fixes: escape back slash/colon, use cd. as cmd.exe nop --- Source/CMakeLists.txt | 9 ++++----- Source/cmGlobalNinjaGenerator.cxx | 11 +++++++++-- Source/cmLocalNinjaGenerator.cxx | 4 ++++ Source/cmNinjaNormalTargetGenerator.cxx | 4 ++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 7ddabbd55..7cc56d706 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,9 +353,8 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) -# Ninja only works on UNIX. -IF(UNIX) - SET(SRCS ${SRCS} + +SET(SRCS ${SRCS} cmGlobalNinjaGenerator.cxx cmGlobalNinjaGenerator.h cmNinjaTypes.h @@ -368,8 +367,8 @@ IF(UNIX) cmNinjaUtilityTargetGenerator.cxx cmNinjaUtilityTargetGenerator.h ) - ADD_DEFINITIONS(-DCMAKE_USE_NINJA) -ENDIF(UNIX) +ADD_DEFINITIONS(-DCMAKE_USE_NINJA) + # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 7ff33bad8..879a97332 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -59,6 +59,7 @@ static bool IsIdentChar(char c) (c == '_') || (c == '$') || (c == '\\'); } + std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, std::ostream &vars) { if (std::find_if(ident.begin(), ident.end(), @@ -69,14 +70,19 @@ std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, vars << names.str() << " = " << ident << "\n"; return "$" + names.str(); } else { - return ident; + return EncodeLiteral(ident); } } + std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit) { std::string result = lit; cmSystemTools::ReplaceString(result, "$", "$$"); + cmSystemTools::ReplaceString(result, ":", "$:"); +#ifdef _WIN32 + cmSystemTools::ReplaceString(result, "/", "\\"); +#endif return result; } @@ -200,7 +206,8 @@ cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command, this->AddCustomCommandRule(); cmNinjaVars vars; - vars["COMMAND"] = command; + + vars["COMMAND"] = EncodeLiteral(command); vars["DESC"] = EncodeLiteral(description); cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream, diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index a4e66e49d..cf0e36a4d 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -294,7 +294,11 @@ std::string cmLocalNinjaGenerator::BuildCommandLine( // 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 diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 4dd6a9b9c..7cce45b1d 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -172,6 +172,10 @@ cmNinjaNormalTargetGenerator i != linkCmds.end(); ++i) { +#ifdef _WIN32 + // HACK: no TARGET_IMPLIB here??? + cmSystemTools::ReplaceString(*i, "/implib:", ""); +#endif this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); } linkCmds.insert(linkCmds.begin(), "$PRE_LINK"); From f999a59afe804063cebf4dd89ef99662829bf3e4 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sat, 18 Feb 2012 11:24:41 +0100 Subject: [PATCH 09/37] Ninja: don't define MSVC_IDE when using the ninja generator Thanks to Oscar Fuentes --- Modules/Platform/Windows-cl.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Platform/Windows-cl.cmake b/Modules/Platform/Windows-cl.cmake index ccccbc926..be6abb690 100644 --- a/Modules/Platform/Windows-cl.cmake +++ b/Modules/Platform/Windows-cl.cmake @@ -37,7 +37,7 @@ SET(CMAKE_COMPILE_RESOURCE "rc /fo ") # that is automatically copied into try_compile directories # by the global generator. SET(MSVC_IDE 1) -IF(CMAKE_GENERATOR MATCHES "Makefiles") +IF(CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja") SET(MSVC_IDE 0) IF(NOT CMAKE_VC_COMPILER_TESTS_RUN) SET(CMAKE_VC_COMPILER_TESTS 1) @@ -125,7 +125,7 @@ IF(CMAKE_GENERATOR MATCHES "Makefiles") ENDIF(CMAKE_COMPILER_RETURN) MAKE_DIRECTORY("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp3") ENDIF(NOT CMAKE_VC_COMPILER_TESTS_RUN) -ENDIF(CMAKE_GENERATOR MATCHES "Makefiles") +ENDIF(CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja") IF(MSVC_C_ARCHITECTURE_ID MATCHES 64) SET(CMAKE_CL_64 1) From 99856d537c996544bf495f33e6b058d83af4c6b7 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sat, 18 Feb 2012 16:32:47 +0000 Subject: [PATCH 10/37] Ninja: Partially revert "win fixes: escape back slash/colon, use cd. as cmd.exe nop" It introduced encoding rules in the wrong place, and broke the CustomCommand test case. This reverts commit 7fb2bb3e8b0dc7bc718aadc4ab9a74cb7fdb656e. --- Source/CMakeLists.txt | 9 +++++---- Source/cmGlobalNinjaGenerator.cxx | 11 ++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 7cc56d706..7ddabbd55 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,8 +353,9 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) - -SET(SRCS ${SRCS} +# Ninja only works on UNIX. +IF(UNIX) + SET(SRCS ${SRCS} cmGlobalNinjaGenerator.cxx cmGlobalNinjaGenerator.h cmNinjaTypes.h @@ -367,8 +368,8 @@ SET(SRCS ${SRCS} cmNinjaUtilityTargetGenerator.cxx cmNinjaUtilityTargetGenerator.h ) -ADD_DEFINITIONS(-DCMAKE_USE_NINJA) - + ADD_DEFINITIONS(-DCMAKE_USE_NINJA) +ENDIF(UNIX) # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 879a97332..7ff33bad8 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -59,7 +59,6 @@ static bool IsIdentChar(char c) (c == '_') || (c == '$') || (c == '\\'); } - std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, std::ostream &vars) { if (std::find_if(ident.begin(), ident.end(), @@ -70,19 +69,14 @@ std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, vars << names.str() << " = " << ident << "\n"; return "$" + names.str(); } else { - return EncodeLiteral(ident); + return ident; } } - std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit) { std::string result = lit; cmSystemTools::ReplaceString(result, "$", "$$"); - cmSystemTools::ReplaceString(result, ":", "$:"); -#ifdef _WIN32 - cmSystemTools::ReplaceString(result, "/", "\\"); -#endif return result; } @@ -206,8 +200,7 @@ cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command, this->AddCustomCommandRule(); cmNinjaVars vars; - - vars["COMMAND"] = EncodeLiteral(command); + vars["COMMAND"] = command; vars["DESC"] = EncodeLiteral(description); cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream, From 9362440a0b9193c417b42c50495d0a3ea6e098c4 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sat, 18 Feb 2012 19:04:55 +0000 Subject: [PATCH 11/37] Ninja: Identifier encoding rules for ' ' and ':' --- Source/cmGlobalNinjaGenerator.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 7ff33bad8..e9264ecba 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -56,7 +56,8 @@ static bool IsIdentChar(char c) ('a' <= c && c <= 'z') || ('+' <= c && c <= '9') || // +,-./ and numbers ('A' <= c && c <= 'Z') || - (c == '_') || (c == '$') || (c == '\\'); + (c == '_') || (c == '$') || (c == '\\') || + (c == ' ') || (c == ':'); } std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, @@ -69,7 +70,10 @@ std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident, vars << names.str() << " = " << ident << "\n"; return "$" + names.str(); } else { - return ident; + std::string result = ident; + cmSystemTools::ReplaceString(result, " ", "$ "); + cmSystemTools::ReplaceString(result, ":", "$:"); + return result; } } From cea03e632b71589592660614a63ff102ba923de8 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 19 Feb 2012 01:50:53 +0000 Subject: [PATCH 12/37] Ninja: Backslash rules for Windows Generally these are only required in build statements, as Ninja wants to be able to chop paths up. But it doesn't hurt to also try to use them in command line arguments. --- Source/cmGlobalNinjaGenerator.cxx | 17 +++++++++++++---- Source/cmGlobalNinjaGenerator.h | 1 + Source/cmLocalNinjaGenerator.cxx | 11 ++++++++--- Source/cmNinjaTargetGenerator.cxx | 7 ------- Source/cmNinjaTargetGenerator.h | 4 +++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index e9264ecba..b1d8e5b97 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -84,6 +84,15 @@ std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit) 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, @@ -122,7 +131,7 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, for(cmNinjaDeps::const_iterator i = outputs.begin(); i != outputs.end(); ++i) - builds << " " << EncodeIdent(*i, os); + builds << " " << EncodeIdent(EncodePath(*i), os); builds << ":"; // Write the rule. @@ -132,7 +141,7 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, for(cmNinjaDeps::const_iterator i = explicitDeps.begin(); i != explicitDeps.end(); ++i) - builds << " " << EncodeIdent(*i, os); + builds << " " << EncodeIdent(EncodePath(*i), os); // Write implicit dependencies. if(!implicitDeps.empty()) @@ -141,7 +150,7 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, for(cmNinjaDeps::const_iterator i = implicitDeps.begin(); i != implicitDeps.end(); ++i) - builds << " " << EncodeIdent(*i, os); + builds << " " << EncodeIdent(EncodePath(*i), os); } // Write order-only dependencies. @@ -151,7 +160,7 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin(); i != orderOnlyDeps.end(); ++i) - builds << " " << EncodeIdent(*i, os); + builds << " " << EncodeIdent(EncodePath(*i), os); } builds << "\n"; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 6f3c6b2c9..3f8644ea1 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -59,6 +59,7 @@ public: 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 diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index cf0e36a4d..28e8d47e5 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -28,6 +28,9 @@ cmLocalNinjaGenerator::cmLocalNinjaGenerator() , HomeRelativeOutputPath("") { this->IsMakefileGenerator = true; +#ifdef _WIN32 + this->WindowsShell = true; +#endif } //---------------------------------------------------------------------------- @@ -256,9 +259,11 @@ void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os) std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path) { - return this->Convert(path, - cmLocalGenerator::HOME_OUTPUT, - cmLocalGenerator::MAKEFILE); + std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT); +#ifdef _WIN32 + cmSystemTools::ReplaceString(convPath, "/", "\\"); +#endif + return convPath; } void diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 1e31044b9..8a563b640 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -212,13 +212,6 @@ ComputeDefines(cmSourceFile *source, const std::string& language) return defines; } -std::string cmNinjaTargetGenerator::ConvertToNinjaPath(const char *path) const -{ - return this->LocalGenerator->Convert(path, - cmLocalGenerator::HOME_OUTPUT, - cmLocalGenerator::MAKEFILE); -} - cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const { // Static libraries never depend on other targets for linking. diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index cf47abf1d..2986844b6 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -75,7 +75,9 @@ protected: std::string ComputeDefines(cmSourceFile *source, const std::string& language); - std::string ConvertToNinjaPath(const char *path) const; + std::string ConvertToNinjaPath(const char *path) const { + return this->GetLocalGenerator()->ConvertToNinjaPath(path); + } cmLocalNinjaGenerator::map_to_ninja_path MapToNinjaPath() const { return this->GetLocalGenerator()->MapToNinjaPath(); } From 43b1d8bad0cf78148e4d6e7b4013850731d5a824 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 19 Feb 2012 02:46:37 +0000 Subject: [PATCH 13/37] Ninja: Shell encode paths used in "cd" commands --- Source/cmLocalNinjaGenerator.cxx | 10 +++++----- Source/cmNinjaNormalTargetGenerator.cxx | 9 ++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 28e8d47e5..32f3833cc 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -325,12 +325,12 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, { 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 "; - if (const char* wd = cc->GetWorkingDirectory()) - cdCmd << wd; - else - cdCmd << this->GetMakefile()->GetStartOutputDirectory(); + cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL); cmdLines.push_back(cdCmd.str()); } for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) { diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 7cce45b1d..4434aef76 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -377,9 +377,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for // the link commands. - if (!preLinkCmdLines.empty()) - preLinkCmdLines.push_back(std::string("cd ") + - this->GetMakefile()->GetHomeOutputDirectory()); + 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); From 0643fee2fe92b7dccc4e63be7329f6da96f2e61f Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 19 Feb 2012 03:07:09 +0000 Subject: [PATCH 14/37] Ninja: Shell encode various CMake invocations --- Source/cmGlobalNinjaGenerator.cxx | 15 +++++++++++---- Source/cmNinjaNormalTargetGenerator.cxx | 8 ++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index b1d8e5b97..7c1529b5b 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -743,12 +743,19 @@ void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) { - cmMakefile* mfRoot = this->LocalGenerators[0]->GetMakefile(); + cmLocalGenerator *lg = this->LocalGenerators[0]; + cmMakefile* mfRoot = lg->GetMakefile(); std::ostringstream cmd; - cmd << mfRoot->GetRequiredDefinition("CMAKE_COMMAND") - << " -H" << mfRoot->GetHomeDirectory() - << " -B" << mfRoot->GetHomeOutputDirectory(); + 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(), diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 4434aef76..0174c3d3d 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -198,7 +198,9 @@ cmNinjaNormalTargetGenerator if (this->TargetNameOut != this->TargetNameReal) { std::string cmakeCommand = - this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"); + this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"), + cmLocalGenerator::SHELL); if (targetType == cmTarget::EXECUTABLE) this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE", cmakeCommand + @@ -238,7 +240,9 @@ cmNinjaNormalTargetGenerator // We have archive link commands set. First, delete the existing archive. std::vector linkCmds; std::string cmakeCommand = - this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"); + 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. From 70c39c845f2f2909413d8bcb19a6b72e19ff83c4 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 19 Feb 2012 03:07:21 +0000 Subject: [PATCH 15/37] Ninja: Shell encode the command used in custom commands --- Source/cmLocalNinjaGenerator.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 32f3833cc..687dbb87d 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -334,7 +334,8 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, cmdLines.push_back(cdCmd.str()); } for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) { - cmdLines.push_back(ccg.GetCommand(i)); + cmdLines.push_back(this->ConvertToOutputFormat(ccg.GetCommand(i).c_str(), + SHELL)); std::string& cmd = cmdLines.back(); ccg.AppendArguments(i, cmd); } From 895914881be32f862dee638a0a86313366f9b7b4 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sun, 19 Feb 2012 11:10:18 +0100 Subject: [PATCH 16/37] Ninja: also build ninja support on Windows --- Source/CMakeLists.txt | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 7ddabbd55..e0a6f4278 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,23 +353,20 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) -# Ninja only works on UNIX. -IF(UNIX) - 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) -ENDIF(UNIX) +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) # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) From adb54f3dbf802f643c8af9f8de6f67f24804dd22 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sun, 19 Feb 2012 11:30:07 +0100 Subject: [PATCH 17/37] Ninja: add some hacks for Windows --- Source/CMakeLists.txt | 4 ++++ Source/cmNinjaNormalTargetGenerator.cxx | 3 +-- Source/cmNinjaTargetGenerator.cxx | 10 ++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e0a6f4278..a53a0c182 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -367,6 +367,10 @@ SET(SRCS ${SRCS} cmNinjaUtilityTargetGenerator.h ) ADD_DEFINITIONS(-DCMAKE_USE_NINJA) +IF(WIN32) + # TODO remove + ADD_DEFINITIONS(-DENABLE_WIN32_NINJA_HACKS) +endif() # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 0174c3d3d..97b46bfdc 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -172,8 +172,7 @@ cmNinjaNormalTargetGenerator i != linkCmds.end(); ++i) { -#ifdef _WIN32 - // HACK: no TARGET_IMPLIB here??? +#ifdef ENABLE_WIN32_NINJA_HACKS cmSystemTools::ReplaceString(*i, "/implib:", ""); #endif this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 8a563b640..cc6024b11 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -223,9 +223,19 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const if(!cli) return cmNinjaDeps(); + +#ifndef ENABLE_WIN32_NINJA_HACKS const std::vector &deps = cli->GetDepends(); cmNinjaDeps result(deps.size()); std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath()); +#else + cmNinjaDeps result; + for(unsigned i = 0; i < cli->GetItems().size(); ++i) { + if( cli->GetItems()[i].Target ) { + result.push_back( cli->GetItems()[i].Target->GetName() ); + } + } +#endif return result; } From 3b847415154b13e401bf9f71042578ad21c9c1c9 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sun, 19 Feb 2012 19:15:57 +0100 Subject: [PATCH 18/37] Ninja: disable unfinished Windows ninja support This way the branch is ready-to-merge, even when the generator is broken on Windows. --- Source/CMakeLists.txt | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index a53a0c182..3ae946469 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,23 +353,21 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) -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) -IF(WIN32) - # TODO remove - ADD_DEFINITIONS(-DENABLE_WIN32_NINJA_HACKS) +if(NOT WIN32) + 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) endif() # create a library used by the command line and the GUI From 21997cea4d8694077e8bcf9ba4cbd3d96a3f163c Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sun, 19 Feb 2012 19:21:05 +0100 Subject: [PATCH 19/37] Ninja: mark the Windows specific hacks with a comment only --- Source/cmNinjaNormalTargetGenerator.cxx | 3 ++- Source/cmNinjaTargetGenerator.cxx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 97b46bfdc..66714e953 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -172,7 +172,8 @@ cmNinjaNormalTargetGenerator i != linkCmds.end(); ++i) { -#ifdef ENABLE_WIN32_NINJA_HACKS +#ifdef _WIN32 + // TODO TARGET_IMPLIB is empty cmSystemTools::ReplaceString(*i, "/implib:", ""); #endif this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index cc6024b11..1a8d13d41 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -224,11 +224,14 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const return cmNinjaDeps(); -#ifndef ENABLE_WIN32_NINJA_HACKS +#ifndef _WIN32 const std::vector &deps = cli->GetDepends(); cmNinjaDeps result(deps.size()); std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath()); #else + // TODO The ninja generator misses a lot on Windows. + // This hack avoids a problem when a Dll is build: + // It builds the .dll but uses the .lib which is then unknown to ninja. cmNinjaDeps result; for(unsigned i = 0; i < cli->GetItems().size(); ++i) { if( cli->GetItems()[i].Target ) { From a1a30340a2eb0174f9f6911ee1e2cf4a36eaac96 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sun, 19 Feb 2012 21:38:10 +0000 Subject: [PATCH 20/37] Ninja: Import library support for Windows --- Source/cmLocalNinjaGenerator.cxx | 1 + Source/cmNinjaNormalTargetGenerator.cxx | 21 +++++++++++++++++---- Source/cmNinjaTargetGenerator.cxx | 13 ------------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 687dbb87d..ea03cbb2e 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -31,6 +31,7 @@ cmLocalNinjaGenerator::cmLocalNinjaGenerator() #ifdef _WIN32 this->WindowsShell = true; #endif + this->TargetImplib = "$TARGET_IMPLIB"; } //---------------------------------------------------------------------------- diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 66714e953..9294a013f 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -172,10 +172,6 @@ cmNinjaNormalTargetGenerator i != linkCmds.end(); ++i) { -#ifdef _WIN32 - // TODO TARGET_IMPLIB is empty - cmSystemTools::ReplaceString(*i, "/implib:", ""); -#endif this->GetLocalGenerator()->ExpandRuleVariables(*i, vars); } linkCmds.insert(linkCmds.begin(), "$PRE_LINK"); @@ -314,6 +310,9 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() 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; @@ -357,6 +356,11 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } } + if (!this->TargetNameImport.empty()) { + vars["TARGET_IMPLIB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + targetOutputImplib.c_str(), cmLocalGenerator::SHELL); + } + std::vector *cmdLists[3] = { &this->GetTarget()->GetPreBuildCommands(), &this->GetTarget()->GetPreLinkCommands(), @@ -434,6 +438,15 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } } + 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()); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 1a8d13d41..8a563b640 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -223,22 +223,9 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const if(!cli) return cmNinjaDeps(); - -#ifndef _WIN32 const std::vector &deps = cli->GetDepends(); cmNinjaDeps result(deps.size()); std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath()); -#else - // TODO The ninja generator misses a lot on Windows. - // This hack avoids a problem when a Dll is build: - // It builds the .dll but uses the .lib which is then unknown to ninja. - cmNinjaDeps result; - for(unsigned i = 0; i < cli->GetItems().size(); ++i) { - if( cli->GetItems()[i].Target ) { - result.push_back( cli->GetItems()[i].Target->GetName() ); - } - } -#endif return result; } From 54bd175eea66704a879fc72278cdbb49efdd801c Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Tue, 21 Feb 2012 21:18:05 +0100 Subject: [PATCH 21/37] Ninja: windows msvc: create for each target a .pdb file --- Source/cmNinjaNormalTargetGenerator.cxx | 3 +++ Source/cmNinjaTargetGenerator.cxx | 19 +++++++++++++++++++ Source/cmNinjaTargetGenerator.h | 1 + 3 files changed, 23 insertions(+) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 9294a013f..6e08bcace 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -136,6 +136,7 @@ cmNinjaNormalTargetGenerator vars.Target = "$out"; vars.TargetSOName = "$SONAME"; vars.TargetInstallNameDir = "$INSTALLNAME_DIR"; + vars.TargetPDB = "$TARGET_PDB"; // Setup the target version. std::string targetVersionMajor; @@ -361,6 +362,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() targetOutputImplib.c_str(), cmLocalGenerator::SHELL); } + vars["TARGET_PDB"] = this->GetTargetPDB(); + std::vector *cmdLists[3] = { &this->GetTarget()->GetPreBuildCommands(), &this->GetTarget()->GetPreLinkCommands(), diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 8a563b640..7b1c7d924 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -270,6 +270,23 @@ 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) @@ -293,6 +310,7 @@ cmNinjaTargetGenerator vars.Object = "$out"; std::string flags = "$FLAGS"; vars.Defines = "$DEFINES"; + vars.TargetPDB = "$TARGET_PDB"; std::string depfile; std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language; @@ -426,6 +444,7 @@ cmNinjaTargetGenerator cmNinjaVars vars; vars["FLAGS"] = this->ComputeFlagsForObject(source, language); vars["DEFINES"] = this->ComputeDefines(source, language); + vars["TARGET_PDB"] = this->GetTargetPDB(); cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), comment, diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 2986844b6..f9270a25c 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -38,6 +38,7 @@ public: virtual void Generate() = 0; + std::string GetTargetPDB() const; std::string GetTargetName() const; protected: From eabc9b0bc5d2dc237d247d1e86fb0818b22d31c0 Mon Sep 17 00:00:00 2001 From: David Cole Date: Wed, 22 Feb 2012 16:31:00 -0500 Subject: [PATCH 22/37] Ninja: CMake: Adapt Ninja generator for per-target include dirs The confluence of the ninja-generator and target-include-directories branches conspired to produce a nice little compiler error when they were both merged into 'next'... Yay for Continuous dashboards! --- Source/cmNinjaTargetGenerator.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 7b1c7d924..c776fcf40 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -145,8 +145,11 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source, // TODO: Handle response file. // Add include directory flags. { + std::vector includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->Target, + language.c_str()); std::string includeFlags = - this->LocalGenerator->GetIncludeFlags(language.c_str(), false); + this->LocalGenerator->GetIncludeFlags(includes, language.c_str(), false); this->LocalGenerator->AppendFlags(flags, includeFlags.c_str()); } From d2731a376cce3044b507bfaf4427d89f0a03d2cf Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Thu, 23 Feb 2012 22:25:03 +0000 Subject: [PATCH 23/37] Ninja: Add a missed license header --- Source/cmNinjaUtilityTargetGenerator.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index bf8194de5..8eeca77d3 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -1,3 +1,15 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2011 Peter Collingbourne + Copyright 2011 Nicolas Despres + + 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" From 80ff2102a41117fc829ad64c7c963bd0858a221b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Mon, 27 Feb 2012 04:05:31 +0000 Subject: [PATCH 24/37] Ninja: Use cmSystemTools::ExpandListArgument to split compile/link commands --- Source/cmNinjaNormalTargetGenerator.cxx | 11 +++++++---- Source/cmNinjaTargetGenerator.cxx | 11 +++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 6e08bcace..613c7b5b8 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -219,6 +219,7 @@ std::vector cmNinjaNormalTargetGenerator ::ComputeLinkCmd() { + std::vector linkCmds; cmTarget::TargetType targetType = this->GetTarget()->GetType(); switch (targetType) { case cmTarget::STATIC_LIBRARY: { @@ -230,7 +231,8 @@ cmNinjaNormalTargetGenerator if (const char *linkCmd = this->GetMakefile()->GetDefinition(linkCmdVar.c_str())) { - return std::vector(1, linkCmd); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); + return linkCmds; } } @@ -249,7 +251,7 @@ cmNinjaNormalTargetGenerator linkCmdVar += "_ARCHIVE_CREATE"; const char *linkCmd = this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); - linkCmds.push_back(linkCmd); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); } { std::string linkCmdVar = "CMAKE_"; @@ -257,7 +259,7 @@ cmNinjaNormalTargetGenerator linkCmdVar += "_ARCHIVE_FINISH"; const char *linkCmd = this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); - linkCmds.push_back(linkCmd); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); } return linkCmds; } @@ -281,7 +283,8 @@ cmNinjaNormalTargetGenerator } const char *linkCmd = this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); - return std::vector(1, linkCmd); + cmSystemTools::ExpandListArgument(linkCmd, linkCmds); + return linkCmds; } default: assert(0 && "Unexpected target type"); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index c776fcf40..d0b415611 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -334,8 +334,15 @@ cmNinjaTargetGenerator compileCmdVar += "_COMPILE_OBJECT"; std::string compileCmd = this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str()); + std::vector compileCmds; + cmSystemTools::ExpandListArgument(compileCmd, compileCmds); - this->GetLocalGenerator()->ExpandRuleVariables(compileCmd, vars); + for (std::vector::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; @@ -343,7 +350,7 @@ cmNinjaTargetGenerator std::ostringstream description; description << "Building " << language << " object $out"; this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language), - compileCmd, + cmdLine, description.str(), comment.str(), depfile); From 7a6b5f465122a9da1f9439ec3cec461e1c0c999c Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 28 Feb 2012 02:55:24 +0000 Subject: [PATCH 25/37] Ninja: Remove an unnecessary variable --- Source/cmNinjaNormalTargetGenerator.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 613c7b5b8..88f6f87ff 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -237,7 +237,6 @@ cmNinjaNormalTargetGenerator } // We have archive link commands set. First, delete the existing archive. - std::vector linkCmds; std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat( this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"), From f1bb08f55b1ee0e8ae72d66289ad89ad27d75701 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Thu, 23 Feb 2012 19:39:54 +0100 Subject: [PATCH 26/37] Ninja: ensure the output dir exists at compile time --- Source/cmNinjaNormalTargetGenerator.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 88f6f87ff..f4069c796 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -42,6 +42,11 @@ cmNinjaNormalTargetGenerator(cmTarget* target) 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() From dbe3dce54670d774fd43e061be673b11e1dd18de Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Tue, 6 Mar 2012 23:41:40 +0100 Subject: [PATCH 27/37] Ninja: add .def file support --- Source/cmGlobalNinjaGenerator.h | 2 ++ Source/cmLocalGenerator.cxx | 2 +- Source/cmNinjaNormalTargetGenerator.cxx | 1 + Source/cmNinjaTargetGenerator.cxx | 36 +++++++++++++++++++++++++ Source/cmNinjaTargetGenerator.h | 6 +++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 3f8644ea1..e77252f12 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -325,6 +325,8 @@ private: typedef std::map TargetAliasMap; TargetAliasMap TargetAliases; + + static cmLocalGenerator* LocalGenerator; }; #endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index aa3286cfe..3920b8955 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -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 += " "; } } diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index f4069c796..9e05b4c81 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -285,6 +285,7 @@ cmNinjaNormalTargetGenerator default: assert(0 && "Unexpected target type"); } + const char *linkCmd = this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str()); cmSystemTools::ExpandListArgument(linkCmd, linkCmds); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index d0b415611..8fa367f03 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -229,6 +229,13 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const const std::vector &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; } @@ -328,6 +335,7 @@ cmNinjaTargetGenerator } vars.Flags = flags.c_str(); + // Rule for compiling object file. std::string compileCmdVar = "CMAKE_"; compileCmdVar += language; @@ -395,6 +403,8 @@ cmNinjaTargetGenerator 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; } @@ -465,3 +475,29 @@ cmNinjaTargetGenerator orderOnlyDeps, vars); } + +//---------------------------------------------------------------------------- +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 index f9270a25c..3e70a2c9a 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -107,12 +107,18 @@ protected: 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 From 8c634330db7988c56d0327d60c8056460634974b Mon Sep 17 00:00:00 2001 From: David Cole Date: Wed, 7 Mar 2012 16:07:45 -0500 Subject: [PATCH 28/37] Ninja: Add friend struct so it can access the private ConvertToNinjaPath. The HP aCC compiler is apparently more strict than all our other dashboard compilers with respect to complaining about this. --- Source/cmLocalNinjaGenerator.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index 8ef4d2851..e9bbf4301 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -95,6 +95,8 @@ private: 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) From d40eebd89df7d0a9bfa12d12c7a11d61e8cfec1a Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Fri, 9 Mar 2012 14:28:21 -0500 Subject: [PATCH 29/37] Ninja: Add a cache option CMAKE_ENABLE_NINJA to enable the ninja generator. Make the option default to on, for platforms where CMake passes all tests with the ninja generator. This is currently only Linux. --- Source/CMakeLists.txt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index eb4327ca6..18f9b8b22 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -353,7 +353,18 @@ IF (WIN32) ENDIF(NOT UNIX) ENDIF (WIN32) -if(NOT 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 @@ -368,7 +379,9 @@ if(NOT WIN32) cmNinjaUtilityTargetGenerator.h ) ADD_DEFINITIONS(-DCMAKE_USE_NINJA) -endif() +ELSE() + MESSAGE(STATUS "Disable ninja generator.") +ENDIF() # create a library used by the command line and the GUI ADD_LIBRARY(CMakeLib ${SRCS}) From 9a0d5a828a8f47b4388dbeab8bde9921ee3b7146 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Sat, 10 Mar 2012 12:19:18 +0100 Subject: [PATCH 30/37] Ninja: add /DEF: flag to linker call --- Source/cmNinjaNormalTargetGenerator.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 9e05b4c81..af7bcd621 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -341,6 +341,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() 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. From ac800f49d07337df79565c77fe0847222ade520b Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 13 Mar 2012 09:17:46 -0400 Subject: [PATCH 31/37] Ninja: Constify use of cmCustomCommand The generator never needs to modify custom command instances. --- Source/cmGlobalNinjaGenerator.h | 4 ++-- Source/cmLocalNinjaGenerator.cxx | 6 +++--- Source/cmLocalNinjaGenerator.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index e77252f12..39df826cf 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -270,7 +270,7 @@ private: /// Called when we have seen the given custom command. Returns true /// if we has seen it before. - bool SeenCustomCommand(cmCustomCommand *cc) { + bool SeenCustomCommand(cmCustomCommand const *cc) { return !this->CustomCommands.insert(cc).second; } @@ -315,7 +315,7 @@ private: cmNinjaDeps AllDependencies; /// The set of custom commands we have seen. - std::set CustomCommands; + std::set CustomCommands; /// The set of custom command outputs we have seen. std::set CustomCommandOutputs; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index ea03cbb2e..78072b51f 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -343,8 +343,8 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, } void -cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(cmCustomCommand *cc, - const cmNinjaDeps& orderOnlyDeps) +cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( + cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps) { if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc)) return; @@ -383,7 +383,7 @@ cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(cmCustomCommand *cc, } } -void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand* cc, +void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc, cmTarget* target) { this->CustomCommandTargets[cc].insert(target); diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index e9bbf4301..28b431d6e 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -118,17 +118,17 @@ private: void AppendCustomCommandLines(const cmCustomCommand *cc, std::vector &cmdLines); void WriteCustomCommandRule(); - void WriteCustomCommandBuildStatement(cmCustomCommand *cc, + void WriteCustomCommandBuildStatement(cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps); - void AddCustomCommandTarget(cmCustomCommand* cc, cmTarget* target); + void AddCustomCommandTarget(cmCustomCommand const* cc, cmTarget* target); void WriteCustomCommandBuildStatements(); private: std::string ConfigName; std::string HomeRelativeOutputPath; - typedef std::map > + typedef std::map > CustomCommandTargetMap; CustomCommandTargetMap CustomCommandTargets; }; From bba37dd51753165ce3ade61badb61c07e1058e90 Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Tue, 13 Mar 2012 17:01:25 -0400 Subject: [PATCH 32/37] Ninja: Fix for PDB files with spaces in the path. This calls ConvertToOutputFormat on the PDB paths for pdb file paths used in both library creation and the building of object files. --- Source/cmNinjaNormalTargetGenerator.cxx | 3 ++- Source/cmNinjaTargetGenerator.cxx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index af7bcd621..439f73413 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -372,7 +372,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() targetOutputImplib.c_str(), cmLocalGenerator::SHELL); } - vars["TARGET_PDB"] = this->GetTargetPDB(); + vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL); std::vector *cmdLists[3] = { &this->GetTarget()->GetPreBuildCommands(), diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 8fa367f03..b9f997dc4 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -464,7 +464,8 @@ cmNinjaTargetGenerator cmNinjaVars vars; vars["FLAGS"] = this->ComputeFlagsForObject(source, language); vars["DEFINES"] = this->ComputeDefines(source, language); - vars["TARGET_PDB"] = this->GetTargetPDB(); + vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat( + this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL); cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(), comment, From 48eb7fc7d7b3fa5570a7b32968fa2cff07bf6fe7 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 16 Mar 2012 09:58:32 -0400 Subject: [PATCH 33/37] Ninja: Avoid using 'this' in member initializers VS complains warning C4355: 'this' : used in base member initializer list so initialize the member in the constructor body instead. --- Source/cmNinjaNormalTargetGenerator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 439f73413..92421815a 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -27,8 +27,8 @@ cmNinjaNormalTargetGenerator(cmTarget* target) , TargetNameReal() , TargetNameImport() , TargetNamePDB() - , TargetLinkLanguage(target->GetLinkerLanguage(this->GetConfigName())) { + this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName()); if (target->GetType() == cmTarget::EXECUTABLE) target->GetExecutableNames(this->TargetNameOut, this->TargetNameReal, From a2514f15fae34abb6f29dddf6f5cfe8b171a8035 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 13 Mar 2012 09:24:54 -0400 Subject: [PATCH 34/37] Simplify cmNinjaTargetGenerator using cmGeneratorTarget Replace the classification of source files in this generator using that computed by cmGeneratorTarget. --- Source/cmNinjaTargetGenerator.cxx | 76 +++++++++++++++---------------- Source/cmNinjaTargetGenerator.h | 5 +- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index b9f997dc4..23662d91a 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -14,6 +14,7 @@ #include "cmGlobalNinjaGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorTarget.h" #include "cmNinjaNormalTargetGenerator.h" #include "cmNinjaUtilityTargetGenerator.h" #include "cmSystemTools.h" @@ -60,6 +61,8 @@ cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmTarget* target) static_cast(Makefile->GetLocalGenerator())), Objects() { + this->GeneratorTarget = + this->GetGlobalGenerator()->GetGeneratorTarget(target); } cmNinjaTargetGenerator::~cmNinjaTargetGenerator() @@ -231,9 +234,9 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath()); // Add a dependency on the link definitions file, if any. - if(!this->ModuleDefinitionFile.empty()) + if(!this->GeneratorTarget->ModuleDefinitionFile.empty()) { - result.push_back(this->ModuleDefinitionFile); + result.push_back(this->GeneratorTarget->ModuleDefinitionFile); } return result; @@ -377,12 +380,26 @@ cmNinjaTargetGenerator << this->GetTargetName() << "\n\n"; - // For each source files of this target. - for(std::vector::const_iterator i = - this->GetTarget()->GetSourceFiles().begin(); - i != this->GetTarget()->GetSourceFiles().end(); - ++i) - this->WriteObjectBuildStatement(*i); + for(std::vector::const_iterator + si = this->GeneratorTarget->CustomCommands.begin(); + si != this->GeneratorTarget->CustomCommands.end(); ++si) + { + cmCustomCommand const* cc = (*si)->GetCustomCommand(); + this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget()); + } + // TODO: this->GeneratorTarget->OSXContent + for(std::vector::const_iterator + si = this->GeneratorTarget->ExternalObjects.begin(); + si != this->GeneratorTarget->ExternalObjects.end(); ++si) + { + this->Objects.push_back(this->GetSourceFilePath(*si)); + } + for(std::vector::const_iterator + si = this->GeneratorTarget->ObjectSources.begin(); + si != this->GeneratorTarget->ObjectSources.end(); ++si) + { + this->WriteObjectBuildStatement(*si); + } this->GetBuildFileStream() << "\n"; } @@ -391,26 +408,10 @@ 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; @@ -435,21 +436,16 @@ cmNinjaTargetGenerator std::back_inserter(orderOnlyDeps), MapToNinjaPath()); } - // Add order-only dependency on any header file with a custom command. - { - const std::vector& sources = - this->GetTarget()->GetSourceFiles(); - for(std::vector::const_iterator si = sources.begin(); - si != sources.end(); ++si) { - if (!(*si)->GetLanguage()) { - if (cmCustomCommand* cc = (*si)->GetCustomCommand()) { - const std::vector& ccoutputs = cc->GetOutputs(); - std::transform(ccoutputs.begin(), ccoutputs.end(), - std::back_inserter(orderOnlyDeps), MapToNinjaPath()); - } - } + // Add order-only dependencies on custom command outputs. + for(std::vector::const_iterator + si = this->GeneratorTarget->CustomCommands.begin(); + si != this->GeneratorTarget->CustomCommands.end(); ++si) + { + cmCustomCommand const* cc = (*si)->GetCustomCommand(); + const std::vector& 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 @@ -482,7 +478,7 @@ void cmNinjaTargetGenerator ::AddModuleDefinitionFlag(std::string& flags) { - if(this->ModuleDefinitionFile.empty()) + if(this->GeneratorTarget->ModuleDefinitionFile.empty()) { return; } @@ -499,6 +495,6 @@ cmNinjaTargetGenerator // vs6's "cl -link" pass it to the linker. std::string flag = defFileFlag; flag += (this->LocalGenerator->ConvertToLinkReference( - this->ModuleDefinitionFile.c_str())); + this->GeneratorTarget->ModuleDefinitionFile.c_str())); this->LocalGenerator->AppendFlags(flags, flag.c_str()); } diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 3e70a2c9a..f639116b9 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -20,6 +20,7 @@ class cmTarget; class cmGlobalNinjaGenerator; class cmGeneratedFileStream; +class cmGeneratorTarget; class cmMakefile; class cmSourceFile; class cmCustomCommand; @@ -112,13 +113,11 @@ protected: private: cmTarget* Target; + cmGeneratorTarget* GeneratorTarget; 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 From f5b06cda0f187929ac68ed64595c22d4e6ec773c Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 13 Mar 2012 09:30:23 -0400 Subject: [PATCH 35/37] Pre-compute object file names before Ninja generation Implement cmGlobalGenerator::ComputeTargetObjects in the Ninja generator to pre-compute all the object file names. Use the results during generation instead of re-computing it later. --- Source/cmGlobalNinjaGenerator.cxx | 29 +++++++++++++++++++++++++++++ Source/cmGlobalNinjaGenerator.h | 6 ++++++ Source/cmLocalNinjaGenerator.cxx | 31 ------------------------------- Source/cmLocalNinjaGenerator.h | 3 --- Source/cmNinjaTargetGenerator.cxx | 5 ++++- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 7c1529b5b..03c8be963 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -14,6 +14,7 @@ #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorTarget.h" #include "cmVersion.h" const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja"; @@ -499,6 +500,34 @@ bool cmGlobalNinjaGenerator::HasRule(const std::string &name) return (rule != this->Rules.end()); } +//---------------------------------------------------------------------------- +// Private virtual overrides + +// TODO: Refactor to combine with cmGlobalUnixMakefileGenerator3 impl. +void cmGlobalNinjaGenerator::ComputeTargetObjects(cmGeneratorTarget* gt) const +{ + cmTarget* target = gt->Target; + + // Compute full path to object file directory for this target. + std::string dir_max; + dir_max += gt->Makefile->GetCurrentOutputDirectory(); + dir_max += "/"; + dir_max += gt->LocalGenerator->GetTargetDirectory(*target); + dir_max += "/"; + gt->ObjectDirectory = dir_max; + + // Compute the name of each object file. + for(std::vector::iterator + si = gt->ObjectSources.begin(); + si != gt->ObjectSources.end(); ++si) + { + cmSourceFile* sf = *si; + std::string objectName = gt->LocalGenerator + ->GetObjectFileNameWithoutTarget(*sf, dir_max); + gt->Objects[sf] = objectName; + } +} + //---------------------------------------------------------------------------- // Private methods diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 39df826cf..321758185 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -18,6 +18,7 @@ class cmLocalGenerator; class cmGeneratedFileStream; +class cmGeneratorTarget; /** * \class cmGlobalNinjaGenerator @@ -235,6 +236,11 @@ protected: /// @see cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS() virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() { return true; } +private: + + /// @see cmGlobalGenerator::ComputeTargetObjects + virtual void ComputeTargetObjects(cmGeneratorTarget* gt) const; + private: // In order to access the AddDependencyToAll() functions and co. friend class cmLocalNinjaGenerator; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 78072b51f..425b21917 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -117,37 +117,6 @@ cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator() return static_cast(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. diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index 28b431d6e..ea44b2fe2 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -59,9 +59,6 @@ public: 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; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 23662d91a..cc6743487 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -256,7 +256,10 @@ cmNinjaTargetGenerator std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); if(!path.empty()) path += "/"; - path += this->LocalGenerator->GetObjectFileName(*this->Target, *source); + std::string const& objectName = this->GeneratorTarget->Objects[source]; + path += this->LocalGenerator->GetTargetDirectory(*this->Target); + path += "/"; + path += objectName; return path; } From 61124de4c06b1195e79ee71326b902baf23c4c32 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 13 Mar 2012 10:05:07 -0400 Subject: [PATCH 36/37] Build object library targets in Ninja Treat OBJECT libraries as STATIC libraries but leave out the archive step. The object files will be left behind for reference by other targets later. --- Source/cmGlobalNinjaGenerator.cxx | 1 + Source/cmNinjaNormalTargetGenerator.cxx | 40 +++++++++++++++++++++---- Source/cmNinjaNormalTargetGenerator.h | 1 + Source/cmNinjaTargetGenerator.cxx | 4 ++- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 03c8be963..9cbd50233 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -664,6 +664,7 @@ cmGlobalNinjaGenerator target->GetFullPath(configName).c_str())); break; + case cmTarget::OBJECT_LIBRARY: case cmTarget::UTILITY: { std::string path = ng->ConvertToNinjaPath( target->GetMakefile()->GetStartOutputDirectory()); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 92421815a..685944582 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -43,10 +43,13 @@ cmNinjaNormalTargetGenerator(cmTarget* target) 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()); + if(target->GetType() != cmTarget::OBJECT_LIBRARY) + { + // 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() @@ -67,8 +70,15 @@ void cmNinjaNormalTargetGenerator::Generate() // Write the build statements this->WriteObjectBuildStatements(); - this->WriteLinkRule(); - this->WriteLinkStatement(); + if(this->GetTarget()->GetType() == cmTarget::OBJECT_LIBRARY) + { + this->WriteObjectLibStatement(); + } + else + { + this->WriteLinkRule(); + this->WriteLinkStatement(); + } this->GetBuildFileStream() << "\n"; this->GetRulesFileStream() << "\n"; @@ -467,3 +477,21 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), this->GetTarget()); } + +//---------------------------------------------------------------------------- +void cmNinjaNormalTargetGenerator::WriteObjectLibStatement() +{ + // Write a phony output that depends on all object files. + cmNinjaDeps outputs; + this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs); + cmNinjaDeps depends = this->GetObjects(); + cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(), + "Object library " + + this->GetTargetName(), + outputs, + depends); + + // Add aliases for the target name. + this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), + this->GetTarget()); +} diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 99f5a133b..1702cafe8 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -32,6 +32,7 @@ private: void WriteLanguagesRules(); void WriteLinkRule(); void WriteLinkStatement(); + void WriteObjectLibStatement(); std::vector ComputeLinkCmd(); private: diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index cc6743487..2a784056a 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -34,6 +34,7 @@ cmNinjaTargetGenerator::New(cmTarget* target) case cmTarget::SHARED_LIBRARY: case cmTarget::STATIC_LIBRARY: case cmTarget::MODULE_LIBRARY: + case cmTarget::OBJECT_LIBRARY: return new cmNinjaNormalTargetGenerator(target); case cmTarget::UTILITY: @@ -221,7 +222,8 @@ ComputeDefines(cmSourceFile *source, const std::string& language) cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const { // Static libraries never depend on other targets for linking. - if (this->Target->GetType() == cmTarget::STATIC_LIBRARY) + if (this->Target->GetType() == cmTarget::STATIC_LIBRARY || + this->Target->GetType() == cmTarget::OBJECT_LIBRARY) return cmNinjaDeps(); cmComputeLinkInformation* cli = From 51997cb6dc93eff826e95ac326eb9af6763eaa32 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 15 Mar 2012 08:56:40 -0400 Subject: [PATCH 37/37] Ninja: Honor $ source expressions Add objects from object libraries referenced using this syntax to the set of objects linked in a target. --- Source/cmNinjaTargetGenerator.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 2a784056a..675a160e0 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -406,6 +406,17 @@ cmNinjaTargetGenerator this->WriteObjectBuildStatement(*si); } + { + // Add object library contents as external objects. + std::vector objs; + this->GeneratorTarget->UseObjectLibraries(objs); + for(std::vector::iterator oi = objs.begin(); + oi != objs.end(); ++oi) + { + this->Objects.push_back(ConvertToNinjaPath(oi->c_str())); + } + } + this->GetBuildFileStream() << "\n"; }