Ninja: Add the Ninja generator

This commit is contained in:
Peter Collingbourne 2011-11-11 05:00:49 +00:00
parent 7eb8d9036c
commit 6dd410c2b9
17 changed files with 2913 additions and 4 deletions

View File

@ -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)

View File

@ -833,6 +833,12 @@ function(ExternalProject_Add_StepTargets name)
foreach(step ${steps})
add_custom_target(${name}-${step}
DEPENDS ${stamp_dir}${cfgdir}/${name}-${step})
# Depend on other external projects (target-level).
get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
foreach(arg IN LISTS deps)
add_dependencies(${name}-${step} ${arg})
endforeach()
endforeach()
endfunction(ExternalProject_Add_StepTargets)
@ -1451,9 +1457,18 @@ function(ExternalProject_Add name)
# depends on the 'done' mark so that it rebuilds when this project
# rebuilds. It is important that 'done' is not the output of any
# custom command so that CMake does not propagate build rules to
# other external project targets.
# other external project targets, which may cause problems during
# parallel builds. However, the Ninja generator needs to see the entire
# dependency graph, and can cope with custom commands belonging to
# multiple targets, so we add the 'done' mark as an output for Ninja only.
set(complete_outputs ${cmf_dir}${cfgdir}/${name}-complete)
if(${CMAKE_GENERATOR} MATCHES "Ninja")
set(complete_outputs
${complete_outputs} ${stamp_dir}${cfgdir}/${name}-done)
endif()
add_custom_command(
OUTPUT ${cmf_dir}${cfgdir}/${name}-complete
OUTPUT ${complete_outputs}
COMMENT "Completed '${name}'"
COMMAND ${CMAKE_COMMAND} -E make_directory ${cmf_dir}${cfgdir}
COMMAND ${CMAKE_COMMAND} -E touch ${cmf_dir}${cfgdir}/${name}-complete

View File

@ -353,6 +353,24 @@ IF (WIN32)
ENDIF(NOT UNIX)
ENDIF (WIN32)
# Ninja only works on UNIX.
IF(UNIX)
SET(SRCS ${SRCS}
cmGlobalNinjaGenerator.cxx
cmGlobalNinjaGenerator.h
cmNinjaTypes.h
cmLocalNinjaGenerator.cxx
cmLocalNinjaGenerator.h
cmNinjaTargetGenerator.cxx
cmNinjaTargetGenerator.h
cmNinjaNormalTargetGenerator.cxx
cmNinjaNormalTargetGenerator.h
cmNinjaUtilityTargetGenerator.cxx
cmNinjaUtilityTargetGenerator.h
)
ADD_DEFINITIONS(-DCMAKE_USE_NINJA)
ENDIF(UNIX)
# create a library used by the command line and the GUI
ADD_LIBRARY(CMakeLib ${SRCS})
TARGET_LINK_LIBRARIES(CMakeLib cmsys

View File

@ -0,0 +1,774 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmGlobalNinjaGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmVersion.h"
const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja";
const char* cmGlobalNinjaGenerator::INDENT = " ";
void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
{
for(int i = 0; i < count; ++i)
os << cmGlobalNinjaGenerator::INDENT;
}
void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
{
os
<< "# ======================================"
<< "=======================================\n";
}
void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
const std::string& comment)
{
if (comment.empty())
return;
std::string replace = comment;
std::string::size_type lpos = 0;
std::string::size_type rpos;
while((rpos = replace.find('\n', lpos)) != std::string::npos)
{
os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
lpos = rpos + 1;
}
os << "# " << replace.substr(lpos) << "\n";
}
static bool IsIdentChar(char c)
{
return
('a' <= c && c <= 'z') ||
('+' <= c && c <= '9') || // +,-./ and numbers
('A' <= c && c <= 'Z') ||
(c == '_') || (c == '$') || (c == '\\');
}
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)
{
const char* configName =
target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE");
cmLocalNinjaGenerator *ng =
static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
switch (target->GetType()) {
case cmTarget::EXECUTABLE:
case cmTarget::SHARED_LIBRARY:
case cmTarget::STATIC_LIBRARY:
case cmTarget::MODULE_LIBRARY:
outputs.push_back(ng->ConvertToNinjaPath(
target->GetFullPath(configName).c_str()));
break;
case cmTarget::UTILITY: {
std::string path = ng->ConvertToNinjaPath(
target->GetMakefile()->GetStartOutputDirectory());
if (path.empty() || path == ".")
outputs.push_back(target->GetName());
else {
path += "/";
path += target->GetName();
outputs.push_back(path);
}
break;
}
case cmTarget::GLOBAL_TARGET:
// Always use the target in HOME instead of an unused duplicate in a
// subdirectory.
outputs.push_back(target->GetName());
break;
default:
return;
}
}
void
cmGlobalNinjaGenerator
::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
{
if (target->GetType() == cmTarget::GLOBAL_TARGET) {
// Global targets only depend on other utilities, which may not appear in
// the TargetDepends set (e.g. "all").
std::set<cmStdString> const& utils = target->GetUtilities();
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());
}

