Merge branch 'ninja-object-library' into object-library

* ninja-object-library: (37 commits)
  Ninja: Honor $<TARGET_OBJECTS:...> source expressions
  Build object library targets in Ninja
  Pre-compute object file names before Ninja generation
  Simplify cmNinjaTargetGenerator using cmGeneratorTarget
  Ninja: Avoid using 'this' in member initializers
  Ninja: Fix for PDB files with spaces in the path.
  Ninja: Constify use of cmCustomCommand
  Ninja: add /DEF: flag to linker call
  Ninja: Add a cache option CMAKE_ENABLE_NINJA to enable the ninja generator.
  Ninja: Add friend struct so it can access the private ConvertToNinjaPath.
  Ninja: add .def file support
  Ninja: ensure the output dir exists at compile time
  Ninja: Remove an unnecessary variable
  Ninja: Use cmSystemTools::ExpandListArgument to split compile/link commands
  Ninja: Add a missed license header
  Ninja: CMake: Adapt Ninja generator for per-target include dirs
  Ninja: windows msvc: create for each target a .pdb file
  Ninja: Import library support for Windows
  Ninja: mark the Windows specific hacks with a comment only
  Ninja: disable unfinished Windows ninja support
  ...
This commit is contained in:
Brad King 2012-03-19 13:11:27 -04:00
commit 93d5509b5b
23 changed files with 3160 additions and 7 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

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

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

