summaryrefslogtreecommitdiffstats
path: root/Source/cmLocalNinjaGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmLocalNinjaGenerator.cxx')
-rw-r--r--Source/cmLocalNinjaGenerator.cxx505
1 files changed, 505 insertions, 0 deletions
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
new file mode 100644
index 0000000..46d7e18
--- /dev/null
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -0,0 +1,505 @@
+/*============================================================================
+ 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 "cmGeneratedFileStream.h"
+#include "cmGlobalNinjaGenerator.h"
+#include "cmMakefile.h"
+#include "cmNinjaTargetGenerator.h"
+#include "cmSourceFile.h"
+#include "cmState.h"
+#include "cmake.h"
+
+#include <assert.h>
+
+cmLocalNinjaGenerator::cmLocalNinjaGenerator(cmGlobalGenerator* gg,
+ cmMakefile* mf)
+ : cmLocalCommonGenerator(gg, mf, cmOutputConverter::HOME_OUTPUT)
+ , HomeRelativeOutputPath("")
+{
+ this->TargetImplib = "$TARGET_IMPLIB";
+}
+
+// Virtual public methods.
+
+cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
+{
+}
+
+void cmLocalNinjaGenerator::Generate()
+{
+ // Compute the path to use when referencing the current output
+ // directory from the top output directory.
+ this->HomeRelativeOutputPath = this->Convert(
+ this->GetCurrentBinaryDirectory(), cmOutputConverter::HOME_OUTPUT);
+ if (this->HomeRelativeOutputPath == ".") {
+ this->HomeRelativeOutputPath = "";
+ }
+
+ this->SetConfigName();
+
+ this->WriteProcessedMakefile(this->GetBuildFileStream());
+#ifdef NINJA_GEN_VERBOSE_FILES
+ this->WriteProcessedMakefile(this->GetRulesFileStream());
+#endif
+
+ // We do that only once for the top CMakeLists.txt file.
+ if (this->IsRootMakefile()) {
+ this->WriteBuildFileTop();
+
+ this->WritePools(this->GetRulesFileStream());
+
+ const std::string showIncludesPrefix =
+ this->GetMakefile()->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX");
+ if (!showIncludesPrefix.empty()) {
+ cmGlobalNinjaGenerator::WriteComment(this->GetRulesFileStream(),
+ "localized /showIncludes string");
+ this->GetRulesFileStream() << "msvc_deps_prefix = " << showIncludesPrefix
+ << "\n\n";
+ }
+ }
+
+ std::vector<cmGeneratorTarget*> targets = this->GetGeneratorTargets();
+ for (std::vector<cmGeneratorTarget*>::iterator t = targets.begin();
+ t != targets.end(); ++t) {
+ if ((*t)->GetType() == cmState::INTERFACE_LIBRARY) {
+ continue;
+ }
+ cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(*t);
+ if (tg) {
+ tg->Generate();
+ // Add the target to "all" if required.
+ if (!this->GetGlobalNinjaGenerator()->IsExcluded(
+ this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0], *t)) {
+ this->GetGlobalNinjaGenerator()->AddDependencyToAll(*t);
+ }
+ delete tg;
+ }
+ }
+
+ this->WriteCustomCommandBuildStatements();
+}
+
+// TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it.
+std::string cmLocalNinjaGenerator::GetTargetDirectory(
+ cmGeneratorTarget 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, cmOutputConverter::OutputFormat format)
+{
+ std::string path = this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(lib);
+ return this->ConvertToOutputFormat(path, format);
+}
+
+std::string cmLocalNinjaGenerator::ConvertToIncludeReference(
+ std::string const& path, cmOutputConverter::OutputFormat format,
+ bool forceFullPaths)
+{
+ return this->Convert(path, forceFullPaths ? cmOutputConverter::FULL
+ : cmOutputConverter::HOME_OUTPUT,
+ format);
+}
+
+// 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();
+}
+
+void cmLocalNinjaGenerator::WriteBuildFileTop()
+{
+ // For the build file.
+ this->WriteProjectHeader(this->GetBuildFileStream());
+ this->WriteNinjaRequiredVersion(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->GetProjectName() << std::endl
+ << "# Configuration: " << this->ConfigName << std::endl;
+ cmGlobalNinjaGenerator::WriteDivider(os);
+}
+
+void cmLocalNinjaGenerator::WriteNinjaRequiredVersion(std::ostream& os)
+{
+ // Default required version
+ std::string requiredVersion =
+ this->GetGlobalNinjaGenerator()->RequiredNinjaVersion();
+
+ // Ninja generator uses the 'console' pool if available (>= 1.5)
+ if (this->GetGlobalNinjaGenerator()->SupportsConsolePool()) {
+ requiredVersion =
+ this->GetGlobalNinjaGenerator()->RequiredNinjaVersionForConsolePool();
+ }
+
+ cmGlobalNinjaGenerator::WriteComment(
+ os, "Minimal version of Ninja required by this file");
+ os << "ninja_required_version = " << requiredVersion << std::endl
+ << std::endl;
+}
+
+void cmLocalNinjaGenerator::WritePools(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+
+ const char* jobpools =
+ this->GetCMakeInstance()->GetState()->GetGlobalProperty("JOB_POOLS");
+ if (jobpools) {
+ cmGlobalNinjaGenerator::WriteComment(
+ os, "Pools defined by global property JOB_POOLS");
+ std::vector<std::string> pools;
+ cmSystemTools::ExpandListArgument(jobpools, pools);
+ for (size_t i = 0; i < pools.size(); ++i) {
+ const std::string pool = pools[i];
+ const std::string::size_type eq = pool.find('=');
+ unsigned int jobs;
+ if (eq != std::string::npos &&
+ sscanf(pool.c_str() + eq, "=%u", &jobs) == 1) {
+ os << "pool " << pool.substr(0, eq) << std::endl;
+ os << " depth = " << jobs << std::endl;
+ os << std::endl;
+ } else {
+ cmSystemTools::Error("Invalid pool defined by property 'JOB_POOLS': ",
+ pool.c_str());
+ }
+ }
+ }
+}
+
+void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os << "# Include auxiliary files.\n"
+ << "\n";
+ cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator();
+ std::string const ninjaRulesFile =
+ ng->NinjaOutputPath(cmGlobalNinjaGenerator::NINJA_RULES_FILE);
+ std::string const rulesFilePath =
+ ng->EncodeIdent(ng->EncodePath(ninjaRulesFile), os);
+ cmGlobalNinjaGenerator::WriteInclude(os, rulesFilePath,
+ "Include rules file.");
+ os << "\n";
+}
+
+void cmLocalNinjaGenerator::ComputeObjectFilenames(
+ std::map<cmSourceFile const*, std::string>& mapping,
+ cmGeneratorTarget const* gt)
+{
+ for (std::map<cmSourceFile const*, std::string>::iterator si =
+ mapping.begin();
+ si != mapping.end(); ++si) {
+ cmSourceFile const* sf = si->first;
+ si->second =
+ this->GetObjectFileNameWithoutTarget(*sf, gt->ObjectDirectory);
+ }
+}
+
+void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
+{
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os << "# Write statements declared in CMakeLists.txt:" << std::endl
+ << "# " << this->Makefile->GetDefinition("CMAKE_CURRENT_LIST_FILE")
+ << std::endl;
+ if (this->IsRootMakefile()) {
+ os << "# Which is the root file." << std::endl;
+ }
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ os << std::endl;
+}
+
+void cmLocalNinjaGenerator::AppendTargetOutputs(cmGeneratorTarget* target,
+ cmNinjaDeps& outputs)
+{
+ this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs);
+}
+
+void cmLocalNinjaGenerator::AppendTargetDepends(cmGeneratorTarget* target,
+ cmNinjaDeps& outputs)
+{
+ this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs);
+}
+
+void cmLocalNinjaGenerator::AppendCustomCommandDeps(
+ cmCustomCommandGenerator const& ccg, cmNinjaDeps& ninjaDeps)
+{
+ const std::vector<std::string>& deps = ccg.GetDepends();
+ for (std::vector<std::string>::const_iterator i = deps.begin();
+ i != deps.end(); ++i) {
+ std::string dep;
+ if (this->GetRealDependency(*i, this->GetConfigName(), dep)) {
+ ninjaDeps.push_back(
+ this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(dep));
+ }
+ }
+}
+
+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
+ }
+
+ std::ostringstream cmd;
+ for (std::vector<std::string>::const_iterator li = cmdLines.begin();
+ li != cmdLines.end(); ++li)
+#ifdef _WIN32
+ {
+ if (li != cmdLines.begin()) {
+ cmd << " && ";
+ } else if (cmdLines.size() > 1) {
+ cmd << "cmd.exe /C \"";
+ }
+ cmd << *li;
+ }
+ if (cmdLines.size() > 1) {
+ cmd << "\"";
+ }
+#else
+ {
+ if (li != cmdLines.begin()) {
+ cmd << " && ";
+ }
+ cmd << *li;
+ }
+#endif
+ return cmd.str();
+}
+
+void cmLocalNinjaGenerator::AppendCustomCommandLines(
+ cmCustomCommandGenerator const& ccg, std::vector<std::string>& cmdLines)
+{
+ if (ccg.GetNumberOfCommands() > 0) {
+ std::string wd = ccg.GetWorkingDirectory();
+ if (wd.empty()) {
+ wd = this->GetCurrentBinaryDirectory();
+ }
+
+ std::ostringstream cdCmd;
+#ifdef _WIN32
+ std::string cdStr = "cd /D ";
+#else
+ std::string cdStr = "cd ";
+#endif
+ cdCmd << cdStr
+ << this->ConvertToOutputFormat(wd, cmOutputConverter::SHELL);
+ cmdLines.push_back(cdCmd.str());
+ }
+
+ std::string launcher = this->MakeCustomLauncher(ccg);
+
+ for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) {
+ cmdLines.push_back(launcher +
+ this->ConvertToOutputFormat(ccg.GetCommand(i),
+ cmOutputConverter::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;
+ }
+
+ cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this);
+
+ const std::vector<std::string>& outputs = ccg.GetOutputs();
+ const std::vector<std::string>& byproducts = ccg.GetByproducts();
+ cmNinjaDeps ninjaOutputs(outputs.size() + byproducts.size()), ninjaDeps;
+
+ bool symbolic = false;
+ for (std::vector<std::string>::const_iterator o = outputs.begin();
+ !symbolic && o != outputs.end(); ++o) {
+ if (cmSourceFile* sf = this->Makefile->GetSource(*o)) {
+ symbolic = sf->GetPropertyAsBool("SYMBOLIC");
+ }
+ }
+
+#if 0
+#error TODO: Once CC in an ExternalProject target must provide the \
+ file of each imported target that has an add_dependencies pointing \
+ at us. How to know which ExternalProject step actually provides it?
+#endif
+ std::transform(outputs.begin(), outputs.end(), ninjaOutputs.begin(),
+ this->GetGlobalNinjaGenerator()->MapToNinjaPath());
+ std::transform(byproducts.begin(), byproducts.end(),
+ ninjaOutputs.begin() + outputs.size(),
+ this->GetGlobalNinjaGenerator()->MapToNinjaPath());
+ this->AppendCustomCommandDeps(ccg, ninjaDeps);
+
+ for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();
+ ++i) {
+ this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i);
+ }
+
+ std::vector<std::string> cmdLines;
+ this->AppendCustomCommandLines(ccg, cmdLines);
+
+ if (cmdLines.empty()) {
+ this->GetGlobalNinjaGenerator()->WritePhonyBuild(
+ this->GetBuildFileStream(),
+ "Phony custom command for " + ninjaOutputs[0], ninjaOutputs, ninjaDeps,
+ cmNinjaDeps(), orderOnlyDeps, cmNinjaVars());
+ } else {
+ this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
+ this->BuildCommandLine(cmdLines), this->ConstructComment(ccg),
+ "Custom command for " + ninjaOutputs[0], cc->GetUsesTerminal(),
+ /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, ninjaDeps,
+ orderOnlyDeps);
+ }
+}
+
+void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc,
+ cmGeneratorTarget* target)
+{
+ CustomCommandTargetMap::value_type v(cc, std::set<cmGeneratorTarget*>());
+ std::pair<CustomCommandTargetMap::iterator, bool> ins =
+ this->CustomCommandTargets.insert(v);
+ if (ins.second) {
+ this->CustomCommands.push_back(cc);
+ }
+ ins.first->second.insert(target);
+}
+
+void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
+{
+ for (std::vector<cmCustomCommand const*>::iterator vi =
+ this->CustomCommands.begin();
+ vi != this->CustomCommands.end(); ++vi) {
+ CustomCommandTargetMap::iterator i = this->CustomCommandTargets.find(*vi);
+ assert(i != this->CustomCommandTargets.end());
+
+ // 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<cmGeneratorTarget*>::iterator j = i->second.begin();
+ assert(j != i->second.end());
+ std::vector<std::string> ccTargetDeps;
+ this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(*j,
+ ccTargetDeps);
+ std::sort(ccTargetDeps.begin(), ccTargetDeps.end());
+ ++j;
+
+ for (; j != i->second.end(); ++j) {
+ std::vector<std::string> jDeps, depsIntersection;
+ this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(*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);
+ }
+}
+
+std::string cmLocalNinjaGenerator::MakeCustomLauncher(
+ cmCustomCommandGenerator const& ccg)
+{
+ const char* property = "RULE_LAUNCH_CUSTOM";
+ const char* property_value = this->Makefile->GetProperty(property);
+
+ if (!property_value || !*property_value) {
+ return std::string();
+ }
+
+ // Expand rules in the empty string. It may insert the launcher and
+ // perform replacements.
+ RuleVariables vars;
+ vars.RuleLauncher = property;
+ std::string output;
+ const std::vector<std::string>& outputs = ccg.GetOutputs();
+ if (!outputs.empty()) {
+ cmOutputConverter::RelativeRoot relative_root =
+ ccg.GetWorkingDirectory().empty() ? cmOutputConverter::START_OUTPUT
+ : cmOutputConverter::NONE;
+
+ output =
+ this->Convert(outputs[0], relative_root, cmOutputConverter::SHELL);
+ }
+ vars.Output = output.c_str();
+
+ std::string launcher;
+ this->ExpandRuleVariables(launcher, vars);
+ if (!launcher.empty()) {
+ launcher += " ";
+ }
+
+ return launcher;
+}