View File

@ -0,0 +1,329 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmGlobalNinjaGenerator_h
# define cmGlobalNinjaGenerator_h
# include "cmGlobalGenerator.h"
# include "cmNinjaTypes.h"
class cmLocalGenerator;
class cmGeneratedFileStream;
/**
* \class cmGlobalNinjaGenerator
* \brief Write a build.ninja file.
*
* The main differences between this generator and the UnixMakefile
* generator family are:
* - We don't care about VERBOSE variable or RULE_MESSAGES property since
* it is handle by Ninja's -v option.
* - We don't care about computing any progress status since Ninja manages
* it itself.
* - We don't care about generating a clean target since Ninja already have
* a clean tool.
* - We generate one build.ninja and one rules.ninja per project.
* - We try to minimize the number of generated rules: one per target and
* language.
* - We use Ninja special variable $in and $out to produce nice output.
* - We extensively use Ninja variable overloading system to minimize the
* number of generated rules.
*/
class cmGlobalNinjaGenerator : public cmGlobalGenerator
{
public:
/// The default name of Ninja's build file. Typically: build.ninja.
static const char* NINJA_BUILD_FILE;
/// The default name of Ninja's rules file. Typically: rules.ninja.
/// It is included in the main build.ninja file.
static const char* NINJA_RULES_FILE;
/// The indentation string used when generating Ninja's build file.
static const char* INDENT;
/// Write @a count times INDENT level to output stream @a os.
static void Indent(std::ostream& os, int count);
/// Write a divider in the given output stream @a os.
static void WriteDivider(std::ostream& os);
static std::string EncodeIdent(const std::string &ident, std::ostream &vars);
static std::string EncodeLiteral(const std::string &lit);
/**
* 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

View File

@ -0,0 +1,413 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmLocalNinjaGenerator.h"
#include "cmCustomCommandGenerator.h"
#include "cmMakefile.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmNinjaTargetGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmSourceFile.h"
#include "cmComputeLinkInformation.h"
#include "cmake.h"
#include <assert.h>
cmLocalNinjaGenerator::cmLocalNinjaGenerator()
: cmLocalGenerator()
, ConfigName("")
, HomeRelativeOutputPath("")
{
this->IsMakefileGenerator = true;
}
//----------------------------------------------------------------------------
// 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);
}
}

View File

@ -0,0 +1,134 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmLocalNinjaGenerator_h
# define cmLocalNinjaGenerator_h
# include "cmLocalGenerator.h"
# include "cmNinjaTypes.h"
class cmGlobalNinjaGenerator;
class cmGeneratedFileStream;
class cmake;
/**
* \class cmLocalNinjaGenerator
* \brief Write a local build.ninja file.
*
* cmLocalNinjaGenerator produces a local build.ninja file from its
* member Makefile.
*/
class cmLocalNinjaGenerator : public cmLocalGenerator
{
public:
/// Default constructor.
cmLocalNinjaGenerator();
/// Destructor.
virtual ~cmLocalNinjaGenerator();
/// Overloaded methods. @see cmLocalGenerator::Generate()
virtual void Generate();
/// Overloaded methods. @see cmLocalGenerator::Configure()
virtual void Configure();
/// Overloaded methods. @see cmLocalGenerator::GetTargetDirectory()
virtual std::string GetTargetDirectory(cmTarget const& target) const;
public:
const cmGlobalNinjaGenerator* GetGlobalNinjaGenerator() const;
cmGlobalNinjaGenerator* GetGlobalNinjaGenerator();
/**
* Shortcut to get the cmake instance throw the global generator.
* @return an instance of the cmake object.
*/
const cmake* GetCMakeInstance() const;
cmake* GetCMakeInstance();
const char* GetConfigName() const
{ return this->ConfigName.c_str(); }
std::string GetObjectFileName(const cmTarget& target,
const cmSourceFile& source);
/// @return whether we are processing the top CMakeLists.txt file.
bool isRootMakefile() const;
/// @returns the relative path between the HomeOutputDirectory and this
/// local generators StartOutputDirectory.
std::string GetHomeRelativeOutputPath() const
{ return this->HomeRelativeOutputPath; }
protected:
virtual std::string ConvertToLinkReference(std::string const& lib);
virtual std::string ConvertToIncludeReference(std::string const& path);
private:
friend class cmGlobalNinjaGenerator;
// In order to access to protected member of the local generator.
friend class cmNinjaTargetGenerator;
friend class cmNinjaNormalTargetGenerator;
friend class cmNinjaUtilityTargetGenerator;
private:
cmGeneratedFileStream& GetBuildFileStream() const;
cmGeneratedFileStream& GetRulesFileStream() const;
void WriteBuildFileTop();
void WriteProjectHeader(std::ostream& os);
void WriteNinjaFilesInclusion(std::ostream& os);
void WriteProcessedMakefile(std::ostream& os);
void SetConfigName();
std::string ConvertToNinjaPath(const char *path);
struct map_to_ninja_path {
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

View File

@ -0,0 +1,430 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmNinjaNormalTargetGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmSourceFile.h"
#include "cmGeneratedFileStream.h"
#include "cmMakefile.h"
#include <assert.h>
cmNinjaNormalTargetGenerator::
cmNinjaNormalTargetGenerator(cmTarget* target)
: cmNinjaTargetGenerator(target)
, TargetNameOut()
, TargetNameSO()
, TargetNameReal()
, TargetNameImport()
, TargetNamePDB()
, 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()
{
cmTarget::TargetType targetType = this->GetTarget()->GetType();
std::string ruleName = this->LanguageLinkerRule();
if (!this->GetGlobalGenerator()->HasRule(ruleName)) {
cmLocalGenerator::RuleVariables vars;
vars.RuleLauncher = "RULE_LAUNCH_LINK";
vars.CMTarget = this->GetTarget();
vars.Language = this->TargetLinkLanguage;
vars.Objects = "$in";
std::string objdir = cmake::GetCMakeFilesDirectoryPostSlash();
objdir += this->GetTargetName();
objdir += ".dir";
objdir = this->GetLocalGenerator()->Convert(objdir.c_str(),
cmLocalGenerator::START_OUTPUT,
cmLocalGenerator::SHELL);
vars.ObjectDir = objdir.c_str();
vars.Target = "$out";
vars.TargetSOName = "$SONAME";
vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
// Setup the target version.
std::string targetVersionMajor;
std::string targetVersionMinor;
{
cmOStringStream majorStream;
cmOStringStream minorStream;
int major;
int minor;
this->GetTarget()->GetTargetVersion(major, minor);
majorStream << major;
minorStream << minor;
targetVersionMajor = majorStream.str();
targetVersionMinor = minorStream.str();
}
vars.TargetVersionMajor = targetVersionMajor.c_str();
vars.TargetVersionMinor = targetVersionMinor.c_str();
vars.LinkLibraries = "$LINK_LIBRARIES";
vars.Flags = "$FLAGS";
vars.LinkFlags = "$LINK_FLAGS";
std::string langFlags;
this->GetLocalGenerator()->AddLanguageFlags(langFlags,
this->TargetLinkLanguage,
this->GetConfigName());
if (targetType != cmTarget::EXECUTABLE)
langFlags += " $ARCH_FLAGS";
vars.LanguageCompileFlags = langFlags.c_str();
// Rule for linking library.
std::vector<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 (targetType == cmTarget::EXECUTABLE)
this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
cmakeCommand +
" -E cmake_symlink_executable"
" $in $out && $POST_BUILD",
"Creating executable symlink $out",
"Rule for creating executable symlink.");
else
this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
cmakeCommand +
" -E cmake_symlink_library"
" $in $SONAME $out && $POST_BUILD",
"Creating library symlink $out",
"Rule for creating library symlink.");
}
}
std::vector<std::string>
cmNinjaNormalTargetGenerator
::ComputeLinkCmd()
{
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;
std::string cmakeCommand =
this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND");
linkCmds.push_back(cmakeCommand + " -E remove $out");
// TODO: Use ARCHIVE_APPEND for archives over a certain size.
{
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
linkCmdVar += "_ARCHIVE_CREATE";
const char *linkCmd =
this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
linkCmds.push_back(linkCmd);
}
{
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
linkCmdVar += "_ARCHIVE_FINISH";
const char *linkCmd =
this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
linkCmds.push_back(linkCmd);
}
return linkCmds;
}
case cmTarget::SHARED_LIBRARY:
case cmTarget::MODULE_LIBRARY:
case cmTarget::EXECUTABLE: {
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
switch (targetType) {
case cmTarget::SHARED_LIBRARY:
linkCmdVar += "_CREATE_SHARED_LIBRARY";
break;
case cmTarget::MODULE_LIBRARY:
linkCmdVar += "_CREATE_SHARED_MODULE";
break;
case cmTarget::EXECUTABLE:
linkCmdVar += "_LINK_EXECUTABLE";
break;
default:
assert(0 && "Unexpected target type");
}
const char *linkCmd =
this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
return std::vector<std::string>(1, linkCmd);
}
default:
assert(0 && "Unexpected target type");
}
}
void cmNinjaNormalTargetGenerator::WriteLinkStatement()
{
cmTarget::TargetType targetType = this->GetTarget()->GetType();
// Write comments.
cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
this->GetBuildFileStream()
<< "# Link build statements for "
<< cmTarget::GetTargetTypeName(targetType)
<< " target "
<< this->GetTargetName()
<< "\n\n";
cmNinjaDeps emptyDeps;
cmNinjaVars vars;
std::string targetOutput = ConvertToNinjaPath(
this->GetTarget()->GetFullPath(this->GetConfigName()).c_str());
std::string targetOutputReal = ConvertToNinjaPath(
this->GetTarget()->GetFullPath(this->GetConfigName(),
/*implib=*/false,
/*realpath=*/true).c_str());
// Compute the comment.
std::ostringstream comment;
comment << "Link the " << this->GetVisibleTypeName() << " "
<< targetOutputReal;
// Compute outputs.
cmNinjaDeps outputs;
outputs.push_back(targetOutputReal);
// Compute specific libraries to link with.
cmNinjaDeps explicitDeps = this->GetObjects(),
implicitDeps = this->ComputeLinkDeps();
this->GetLocalGenerator()->GetTargetFlags(vars["LINK_LIBRARIES"],
vars["FLAGS"],
vars["LINK_FLAGS"],
*this->GetTarget());
// Compute architecture specific link flags. Yes, these go into a different
// variable for executables, probably due to a mistake made when duplicating
// code between the Makefile executable and library generators.
this->GetLocalGenerator()
->AddArchitectureFlags(targetType == cmTarget::EXECUTABLE
? vars["FLAGS"]
: vars["ARCH_FLAGS"],
this->GetTarget(),
this->TargetLinkLanguage,
this->GetConfigName());
vars["SONAME"] = this->TargetNameSO;
if (targetType == cmTarget::SHARED_LIBRARY) {
std::string install_name_dir =
this->GetTarget()->GetInstallNameDirForBuildTree(this->GetConfigName());
if (!install_name_dir.empty()) {
vars["INSTALLNAME_DIR"] =
this->GetLocalGenerator()->Convert(install_name_dir.c_str(),
cmLocalGenerator::NONE,
cmLocalGenerator::SHELL, false);
}
}
std::vector<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 (targetType == cmTarget::EXECUTABLE) {
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
"Create executable symlink " + targetOutput,
"CMAKE_SYMLINK_EXECUTABLE",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
} else {
symlinkVars["SONAME"] = this->GetTargetFilePath(this->TargetNameSO);
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
"Create library symlink " + targetOutput,
"CMAKE_SYMLINK_LIBRARY",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
}
}
// Add aliases for the file name and the target name.
this->GetGlobalGenerator()->AddTargetAlias(this->TargetNameOut,
this->GetTarget());
this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
this->GetTarget());
}