@ -37,7 +37,7 @@ SET(CMAKE_COMPILE_RESOURCE "rc <FLAGS> /fo<OBJECT> <SOURCE>")
# that is automatically copied into try_compile directories
# by the global generator.
SET(MSVC_IDE 1)
IF(CMAKE_GENERATOR MATCHES "Makefiles")
IF(CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja")
SET(MSVC_IDE 0)
IF(NOT CMAKE_VC_COMPILER_TESTS_RUN)
SET(CMAKE_VC_COMPILER_TESTS 1)
@ -125,7 +125,7 @@ IF(CMAKE_GENERATOR MATCHES "Makefiles")
ENDIF(CMAKE_COMPILER_RETURN)
MAKE_DIRECTORY("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp3")
ENDIF(NOT CMAKE_VC_COMPILER_TESTS_RUN)
ENDIF(CMAKE_GENERATOR MATCHES "Makefiles")
ENDIF(CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja")
IF(MSVC_C_ARCHITECTURE_ID MATCHES 64)
SET(CMAKE_CL_64 1)

View File

@ -355,6 +355,36 @@ IF (WIN32)
ENDIF(NOT UNIX)
ENDIF (WIN32)
# turn on Ninja by default
set(_CMAKE_DEFAULT_NINJA_VALUE TRUE)
# turn it off for platforms where it does not pass all the
# tests
if(WIN32 OR APPLE)
SET(_CMAKE_DEFAULT_NINJA_VALUE FALSE)
endif()
SET(CMAKE_ENABLE_NINJA ${_CMAKE_DEFAULT_NINJA_VALUE} CACHE BOOL
"Enable the ninja generator for CMake. currently not fully working for Windows or OSX")
MARK_AS_ADVANCED(CMAKE_ENABLE_NINJA)
IF(CMAKE_ENABLE_NINJA)
MESSAGE(STATUS "Enable ninja generator.")
SET(SRCS ${SRCS}
cmGlobalNinjaGenerator.cxx
cmGlobalNinjaGenerator.h
cmNinjaTypes.h
cmLocalNinjaGenerator.cxx
cmLocalNinjaGenerator.h
cmNinjaTargetGenerator.cxx
cmNinjaTargetGenerator.h
cmNinjaNormalTargetGenerator.cxx
cmNinjaNormalTargetGenerator.h
cmNinjaUtilityTargetGenerator.cxx
cmNinjaUtilityTargetGenerator.h
)
ADD_DEFINITIONS(-DCMAKE_USE_NINJA)
ELSE()
MESSAGE(STATUS "Disable ninja generator.")
ENDIF()
# create a library used by the command line and the GUI
ADD_LIBRARY(CMakeLib ${SRCS})
TARGET_LINK_LIBRARIES(CMakeLib cmsys

View File

@ -0,0 +1,822 @@
/*============================================================================
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 "cmGeneratorTarget.h"
#include "cmVersion.h"
const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja";
const char* cmGlobalNinjaGenerator::INDENT = " ";
void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
{
for(int i = 0; i < count; ++i)
os << cmGlobalNinjaGenerator::INDENT;
}
void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
{
os
<< "# ======================================"
<< "=======================================\n";
}
void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
const std::string& comment)
{
if (comment.empty())
return;
std::string replace = comment;
std::string::size_type lpos = 0;
std::string::size_type rpos;
while((rpos = replace.find('\n', lpos)) != std::string::npos)
{
os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
lpos = rpos + 1;
}
os << "# " << replace.substr(lpos) << "\n";
}
static bool IsIdentChar(char c)
{
return
('a' <= c && c <= 'z') ||
('+' <= c && c <= '9') || // +,-./ and numbers
('A' <= c && c <= 'Z') ||
(c == '_') || (c == '$') || (c == '\\') ||
(c == ' ') || (c == ':');
}
std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident,
std::ostream &vars) {
if (std::find_if(ident.begin(), ident.end(),
std::not1(std::ptr_fun(IsIdentChar))) != ident.end()) {
static unsigned VarNum = 0;
std::ostringstream names;
names << "ident" << VarNum++;
vars << names.str() << " = " << ident << "\n";
return "$" + names.str();
} else {
std::string result = ident;
cmSystemTools::ReplaceString(result, " ", "$ ");
cmSystemTools::ReplaceString(result, ":", "$:");
return result;
}
}
std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit)
{
std::string result = lit;
cmSystemTools::ReplaceString(result, "$", "$$");
return result;
}
std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path)
{
std::string result = path;
#ifdef _WIN32
cmSystemTools::ReplaceString(result, "/", "\\");
#endif
return EncodeLiteral(result);
}
void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
const std::string& comment,
const std::string& rule,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps,
const cmNinjaDeps& orderOnlyDeps,
const cmNinjaVars& variables)
{
// Make sure there is a rule.
if(rule.empty())
{
cmSystemTools::Error("No rule for WriteBuildStatement! called "
"with comment: ",
comment.c_str());
return;
}
// Make sure there is at least one output file.
if(outputs.empty())
{
cmSystemTools::Error("No output files for WriteBuildStatement! called "
"with comment: ",
comment.c_str());
return;
}
cmGlobalNinjaGenerator::WriteComment(os, comment);
std::ostringstream builds;
// TODO: Better formatting for when there are multiple input/output files.
// Write outputs files.
builds << "build";
for(cmNinjaDeps::const_iterator i = outputs.begin();
i != outputs.end();
++i)
builds << " " << EncodeIdent(EncodePath(*i), os);
builds << ":";
// Write the rule.
builds << " " << rule;
// Write explicit dependencies.
for(cmNinjaDeps::const_iterator i = explicitDeps.begin();
i != explicitDeps.end();
++i)
builds << " " << EncodeIdent(EncodePath(*i), os);
// Write implicit dependencies.
if(!implicitDeps.empty())
{
builds << " |";
for(cmNinjaDeps::const_iterator i = implicitDeps.begin();
i != implicitDeps.end();
++i)
builds << " " << EncodeIdent(EncodePath(*i), os);
}
// Write order-only dependencies.
if(!orderOnlyDeps.empty())
{
builds << " ||";
for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin();
i != orderOnlyDeps.end();
++i)
builds << " " << EncodeIdent(EncodePath(*i), os);
}
builds << "\n";
os << builds.str();
// Write the variables bound to this build statement.
for(cmNinjaVars::const_iterator i = variables.begin();
i != variables.end();
++i)
cmGlobalNinjaGenerator::WriteVariable(os, i->first, i->second, "", 1);
}
void cmGlobalNinjaGenerator::WritePhonyBuild(std::ostream& os,
const std::string& comment,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps,
const cmNinjaDeps& orderOnlyDeps,
const cmNinjaVars& variables)
{
cmGlobalNinjaGenerator::WriteBuild(os,
comment,
"phony",
outputs,
explicitDeps,
implicitDeps,
orderOnlyDeps,
variables);
}
void cmGlobalNinjaGenerator::AddCustomCommandRule()
{
this->AddRule("CUSTOM_COMMAND",
"$COMMAND",
"$DESC",
"Rule for running custom commands.",
/*depfile*/ "",
/*restat*/ true);
}
void
cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command,
const std::string& description,
const std::string& comment,
const cmNinjaDeps& outputs,
const cmNinjaDeps& deps,
const cmNinjaDeps& orderOnlyDeps)
{
this->AddCustomCommandRule();
cmNinjaVars vars;
vars["COMMAND"] = command;
vars["DESC"] = EncodeLiteral(description);
cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream,
comment,
"CUSTOM_COMMAND",
outputs,
deps,
cmNinjaDeps(),
orderOnlyDeps,
vars);
}
void cmGlobalNinjaGenerator::WriteRule(std::ostream& os,
const std::string& name,
const std::string& command,
const std::string& description,
const std::string& comment,
const std::string& depfile,
bool restat,
bool generator)
{
// Make sure the rule has a name.
if(name.empty())
{
cmSystemTools::Error("No name given for WriteRuleStatement! called "
"with comment: ",
comment.c_str());
return;
}
// Make sure a command is given.
if(command.empty())
{
cmSystemTools::Error("No command given for WriteRuleStatement! called "
"with comment: ",
comment.c_str());
return;
}
cmGlobalNinjaGenerator::WriteComment(os, comment);
// Write the rule.
os << "rule " << name << "\n";
// Write the depfile if any.
if(!depfile.empty())
{
cmGlobalNinjaGenerator::Indent(os, 1);
os << "depfile = " << depfile << "\n";
}
// Write the command.
cmGlobalNinjaGenerator::Indent(os, 1);
os << "command = " << command << "\n";
// Write the description if any.
if(!description.empty())
{
cmGlobalNinjaGenerator::Indent(os, 1);
os << "description = " << description << "\n";
}
if(restat)
{
cmGlobalNinjaGenerator::Indent(os, 1);
os << "restat = 1\n";
}
if(generator)
{
cmGlobalNinjaGenerator::Indent(os, 1);
os << "generator = 1\n";
}
}
void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
const std::string& name,
const std::string& value,
const std::string& comment,
int indent)
{
// Make sure we have a name.
if(name.empty())
{
cmSystemTools::Error("No name given for WriteVariable! called "
"with comment: ",
comment.c_str());
return;
}
// Do not add a variable if the value is empty.
std::string val = cmSystemTools::TrimWhitespace(value);
if(val.empty())
{
return;
}
cmGlobalNinjaGenerator::WriteComment(os, comment);
cmGlobalNinjaGenerator::Indent(os, indent);
os << name << " = " << val << "\n";
}
void cmGlobalNinjaGenerator::WriteInclude(std::ostream& os,
const std::string& filename,
const std::string& comment)
{
cmGlobalNinjaGenerator::WriteComment(os, comment);
os << "include " << filename << "\n";
}
void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os,
const cmNinjaDeps& targets,
const std::string& comment)
{
cmGlobalNinjaGenerator::WriteComment(os, comment);
os << "default";
for(cmNinjaDeps::const_iterator i = targets.begin(); i != targets.end(); ++i)
os << " " << *i;
os << "\n";
}
cmGlobalNinjaGenerator::cmGlobalNinjaGenerator()
: cmGlobalGenerator()
, BuildFileStream(0)
, RulesFileStream(0)
, Rules()
, AllDependencies()
{
// // Ninja is not ported to non-Unix OS yet.
// this->ForceUnixPaths = true;
this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
}
//----------------------------------------------------------------------------
// Virtual public methods.
cmLocalGenerator* cmGlobalNinjaGenerator::CreateLocalGenerator()
{
cmLocalGenerator* lg = new cmLocalNinjaGenerator;
lg->SetGlobalGenerator(this);
return lg;
}
void cmGlobalNinjaGenerator
::GetDocumentation(cmDocumentationEntry& entry) const
{
entry.Name = this->GetName();
entry.Brief = "Generates build.ninja files (experimental).";
entry.Full =
"A build.ninja file is generated into the build tree. Recent "
"versions of the ninja program can build the project through the "
"\"all\" target. An \"install\" target is also provided.";
}
// Implemented in all cmGlobaleGenerator sub-classes.
// Used in:
// Source/cmLocalGenerator.cxx
// Source/cmake.cxx
void cmGlobalNinjaGenerator::Generate()
{
this->OpenBuildFileStream();
this->OpenRulesFileStream();
this->cmGlobalGenerator::Generate();
this->WriteAssumedSourceDependencies();
this->WriteTargetAliases(*this->BuildFileStream);
this->WriteBuiltinTargets(*this->BuildFileStream);
this->CloseRulesFileStream();
this->CloseBuildFileStream();
}
// Implemented in all cmGlobaleGenerator sub-classes.
// Used in:
// Source/cmMakefile.cxx:
void cmGlobalNinjaGenerator
::EnableLanguage(std::vector<std::string>const& languages,
cmMakefile *mf,
bool optional)
{
this->cmGlobalGenerator::EnableLanguage(languages, mf, optional);
std::string path;
for(std::vector<std::string>::const_iterator l = languages.begin();
l != languages.end(); ++l)
{
if(*l == "NONE")
{
continue;
}
if(*l == "Fortran")
{
std::string message = "The \"";
message += this->GetName();
message += "\" generator does not support the language \"";
message += *l;
message += "\" yet.";
cmSystemTools::Error(message.c_str());
}
this->ResolveLanguageCompiler(*l, mf, optional);
}
}
// Implemented by:
// cmGlobalUnixMakefileGenerator3
// cmGlobalVisualStudio10Generator
// cmGlobalVisualStudio6Generator
// cmGlobalVisualStudio7Generator
// cmGlobalXCodeGenerator
// Called by:
// cmGlobalGenerator::Build()
std::string cmGlobalNinjaGenerator
::GenerateBuildCommand(const char* makeProgram,
const char* projectName,
const char* additionalOptions,
const char* targetName,
const char* config,
bool ignoreErrors,
bool fast)
{
// Project name and config are not used yet.
(void)projectName;
(void)config;
// Ninja does not have -i equivalent option yet.
(void)ignoreErrors;
// We do not handle fast build yet.
(void)fast;
std::string makeCommand =
cmSystemTools::ConvertToUnixOutputPath(makeProgram);
if(additionalOptions)
{
makeCommand += " ";
makeCommand += additionalOptions;
}
if(targetName)
{
if(strcmp(targetName, "clean") == 0)
{
makeCommand += " -t clean";
}
else
{
makeCommand += " ";
makeCommand += targetName;
}
}
return makeCommand;
}
//----------------------------------------------------------------------------
// Non-virtual public methods.
void cmGlobalNinjaGenerator::AddRule(const std::string& name,
const std::string& command,
const std::string& description,
const std::string& comment,
const std::string& depfile,
bool restat,
bool generator)
{
// Do not add the same rule twice.
if (this->HasRule(name))
return;
this->Rules.insert(name);
cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
name,
command,
description,
comment,
depfile,
restat,
generator);
}
bool cmGlobalNinjaGenerator::HasRule(const std::string &name)
{
RulesSetType::const_iterator rule = this->Rules.find(name);
return (rule != this->Rules.end());
}
//----------------------------------------------------------------------------
// Private virtual overrides
// TODO: Refactor to combine with cmGlobalUnixMakefileGenerator3 impl.
void cmGlobalNinjaGenerator::ComputeTargetObjects(cmGeneratorTarget* gt) const
{
cmTarget* target = gt->Target;
// Compute full path to object file directory for this target.
std::string dir_max;
dir_max += gt->Makefile->GetCurrentOutputDirectory();
dir_max += "/";
dir_max += gt->LocalGenerator->GetTargetDirectory(*target);
dir_max += "/";
gt->ObjectDirectory = dir_max;
// Compute the name of each object file.
for(std::vector<cmSourceFile*>::iterator
si = gt->ObjectSources.begin();
si != gt->ObjectSources.end(); ++si)
{
cmSourceFile* sf = *si;
std::string objectName = gt->LocalGenerator
->GetObjectFileNameWithoutTarget(*sf, dir_max);
gt->Objects[sf] = objectName;
}
}
//----------------------------------------------------------------------------
// Private methods
void cmGlobalNinjaGenerator::OpenBuildFileStream()
{
// Compute Ninja's build file path.
std::string buildFilePath =
this->GetCMakeInstance()->GetHomeOutputDirectory();
buildFilePath += "/";
buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;
// Get a stream where to generate things.
if (!this->BuildFileStream)
{
this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str());
if (!this->BuildFileStream)
{
// An error message is generated by the constructor if it cannot
// open the file.
return;
}
}
// Write the do not edit header.
this->WriteDisclaimer(*this->BuildFileStream);
// Write a comment about this file.
*this->BuildFileStream
<< "# This file contains all the build statements describing the\n"
<< "# compilation DAG.\n\n"
;
}
void cmGlobalNinjaGenerator::CloseBuildFileStream()
{
if (this->BuildFileStream)
{
delete this->BuildFileStream;
this->BuildFileStream = 0;
}
else
{
cmSystemTools::Error("Build file stream was not open.");
}
}
void cmGlobalNinjaGenerator::OpenRulesFileStream()
{
// Compute Ninja's build file path.
std::string rulesFilePath =
this->GetCMakeInstance()->GetHomeOutputDirectory();
rulesFilePath += "/";
rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;
// Get a stream where to generate things.
if (!this->RulesFileStream)
{
this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str());
if (!this->RulesFileStream)
{
// An error message is generated by the constructor if it cannot
// open the file.
return;
}
}
// Write the do not edit header.
this->WriteDisclaimer(*this->RulesFileStream);
// Write comment about this file.
*this->RulesFileStream
<< "# This file contains all the rules used to get the outputs files\n"
<< "# built from the input files.\n"
<< "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
;
}
void cmGlobalNinjaGenerator::CloseRulesFileStream()
{
if (this->RulesFileStream)
{
delete this->RulesFileStream;
this->RulesFileStream = 0;
}
else
{
cmSystemTools::Error("Rules file stream was not open.");
}
}
void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os)
{
os
<< "# CMAKE generated file: DO NOT EDIT!\n"
<< "# Generated by \"" << this->GetName() << "\""
<< " Generator, CMake Version "
<< cmVersion::GetMajorVersion() << "."
<< cmVersion::GetMinorVersion() << "\n\n";
}
void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target)
{
this->AppendTargetOutputs(target, this->AllDependencies);
}
void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
{
for (std::map<std::string, std::set<std::string> >::iterator
i = this->AssumedSourceDependencies.begin();
i != this->AssumedSourceDependencies.end(); ++i) {
cmNinjaDeps deps;
std::copy(i->second.begin(), i->second.end(), std::back_inserter(deps));
WriteCustomCommandBuild(/*command=*/"", /*description=*/"",
"Assume dependencies for generated source file.",
cmNinjaDeps(1, i->first), deps);
}
}
void
cmGlobalNinjaGenerator
::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
{
const char* configName =
target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE");
cmLocalNinjaGenerator *ng =
static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
switch (target->GetType()) {
case cmTarget::EXECUTABLE:
case cmTarget::SHARED_LIBRARY:
case cmTarget::STATIC_LIBRARY:
case cmTarget::MODULE_LIBRARY:
outputs.push_back(ng->ConvertToNinjaPath(
target->GetFullPath(configName).c_str()));
break;
case cmTarget::OBJECT_LIBRARY:
case cmTarget::UTILITY: {
std::string path = ng->ConvertToNinjaPath(
target->GetMakefile()->GetStartOutputDirectory());
if (path.empty() || path == ".")
outputs.push_back(target->GetName());
else {
path += "/";
path += target->GetName();
outputs.push_back(path);
}
break;
}
case cmTarget::GLOBAL_TARGET:
// Always use the target in HOME instead of an unused duplicate in a
// subdirectory.
outputs.push_back(target->GetName());
break;
default:
return;
}
}
void
cmGlobalNinjaGenerator
::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
{
if (target->GetType() == cmTarget::GLOBAL_TARGET) {
// Global targets only depend on other utilities, which may not appear in
// the TargetDepends set (e.g. "all").
std::set<cmStdString> const& utils = target->GetUtilities();
std::copy(utils.begin(), utils.end(), std::back_inserter(outputs));
} else {
cmTargetDependSet const& targetDeps =
this->GetTargetDirectDepends(*target);
for (cmTargetDependSet::const_iterator i = targetDeps.begin();
i != targetDeps.end(); ++i) {
this->AppendTargetOutputs(*i, outputs);
}
}
}
void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
cmTarget* target) {
cmNinjaDeps outputs;
this->AppendTargetOutputs(target, outputs);
// Mark the target's outputs as ambiguous to ensure that no other target uses
// the output as an alias.
for (cmNinjaDeps::iterator i = outputs.begin(); i != outputs.end(); ++i)
TargetAliases[*i] = 0;
// Insert the alias into the map. If the alias was already present in the
// map and referred to another target, mark it as ambiguous.
std::pair<TargetAliasMap::iterator, bool> newAlias =
TargetAliases.insert(make_pair(alias, target));
if (newAlias.second && newAlias.first->second != target)
newAlias.first->second = 0;
}
void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
{
cmGlobalNinjaGenerator::WriteDivider(os);
os << "# Target aliases.\n\n";
for (TargetAliasMap::iterator i = TargetAliases.begin();
i != TargetAliases.end(); ++i) {
// Don't write ambiguous aliases.
if (!i->second)
continue;
cmNinjaDeps deps;
this->AppendTargetOutputs(i->second, deps);
cmGlobalNinjaGenerator::WritePhonyBuild(os,
"",
cmNinjaDeps(1, i->first),
deps);
}
}
void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
{
// Write headers.
cmGlobalNinjaGenerator::WriteDivider(os);
os << "# Built-in targets\n\n";
this->WriteTargetAll(os);
this->WriteTargetRebuildManifest(os);
}
void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
{
cmNinjaDeps outputs;
outputs.push_back("all");
cmGlobalNinjaGenerator::WritePhonyBuild(os,
"The main all target.",
outputs,
this->AllDependencies);
cmGlobalNinjaGenerator::WriteDefault(os,
outputs,
"Make the all target the default.");
}
void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
{
cmLocalGenerator *lg = this->LocalGenerators[0];
cmMakefile* mfRoot = lg->GetMakefile();
std::ostringstream cmd;
cmd << lg->ConvertToOutputFormat(
mfRoot->GetRequiredDefinition("CMAKE_COMMAND"),
cmLocalGenerator::SHELL)
<< " -H"
<< lg->ConvertToOutputFormat(mfRoot->GetHomeDirectory(),
cmLocalGenerator::SHELL)
<< " -B"
<< lg->ConvertToOutputFormat(mfRoot->GetHomeOutputDirectory(),
cmLocalGenerator::SHELL);
WriteRule(*this->RulesFileStream,
"RERUN_CMAKE",
cmd.str(),
"Re-running CMake...",
"Rule for re-running cmake.",
/*depfile=*/ "",
/*restat=*/ false,
/*generator=*/ true);
cmNinjaDeps implicitDeps;
for (std::vector<cmLocalGenerator *>::const_iterator i =
this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) {
const std::vector<std::string>& lf = (*i)->GetMakefile()->GetListFiles();
implicitDeps.insert(implicitDeps.end(), lf.begin(), lf.end());
}
std::sort(implicitDeps.begin(), implicitDeps.end());
implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
implicitDeps.end());
implicitDeps.push_back("CMakeCache.txt");
WriteBuild(os,
"Re-run CMake if any of its inputs changed.",
"RERUN_CMAKE",
/*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
/*explicitDeps=*/ cmNinjaDeps(),
implicitDeps,
/*orderOnlyDeps=*/ cmNinjaDeps(),
/*variables=*/ cmNinjaVars());
WritePhonyBuild(os,
"A missing CMake input file is not an error.",
implicitDeps,
cmNinjaDeps());
}

