[cmake-developers] [PATCH 3/3] Add the Ninja generator
Peter Collingbourne
peter at pcc.me.uk
Fri Nov 11 21:36:07 EST 2011
---
Modules/CMakeNinjaFindMake.cmake | 17 +
Modules/Compiler/GNU.cmake | 9 +
Modules/ExternalProject.cmake | 19 +-
Source/CMakeLists.txt | 18 +
Source/cmGlobalNinjaGenerator.cxx | 772 ++++++++++++++++++++
Source/cmGlobalNinjaGenerator.h | 329 +++++++++
Source/cmLocalNinjaGenerator.cxx | 413 +++++++++++
Source/cmLocalNinjaGenerator.h | 134 ++++
Source/cmNinjaNormalTargetGenerator.cxx | 402 ++++++++++
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/BuildDepends/CMakeLists.txt | 2 +
Tests/CMakeLists.txt | 14 +-
Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt | 6 +
19 files changed, 2894 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 0000000..f15c3e0
--- /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/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake
index 8d6f5df..bdcaf9d 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 <DEPFILE>")
+ endif()
+
# Initial configuration flags.
set(CMAKE_${lang}_FLAGS_INIT "")
set(CMAKE_${lang}_FLAGS_DEBUG_INIT "-g")
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index a37771b..d8c5b9a 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)
@@ -1450,9 +1456,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 ba41d98..95725ba 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -346,6 +346,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 0000000..2579c3c
--- /dev/null
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -0,0 +1,772 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#include "cmGlobalNinjaGenerator.h"
+#include "cmLocalNinjaGenerator.h"
+#include "cmMakefile.h"
+#include "cmGeneratedFileStream.h"
+#include "cmVersion.h"
+
+const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
+const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja";
+const char* cmGlobalNinjaGenerator::INDENT = " ";
+
+void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
+{
+ for(int i = 0; i < count; ++i)
+ os << cmGlobalNinjaGenerator::INDENT;
+}
+
+void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
+{
+ os
+ << "# ======================================"
+ << "=======================================\n";
+}
+
+void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
+ const std::string& comment)
+{
+ if (comment.empty())
+ return;
+
+ std::string replace = comment;
+ std::string::size_type lpos = 0;
+ std::string::size_type rpos;
+ while((rpos = replace.find('\n', lpos)) != std::string::npos)
+ {
+ os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
+ lpos = rpos + 1;
+ }
+ os << "# " << replace.substr(lpos) << "\n";
+}
+
+static bool IsIdentChar(char c)
+{
+ return
+ ('a' <= c && c <= 'z') ||
+ ('+' <= c && c <= '9') || // +,-./ and numbers
+ ('A' <= c && c <= 'Z') ||
+ (c == '_') || (c == '$') || (c == '\\');
+}
+
+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::vector<std::string>const& languages,
+ cmMakefile *mf,
+ bool optional)
+{
+ this->cmGlobalGenerator::EnableLanguage(languages, mf, optional);
+ std::string path;
+ for(std::vector<std::string>::const_iterator l = languages.begin();
+ l != languages.end(); ++l)
+ {
+ if(*l == "NONE")
+ {
+ continue;
+ }
+ if(*l == "Fortran")
+ {
+ std::string message = "The \"";
+ message += this->GetName();
+ message += "\" generator does not support the language \"";
+ message += *l;
+ message += "\" yet.";
+ cmSystemTools::Error(message.c_str());
+ }
+ this->ResolveLanguageCompiler(*l, mf, optional);
+ }
+}
+
+// Implemented by:
+// cmGlobalUnixMakefileGenerator3
+// cmGlobalVisualStudio10Generator
+// cmGlobalVisualStudio6Generator
+// cmGlobalVisualStudio7Generator
+// cmGlobalXCodeGenerator
+// Called by:
+// cmGlobalGenerator::Build()
+std::string cmGlobalNinjaGenerator
+::GenerateBuildCommand(const char* makeProgram,
+ const char* projectName,
+ const char* additionalOptions,
+ const char* targetName,
+ const char* config,
+ bool ignoreErrors,
+ bool fast)
+{
+ // Project name and config are not used yet.
+ (void)projectName;
+ (void)config;
+ // Ninja does not have -i equivalent option yet.
+ (void)ignoreErrors;
+ // We do not handle fast build yet.
+ (void)fast;
+
+ std::string makeCommand =
+ cmSystemTools::ConvertToUnixOutputPath(makeProgram);
+
+ if(additionalOptions)
+ {
+ makeCommand += " ";
+ makeCommand += additionalOptions;
+ }
+ if(targetName)
+ {
+ if(strcmp(targetName, "clean") == 0)
+ {
+ makeCommand += " -t clean";
+ }
+ else
+ {
+ makeCommand += " ";
+ makeCommand += targetName;
+ }
+ }
+
+ return makeCommand;
+}
+
+//----------------------------------------------------------------------------
+// Non-virtual public methods.
+
+void cmGlobalNinjaGenerator::AddRule(const std::string& name,
+ const std::string& command,
+ const std::string& description,
+ const std::string& comment,
+ const std::string& depfile,
+ bool restat,
+ bool generator)
+{
+ // Do not add the same rule twice.
+ if (this->HasRule(name))
+ return;
+
+ this->Rules.insert(name);
+ cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
+ name,
+ command,
+ description,
+ comment,
+ depfile,
+ restat,
+ generator);
+}
+
+bool cmGlobalNinjaGenerator::HasRule(const std::string &name)
+{
+ RulesSetType::const_iterator rule = this->Rules.find(name);
+ return (rule != this->Rules.end());
+}
+
+//----------------------------------------------------------------------------
+// Private methods
+
+void cmGlobalNinjaGenerator::OpenBuildFileStream()
+{
+ // Compute Ninja's build file path.
+ std::string buildFilePath =
+ this->GetCMakeInstance()->GetHomeOutputDirectory();
+ buildFilePath += "/";
+ buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;
+
+ // Get a stream where to generate things.
+ if (!this->BuildFileStream)
+ {
+ this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str());
+ if (!this->BuildFileStream)
+ {
+ // An error message is generated by the constructor if it cannot
+ // open the file.
+ return;
+ }
+ }
+
+ // Write the do not edit header.
+ this->WriteDisclaimer(*this->BuildFileStream);
+
+ // Write a comment about this file.
+ *this->BuildFileStream
+ << "# This file contains all the build statements describing the\n"
+ << "# compilation DAG.\n\n"
+ ;
+}
+
+void cmGlobalNinjaGenerator::CloseBuildFileStream()
+{
+ if (this->BuildFileStream)
+ {
+ delete this->BuildFileStream;
+ this->BuildFileStream = 0;
+ }
+ else
+ {
+ cmSystemTools::Error("Build file stream was not open.");
+ }
+}
+
+void cmGlobalNinjaGenerator::OpenRulesFileStream()
+{
+ // Compute Ninja's build file path.
+ std::string rulesFilePath =
+ this->GetCMakeInstance()->GetHomeOutputDirectory();
+ rulesFilePath += "/";
+ rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;
+
+ // Get a stream where to generate things.
+ if (!this->RulesFileStream)
+ {
+ this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str());
+ if (!this->RulesFileStream)
+ {
+ // An error message is generated by the constructor if it cannot
+ // open the file.
+ return;
+ }
+ }
+
+ // Write the do not edit header.
+ this->WriteDisclaimer(*this->RulesFileStream);
+
+ // Write comment about this file.
+ *this->RulesFileStream
+ << "# This file contains all the rules used to get the outputs files\n"
+ << "# built from the input files.\n"
+ << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
+ ;
+}
+
+void cmGlobalNinjaGenerator::CloseRulesFileStream()
+{
+ if (this->RulesFileStream)
+ {
+ delete this->RulesFileStream;
+ this->RulesFileStream = 0;
+ }
+ else
+ {
+ cmSystemTools::Error("Rules file stream was not open.");
+ }
+}
+
+void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os)
+{
+ os
+ << "# CMAKE generated file: DO NOT EDIT!\n"
+ << "# Generated by \"" << this->GetName() << "\""
+ << " Generator, CMake Version "
+ << cmVersion::GetMajorVersion() << "."
+ << cmVersion::GetMinorVersion() << "\n\n";
+}
+
+void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target)
+{
+ this->AppendTargetOutputs(target, this->AllDependencies);
+}
+
+void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies(std::ostream& os)
+{
+ for (std::map<std::string, std::set<std::string> >::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)
+{
+ std::string name, dir;
+ const char* configName =
+ target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE");
+
+ switch (target->GetType()) {
+ case cmTarget::EXECUTABLE:
+ case cmTarget::SHARED_LIBRARY:
+ case cmTarget::STATIC_LIBRARY:
+ case cmTarget::MODULE_LIBRARY:
+ name = target->GetFullName(configName);
+ dir = target->GetDirectory(configName);
+ break;
+
+ case cmTarget::UTILITY:
+ dir = target->GetMakefile()->GetStartOutputDirectory();
+ case cmTarget::GLOBAL_TARGET:
+ // dir is always "" for GLOBAL_TARGETs so that we use the target in HOME
+ name = target->GetName();
+ break;
+
+ default:
+ return;
+ }
+
+ std::string path =
+ static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0])
+ ->ConvertToNinjaPath(dir.c_str());
+ if (path.empty() || path == ".")
+ outputs.push_back(name);
+ else {
+ path += "/";
+ path += name;
+ outputs.push_back(path);
+ }
+}
+
+void
+cmGlobalNinjaGenerator
+::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
+{
+ if (target->GetType() == cmTarget::GLOBAL_TARGET) {
+ // Global targets only depend on other utilities, which may not appear in
+ // the TargetDepends set (e.g. "all").
+ std::set<cmStdString> const& utils = target->GetUtilities();
+ 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<TargetAliasMap::iterator, bool> newAlias =
+ TargetAliases.insert(make_pair(alias, target));
+ if (newAlias.second && newAlias.first->second != target)
+ newAlias.first->second = 0;
+}
+
+void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os << "# Target aliases.\n\n";
+
+ for (TargetAliasMap::iterator i = TargetAliases.begin();
+ i != TargetAliases.end(); ++i) {
+ // Don't write ambiguous aliases.
+ if (!i->second)
+ continue;
+
+ cmNinjaDeps deps;
+ this->AppendTargetOutputs(i->second, deps);
+
+ cmGlobalNinjaGenerator::WritePhonyBuild(os,
+ "",
+ cmNinjaDeps(1, i->first),
+ deps);
+ }
+}
+
+void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
+{
+ // Write headers.
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os << "# Built-in targets\n\n";
+
+ this->WriteTargetAll(os);
+ this->WriteTargetRebuildManifest(os);
+}
+
+void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
+{
+ cmNinjaDeps outputs;
+ outputs.push_back("all");
+
+ cmGlobalNinjaGenerator::WritePhonyBuild(os,
+ "The main all target.",
+ outputs,
+ this->AllDependencies);
+
+ cmGlobalNinjaGenerator::WriteDefault(os,
+ outputs,
+ "Make the all target the default.");
+}
+
+void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
+{
+ 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<cmLocalGenerator *>::const_iterator i =
+ this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) {
+ const std::vector<std::string>& lf = (*i)->GetMakefile()->GetListFiles();
+ implicitDeps.insert(implicitDeps.end(), lf.begin(), lf.end());
+ }
+ std::sort(implicitDeps.begin(), implicitDeps.end());
+ implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
+ implicitDeps.end());
+ implicitDeps.push_back("CMakeCache.txt");
+
+ WriteBuild(os,
+ "Re-run CMake if any of its inputs changed.",
+ "RERUN_CMAKE",
+ /*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
+ /*explicitDeps=*/ cmNinjaDeps(),
+ implicitDeps,
+ /*orderOnlyDeps=*/ cmNinjaDeps(),
+ /*variables=*/ cmNinjaVars());
+
+ WritePhonyBuild(os,
+ "A missing CMake input file is not an error.",
+ implicitDeps,
+ cmNinjaDeps(),
+ cmNinjaDeps(),
+ cmNinjaDeps(),
+ cmNinjaVars());
+}
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
new file mode 100644
index 0000000..171d14b
--- /dev/null
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -0,0 +1,329 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#ifndef cmGlobalNinjaGenerator_h
+# define cmGlobalNinjaGenerator_h
+
+# include "cmGlobalGenerator.h"
+# include "cmNinjaTypes.h"
+
+class cmLocalGenerator;
+class cmGeneratedFileStream;
+
+/**
+ * \class cmGlobalNinjaGenerator
+ * \brief Write a build.ninja file.
+ *
+ * The main differences between this generator and the UnixMakefile
+ * generator family are:
+ * - We don't care about VERBOSE variable or RULE_MESSAGES property since
+ * it is handle by Ninja's -v option.
+ * - We don't care about computing any progress status since Ninja manages
+ * it itself.
+ * - We don't care about generating a clean target since Ninja already have
+ * a clean tool.
+ * - We generate one build.ninja and one rules.ninja per project.
+ * - We try to minimize the number of generated rules: one per target and
+ * language.
+ * - We use Ninja special variable $in and $out to produce nice output.
+ * - We extensively use Ninja variable overloading system to minimize the
+ * number of generated rules.
+ */
+class cmGlobalNinjaGenerator : public cmGlobalGenerator
+{
+public:
+ /// The default name of Ninja's build file. Typically: build.ninja.
+ static const char* NINJA_BUILD_FILE;
+
+ /// The default name of Ninja's rules file. Typically: rules.ninja.
+ /// It is included in the main build.ninja file.
+ static const char* NINJA_RULES_FILE;
+
+ /// The indentation string used when generating Ninja's build file.
+ static const char* INDENT;
+
+ /// Write @a count times INDENT level to output stream @a os.
+ static void Indent(std::ostream& os, int count);
+
+ /// Write a divider in the given output stream @a os.
+ static void WriteDivider(std::ostream& os);
+
+ static std::string EncodeIdent(const std::string &ident, std::ostream &vars);
+ static std::string EncodeLiteral(const std::string &lit);
+
+ /**
+ * Write the given @a comment to the output stream @a os. It
+ * handles new line character properly.
+ */
+ static void WriteComment(std::ostream& os, const std::string& comment);
+
+ /**
+ * Write a build statement to @a os with the @a comment using
+ * the @a rule the list of @a outputs files and inputs.
+ * It also writes the variables bound to this build statement.
+ * @warning no escaping of any kind is done here.
+ */
+ static void WriteBuild(std::ostream& os,
+ const std::string& comment,
+ const std::string& rule,
+ const cmNinjaDeps& outputs,
+ const cmNinjaDeps& explicitDeps,
+ const cmNinjaDeps& implicitDeps,
+ const cmNinjaDeps& orderOnlyDeps,
+ const cmNinjaVars& variables);
+
+ /**
+ * Helper to write a build statement with the special 'phony' rule.
+ */
+ static void WritePhonyBuild(std::ostream& os,
+ const std::string& comment,
+ const cmNinjaDeps& outputs,
+ const cmNinjaDeps& explicitDeps,
+ const cmNinjaDeps& implicitDeps = cmNinjaDeps(),
+ const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps(),
+ const cmNinjaVars& variables = cmNinjaVars());
+
+ void WriteCustomCommandBuild(const std::string& command,
+ const std::string& description,
+ const std::string& comment,
+ const cmNinjaDeps& outputs,
+ const cmNinjaDeps& deps = cmNinjaDeps(),
+ const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps());
+
+ /**
+ * Write a rule statement named @a name to @a os with the @a comment,
+ * the mandatory @a command, the @a depfile and the @a description.
+ * It also writes the variables bound to this rule statement.
+ * @warning no escaping of any kind is done here.
+ */
+ static void WriteRule(std::ostream& os,
+ const std::string& name,
+ const std::string& command,
+ const std::string& description,
+ const std::string& comment = "",
+ const std::string& depfile = "",
+ bool restat = false,
+ bool generator = false);
+
+ /**
+ * Write a variable named @a name to @a os with value @a value and an
+ * optional @a comment. An @a indent level can be specified.
+ * @warning no escaping of any kind is done here.
+ */
+ static void WriteVariable(std::ostream& os,
+ const std::string& name,
+ const std::string& value,
+ const std::string& comment = "",
+ int indent = 0);
+
+ /**
+ * Write an include statement including @a filename with an optional
+ * @a comment to the @a os stream.
+ */
+ static void WriteInclude(std::ostream& os,
+ const std::string& filename,
+ const std::string& comment = "");
+
+ /**
+ * Write a default target statement specifying @a targets as
+ * the default targets.
+ */
+ static void WriteDefault(std::ostream& os,
+ const cmNinjaDeps& targets,
+ const std::string& comment = "");
+
+public:
+ /// Default constructor.
+ cmGlobalNinjaGenerator();
+
+ /// Convenience method for creating an instance of this class.
+ static cmGlobalGenerator* New() {
+ return new cmGlobalNinjaGenerator; }
+
+ /// Destructor.
+ virtual ~cmGlobalNinjaGenerator() { }
+
+ /// Overloaded methods. @see cmGlobalGenerator::CreateLocalGenerator()
+ virtual cmLocalGenerator* CreateLocalGenerator();
+
+ /// Overloaded methods. @see cmGlobalGenerator::GetName().
+ virtual const char* GetName() const {
+ return cmGlobalNinjaGenerator::GetActualName(); }
+
+ /// @return the name of this generator.
+ static const char* GetActualName() { return "Ninja"; }
+
+ /// Overloaded methods. @see cmGlobalGenerator::GetDocumentation()
+ virtual void GetDocumentation(cmDocumentationEntry& entry) const;
+
+ /// Overloaded methods. @see cmGlobalGenerator::Generate()
+ virtual void Generate();
+
+ /// Overloaded methods. @see cmGlobalGenerator::EnableLanguage()
+ virtual void EnableLanguage(std::vector<std::string>const& languages,
+ cmMakefile* mf,
+ bool optional);
+
+ /// Overloaded methods. @see cmGlobalGenerator::GenerateBuildCommand()
+ virtual std::string GenerateBuildCommand(const char* makeProgram,
+ const char* projectName,
+ const char* additionalOptions,
+ const char* targetName,
+ const char* config,
+ bool ignoreErrors,
+ bool fast);
+
+ // Setup target names
+ virtual const char* GetAllTargetName() const { return "all"; }
+ virtual const char* GetInstallTargetName() const { return "install"; }
+ virtual const char* GetInstallLocalTargetName() const {
+ return "install/local";
+ }
+ virtual const char* GetInstallStripTargetName() const {
+ return "install/strip";
+ }
+ virtual const char* GetTestTargetName() const { return "test"; }
+ virtual const char* GetPackageTargetName() const { return "package"; }
+ virtual const char* GetPackageSourceTargetName() const {
+ return "package_source";
+ }
+ virtual const char* GetEditCacheTargetName() const {
+ return "edit_cache";
+ }
+ virtual const char* GetRebuildCacheTargetName() const {
+ return "rebuild_cache";
+ }
+ virtual const char* GetCleanTargetName() const { return "clean"; }
+
+public:
+ cmGeneratedFileStream* GetBuildFileStream() const
+ { return this->BuildFileStream; }
+
+ cmGeneratedFileStream* GetRulesFileStream() const
+ { return this->RulesFileStream; }
+
+ /**
+ * Add a rule to the generated build system.
+ * Call WriteRule() behind the scene but perform some check before like:
+ * - Do not add twice the same rule.
+ */
+ void AddRule(const std::string& name,
+ const std::string& command,
+ const std::string& description,
+ const std::string& comment = "",
+ const std::string& depfile = "",
+ bool restat = false,
+ bool generator = false);
+
+ bool HasRule(const std::string& name);
+
+ void AddCustomCommandRule();
+
+protected:
+
+ /// Overloaded methods.
+ /// @see cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS()
+ virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() { return true; }
+
+private:
+ // In order to access the AddDependencyToAll() functions and co.
+ friend class cmLocalNinjaGenerator;
+
+ // In order to access the SeenCustomCommand() function.
+ friend class cmNinjaTargetGenerator;
+ friend class cmNinjaNormalTargetGenerator;
+ friend class cmNinjaUtilityTargetGenerator;
+
+private:
+ void OpenBuildFileStream();
+ void CloseBuildFileStream();
+
+ void OpenRulesFileStream();
+ void CloseRulesFileStream();
+
+ /// Write the common disclaimer text at the top of each build file.
+ void WriteDisclaimer(std::ostream& os);
+
+ void AddDependencyToAll(cmTarget* target);
+
+ void WriteAssumedSourceDependencies(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<std::string> &ASD = this->AssumedSourceDependencies[source];
+ // Because we may see the same source file multiple times (same source
+ // specified in multiple targets), compute the union of any assumed
+ // dependencies.
+ ASD.insert(deps.begin(), deps.end());
+ }
+
+private:
+ /// The file containing the build statement. (the relation ship of the
+ /// compilation DAG).
+ cmGeneratedFileStream* BuildFileStream;
+ /// The file containing the rule statements. (The action attached to each
+ /// edge of the compilation DAG).
+ cmGeneratedFileStream* RulesFileStream;
+
+ /// The type used to store the set of rules added to the generated build
+ /// system.
+ typedef std::set<std::string> RulesSetType;
+
+ /// The set of rules added to the generated build system.
+ RulesSetType Rules;
+
+ /// The set of dependencies to add to the "all" target.
+ cmNinjaDeps AllDependencies;
+
+ /// The set of custom commands we have seen.
+ std::set<cmCustomCommand *> CustomCommands;
+
+ /// The set of custom command outputs we have seen.
+ std::set<std::string> CustomCommandOutputs;
+
+ /// The mapping from source file to assumed dependencies.
+ std::map<std::string, std::set<std::string> > AssumedSourceDependencies;
+
+ typedef std::map<std::string, cmTarget*> TargetAliasMap;
+ TargetAliasMap TargetAliases;
+};
+
+#endif // ! cmGlobalNinjaGenerator_h
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
new file mode 100644
index 0000000..6ae5032
--- /dev/null
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -0,0 +1,413 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#include "cmLocalNinjaGenerator.h"
+#include "cmCustomCommandGenerator.h"
+#include "cmMakefile.h"
+#include "cmGlobalNinjaGenerator.h"
+#include "cmNinjaTargetGenerator.h"
+#include "cmGeneratedFileStream.h"
+#include "cmSourceFile.h"
+#include "cmComputeLinkInformation.h"
+#include "cmake.h"
+
+#include <assert.h>
+
+cmLocalNinjaGenerator::cmLocalNinjaGenerator()
+ : cmLocalGenerator()
+ , ConfigName("")
+ , HomeRelativeOutputPath("")
+{
+ this->IsMakefileGenerator = true;
+}
+
+//----------------------------------------------------------------------------
+// Virtual public methods.
+
+cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
+{
+}
+
+void cmLocalNinjaGenerator::Generate()
+{
+ this->SetConfigName();
+
+ this->WriteProcessedMakefile(this->GetBuildFileStream());
+ this->WriteProcessedMakefile(this->GetRulesFileStream());
+
+ this->WriteBuildFileTop();
+
+ cmTargets& targets = this->GetMakefile()->GetTargets();
+ for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
+ {
+ cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(&t->second);
+ if(tg)
+ {
+ tg->Generate();
+ // Add the target to "all" if required.
+ if (!this->GetGlobalNinjaGenerator()->IsExcluded(
+ this->GetGlobalNinjaGenerator()->LocalGenerators[0],
+ t->second))
+ this->GetGlobalNinjaGenerator()->AddDependencyToAll(&t->second);
+ delete tg;
+ }
+ }
+
+ this->WriteCustomCommandBuildStatements();
+}
+
+// Implemented in:
+// cmLocalUnixMakefileGenerator3.
+// Used in:
+// Source/cmMakefile.cxx
+// Source/cmGlobalGenerator.cxx
+void cmLocalNinjaGenerator::Configure()
+{
+ // Compute the path to use when referencing the current output
+ // directory from the top output directory.
+ this->HomeRelativeOutputPath =
+ this->Convert(this->Makefile->GetStartOutputDirectory(), HOME_OUTPUT);
+ if(this->HomeRelativeOutputPath == ".")
+ {
+ this->HomeRelativeOutputPath = "";
+ }
+ this->cmLocalGenerator::Configure();
+
+}
+
+// TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it.
+std::string cmLocalNinjaGenerator
+::GetTargetDirectory(cmTarget const& target) const
+{
+ std::string dir = cmake::GetCMakeFilesDirectoryPostSlash();
+ dir += target.GetName();
+#if defined(__VMS)
+ dir += "_dir";
+#else
+ dir += ".dir";
+#endif
+ return dir;
+}
+
+//----------------------------------------------------------------------------
+// Non-virtual public methods.
+
+const cmGlobalNinjaGenerator*
+cmLocalNinjaGenerator::GetGlobalNinjaGenerator() const
+{
+ return
+ static_cast<const cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
+}
+
+cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator()
+{
+ return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
+}
+
+// TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it.
+std::string
+cmLocalNinjaGenerator
+::GetObjectFileName(const cmTarget& target,
+ const cmSourceFile& source)
+{
+ // Make sure we never hit this old case.
+ if(source.GetProperty("MACOSX_PACKAGE_LOCATION"))
+ {
+ std::string msg = "MACOSX_PACKAGE_LOCATION set on source file: ";
+ msg += source.GetFullPath();
+ this->GetMakefile()->IssueMessage(cmake::INTERNAL_ERROR,
+ msg.c_str());
+ }
+
+ // Start with the target directory.
+ std::string obj = this->GetTargetDirectory(target);
+ obj += "/";
+
+ // Get the object file name without the target directory.
+ std::string dir_max;
+ dir_max += this->Makefile->GetCurrentOutputDirectory();
+ dir_max += "/";
+ dir_max += obj;
+ std::string objectName =
+ this->GetObjectFileNameWithoutTarget(source, dir_max, 0);
+ // Append the object name to the target directory.
+ obj += objectName;
+ return obj;
+}
+
+//----------------------------------------------------------------------------
+// Virtual protected methods.
+
+std::string
+cmLocalNinjaGenerator::ConvertToLinkReference(std::string const& lib)
+{
+ return this->Convert(lib.c_str(), HOME_OUTPUT, SHELL);
+}
+
+std::string
+cmLocalNinjaGenerator::ConvertToIncludeReference(std::string const& path)
+{
+ return this->Convert(path.c_str(), HOME_OUTPUT, SHELL);
+}
+
+//----------------------------------------------------------------------------
+// Private methods.
+
+cmGeneratedFileStream& cmLocalNinjaGenerator::GetBuildFileStream() const
+{
+ return *this->GetGlobalNinjaGenerator()->GetBuildFileStream();
+}
+
+cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const
+{
+ return *this->GetGlobalNinjaGenerator()->GetRulesFileStream();
+}
+
+const cmake* cmLocalNinjaGenerator::GetCMakeInstance() const
+{
+ return this->GetGlobalGenerator()->GetCMakeInstance();
+}
+
+cmake* cmLocalNinjaGenerator::GetCMakeInstance()
+{
+ return this->GetGlobalGenerator()->GetCMakeInstance();
+}
+
+bool cmLocalNinjaGenerator::isRootMakefile() const
+{
+ return (strcmp(this->Makefile->GetCurrentDirectory(),
+ this->GetCMakeInstance()->GetHomeDirectory()) == 0);
+}
+
+void cmLocalNinjaGenerator::WriteBuildFileTop()
+{
+ // We do that only once for the top CMakeLists.txt file.
+ if(!this->isRootMakefile())
+ return;
+
+ // For the build file.
+ this->WriteProjectHeader(this->GetBuildFileStream());
+ this->WriteNinjaFilesInclusion(this->GetBuildFileStream());
+
+ // For the rule file.
+ this->WriteProjectHeader(this->GetRulesFileStream());
+}
+
+void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os
+ << "# Project: " << this->GetMakefile()->GetProjectName() << std::endl
+ << "# Configuration: " << this->ConfigName << std::endl
+ ;
+ cmGlobalNinjaGenerator::WriteDivider(os);
+}
+
+void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os
+ << "# Include auxiliary files.\n"
+ << "\n"
+ ;
+ cmGlobalNinjaGenerator::WriteInclude(os,
+ cmGlobalNinjaGenerator::NINJA_RULES_FILE,
+ "Include rules file.");
+ os << "\n";
+}
+
+void cmLocalNinjaGenerator::SetConfigName()
+{
+ // Store the configuration name that will be generated.
+ if(const char* config =
+ this->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"))
+ {
+ // Use the build type given by the user.
+ this->ConfigName = config;
+ }
+ else
+ {
+ // No configuration type given.
+ this->ConfigName = "";
+ }
+}
+
+void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os
+ << "# Write statements declared in CMakeLists.txt:" << std::endl
+ << "# " << this->Makefile->GetCurrentListFile() << std::endl
+ ;
+ if(this->isRootMakefile())
+ os << "# Which is the root file." << std::endl;
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os << std::endl;
+}
+
+std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path)
+{
+ 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<std::string> &deps = cc->GetDepends();
+ for (std::vector<std::string>::const_iterator i = deps.begin();
+ i != deps.end(); ++i) {
+ std::string dep;
+ if (this->GetRealDependency(i->c_str(), this->GetConfigName(), dep))
+ ninjaDeps.push_back(ConvertToNinjaPath(dep.c_str()));
+ }
+}
+
+std::string cmLocalNinjaGenerator::BuildCommandLine(
+ const std::vector<std::string> &cmdLines)
+{
+ // If we have no commands but we need to build a command anyway, use ":".
+ // This happens when building a POST_BUILD value for link targets that
+ // don't use POST_BUILD.
+ if (cmdLines.empty())
+ 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<std::string>::const_iterator li = cmdLines.begin();
+ li != cmdLines.end(); ++li) {
+ if (li != cmdLines.begin())
+ cmd << " && ";
+ cmd << *li;
+ }
+ return cmd.str();
+}
+
+void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc,
+ std::vector<std::string> &cmdLines)
+{
+ cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile);
+ if (ccg.GetNumberOfCommands() > 0) {
+ 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<std::string> &outputs = cc->GetOutputs();
+ cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps;
+
+ std::transform(outputs.begin(), outputs.end(),
+ ninjaOutputs.begin(), MapToNinjaPath());
+ this->AppendCustomCommandDeps(cc, ninjaDeps);
+
+ for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();
+ ++i)
+ this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i);
+
+ std::vector<std::string> cmdLines;
+ this->AppendCustomCommandLines(cc, cmdLines);
+
+ if (cmdLines.empty()) {
+ cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
+ "Phony custom command for " +
+ ninjaOutputs[0],
+ ninjaOutputs,
+ ninjaDeps,
+ cmNinjaDeps(),
+ orderOnlyDeps,
+ cmNinjaVars());
+ } else {
+ this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
+ this->BuildCommandLine(cmdLines),
+ this->ConstructComment(*cc),
+ "Custom command for " + ninjaOutputs[0],
+ ninjaOutputs,
+ ninjaDeps,
+ orderOnlyDeps);
+ }
+}
+
+void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand* cc,
+ cmTarget* target)
+{
+ this->CustomCommandTargets[cc].insert(target);
+}
+
+void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
+{
+ for (CustomCommandTargetMap::iterator i = this->CustomCommandTargets.begin();
+ i != this->CustomCommandTargets.end(); ++i) {
+ // A custom command may appear on multiple targets. However, some build
+ // systems exist where the target dependencies on some of the targets are
+ // overspecified, leading to a dependency cycle. If we assume all target
+ // dependencies are a superset of the true target dependencies for this
+ // custom command, we can take the set intersection of all target
+ // dependencies to obtain a correct dependency list.
+ //
+ // FIXME: This won't work in certain obscure scenarios involving indirect
+ // dependencies.
+ std::set<cmTarget*>::iterator j = i->second.begin();
+ assert(j != i->second.end());
+ std::vector<std::string> ccTargetDeps;
+ this->AppendTargetDepends(*j, ccTargetDeps);
+ std::sort(ccTargetDeps.begin(), ccTargetDeps.end());
+ ++j;
+
+ for (; j != i->second.end(); ++j) {
+ std::vector<std::string> jDeps, depsIntersection;
+ this->AppendTargetDepends(*j, jDeps);
+ std::sort(jDeps.begin(), jDeps.end());
+ std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(),
+ jDeps.begin(), jDeps.end(),
+ std::back_inserter(depsIntersection));
+ ccTargetDeps = depsIntersection;
+ }
+
+ this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps);
+ }
+}
diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h
new file mode 100644
index 0000000..218eee1
--- /dev/null
+++ b/Source/cmLocalNinjaGenerator.h
@@ -0,0 +1,134 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#ifndef cmLocalNinjaGenerator_h
+# define cmLocalNinjaGenerator_h
+
+# include "cmLocalGenerator.h"
+# include "cmNinjaTypes.h"
+
+class cmGlobalNinjaGenerator;
+class cmGeneratedFileStream;
+class cmake;
+
+/**
+ * \class cmLocalNinjaGenerator
+ * \brief Write a local build.ninja file.
+ *
+ * cmLocalNinjaGenerator produces a local build.ninja file from its
+ * member Makefile.
+ */
+class cmLocalNinjaGenerator : public cmLocalGenerator
+{
+public:
+ /// Default constructor.
+ cmLocalNinjaGenerator();
+
+ /// Destructor.
+ virtual ~cmLocalNinjaGenerator();
+
+ /// Overloaded methods. @see cmLocalGenerator::Generate()
+ virtual void Generate();
+
+ /// Overloaded methods. @see cmLocalGenerator::Configure()
+ virtual void Configure();
+
+ /// Overloaded methods. @see cmLocalGenerator::GetTargetDirectory()
+ virtual std::string GetTargetDirectory(cmTarget const& target) const;
+
+public:
+ const cmGlobalNinjaGenerator* GetGlobalNinjaGenerator() const;
+ cmGlobalNinjaGenerator* GetGlobalNinjaGenerator();
+
+ /**
+ * Shortcut to get the cmake instance throw the global generator.
+ * @return an instance of the cmake object.
+ */
+ const cmake* GetCMakeInstance() const;
+ cmake* GetCMakeInstance();
+
+ const char* GetConfigName() const
+ { return this->ConfigName.c_str(); }
+
+ std::string GetObjectFileName(const cmTarget& target,
+ const cmSourceFile& source);
+
+ /// @return whether we are processing the top CMakeLists.txt file.
+ bool isRootMakefile() const;
+
+ /// @returns the relative path between the HomeOutputDirectory and this
+ /// local generators StartOutputDirectory.
+ std::string GetHomeRelativeOutputPath() const
+ { return this->HomeRelativeOutputPath; }
+
+protected:
+ virtual std::string ConvertToLinkReference(std::string const& lib);
+ virtual std::string ConvertToIncludeReference(std::string const& path);
+
+private:
+ friend class cmGlobalNinjaGenerator;
+
+ // In order to access to protected member of the local generator.
+ friend class cmNinjaTargetGenerator;
+ friend class cmNinjaNormalTargetGenerator;
+ friend class cmNinjaUtilityTargetGenerator;
+
+private:
+ cmGeneratedFileStream& GetBuildFileStream() const;
+ cmGeneratedFileStream& GetRulesFileStream() const;
+
+ void WriteBuildFileTop();
+ void WriteProjectHeader(std::ostream& os);
+ void WriteNinjaFilesInclusion(std::ostream& os);
+ void WriteProcessedMakefile(std::ostream& os);
+
+ void SetConfigName();
+
+ std::string ConvertToNinjaPath(const char *path);
+
+ struct map_to_ninja_path {
+ 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<std::string> &cmdLines);
+ void AppendCustomCommandLines(const cmCustomCommand *cc,
+ std::vector<std::string> &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<cmCustomCommand*, std::set<cmTarget*> >
+ CustomCommandTargetMap;
+ CustomCommandTargetMap CustomCommandTargets;
+};
+
+#endif // ! cmLocalNinjaGenerator_h
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
new file mode 100644
index 0000000..bdd4f3d
--- /dev/null
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -0,0 +1,402 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#include "cmNinjaNormalTargetGenerator.h"
+#include "cmLocalNinjaGenerator.h"
+#include "cmGlobalNinjaGenerator.h"
+#include "cmSourceFile.h"
+#include "cmGeneratedFileStream.h"
+#include "cmMakefile.h"
+
+#include <assert.h>
+
+cmNinjaNormalTargetGenerator::
+cmNinjaNormalTargetGenerator(cmTarget* target)
+ : cmNinjaTargetGenerator(target)
+ , TargetNameOut()
+ , TargetNameSO()
+ , TargetNameReal()
+ , TargetNameImport()
+ , TargetNamePDB()
+ , 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<cmStdString> languages;
+ this->GetTarget()->GetLanguages(languages);
+ for(std::set<cmStdString>::const_iterator l = languages.begin();
+ l != languages.end();
+ ++l)
+ this->WriteLanguageRules(*l);
+}
+
+const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
+{
+ switch (this->GetTarget()->GetType()) {
+ case cmTarget::STATIC_LIBRARY:
+ return "static library";
+ case cmTarget::SHARED_LIBRARY:
+ return "shared library";
+ case cmTarget::MODULE_LIBRARY:
+ return "shared module";
+ case cmTarget::EXECUTABLE:
+ return "executable";
+ default:
+ return 0;
+ }
+}
+
+std::string
+cmNinjaNormalTargetGenerator
+::LanguageLinkerRule() const
+{
+ return std::string(this->TargetLinkLanguage)
+ + "_"
+ + cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
+ + "_LINKER";
+}
+
+void
+cmNinjaNormalTargetGenerator
+::WriteLinkRule()
+{
+ 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";
+
+ // 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());
+ langFlags += "$ARCHITECTURE_FLAGS";
+ vars.LanguageCompileFlags = langFlags.c_str();
+
+ // Rule for linking library.
+ std::vector<std::string> linkCmds = this->ComputeLinkCmd();
+ for(std::vector<std::string>::iterator i = linkCmds.begin();
+ i != linkCmds.end();
+ ++i)
+ {
+ this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
+ }
+ linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
+ linkCmds.push_back("$POST_BUILD");
+ std::string linkCmd =
+ this->GetLocalGenerator()->BuildCommandLine(linkCmds);
+
+ // Write the linker rule.
+ std::ostringstream comment;
+ comment << "Rule for linking " << this->TargetLinkLanguage << " "
+ << this->GetVisibleTypeName() << ".";
+ std::ostringstream description;
+ description << "Linking " << this->TargetLinkLanguage << " "
+ << this->GetVisibleTypeName() << " $out";
+ this->GetGlobalGenerator()->AddRule(ruleName,
+ linkCmd,
+ description.str(),
+ comment.str());
+ }
+
+ if (this->TargetNameOut != this->TargetNameReal) {
+ std::string cmakeCommand =
+ this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND");
+ if (this->GetTarget()->GetType() == cmTarget::EXECUTABLE)
+ this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
+ cmakeCommand +
+ " -E cmake_symlink_executable"
+ " $in $out && $POST_BUILD",
+ "Creating executable symlink $out",
+ "Rule for creating executable symlink.");
+ else
+ this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
+ cmakeCommand +
+ " -E cmake_symlink_library"
+ " $in $SONAME $out && $POST_BUILD",
+ "Creating library symlink $out",
+ "Rule for creating library symlink.");
+ }
+}
+
+std::vector<std::string>
+cmNinjaNormalTargetGenerator
+::ComputeLinkCmd()
+{
+ 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<std::string>(1, linkCmd);
+ }
+ }
+
+ // We have archive link commands set. First, delete the existing archive.
+ std::vector<std::string> linkCmds;
+ linkCmds.push_back("rm -f $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<std::string>(1, linkCmd);
+ }
+ default:
+ assert(0 && "Unexpected target type");
+ }
+}
+
+void cmNinjaNormalTargetGenerator::WriteLinkStatement()
+{
+ // Write comments.
+ cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
+ this->GetBuildFileStream()
+ << "# Link build statements for "
+ << cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
+ << " target "
+ << this->GetTargetName()
+ << "\n\n";
+
+ cmNinjaDeps emptyDeps;
+ cmNinjaVars vars;
+
+ std::string targetOutput = this->GetTargetFilePath(this->TargetNameOut);
+ std::string targetOutputReal = this->GetTargetFilePath(this->TargetNameReal);
+
+ // 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 specific link flags.
+ this->GetLocalGenerator()->AddArchitectureFlags(vars["ARCHITECTURE_FLAGS"],
+ this->GetTarget(),
+ this->TargetLinkLanguage,
+ this->GetConfigName());
+ vars["SONAME"] = this->TargetNameSO;
+
+ std::vector<cmCustomCommand> *cmdLists[3] = {
+ &this->GetTarget()->GetPreBuildCommands(),
+ &this->GetTarget()->GetPreLinkCommands(),
+ &this->GetTarget()->GetPostBuildCommands()
+ };
+
+ std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
+ std::vector<std::string> *cmdLineLists[3] = {
+ &preLinkCmdLines,
+ &preLinkCmdLines,
+ &postBuildCmdLines
+ };
+
+ for (unsigned i = 0; i != 3; ++i) {
+ for (std::vector<cmCustomCommand>::const_iterator
+ ci = cmdLists[i]->begin();
+ ci != cmdLists[i]->end(); ++ci) {
+ this->GetLocalGenerator()->AppendCustomCommandLines(&*ci,
+ *cmdLineLists[i]);
+ }
+ }
+
+ // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for
+ // the link commands.
+ if (!preLinkCmdLines.empty())
+ 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 (this->GetTarget()->GetType() == 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 0000000..99f5a13
--- /dev/null
+++ b/Source/cmNinjaNormalTargetGenerator.h
@@ -0,0 +1,47 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#ifndef cmNinjaNormalTargetGenerator_h
+# define cmNinjaNormalTargetGenerator_h
+
+# include "cmNinjaTargetGenerator.h"
+# include "cmNinjaTypes.h"
+
+class cmSourceFile;
+
+class cmNinjaNormalTargetGenerator : public cmNinjaTargetGenerator
+{
+public:
+ cmNinjaNormalTargetGenerator(cmTarget* target);
+ ~cmNinjaNormalTargetGenerator();
+
+ void Generate();
+
+private:
+ std::string LanguageLinkerRule() const;
+ const char* GetVisibleTypeName() const;
+ void WriteLanguagesRules();
+ void WriteLinkRule();
+ void WriteLinkStatement();
+ std::vector<std::string> ComputeLinkCmd();
+
+private:
+ // Target name info.
+ std::string TargetNameOut;
+ std::string TargetNameSO;
+ std::string TargetNameReal;
+ std::string TargetNameImport;
+ std::string TargetNamePDB;
+ const char *TargetLinkLanguage;
+};
+
+#endif // ! cmNinjaNormalTargetGenerator_h
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
new file mode 100644
index 0000000..e48ac12
--- /dev/null
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -0,0 +1,445 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#include "cmNinjaTargetGenerator.h"
+#include "cmGlobalNinjaGenerator.h"
+#include "cmLocalNinjaGenerator.h"
+#include "cmGeneratedFileStream.h"
+#include "cmNinjaNormalTargetGenerator.h"
+#include "cmNinjaUtilityTargetGenerator.h"
+#include "cmSystemTools.h"
+#include "cmMakefile.h"
+#include "cmComputeLinkInformation.h"
+#include "cmSourceFile.h"
+#include "cmCustomCommandGenerator.h"
+
+#include <algorithm>
+
+cmNinjaTargetGenerator *
+cmNinjaTargetGenerator::New(cmTarget* target)
+{
+ switch (target->GetType())
+ {
+ case cmTarget::EXECUTABLE:
+ case cmTarget::SHARED_LIBRARY:
+ case cmTarget::STATIC_LIBRARY:
+ case cmTarget::MODULE_LIBRARY:
+ return new cmNinjaNormalTargetGenerator(target);
+
+ case cmTarget::UTILITY:
+ return new cmNinjaUtilityTargetGenerator(target);;
+
+ case cmTarget::GLOBAL_TARGET: {
+ // We only want to process global targets that live in the home
+ // (i.e. top-level) directory. CMake creates copies of these targets
+ // in every directory, which we don't need.
+ cmMakefile *mf = target->GetMakefile();
+ if (strcmp(mf->GetStartDirectory(), mf->GetHomeDirectory()) == 0)
+ return new cmNinjaUtilityTargetGenerator(target);
+ // else fallthrough
+ }
+
+ default:
+ return 0;
+ }
+}
+
+cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmTarget* target)
+ : Target(target),
+ Makefile(target->GetMakefile()),
+ LocalGenerator(
+ static_cast<cmLocalNinjaGenerator*>(Makefile->GetLocalGenerator())),
+ Objects()
+{
+}
+
+cmNinjaTargetGenerator::~cmNinjaTargetGenerator()
+{
+}
+
+cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const
+{
+ return *this->GetGlobalGenerator()->GetBuildFileStream();
+}
+
+cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
+{
+ return *this->GetGlobalGenerator()->GetRulesFileStream();
+}
+
+cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
+{
+ return this->LocalGenerator->GetGlobalNinjaGenerator();
+}
+
+const char* cmNinjaTargetGenerator::GetConfigName() const
+{
+ return this->LocalGenerator->ConfigName.c_str();
+}
+
+// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
+const char* cmNinjaTargetGenerator::GetFeature(const char* feature)
+{
+ return this->Target->GetFeature(feature, this->GetConfigName());
+}
+
+// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
+bool cmNinjaTargetGenerator::GetFeatureAsBool(const char* feature)
+{
+ return cmSystemTools::IsOn(this->GetFeature(feature));
+}
+
+// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
+void cmNinjaTargetGenerator::AddFeatureFlags(std::string& flags,
+ const char* lang)
+{
+ // Add language-specific flags.
+ this->LocalGenerator->AddLanguageFlags(flags, lang, this->GetConfigName());
+
+ if(this->GetFeatureAsBool("INTERPROCEDURAL_OPTIMIZATION"))
+ {
+ this->LocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
+ }
+}
+
+// TODO: Most of the code is picked up from
+// void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
+// void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
+// Refactor it.
+std::string
+cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
+ const std::string& language)
+{
+ std::string flags;
+
+ this->AddFeatureFlags(flags, language.c_str());
+
+ this->GetLocalGenerator()->AddArchitectureFlags(flags,
+ this->GetTarget(),
+ language.c_str(),
+ this->GetConfigName());
+
+ // TODO: Fortran support.
+ // // Fortran-specific flags computed for this target.
+ // if(*l == "Fortran")
+ // {
+ // this->AddFortranFlags(flags);
+ // }
+
+ // Add shared-library flags if needed.
+ {
+ bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) ||
+ (this->Target->GetType() == cmTarget::MODULE_LIBRARY));
+ this->GetLocalGenerator()->AddSharedFlags(flags, language.c_str(), shared);
+ }
+
+ // TODO: Handle response file.
+ // Add include directory flags.
+ {
+ std::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<std::string> &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>",
+ 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<cmSourceFile*>::const_iterator i =
+ this->GetTarget()->GetSourceFiles().begin();
+ i != this->GetTarget()->GetSourceFiles().end();
+ ++i)
+ this->WriteObjectBuildStatement(*i);
+
+ this->GetBuildFileStream() << "\n";
+}
+
+void
+cmNinjaTargetGenerator
+::WriteObjectBuildStatement(cmSourceFile* source)
+{
+ if (cmCustomCommand *cc = source->GetCustomCommand())
+ this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget());
+
+ cmNinjaDeps emptyDeps;
+
+ std::string comment;
+ const char* language = source->GetLanguage();
+ // If we cannot get the language this is probably a non-source file provided
+ // in the list (typically an header file).
+ if (!language) {
+ if (source->GetPropertyAsBool("EXTERNAL_OBJECT"))
+ this->Objects.push_back(this->GetSourceFilePath(source));
+ return;
+ }
+
+ if (source->GetPropertyAsBool("HEADER_FILE_ONLY"))
+ return;
+
+ std::string rule = this->LanguageCompilerRule(language);
+
+ cmNinjaDeps outputs;
+ std::string objectFileName = this->GetObjectFilePath(source);
+ outputs.push_back(objectFileName);
+ // Add this object to the list of object files.
+ this->Objects.push_back(objectFileName);
+
+ cmNinjaDeps explicitDeps;
+ std::string sourceFileName = this->GetSourceFilePath(source);
+ explicitDeps.push_back(sourceFileName);
+
+ // Ensure that the target dependencies are built before any source file in
+ // the target, using order-only dependencies.
+ cmNinjaDeps orderOnlyDeps;
+ this->GetLocalGenerator()->AppendTargetDepends(this->Target, orderOnlyDeps);
+
+ if(const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
+ std::vector<std::string> depList;
+ cmSystemTools::ExpandListArgument(objectDeps, depList);
+ std::transform(depList.begin(), depList.end(),
+ std::back_inserter(orderOnlyDeps), MapToNinjaPath());
+ }
+
+ // Add order-only dependency on any header file with a custom command.
+ {
+ const std::vector<cmSourceFile*>& sources =
+ this->GetTarget()->GetSourceFiles();
+ for(std::vector<cmSourceFile*>::const_iterator si = sources.begin();
+ si != sources.end(); ++si) {
+ if (!(*si)->GetLanguage()) {
+ if (cmCustomCommand* cc = (*si)->GetCustomCommand()) {
+ const std::vector<std::string>& ccoutputs = cc->GetOutputs();
+ std::transform(ccoutputs.begin(), ccoutputs.end(),
+ std::back_inserter(orderOnlyDeps), MapToNinjaPath());
+ }
+ }
+ }
+ }
+
+ // If the source file is GENERATED and does not have a custom command
+ // (either attached to this source file or another one), assume that one of
+ // the target dependencies, OBJECT_DEPENDS or header file custom commands
+ // will rebuild the file.
+ if (source->GetPropertyAsBool("GENERATED") && !source->GetCustomCommand() &&
+ !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) {
+ this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName,
+ orderOnlyDeps);
+ }
+
+ cmNinjaVars vars;
+ vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
+ vars["DEFINES"] = this->ComputeDefines(source, language);
+
+ 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 0000000..cf47abf
--- /dev/null
+++ b/Source/cmNinjaTargetGenerator.h
@@ -0,0 +1,115 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#ifndef cmNinjaTargetGenerator_h
+#define cmNinjaTargetGenerator_h
+
+#include "cmStandardIncludes.h"
+#include "cmNinjaTypes.h"
+#include "cmLocalNinjaGenerator.h"
+
+class cmTarget;
+class cmGlobalNinjaGenerator;
+class cmGeneratedFileStream;
+class cmMakefile;
+class cmSourceFile;
+class cmCustomCommand;
+
+class cmNinjaTargetGenerator
+{
+public:
+ /// Create a cmNinjaTargetGenerator according to the @a target's type.
+ static cmNinjaTargetGenerator* New(cmTarget* target);
+
+ /// Build a NinjaTargetGenerator.
+ cmNinjaTargetGenerator(cmTarget* target);
+
+ /// Destructor.
+ virtual ~cmNinjaTargetGenerator();
+
+ virtual void Generate() = 0;
+
+ std::string 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 0000000..29ef153
--- /dev/null
+++ b/Source/cmNinjaTypes.h
@@ -0,0 +1,19 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#ifndef cmNinjaTypes_h
+# define cmNinjaTypes_h
+
+typedef std::vector<std::string> cmNinjaDeps;
+typedef std::map<std::string, std::string> cmNinjaVars;
+
+#endif // ! cmGlobalNinjaGenerator_h
diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx
new file mode 100644
index 0000000..0a15834
--- /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<std::string> commands;
+ cmNinjaDeps deps, outputs;
+
+ const std::vector<cmCustomCommand> *cmdLists[2] = {
+ &this->GetTarget()->GetPreBuildCommands(),
+ &this->GetTarget()->GetPostBuildCommands()
+ };
+
+ for (unsigned i = 0; i != 2; ++i) {
+ for (std::vector<cmCustomCommand>::const_iterator
+ ci = cmdLists[i]->begin(); ci != cmdLists[i]->end(); ++ci) {
+ this->GetLocalGenerator()->AppendCustomCommandDeps(&*ci, deps);
+ this->GetLocalGenerator()->AppendCustomCommandLines(&*ci, commands);
+ }
+ }
+
+ const std::vector<cmSourceFile*>& sources =
+ this->GetTarget()->GetSourceFiles();
+ for(std::vector<cmSourceFile*>::const_iterator source = sources.begin();
+ source != sources.end(); ++source)
+ {
+ if(cmCustomCommand* cc = (*source)->GetCustomCommand())
+ {
+ this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget());
+
+ // Depend on all custom command outputs.
+ const std::vector<std::string>& 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 0000000..8b82ce4
--- /dev/null
+++ b/Source/cmNinjaUtilityTargetGenerator.h
@@ -0,0 +1,30 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2011 Peter Collingbourne <peter at pcc.me.uk>
+ Copyright 2011 Nicolas Despres <nicolas.despres at gmail.com>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#ifndef cmNinjaUtilityTargetGenerator_h
+# define cmNinjaUtilityTargetGenerator_h
+
+# include "cmNinjaTargetGenerator.h"
+# include "cmNinjaTypes.h"
+
+class cmSourceFile;
+
+class cmNinjaUtilityTargetGenerator : public cmNinjaTargetGenerator
+{
+public:
+ cmNinjaUtilityTargetGenerator(cmTarget* target);
+ ~cmNinjaUtilityTargetGenerator();
+
+ void Generate();
+};
+
+#endif // ! cmNinjaUtilityTargetGenerator_h
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index d691f46..1d793d7 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/BuildDepends/CMakeLists.txt b/Tests/BuildDepends/CMakeLists.txt
index 31392b5..aa32d67 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
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 21d1196..4670526 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -325,6 +325,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
@@ -332,13 +341,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 c7cc090..c362e79 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)
--
1.7.5.3
More information about the cmake-developers
mailing list