View File

@ -0,0 +1,47 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmNinjaNormalTargetGenerator_h
# define cmNinjaNormalTargetGenerator_h
# include "cmNinjaTargetGenerator.h"
# include "cmNinjaTypes.h"
class cmSourceFile;
class cmNinjaNormalTargetGenerator : public cmNinjaTargetGenerator
{
public:
cmNinjaNormalTargetGenerator(cmTarget* target);
~cmNinjaNormalTargetGenerator();
void Generate();
private:
std::string LanguageLinkerRule() const;
const char* GetVisibleTypeName() const;
void WriteLanguagesRules();
void WriteLinkRule();
void WriteLinkStatement();
std::vector<std::string> ComputeLinkCmd();
private:
// Target name info.
std::string TargetNameOut;
std::string TargetNameSO;
std::string TargetNameReal;
std::string TargetNameImport;
std::string TargetNamePDB;
const char *TargetLinkLanguage;
};
#endif // ! cmNinjaNormalTargetGenerator_h

View File

@ -0,0 +1,445 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmNinjaTargetGenerator.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmNinjaNormalTargetGenerator.h"
#include "cmNinjaUtilityTargetGenerator.h"
#include "cmSystemTools.h"
#include "cmMakefile.h"
#include "cmComputeLinkInformation.h"
#include "cmSourceFile.h"
#include "cmCustomCommandGenerator.h"
#include <algorithm>
cmNinjaTargetGenerator *
cmNinjaTargetGenerator::New(cmTarget* target)
{
switch (target->GetType())
{
case cmTarget::EXECUTABLE:
case cmTarget::SHARED_LIBRARY:
case cmTarget::STATIC_LIBRARY:
case cmTarget::MODULE_LIBRARY:
return new cmNinjaNormalTargetGenerator(target);
case cmTarget::UTILITY:
return new cmNinjaUtilityTargetGenerator(target);;
case cmTarget::GLOBAL_TARGET: {
// We only want to process global targets that live in the home
// (i.e. top-level) directory. CMake creates copies of these targets
// in every directory, which we don't need.
cmMakefile *mf = target->GetMakefile();
if (strcmp(mf->GetStartDirectory(), mf->GetHomeDirectory()) == 0)
return new cmNinjaUtilityTargetGenerator(target);
// else fallthrough
}
default:
return 0;
}
}
cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmTarget* target)
: Target(target),
Makefile(target->GetMakefile()),
LocalGenerator(
static_cast<cmLocalNinjaGenerator*>(Makefile->GetLocalGenerator())),
Objects()
{
}
cmNinjaTargetGenerator::~cmNinjaTargetGenerator()
{
}
cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const
{
return *this->GetGlobalGenerator()->GetBuildFileStream();
}
cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
{
return *this->GetGlobalGenerator()->GetRulesFileStream();
}
cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
{
return this->LocalGenerator->GetGlobalNinjaGenerator();
}
const char* cmNinjaTargetGenerator::GetConfigName() const
{
return this->LocalGenerator->ConfigName.c_str();
}
// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
const char* cmNinjaTargetGenerator::GetFeature(const char* feature)
{
return this->Target->GetFeature(feature, this->GetConfigName());
}
// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
bool cmNinjaTargetGenerator::GetFeatureAsBool(const char* feature)
{
return cmSystemTools::IsOn(this->GetFeature(feature));
}
// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
void cmNinjaTargetGenerator::AddFeatureFlags(std::string& flags,
const char* lang)
{
// Add language-specific flags.
this->LocalGenerator->AddLanguageFlags(flags, lang, this->GetConfigName());
if(this->GetFeatureAsBool("INTERPROCEDURAL_OPTIMIZATION"))
{
this->LocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
}
}
// TODO: Most of the code is picked up from
// void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
// Refactor it.
std::string
cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
const std::string& language)
{
std::string flags;
this->AddFeatureFlags(flags, language.c_str());
this->GetLocalGenerator()->AddArchitectureFlags(flags,
this->GetTarget(),
language.c_str(),
this->GetConfigName());
// TODO: Fortran support.
// // Fortran-specific flags computed for this target.
// if(*l == "Fortran")
// {
// this->AddFortranFlags(flags);
// }
// Add shared-library flags if needed.
{
bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) ||
(this->Target->GetType() == cmTarget::MODULE_LIBRARY));
this->GetLocalGenerator()->AddSharedFlags(flags, language.c_str(), shared);
}
// TODO: Handle response file.
// Add include directory flags.
{
std::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);
}