View File

@ -0,0 +1,338 @@
/*============================================================================
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 cmGeneratorTarget;
/**
* \class cmGlobalNinjaGenerator
* \brief Write a build.ninja file.
*
* The main differences between this generator and the UnixMakefile
* generator family are:
* - We don't care about VERBOSE variable or RULE_MESSAGES property since
* it is handle by Ninja's -v option.
* - We don't care about computing any progress status since Ninja manages
* it itself.
* - We don't care about generating a clean target since Ninja already have
* a clean tool.
* - We generate one build.ninja and one rules.ninja per project.
* - We try to minimize the number of generated rules: one per target and
* language.
* - We use Ninja special variable $in and $out to produce nice output.
* - We extensively use Ninja variable overloading system to minimize the
* number of generated rules.
*/
class cmGlobalNinjaGenerator : public cmGlobalGenerator
{
public:
/// The default name of Ninja's build file. Typically: build.ninja.
static const char* NINJA_BUILD_FILE;
/// The default name of Ninja's rules file. Typically: rules.ninja.
/// It is included in the main build.ninja file.
static const char* NINJA_RULES_FILE;
/// The indentation string used when generating Ninja's build file.
static const char* INDENT;
/// Write @a count times INDENT level to output stream @a os.
static void Indent(std::ostream& os, int count);
/// Write a divider in the given output stream @a os.
static void WriteDivider(std::ostream& os);
static std::string EncodeIdent(const std::string &ident, std::ostream &vars);
static std::string EncodeLiteral(const std::string &lit);
static std::string EncodePath(const std::string &path);
/**
* Write the given @a comment to the output stream @a os. It
* handles new line character properly.
*/
static void WriteComment(std::ostream& os, const std::string& comment);
/**
* Write a build statement to @a os with the @a comment using
* the @a rule the list of @a outputs files and inputs.
* It also writes the variables bound to this build statement.
* @warning no escaping of any kind is done here.
*/
static void WriteBuild(std::ostream& os,
const std::string& comment,
const std::string& rule,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps,
const cmNinjaDeps& orderOnlyDeps,
const cmNinjaVars& variables);
/**
* Helper to write a build statement with the special 'phony' rule.
*/
static void WritePhonyBuild(std::ostream& os,
const std::string& comment,
const cmNinjaDeps& outputs,
const cmNinjaDeps& explicitDeps,
const cmNinjaDeps& implicitDeps = cmNinjaDeps(),
const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps(),
const cmNinjaVars& variables = cmNinjaVars());
void WriteCustomCommandBuild(const std::string& command,
const std::string& description,
const std::string& comment,
const cmNinjaDeps& outputs,
const cmNinjaDeps& deps = cmNinjaDeps(),
const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps());
/**
* Write a rule statement named @a name to @a os with the @a comment,
* the mandatory @a command, the @a depfile and the @a description.
* It also writes the variables bound to this rule statement.
* @warning no escaping of any kind is done here.
*/
static void WriteRule(std::ostream& os,
const std::string& name,
const std::string& command,
const std::string& description,
const std::string& comment = "",
const std::string& depfile = "",
bool restat = false,
bool generator = false);
/**
* Write a variable named @a name to @a os with value @a value and an
* optional @a comment. An @a indent level can be specified.
* @warning no escaping of any kind is done here.
*/
static void WriteVariable(std::ostream& os,
const std::string& name,
const std::string& value,
const std::string& comment = "",
int indent = 0);
/**
* Write an include statement including @a filename with an optional
* @a comment to the @a os stream.
*/
static void WriteInclude(std::ostream& os,
const std::string& filename,
const std::string& comment = "");
/**
* Write a default target statement specifying @a targets as
* the default targets.
*/
static void WriteDefault(std::ostream& os,
const cmNinjaDeps& targets,
const std::string& comment = "");
public:
/// Default constructor.
cmGlobalNinjaGenerator();
/// Convenience method for creating an instance of this class.
static cmGlobalGenerator* New() {
return new cmGlobalNinjaGenerator; }
/// Destructor.
virtual ~cmGlobalNinjaGenerator() { }
/// Overloaded methods. @see cmGlobalGenerator::CreateLocalGenerator()
virtual cmLocalGenerator* CreateLocalGenerator();
/// Overloaded methods. @see cmGlobalGenerator::GetName().
virtual const char* GetName() const {
return cmGlobalNinjaGenerator::GetActualName(); }
/// @return the name of this generator.
static const char* GetActualName() { return "Ninja"; }
/// Overloaded methods. @see cmGlobalGenerator::GetDocumentation()
virtual void GetDocumentation(cmDocumentationEntry& entry) const;
/// Overloaded methods. @see cmGlobalGenerator::Generate()
virtual void Generate();
/// Overloaded methods. @see cmGlobalGenerator::EnableLanguage()
virtual void EnableLanguage(std::vector<std::string>const& languages,
cmMakefile* mf,
bool optional);
/// Overloaded methods. @see cmGlobalGenerator::GenerateBuildCommand()
virtual std::string GenerateBuildCommand(const char* makeProgram,
const char* projectName,
const char* additionalOptions,
const char* targetName,
const char* config,
bool ignoreErrors,
bool fast);
// Setup target names
virtual const char* GetAllTargetName() const { return "all"; }
virtual const char* GetInstallTargetName() const { return "install"; }
virtual const char* GetInstallLocalTargetName() const {
return "install/local";
}
virtual const char* GetInstallStripTargetName() const {
return "install/strip";
}
virtual const char* GetTestTargetName() const { return "test"; }
virtual const char* GetPackageTargetName() const { return "package"; }
virtual const char* GetPackageSourceTargetName() const {
return "package_source";
}
virtual const char* GetEditCacheTargetName() const {
return "edit_cache";
}
virtual const char* GetRebuildCacheTargetName() const {
return "rebuild_cache";
}
virtual const char* GetCleanTargetName() const { return "clean"; }
public:
cmGeneratedFileStream* GetBuildFileStream() const
{ return this->BuildFileStream; }
cmGeneratedFileStream* GetRulesFileStream() const
{ return this->RulesFileStream; }
/**
* Add a rule to the generated build system.
* Call WriteRule() behind the scene but perform some check before like:
* - Do not add twice the same rule.
*/
void AddRule(const std::string& name,
const std::string& command,
const std::string& description,
const std::string& comment = "",
const std::string& depfile = "",
bool restat = false,
bool generator = false);
bool HasRule(const std::string& name);
void AddCustomCommandRule();
protected:
/// Overloaded methods.
/// @see cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS()
virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() { return true; }
private:
/// @see cmGlobalGenerator::ComputeTargetObjects
virtual void ComputeTargetObjects(cmGeneratorTarget* gt) const;
private:
// In order to access the AddDependencyToAll() functions and co.
friend class cmLocalNinjaGenerator;
// In order to access the SeenCustomCommand() function.
friend class cmNinjaTargetGenerator;
friend class cmNinjaNormalTargetGenerator;
friend class cmNinjaUtilityTargetGenerator;
private:
void OpenBuildFileStream();
void CloseBuildFileStream();
void OpenRulesFileStream();
void CloseRulesFileStream();
/// Write the common disclaimer text at the top of each build file.
void WriteDisclaimer(std::ostream& os);
void AddDependencyToAll(cmTarget* target);
void WriteAssumedSourceDependencies();
void AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs);
void AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs);
void AddTargetAlias(const std::string& alias, cmTarget* target);
void WriteTargetAliases(std::ostream& os);
void WriteBuiltinTargets(std::ostream& os);
void WriteTargetAll(std::ostream& os);
void WriteTargetRebuildManifest(std::ostream& os);
/// Called when we have seen the given custom command. Returns true
/// if we has seen it before.
bool SeenCustomCommand(cmCustomCommand const *cc) {
return !this->CustomCommands.insert(cc).second;
}
/// Called when we have seen the given custom command output.
void SeenCustomCommandOutput(const std::string &output) {
this->CustomCommandOutputs.insert(output);
// We don't need the assumed dependencies anymore, because we have
// an output.
this->AssumedSourceDependencies.erase(output);
}
bool HasCustomCommandOutput(const std::string &output) {
return this->CustomCommandOutputs.find(output) !=
this->CustomCommandOutputs.end();
}
void AddAssumedSourceDependencies(const std::string &source,
const cmNinjaDeps &deps) {
std::set<std::string> &ASD = this->AssumedSourceDependencies[source];
// Because we may see the same source file multiple times (same source
// specified in multiple targets), compute the union of any assumed
// dependencies.
ASD.insert(deps.begin(), deps.end());
}
private:
/// The file containing the build statement. (the relation ship of the
/// compilation DAG).
cmGeneratedFileStream* BuildFileStream;
/// The file containing the rule statements. (The action attached to each
/// edge of the compilation DAG).
cmGeneratedFileStream* RulesFileStream;
/// The type used to store the set of rules added to the generated build
/// system.
typedef std::set<std::string> RulesSetType;
/// The set of rules added to the generated build system.
RulesSetType Rules;
/// The set of dependencies to add to the "all" target.
cmNinjaDeps AllDependencies;
/// The set of custom commands we have seen.
std::set<cmCustomCommand const*> CustomCommands;
/// The set of custom command outputs we have seen.
std::set<std::string> CustomCommandOutputs;
/// The mapping from source file to assumed dependencies.
std::map<std::string, std::set<std::string> > AssumedSourceDependencies;
typedef std::map<std::string, cmTarget*> TargetAliasMap;
TargetAliasMap TargetAliases;
static cmLocalGenerator* LocalGenerator;
};
#endif // ! cmGlobalNinjaGenerator_h

View File

@ -1503,7 +1503,7 @@ void cmLocalGenerator::GetTargetFlags(std::string& linkLibs,
linkFlags +=
this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG");
linkFlags += this->Convert(sf->GetFullPath().c_str(),
START_OUTPUT, SHELL);
FULL, SHELL);
linkFlags += " ";
}
}
@ -1583,6 +1583,16 @@ void cmLocalGenerator::GetTargetFlags(std::string& linkLibs,
this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE");
linkFlags += " ";
}
if (target.IsExecutableWithExports())
{
std::string exportFlagVar = "CMAKE_EXE_EXPORTS_";
exportFlagVar += linkLanguage;
exportFlagVar += "_FLAG";
linkFlags +=
this->Makefile->GetSafeDefinition(exportFlagVar.c_str());
linkFlags += " ";
}
const char* targetLinkFlags = target.GetProperty("LINK_FLAGS");
if(targetLinkFlags)
{

View File

@ -0,0 +1,393 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmLocalNinjaGenerator.h"
#include "cmCustomCommandGenerator.h"
#include "cmMakefile.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmNinjaTargetGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmSourceFile.h"
#include "cmComputeLinkInformation.h"
#include "cmake.h"
#include <assert.h>
cmLocalNinjaGenerator::cmLocalNinjaGenerator()
: cmLocalGenerator()
, ConfigName("")
, HomeRelativeOutputPath("")
{
this->IsMakefileGenerator = true;
#ifdef _WIN32
this->WindowsShell = true;
#endif
this->TargetImplib = "$TARGET_IMPLIB";
}
//----------------------------------------------------------------------------
// Virtual public methods.
cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
{
}
void cmLocalNinjaGenerator::Generate()
{
this->SetConfigName();
this->WriteProcessedMakefile(this->GetBuildFileStream());
this->WriteProcessedMakefile(this->GetRulesFileStream());
this->WriteBuildFileTop();
cmTargets& targets = this->GetMakefile()->GetTargets();
for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
{
cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(&t->second);
if(tg)
{
tg->Generate();
// Add the target to "all" if required.
if (!this->GetGlobalNinjaGenerator()->IsExcluded(
this->GetGlobalNinjaGenerator()->LocalGenerators[0],
t->second))
this->GetGlobalNinjaGenerator()->AddDependencyToAll(&t->second);
delete tg;
}
}
this->WriteCustomCommandBuildStatements();
}
// Implemented in:
// cmLocalUnixMakefileGenerator3.
// Used in:
// Source/cmMakefile.cxx
// Source/cmGlobalGenerator.cxx
void cmLocalNinjaGenerator::Configure()
{
// Compute the path to use when referencing the current output
// directory from the top output directory.
this->HomeRelativeOutputPath =
this->Convert(this->Makefile->GetStartOutputDirectory(), HOME_OUTPUT);
if(this->HomeRelativeOutputPath == ".")
{
this->HomeRelativeOutputPath = "";
}
this->cmLocalGenerator::Configure();
}
// TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it.
std::string cmLocalNinjaGenerator
::GetTargetDirectory(cmTarget const& target) const
{
std::string dir = cmake::GetCMakeFilesDirectoryPostSlash();
dir += target.GetName();
#if defined(__VMS)
dir += "_dir";
#else
dir += ".dir";
#endif
return dir;
}
//----------------------------------------------------------------------------
// Non-virtual public methods.
const cmGlobalNinjaGenerator*
cmLocalNinjaGenerator::GetGlobalNinjaGenerator() const
{
return
static_cast<const cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
}
cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator()
{
return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
}
//----------------------------------------------------------------------------
// Virtual protected methods.
std::string
cmLocalNinjaGenerator::ConvertToLinkReference(std::string const& lib)
{
return this->Convert(lib.c_str(), HOME_OUTPUT, SHELL);
}
std::string
cmLocalNinjaGenerator::ConvertToIncludeReference(std::string const& path)
{
return this->Convert(path.c_str(), HOME_OUTPUT, SHELL);
}
//----------------------------------------------------------------------------
// Private methods.
cmGeneratedFileStream& cmLocalNinjaGenerator::GetBuildFileStream() const
{
return *this->GetGlobalNinjaGenerator()->GetBuildFileStream();
}
cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const
{
return *this->GetGlobalNinjaGenerator()->GetRulesFileStream();
}
const cmake* cmLocalNinjaGenerator::GetCMakeInstance() const
{
return this->GetGlobalGenerator()->GetCMakeInstance();
}
cmake* cmLocalNinjaGenerator::GetCMakeInstance()
{
return this->GetGlobalGenerator()->GetCMakeInstance();
}
bool cmLocalNinjaGenerator::isRootMakefile() const
{
return (strcmp(this->Makefile->GetCurrentDirectory(),
this->GetCMakeInstance()->GetHomeDirectory()) == 0);
}
void cmLocalNinjaGenerator::WriteBuildFileTop()
{
// We do that only once for the top CMakeLists.txt file.
if(!this->isRootMakefile())
return;
// For the build file.
this->WriteProjectHeader(this->GetBuildFileStream());
this->WriteNinjaFilesInclusion(this->GetBuildFileStream());
// For the rule file.
this->WriteProjectHeader(this->GetRulesFileStream());
}
void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os)
{
cmGlobalNinjaGenerator::WriteDivider(os);
os
<< "# Project: " << this->GetMakefile()->GetProjectName() << std::endl
<< "# Configuration: " << this->ConfigName << std::endl
;
cmGlobalNinjaGenerator::WriteDivider(os);
}
void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
{
cmGlobalNinjaGenerator::WriteDivider(os);
os
<< "# Include auxiliary files.\n"
<< "\n"
;
cmGlobalNinjaGenerator::WriteInclude(os,
cmGlobalNinjaGenerator::NINJA_RULES_FILE,
"Include rules file.");
os << "\n";
}
void cmLocalNinjaGenerator::SetConfigName()
{
// Store the configuration name that will be generated.
if(const char* config =
this->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"))
{
// Use the build type given by the user.
this->ConfigName = config;
}
else
{
// No configuration type given.
this->ConfigName = "";
}
}
void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
{
cmGlobalNinjaGenerator::WriteDivider(os);
os
<< "# Write statements declared in CMakeLists.txt:" << std::endl
<< "# " << this->Makefile->GetCurrentListFile() << std::endl
;
if(this->isRootMakefile())
os << "# Which is the root file." << std::endl;
cmGlobalNinjaGenerator::WriteDivider(os);
os << std::endl;
}
std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path)
{
std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT);
#ifdef _WIN32
cmSystemTools::ReplaceString(convPath, "/", "\\");
#endif
return convPath;
}
void
cmLocalNinjaGenerator
::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
{
this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs);
}
void
cmLocalNinjaGenerator
::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
{
this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs);
}
void cmLocalNinjaGenerator::AppendCustomCommandDeps(const cmCustomCommand *cc,
cmNinjaDeps &ninjaDeps)
{
const std::vector<std::string> &deps = cc->GetDepends();
for (std::vector<std::string>::const_iterator i = deps.begin();
i != deps.end(); ++i) {
std::string dep;
if (this->GetRealDependency(i->c_str(), this->GetConfigName(), dep))
ninjaDeps.push_back(ConvertToNinjaPath(dep.c_str()));
}
}
std::string cmLocalNinjaGenerator::BuildCommandLine(
const std::vector<std::string> &cmdLines)
{
// If we have no commands but we need to build a command anyway, use ":".
// This happens when building a POST_BUILD value for link targets that
// don't use POST_BUILD.
if (cmdLines.empty())
#ifdef _WIN32
return "cd.";
#else
return ":";
#endif
// TODO: This will work only on Unix platforms. I don't
// want to use a link.txt file because I will lose the benefit of the
// $in variables. A discussion about dealing with multiple commands in
// a rule is started here:
// groups.google.com/group/ninja-build/browse_thread/thread/d515f23a78986008
std::ostringstream cmd;
for (std::vector<std::string>::const_iterator li = cmdLines.begin();
li != cmdLines.end(); ++li) {
if (li != cmdLines.begin())
cmd << " && ";
cmd << *li;
}
return cmd.str();
}
void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc,
std::vector<std::string> &cmdLines)
{
cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile);
if (ccg.GetNumberOfCommands() > 0) {
const char* wd = cc->GetWorkingDirectory();
if (!wd)
wd = this->GetMakefile()->GetStartOutputDirectory();
std::ostringstream cdCmd;
cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL);
cmdLines.push_back(cdCmd.str());
}
for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) {
cmdLines.push_back(this->ConvertToOutputFormat(ccg.GetCommand(i).c_str(),
SHELL));
std::string& cmd = cmdLines.back();
ccg.AppendArguments(i, cmd);
}
}
void
cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps)
{
if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc))
return;
const std::vector<std::string> &outputs = cc->GetOutputs();
cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps;
std::transform(outputs.begin(), outputs.end(),
ninjaOutputs.begin(), MapToNinjaPath());
this->AppendCustomCommandDeps(cc, ninjaDeps);
for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();
++i)
this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i);
std::vector<std::string> cmdLines;
this->AppendCustomCommandLines(cc, cmdLines);
if (cmdLines.empty()) {
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Phony custom command for " +
ninjaOutputs[0],
ninjaOutputs,
ninjaDeps,
cmNinjaDeps(),
orderOnlyDeps,
cmNinjaVars());
} else {
this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
this->BuildCommandLine(cmdLines),
this->ConstructComment(*cc),
"Custom command for " + ninjaOutputs[0],
ninjaOutputs,
ninjaDeps,
orderOnlyDeps);
}
}
void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc,
cmTarget* target)
{
this->CustomCommandTargets[cc].insert(target);
}
void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
{
for (CustomCommandTargetMap::iterator i = this->CustomCommandTargets.begin();
i != this->CustomCommandTargets.end(); ++i) {
// A custom command may appear on multiple targets. However, some build
// systems exist where the target dependencies on some of the targets are
// overspecified, leading to a dependency cycle. If we assume all target
// dependencies are a superset of the true target dependencies for this
// custom command, we can take the set intersection of all target
// dependencies to obtain a correct dependency list.
//
// FIXME: This won't work in certain obscure scenarios involving indirect
// dependencies.
std::set<cmTarget*>::iterator j = i->second.begin();
assert(j != i->second.end());
std::vector<std::string> ccTargetDeps;
this->AppendTargetDepends(*j, ccTargetDeps);
std::sort(ccTargetDeps.begin(), ccTargetDeps.end());
++j;
for (; j != i->second.end(); ++j) {
std::vector<std::string> jDeps, depsIntersection;
this->AppendTargetDepends(*j, jDeps);
std::sort(jDeps.begin(), jDeps.end());
std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(),
jDeps.begin(), jDeps.end(),
std::back_inserter(depsIntersection));
ccTargetDeps = depsIntersection;
}
this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps);
}
}