View File

@ -0,0 +1,115 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmNinjaTargetGenerator_h
#define cmNinjaTargetGenerator_h
#include "cmStandardIncludes.h"
#include "cmNinjaTypes.h"
#include "cmLocalNinjaGenerator.h"
class cmTarget;
class cmGlobalNinjaGenerator;
class cmGeneratedFileStream;
class cmMakefile;
class cmSourceFile;
class cmCustomCommand;
class cmNinjaTargetGenerator
{
public:
/// Create a cmNinjaTargetGenerator according to the @a target's type.
static cmNinjaTargetGenerator* New(cmTarget* target);
/// Build a NinjaTargetGenerator.
cmNinjaTargetGenerator(cmTarget* target);
/// Destructor.
virtual ~cmNinjaTargetGenerator();
virtual void Generate() = 0;
std::string 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

19
Source/cmNinjaTypes.h Normal file
View File

@ -0,0 +1,19 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmNinjaTypes_h
# define cmNinjaTypes_h
typedef std::vector<std::string> cmNinjaDeps;
typedef std::map<std::string, std::string> cmNinjaVars;
#endif // ! cmNinjaTypes_h

View File

@ -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());
}

View File

@ -0,0 +1,30 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmNinjaUtilityTargetGenerator_h
# define cmNinjaUtilityTargetGenerator_h
# include "cmNinjaTargetGenerator.h"
# include "cmNinjaTypes.h"
class cmSourceFile;
class cmNinjaUtilityTargetGenerator : public cmNinjaTargetGenerator
{
public:
cmNinjaUtilityTargetGenerator(cmTarget* target);
~cmNinjaUtilityTargetGenerator();
void Generate();
};
#endif // ! cmNinjaUtilityTargetGenerator_h