View File

@ -0,0 +1,133 @@
/*============================================================================
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(); }
/// @return whether we are processing the top CMakeLists.txt file.
bool isRootMakefile() const;
/// @returns the relative path between the HomeOutputDirectory and this
/// local generators StartOutputDirectory.
std::string GetHomeRelativeOutputPath() const
{ return this->HomeRelativeOutputPath; }
protected:
virtual std::string ConvertToLinkReference(std::string const& lib);
virtual std::string ConvertToIncludeReference(std::string const& path);
private:
friend class cmGlobalNinjaGenerator;
// In order to access to protected member of the local generator.
friend class cmNinjaTargetGenerator;
friend class cmNinjaNormalTargetGenerator;
friend class cmNinjaUtilityTargetGenerator;
private:
cmGeneratedFileStream& GetBuildFileStream() const;
cmGeneratedFileStream& GetRulesFileStream() const;
void WriteBuildFileTop();
void WriteProjectHeader(std::ostream& os);
void WriteNinjaFilesInclusion(std::ostream& os);
void WriteProcessedMakefile(std::ostream& os);
void SetConfigName();
std::string ConvertToNinjaPath(const char *path);
struct map_to_ninja_path;
friend struct map_to_ninja_path;
struct map_to_ninja_path {
cmLocalNinjaGenerator *LocalGenerator;
map_to_ninja_path(cmLocalNinjaGenerator *LocalGen)
: LocalGenerator(LocalGen) {}
std::string operator()(const std::string &path) {
return LocalGenerator->ConvertToNinjaPath(path.c_str());
}
};
map_to_ninja_path MapToNinjaPath() {
return map_to_ninja_path(this);
}
void AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs);
void AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs);
void AppendCustomCommandDeps(const cmCustomCommand *cc,
cmNinjaDeps &ninjaDeps);
std::string BuildCommandLine(const std::vector<std::string> &cmdLines);
void AppendCustomCommandLines(const cmCustomCommand *cc,
std::vector<std::string> &cmdLines);
void WriteCustomCommandRule();
void WriteCustomCommandBuildStatement(cmCustomCommand const *cc,
const cmNinjaDeps& orderOnlyDeps);
void AddCustomCommandTarget(cmCustomCommand const* cc, cmTarget* target);
void WriteCustomCommandBuildStatements();
private:
std::string ConfigName;
std::string HomeRelativeOutputPath;
typedef std::map<cmCustomCommand const*, std::set<cmTarget*> >
CustomCommandTargetMap;
CustomCommandTargetMap CustomCommandTargets;
};
#endif // ! cmLocalNinjaGenerator_h

View File

@ -0,0 +1,497 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmNinjaNormalTargetGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmSourceFile.h"
#include "cmGeneratedFileStream.h"
#include "cmMakefile.h"
#include <assert.h>
cmNinjaNormalTargetGenerator::
cmNinjaNormalTargetGenerator(cmTarget* target)
: cmNinjaTargetGenerator(target)
, TargetNameOut()
, TargetNameSO()
, TargetNameReal()
, TargetNameImport()
, TargetNamePDB()
{
this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName());
if (target->GetType() == cmTarget::EXECUTABLE)
target->GetExecutableNames(this->TargetNameOut,
this->TargetNameReal,
this->TargetNameImport,
this->TargetNamePDB,
GetLocalGenerator()->GetConfigName());
else
target->GetLibraryNames(this->TargetNameOut,
this->TargetNameSO,
this->TargetNameReal,
this->TargetNameImport,
this->TargetNamePDB,
GetLocalGenerator()->GetConfigName());
if(target->GetType() != cmTarget::OBJECT_LIBRARY)
{
// on Windows the output dir is already needed at compile time
// ensure the directory exists (OutDir test)
std::string outpath = target->GetDirectory(this->GetConfigName());
cmSystemTools::MakeDirectory(outpath.c_str());
}
}
cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator()
{
}
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();
if(this->GetTarget()->GetType() == cmTarget::OBJECT_LIBRARY)
{
this->WriteObjectLibStatement();
}
else
{
this->WriteLinkRule();
this->WriteLinkStatement();
}
this->GetBuildFileStream() << "\n";
this->GetRulesFileStream() << "\n";
}
void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
{
cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
this->GetRulesFileStream()
<< "# Rules for each languages for "
<< cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
<< " target "
<< this->GetTargetName()
<< "\n\n";
std::set<cmStdString> languages;
this->GetTarget()->GetLanguages(languages);
for(std::set<cmStdString>::const_iterator l = languages.begin();
l != languages.end();
++l)
this->WriteLanguageRules(*l);
}
const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
{
switch (this->GetTarget()->GetType()) {
case cmTarget::STATIC_LIBRARY:
return "static library";
case cmTarget::SHARED_LIBRARY:
return "shared library";
case cmTarget::MODULE_LIBRARY:
return "shared module";
case cmTarget::EXECUTABLE:
return "executable";
default:
return 0;
}
}
std::string
cmNinjaNormalTargetGenerator
::LanguageLinkerRule() const
{
return std::string(this->TargetLinkLanguage)
+ "_"
+ cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
+ "_LINKER";
}
void
cmNinjaNormalTargetGenerator
::WriteLinkRule()
{
cmTarget::TargetType targetType = this->GetTarget()->GetType();
std::string ruleName = this->LanguageLinkerRule();
if (!this->GetGlobalGenerator()->HasRule(ruleName)) {
cmLocalGenerator::RuleVariables vars;
vars.RuleLauncher = "RULE_LAUNCH_LINK";
vars.CMTarget = this->GetTarget();
vars.Language = this->TargetLinkLanguage;
vars.Objects = "$in";
std::string objdir = cmake::GetCMakeFilesDirectoryPostSlash();
objdir += this->GetTargetName();
objdir += ".dir";
objdir = this->GetLocalGenerator()->Convert(objdir.c_str(),
cmLocalGenerator::START_OUTPUT,
cmLocalGenerator::SHELL);
vars.ObjectDir = objdir.c_str();
vars.Target = "$out";
vars.TargetSOName = "$SONAME";
vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
vars.TargetPDB = "$TARGET_PDB";
// Setup the target version.
std::string targetVersionMajor;
std::string targetVersionMinor;
{
cmOStringStream majorStream;
cmOStringStream minorStream;
int major;
int minor;
this->GetTarget()->GetTargetVersion(major, minor);
majorStream << major;
minorStream << minor;
targetVersionMajor = majorStream.str();
targetVersionMinor = minorStream.str();
}
vars.TargetVersionMajor = targetVersionMajor.c_str();
vars.TargetVersionMinor = targetVersionMinor.c_str();
vars.LinkLibraries = "$LINK_LIBRARIES";
vars.Flags = "$FLAGS";
vars.LinkFlags = "$LINK_FLAGS";
std::string langFlags;
this->GetLocalGenerator()->AddLanguageFlags(langFlags,
this->TargetLinkLanguage,
this->GetConfigName());
if (targetType != cmTarget::EXECUTABLE)
langFlags += " $ARCH_FLAGS";
vars.LanguageCompileFlags = langFlags.c_str();
// Rule for linking library.
std::vector<std::string> linkCmds = this->ComputeLinkCmd();
for(std::vector<std::string>::iterator i = linkCmds.begin();
i != linkCmds.end();
++i)
{
this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
}
linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
linkCmds.push_back("$POST_BUILD");
std::string linkCmd =
this->GetLocalGenerator()->BuildCommandLine(linkCmds);
// Write the linker rule.
std::ostringstream comment;
comment << "Rule for linking " << this->TargetLinkLanguage << " "
<< this->GetVisibleTypeName() << ".";
std::ostringstream description;
description << "Linking " << this->TargetLinkLanguage << " "
<< this->GetVisibleTypeName() << " $out";
this->GetGlobalGenerator()->AddRule(ruleName,
linkCmd,
description.str(),
comment.str());
}
if (this->TargetNameOut != this->TargetNameReal) {
std::string cmakeCommand =
this->GetLocalGenerator()->ConvertToOutputFormat(
this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
cmLocalGenerator::SHELL);
if (targetType == cmTarget::EXECUTABLE)
this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
cmakeCommand +
" -E cmake_symlink_executable"
" $in $out && $POST_BUILD",
"Creating executable symlink $out",
"Rule for creating executable symlink.");
else
this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
cmakeCommand +
" -E cmake_symlink_library"
" $in $SONAME $out && $POST_BUILD",
"Creating library symlink $out",
"Rule for creating library symlink.");
}
}
std::vector<std::string>
cmNinjaNormalTargetGenerator
::ComputeLinkCmd()
{
std::vector<std::string> linkCmds;
cmTarget::TargetType targetType = this->GetTarget()->GetType();
switch (targetType) {
case cmTarget::STATIC_LIBRARY: {
// Check if you have a non archive way to create the static library.
{
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
linkCmdVar += "_CREATE_STATIC_LIBRARY";
if (const char *linkCmd =
this->GetMakefile()->GetDefinition(linkCmdVar.c_str()))
{
cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
return linkCmds;
}
}
// We have archive link commands set. First, delete the existing archive.
std::string cmakeCommand =
this->GetLocalGenerator()->ConvertToOutputFormat(
this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
cmLocalGenerator::SHELL);
linkCmds.push_back(cmakeCommand + " -E remove $out");
// TODO: Use ARCHIVE_APPEND for archives over a certain size.
{
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
linkCmdVar += "_ARCHIVE_CREATE";
const char *linkCmd =
this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
}
{
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
linkCmdVar += "_ARCHIVE_FINISH";
const char *linkCmd =
this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
}
return linkCmds;
}
case cmTarget::SHARED_LIBRARY:
case cmTarget::MODULE_LIBRARY:
case cmTarget::EXECUTABLE: {
std::string linkCmdVar = "CMAKE_";
linkCmdVar += this->TargetLinkLanguage;
switch (targetType) {
case cmTarget::SHARED_LIBRARY:
linkCmdVar += "_CREATE_SHARED_LIBRARY";
break;
case cmTarget::MODULE_LIBRARY:
linkCmdVar += "_CREATE_SHARED_MODULE";
break;
case cmTarget::EXECUTABLE:
linkCmdVar += "_LINK_EXECUTABLE";
break;
default:
assert(0 && "Unexpected target type");
}
const char *linkCmd =
this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
return linkCmds;
}
default:
assert(0 && "Unexpected target type");
}
return std::vector<std::string>();
}
void cmNinjaNormalTargetGenerator::WriteLinkStatement()
{
cmTarget::TargetType targetType = this->GetTarget()->GetType();
// Write comments.
cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
this->GetBuildFileStream()
<< "# Link build statements for "
<< cmTarget::GetTargetTypeName(targetType)
<< " target "
<< this->GetTargetName()
<< "\n\n";
cmNinjaDeps emptyDeps;
cmNinjaVars vars;
std::string targetOutput = ConvertToNinjaPath(
this->GetTarget()->GetFullPath(this->GetConfigName()).c_str());
std::string targetOutputReal = ConvertToNinjaPath(
this->GetTarget()->GetFullPath(this->GetConfigName(),
/*implib=*/false,
/*realpath=*/true).c_str());
std::string targetOutputImplib = ConvertToNinjaPath(
this->GetTarget()->GetFullPath(this->GetConfigName(),
/*implib=*/true).c_str());
// Compute the comment.
std::ostringstream comment;
comment << "Link the " << this->GetVisibleTypeName() << " "
<< targetOutputReal;
// Compute outputs.
cmNinjaDeps outputs;
outputs.push_back(targetOutputReal);
// Compute specific libraries to link with.
cmNinjaDeps explicitDeps = this->GetObjects(),
implicitDeps = this->ComputeLinkDeps();
this->GetLocalGenerator()->GetTargetFlags(vars["LINK_LIBRARIES"],
vars["FLAGS"],
vars["LINK_FLAGS"],
*this->GetTarget());
this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]);
// Compute architecture specific link flags. Yes, these go into a different
// variable for executables, probably due to a mistake made when duplicating
// code between the Makefile executable and library generators.
this->GetLocalGenerator()
->AddArchitectureFlags(targetType == cmTarget::EXECUTABLE
? vars["FLAGS"]
: vars["ARCH_FLAGS"],
this->GetTarget(),
this->TargetLinkLanguage,
this->GetConfigName());
vars["SONAME"] = this->TargetNameSO;
if (targetType == cmTarget::SHARED_LIBRARY) {
std::string install_name_dir =
this->GetTarget()->GetInstallNameDirForBuildTree(this->GetConfigName());
if (!install_name_dir.empty()) {
vars["INSTALLNAME_DIR"] =
this->GetLocalGenerator()->Convert(install_name_dir.c_str(),
cmLocalGenerator::NONE,
cmLocalGenerator::SHELL, false);
}
}
if (!this->TargetNameImport.empty()) {
vars["TARGET_IMPLIB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
targetOutputImplib.c_str(), cmLocalGenerator::SHELL);
}
vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL);
std::vector<cmCustomCommand> *cmdLists[3] = {
&this->GetTarget()->GetPreBuildCommands(),
&this->GetTarget()->GetPreLinkCommands(),
&this->GetTarget()->GetPostBuildCommands()
};
std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
std::vector<std::string> *cmdLineLists[3] = {
&preLinkCmdLines,
&preLinkCmdLines,
&postBuildCmdLines
};
for (unsigned i = 0; i != 3; ++i) {
for (std::vector<cmCustomCommand>::const_iterator
ci = cmdLists[i]->begin();
ci != cmdLists[i]->end(); ++ci) {
this->GetLocalGenerator()->AppendCustomCommandLines(&*ci,
*cmdLineLists[i]);
}
}
// If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for
// the link commands.
if (!preLinkCmdLines.empty()) {
std::string path = this->GetLocalGenerator()->ConvertToOutputFormat(
this->GetMakefile()->GetHomeOutputDirectory(),
cmLocalGenerator::SHELL);
preLinkCmdLines.push_back("cd " + path);
}
vars["PRE_LINK"] =
this->GetLocalGenerator()->BuildCommandLine(preLinkCmdLines);
std::string postBuildCmdLine =
this->GetLocalGenerator()->BuildCommandLine(postBuildCmdLines);
cmNinjaVars symlinkVars;
if (targetOutput == targetOutputReal) {
vars["POST_BUILD"] = postBuildCmdLine;
} else {
vars["POST_BUILD"] = ":";
symlinkVars["POST_BUILD"] = postBuildCmdLine;
}
// Write the build statement for this target.
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
comment.str(),
this->LanguageLinkerRule(),
outputs,
explicitDeps,
implicitDeps,
emptyDeps,
vars);
if (targetOutput != targetOutputReal) {
if (targetType == cmTarget::EXECUTABLE) {
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
"Create executable symlink " + targetOutput,
"CMAKE_SYMLINK_EXECUTABLE",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
} else {
symlinkVars["SONAME"] = this->GetTargetFilePath(this->TargetNameSO);
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
"Create library symlink " + targetOutput,
"CMAKE_SYMLINK_LIBRARY",
cmNinjaDeps(1, targetOutput),
cmNinjaDeps(1, targetOutputReal),
emptyDeps,
emptyDeps,
symlinkVars);
}
}
if (!this->TargetNameImport.empty()) {
// Since using multiple outputs would mess up the $out variable, use an
// alias for the import library.
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Alias for import library.",
cmNinjaDeps(1, targetOutputImplib),
cmNinjaDeps(1, targetOutputReal));
}
// Add aliases for the file name and the target name.
this->GetGlobalGenerator()->AddTargetAlias(this->TargetNameOut,
this->GetTarget());
this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
this->GetTarget());
}
//----------------------------------------------------------------------------
void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
{
// Write a phony output that depends on all object files.
cmNinjaDeps outputs;
this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs);
cmNinjaDeps depends = this->GetObjects();
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Object library "
+ this->GetTargetName(),
outputs,
depends);
// Add aliases for the target name.
this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
this->GetTarget());
}