View File

@ -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;

View File

@ -334,6 +334,15 @@ IF(BUILD_TESTING)
--build-target car
--test-command car
)
IF(${CMAKE_TEST_GENERATOR} MATCHES "Ninja")
# The Ninja generator does not create a recursive build system. Start
# from the root directory.
SET(SubProject_SUBDIR)
ELSE()
SET(SubProject_SUBDIR "/foo")
ENDIF()
# For stage 2, do not run cmake again.
# Then build the foo sub project which should build
# the bar library which should be referenced because
@ -341,13 +350,14 @@ IF(BUILD_TESTING)
# directly in the foo sub project
ADD_TEST(SubProject-Stage2 ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/SubProject/foo"
"${CMake_BINARY_DIR}/Tests/SubProject/foo"
"${CMake_SOURCE_DIR}/Tests/SubProject${SubProject_SUBDIR}"
"${CMake_BINARY_DIR}/Tests/SubProject${SubProject_SUBDIR}"
--build-generator ${CMAKE_TEST_GENERATOR}
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
--build-nocmake
--build-project foo
--build-target foo
--build-exe-dir "${CMake_BINARY_DIR}/Tests/SubProject/foo"
--test-command foo
)
SET_TESTS_PROPERTIES ( SubProject-Stage2 PROPERTIES DEPENDS SubProject)

View File

@ -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)