View File

@ -0,0 +1,48 @@
/*============================================================================
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();
void WriteObjectLibStatement();
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,516 @@
/*============================================================================
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 "cmGeneratorTarget.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:
case cmTarget::OBJECT_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()
{
this->GeneratorTarget =
this->GetGlobalGenerator()->GetGeneratorTarget(target);
}
cmNinjaTargetGenerator::~cmNinjaTargetGenerator()
{
}
cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const
{
return *this->GetGlobalGenerator()->GetBuildFileStream();
}
cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
{
return *this->GetGlobalGenerator()->GetRulesFileStream();
}
cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
{
return this->LocalGenerator->GetGlobalNinjaGenerator();
}
const char* cmNinjaTargetGenerator::GetConfigName() const
{
return this->LocalGenerator->ConfigName.c_str();
}
// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
const char* cmNinjaTargetGenerator::GetFeature(const char* feature)
{
return this->Target->GetFeature(feature, this->GetConfigName());
}
// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
bool cmNinjaTargetGenerator::GetFeatureAsBool(const char* feature)
{
return cmSystemTools::IsOn(this->GetFeature(feature));
}
// TODO: Picked up from cmMakefileTargetGenerator. Refactor it.
void cmNinjaTargetGenerator::AddFeatureFlags(std::string& flags,
const char* lang)
{
// Add language-specific flags.
this->LocalGenerator->AddLanguageFlags(flags, lang, this->GetConfigName());
if(this->GetFeatureAsBool("INTERPROCEDURAL_OPTIMIZATION"))
{
this->LocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
}
}
// TODO: Most of the code is picked up from
// void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
// Refactor it.
std::string
cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
const std::string& language)
{
std::string flags;
this->AddFeatureFlags(flags, language.c_str());
this->GetLocalGenerator()->AddArchitectureFlags(flags,
this->GetTarget(),
language.c_str(),
this->GetConfigName());
// TODO: Fortran support.
// // Fortran-specific flags computed for this target.
// if(*l == "Fortran")
// {
// this->AddFortranFlags(flags);
// }
// Add shared-library flags if needed.
{
bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) ||
(this->Target->GetType() == cmTarget::MODULE_LIBRARY));
this->GetLocalGenerator()->AddSharedFlags(flags, language.c_str(), shared);
}
// TODO: Handle response file.
// Add include directory flags.
{
std::vector<std::string> includes;
this->LocalGenerator->GetIncludeDirectories(includes, this->Target,
language.c_str());
std::string includeFlags =
this->LocalGenerator->GetIncludeFlags(includes, language.c_str(), false);
this->LocalGenerator->AppendFlags(flags, includeFlags.c_str());
}
// Append old-style preprocessor definition flags.
this->LocalGenerator->AppendFlags(flags, this->Makefile->GetDefineFlags());
// Add target-specific and source-specific flags.
this->LocalGenerator->AppendFlags(flags,
this->Target->GetProperty("COMPILE_FLAGS"));
this->LocalGenerator->AppendFlags(flags,
source->GetProperty("COMPILE_FLAGS"));
// TODO: Handle Apple frameworks.
return flags;
}
// TODO: Refactor with
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
std::string
cmNinjaTargetGenerator::
ComputeDefines(cmSourceFile *source, const std::string& language)
{
std::string defines;
// Add the export symbol definition for shared library objects.
if(const char* exportMacro = this->Target->GetExportMacro())
{
this->LocalGenerator->AppendDefines(defines, exportMacro,
language.c_str());
}
// Add preprocessor definitions for this target and configuration.
this->LocalGenerator->AppendDefines
(defines,
this->Makefile->GetProperty("COMPILE_DEFINITIONS"),
language.c_str());
this->LocalGenerator->AppendDefines
(defines,
this->Target->GetProperty("COMPILE_DEFINITIONS"),
language.c_str());
this->LocalGenerator->AppendDefines
(defines,
source->GetProperty("COMPILE_DEFINITIONS"),
language.c_str());
{
std::string defPropName = "COMPILE_DEFINITIONS_";
defPropName += cmSystemTools::UpperCase(this->GetConfigName());
this->LocalGenerator->AppendDefines
(defines,
this->Makefile->GetProperty(defPropName.c_str()),
language.c_str());
this->LocalGenerator->AppendDefines
(defines,
this->Target->GetProperty(defPropName.c_str()),
language.c_str());
this->LocalGenerator->AppendDefines
(defines,
source->GetProperty(defPropName.c_str()),
language.c_str());
}
return defines;
}
cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
{
// Static libraries never depend on other targets for linking.
if (this->Target->GetType() == cmTarget::STATIC_LIBRARY ||
this->Target->GetType() == cmTarget::OBJECT_LIBRARY)
return cmNinjaDeps();
cmComputeLinkInformation* cli =
this->Target->GetLinkInformation(this->GetConfigName());
if(!cli)
return cmNinjaDeps();
const std::vector<std::string> &deps = cli->GetDepends();
cmNinjaDeps result(deps.size());
std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath());
// Add a dependency on the link definitions file, if any.
if(!this->GeneratorTarget->ModuleDefinitionFile.empty())
{
result.push_back(this->GeneratorTarget->ModuleDefinitionFile);
}
return result;
}
std::string
cmNinjaTargetGenerator
::GetSourceFilePath(cmSourceFile* source) const
{
return ConvertToNinjaPath(source->GetFullPath().c_str());
}
std::string
cmNinjaTargetGenerator
::GetObjectFilePath(cmSourceFile* source) const
{
std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
if(!path.empty())
path += "/";
std::string const& objectName = this->GeneratorTarget->Objects[source];
path += this->LocalGenerator->GetTargetDirectory(*this->Target);
path += "/";
path += objectName;
return path;
}
std::string cmNinjaTargetGenerator::GetTargetOutputDir() const
{
std::string dir = this->Target->GetDirectory(this->GetConfigName());
return ConvertToNinjaPath(dir.c_str());
}
std::string
cmNinjaTargetGenerator
::GetTargetFilePath(const std::string& name) const
{
std::string path = this->GetTargetOutputDir();
if (path.empty() || path == ".")
return name;
path += "/";
path += name;
return path;
}
std::string cmNinjaTargetGenerator::GetTargetName() const
{
return this->Target->GetName();
}
std::string cmNinjaTargetGenerator::GetTargetPDB() const
{
std::string targetFullPathPDB;
if(this->Target->GetType() == cmTarget::EXECUTABLE ||
this->Target->GetType() == cmTarget::STATIC_LIBRARY ||
this->Target->GetType() == cmTarget::SHARED_LIBRARY ||
this->Target->GetType() == cmTarget::MODULE_LIBRARY)
{
targetFullPathPDB = this->Target->GetDirectory(this->GetConfigName());
targetFullPathPDB += "/";
targetFullPathPDB += this->Target->GetPDBName(this->GetConfigName());
}
return ConvertToNinjaPath(targetFullPathPDB.c_str());
}
void
cmNinjaTargetGenerator
::WriteLanguageRules(const std::string& language)
{
this->GetRulesFileStream()
<< "# Rules for language " << language << "\n\n";
this->WriteCompileRule(language);
this->GetRulesFileStream() << "\n";
}
void
cmNinjaTargetGenerator
::WriteCompileRule(const std::string& language)
{
cmLocalGenerator::RuleVariables vars;
vars.RuleLauncher = "RULE_LAUNCH_COMPILE";
vars.CMTarget = this->GetTarget();
std::string lang = language;
vars.Language = lang.c_str();
vars.Source = "$in";
vars.Object = "$out";
std::string flags = "$FLAGS";
vars.Defines = "$DEFINES";
vars.TargetPDB = "$TARGET_PDB";
std::string depfile;
std::string depfileFlagsName = "CMAKE_DEPFILE_FLAGS_" + language;
const char *depfileFlags =
this->GetMakefile()->GetDefinition(depfileFlagsName.c_str());
if (depfileFlags) {
std::string depfileFlagsStr = depfileFlags;
depfile = "$out.d";
cmSystemTools::ReplaceString(depfileFlagsStr, "<DEPFILE>",
depfile.c_str());
flags += " " + depfileFlagsStr;
}
vars.Flags = flags.c_str();
// Rule for compiling object file.
std::string compileCmdVar = "CMAKE_";
compileCmdVar += language;
compileCmdVar += "_COMPILE_OBJECT";
std::string compileCmd =
this->GetMakefile()->GetRequiredDefinition(compileCmdVar.c_str());
std::vector<std::string> compileCmds;
cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
for (std::vector<std::string>::iterator i = compileCmds.begin();
i != compileCmds.end(); ++i)
this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
std::string cmdLine =
this->GetLocalGenerator()->BuildCommandLine(compileCmds);
// Write the rule for compiling file of the given language.
std::ostringstream comment;
comment << "Rule for compiling " << language << " files.";
std::ostringstream description;
description << "Building " << language << " object $out";
this->GetGlobalGenerator()->AddRule(this->LanguageCompilerRule(language),
cmdLine,
description.str(),
comment.str(),
depfile);
}
void
cmNinjaTargetGenerator
::WriteObjectBuildStatements()
{
// Write comments.
cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
this->GetBuildFileStream()
<< "# Object build statements for "
<< cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
<< " target "
<< this->GetTargetName()
<< "\n\n";
for(std::vector<cmSourceFile*>::const_iterator
si = this->GeneratorTarget->CustomCommands.begin();
si != this->GeneratorTarget->CustomCommands.end(); ++si)
{
cmCustomCommand const* cc = (*si)->GetCustomCommand();
this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget());
}
// TODO: this->GeneratorTarget->OSXContent
for(std::vector<cmSourceFile*>::const_iterator
si = this->GeneratorTarget->ExternalObjects.begin();
si != this->GeneratorTarget->ExternalObjects.end(); ++si)
{
this->Objects.push_back(this->GetSourceFilePath(*si));
}
for(std::vector<cmSourceFile*>::const_iterator
si = this->GeneratorTarget->ObjectSources.begin();
si != this->GeneratorTarget->ObjectSources.end(); ++si)
{
this->WriteObjectBuildStatement(*si);
}
{
// Add object library contents as external objects.
std::vector<std::string> objs;
this->GeneratorTarget->UseObjectLibraries(objs);
for(std::vector<std::string>::iterator oi = objs.begin();
oi != objs.end(); ++oi)
{
this->Objects.push_back(ConvertToNinjaPath(oi->c_str()));
}
}
this->GetBuildFileStream() << "\n";
}
void
cmNinjaTargetGenerator
::WriteObjectBuildStatement(cmSourceFile* source)
{
cmNinjaDeps emptyDeps;
std::string comment;
const char* language = source->GetLanguage();
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 dependencies on custom command outputs.
for(std::vector<cmSourceFile*>::const_iterator
si = this->GeneratorTarget->CustomCommands.begin();
si != this->GeneratorTarget->CustomCommands.end(); ++si)
{
cmCustomCommand const* cc = (*si)->GetCustomCommand();
const std::vector<std::string>& ccoutputs = cc->GetOutputs();
std::transform(ccoutputs.begin(), ccoutputs.end(),
std::back_inserter(orderOnlyDeps), MapToNinjaPath());
}
// If the source file is GENERATED and does not have a custom command
// (either attached to this source file or another one), assume that one of
// the target dependencies, OBJECT_DEPENDS or header file custom commands
// will rebuild the file.
if (source->GetPropertyAsBool("GENERATED") && !source->GetCustomCommand() &&
!this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) {
this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName,
orderOnlyDeps);
}
cmNinjaVars vars;
vars["FLAGS"] = this->ComputeFlagsForObject(source, language);
vars["DEFINES"] = this->ComputeDefines(source, language);
vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL);
cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
comment,
rule,
outputs,
explicitDeps,
emptyDeps,
orderOnlyDeps,
vars);
}
//----------------------------------------------------------------------------
void
cmNinjaTargetGenerator
::AddModuleDefinitionFlag(std::string& flags)
{
if(this->GeneratorTarget->ModuleDefinitionFile.empty())
{
return;
}
// TODO: Create a per-language flag variable.
const char* defFileFlag =
this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG");
if(!defFileFlag)
{
return;
}
// Append the flag and value. Use ConvertToLinkReference to help
// vs6's "cl -link" pass it to the linker.
std::string flag = defFileFlag;
flag += (this->LocalGenerator->ConvertToLinkReference(
this->GeneratorTarget->ModuleDefinitionFile.c_str()));
this->LocalGenerator->AppendFlags(flags, flag.c_str());
}

View File

@ -0,0 +1,123 @@
/*============================================================================
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 cmGeneratorTarget;
class cmMakefile;
class cmSourceFile;
class cmCustomCommand;
class cmNinjaTargetGenerator
{
public:
/// Create a cmNinjaTargetGenerator according to the @a target's type.
static cmNinjaTargetGenerator* New(cmTarget* target);
/// Build a NinjaTargetGenerator.
cmNinjaTargetGenerator(cmTarget* target);
/// Destructor.
virtual ~cmNinjaTargetGenerator();
virtual void Generate() = 0;
std::string GetTargetPDB() const;
std::string GetTargetName() const;
protected:
cmGeneratedFileStream& GetBuildFileStream() const;
cmGeneratedFileStream& GetRulesFileStream() const;
cmTarget* GetTarget() const
{ return this->Target; }
cmLocalNinjaGenerator* GetLocalGenerator() const
{ return this->LocalGenerator; }
cmGlobalNinjaGenerator* GetGlobalGenerator() const;
cmMakefile* GetMakefile() const
{ return this->Makefile; }
const char* GetConfigName() const;
std::string LanguageCompilerRule(const std::string& lang) const
{ return lang + "_COMPILER"; }
const char* GetFeature(const char* feature);
bool GetFeatureAsBool(const char* feature);
void AddFeatureFlags(std::string& flags, const char* lang);
/**
* Compute the flags for compilation of object files for a given @a language.
* @note Generally it is the value of the variable whose name is computed
* by LanguageFlagsVarName().
*/
std::string ComputeFlagsForObject(cmSourceFile *source,
const std::string& language);
std::string ComputeDefines(cmSourceFile *source,
const std::string& language);
std::string ConvertToNinjaPath(const char *path) const {
return this->GetLocalGenerator()->ConvertToNinjaPath(path);
}
cmLocalNinjaGenerator::map_to_ninja_path MapToNinjaPath() const {
return this->GetLocalGenerator()->MapToNinjaPath();
}
/// @return the list of link dependency for the given target @a target.
cmNinjaDeps ComputeLinkDeps() const;
/// @return the source file path for the given @a source.
std::string GetSourceFilePath(cmSourceFile* source) const;
/// @return the object file path for the given @a source.
std::string GetObjectFilePath(cmSourceFile* source) const;
/// @return the file path where the target named @a name is generated.
std::string GetTargetFilePath(const std::string& name) const;
/// @return the output path for the target.
virtual std::string GetTargetOutputDir() const;
void WriteLanguageRules(const std::string& language);
void WriteCompileRule(const std::string& language);
void WriteObjectBuildStatements();
void WriteObjectBuildStatement(cmSourceFile* source);
void WriteCustomCommandBuildStatement(cmCustomCommand *cc);
cmNinjaDeps GetObjects() const
{ return this->Objects; }
// Helper to add flag for windows .def file.
void AddModuleDefinitionFlag(std::string& flags);
private:
cmTarget* Target;
cmGeneratorTarget* GeneratorTarget;
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,108 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmNinjaUtilityTargetGenerator.h"
#include "cmCustomCommand.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmTarget.h"
cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator(cmTarget *target)
: cmNinjaTargetGenerator(target) {}
cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator() {}
void cmNinjaUtilityTargetGenerator::Generate()
{
std::vector<std::string> commands;
cmNinjaDeps deps, outputs;
const std::vector<cmCustomCommand> *cmdLists[2] = {
&this->GetTarget()->GetPreBuildCommands(),
&this->GetTarget()->GetPostBuildCommands()
};
for (unsigned i = 0; i != 2; ++i) {
for (std::vector<cmCustomCommand>::const_iterator
ci = cmdLists[i]->begin(); ci != cmdLists[i]->end(); ++ci) {
this->GetLocalGenerator()->AppendCustomCommandDeps(&*ci, deps);
this->GetLocalGenerator()->AppendCustomCommandLines(&*ci, commands);
}
}
const std::vector<cmSourceFile*>& sources =
this->GetTarget()->GetSourceFiles();
for(std::vector<cmSourceFile*>::const_iterator source = sources.begin();
source != sources.end(); ++source)
{
if(cmCustomCommand* cc = (*source)->GetCustomCommand())
{
this->GetLocalGenerator()->AddCustomCommandTarget(cc, this->GetTarget());
// Depend on all custom command outputs.
const std::vector<std::string>& ccOutputs = cc->GetOutputs();
std::transform(ccOutputs.begin(), ccOutputs.end(),
std::back_inserter(deps), MapToNinjaPath());
}
}
this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs);
this->GetLocalGenerator()->AppendTargetDepends(this->GetTarget(), deps);
if (commands.empty()) {
cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
"Utility command for "
+ this->GetTargetName(),
outputs,
deps);
} else {
std::string command =
this->GetLocalGenerator()->BuildCommandLine(commands);
const char *echoStr = this->GetTarget()->GetProperty("EchoString");
std::string desc;
if (echoStr)
desc = echoStr;
else
desc = "Running utility command for " + this->GetTargetName();
// TODO: fix problematic global targets. For now, search and replace the
// makefile vars.
cmSystemTools::ReplaceString(command, "$(CMAKE_SOURCE_DIR)",
this->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));
}
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

@ -198,6 +198,20 @@ std::string cmSystemTools::EscapeQuotes(const char* str)
return result;
}
std::string cmSystemTools::TrimWhitespace(const std::string& s)
{
std::string::const_iterator start = s.begin();
while(start != s.end() && *start == ' ')
++start;
if (start == s.end())
return "";
std::string::const_iterator stop = s.end()-1;
while(*stop == ' ')
--stop;
return std::string(start, stop+1);
}
void cmSystemTools::Error(const char* m1, const char* m2,
const char* m3, const char* m4)
{

View File

@ -49,6 +49,11 @@ public:
///! Escape quotes in a string.
static std::string EscapeQuotes(const char* str);
/**
* Returns a string that has whitespace removed from the start and the end.
*/
static std::string TrimWhitespace(const std::string& s);
typedef void (*ErrorCallback)(const char*, const char*, bool&, void*);
/**
* Set the function used by GUI's to display error messages

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
@ -2593,6 +2597,10 @@ void cmake::AddDefaultGenerators()
#endif
this->Generators[cmGlobalUnixMakefileGenerator3::GetActualName()] =
&cmGlobalUnixMakefileGenerator3::New;
#ifdef CMAKE_USE_NINJA
this->Generators[cmGlobalNinjaGenerator::GetActualName()] =
&cmGlobalNinjaGenerator::New;
#endif
#ifdef CMAKE_USE_XCODE
this->Generators[cmGlobalXCodeGenerator::GetActualName()] =
&cmGlobalXCodeGenerator::New;

View File

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

View File

@ -408,6 +408,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
@ -415,13 +424,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)