1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2011 Peter Collingbourne <peter@pcc.me.uk>
4 Copyright 2011 Nicolas Despres <nicolas.despres@gmail.com>
6 Distributed under the OSI-approved BSD License (the "License");
7 see accompanying file Copyright.txt for details.
9 This software is distributed WITHOUT ANY WARRANTY; without even the
10 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 See the License for more information.
12 ============================================================================*/
13 #include "cmLocalNinjaGenerator.h"
14 #include "cmCustomCommandGenerator.h"
15 #include "cmMakefile.h"
16 #include "cmGlobalNinjaGenerator.h"
17 #include "cmNinjaTargetGenerator.h"
18 #include "cmGeneratedFileStream.h"
19 #include "cmSourceFile.h"
20 #include "cmComputeLinkInformation.h"
25 cmLocalNinjaGenerator::cmLocalNinjaGenerator()
28 , HomeRelativeOutputPath("")
30 this->IsMakefileGenerator = true;
32 this->WindowsShell = true;
34 this->TargetImplib = "$TARGET_IMPLIB";
37 //----------------------------------------------------------------------------
38 // Virtual public methods.
40 cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
44 void cmLocalNinjaGenerator::Generate()
46 this->SetConfigName();
48 this->WriteProcessedMakefile(this->GetBuildFileStream());
49 #ifdef NINJA_GEN_VERBOSE_FILES
50 this->WriteProcessedMakefile(this->GetRulesFileStream());
53 this->WriteBuildFileTop();
55 cmTargets& targets = this->GetMakefile()->GetTargets();
56 for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
58 cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(&t->second);
62 // Add the target to "all" if required.
63 if (!this->GetGlobalNinjaGenerator()->IsExcluded(
64 this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0],
66 this->GetGlobalNinjaGenerator()->AddDependencyToAll(&t->second);
71 this->WriteCustomCommandBuildStatements();
75 // cmLocalUnixMakefileGenerator3.
77 // Source/cmMakefile.cxx
78 // Source/cmGlobalGenerator.cxx
79 void cmLocalNinjaGenerator::Configure()
81 // Compute the path to use when referencing the current output
82 // directory from the top output directory.
83 this->HomeRelativeOutputPath =
84 this->Convert(this->Makefile->GetStartOutputDirectory(), HOME_OUTPUT);
85 if(this->HomeRelativeOutputPath == ".")
87 this->HomeRelativeOutputPath = "";
89 this->cmLocalGenerator::Configure();
93 // TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it.
94 std::string cmLocalNinjaGenerator
95 ::GetTargetDirectory(cmTarget const& target) const
97 std::string dir = cmake::GetCMakeFilesDirectoryPostSlash();
98 dir += target.GetName();
107 //----------------------------------------------------------------------------
108 // Non-virtual public methods.
110 const cmGlobalNinjaGenerator*
111 cmLocalNinjaGenerator::GetGlobalNinjaGenerator() const
114 static_cast<const cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
117 cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator()
119 return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
122 //----------------------------------------------------------------------------
123 // Virtual protected methods.
126 cmLocalNinjaGenerator::ConvertToLinkReference(std::string const& lib)
128 return this->Convert(lib.c_str(), HOME_OUTPUT, SHELL);
132 cmLocalNinjaGenerator::ConvertToIncludeReference(std::string const& path)
134 return this->Convert(path.c_str(), HOME_OUTPUT, SHELL);
137 //----------------------------------------------------------------------------
140 cmGeneratedFileStream& cmLocalNinjaGenerator::GetBuildFileStream() const
142 return *this->GetGlobalNinjaGenerator()->GetBuildFileStream();
145 cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const
147 return *this->GetGlobalNinjaGenerator()->GetRulesFileStream();
150 const cmake* cmLocalNinjaGenerator::GetCMakeInstance() const
152 return this->GetGlobalGenerator()->GetCMakeInstance();
155 cmake* cmLocalNinjaGenerator::GetCMakeInstance()
157 return this->GetGlobalGenerator()->GetCMakeInstance();
160 bool cmLocalNinjaGenerator::isRootMakefile() const
162 return (strcmp(this->Makefile->GetCurrentDirectory(),
163 this->GetCMakeInstance()->GetHomeDirectory()) == 0);
166 void cmLocalNinjaGenerator::WriteBuildFileTop()
168 // We do that only once for the top CMakeLists.txt file.
169 if(!this->isRootMakefile())
172 // For the build file.
173 this->WriteProjectHeader(this->GetBuildFileStream());
174 this->WriteNinjaFilesInclusion(this->GetBuildFileStream());
176 // For the rule file.
177 this->WriteProjectHeader(this->GetRulesFileStream());
180 void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os)
182 cmGlobalNinjaGenerator::WriteDivider(os);
184 << "# Project: " << this->GetMakefile()->GetProjectName() << std::endl
185 << "# Configuration: " << this->ConfigName << std::endl
187 cmGlobalNinjaGenerator::WriteDivider(os);
190 void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
192 cmGlobalNinjaGenerator::WriteDivider(os);
194 << "# Include auxiliary files.\n"
197 cmGlobalNinjaGenerator::WriteInclude(os,
198 cmGlobalNinjaGenerator::NINJA_RULES_FILE,
199 "Include rules file.");
203 void cmLocalNinjaGenerator::SetConfigName()
205 // Store the configuration name that will be generated.
206 if(const char* config =
207 this->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"))
209 // Use the build type given by the user.
210 this->ConfigName = config;
214 // No configuration type given.
215 this->ConfigName = "";
219 void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
221 cmGlobalNinjaGenerator::WriteDivider(os);
223 << "# Write statements declared in CMakeLists.txt:" << std::endl
224 << "# " << this->Makefile->GetCurrentListFile() << std::endl
226 if(this->isRootMakefile())
227 os << "# Which is the root file." << std::endl;
228 cmGlobalNinjaGenerator::WriteDivider(os);
232 std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path)
234 std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT);
236 cmSystemTools::ReplaceString(convPath, "/", "\\");
242 cmLocalNinjaGenerator
243 ::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
245 this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs);
249 cmLocalNinjaGenerator
250 ::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
252 this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs);
255 void cmLocalNinjaGenerator::AppendCustomCommandDeps(const cmCustomCommand *cc,
256 cmNinjaDeps &ninjaDeps)
258 const std::vector<std::string> &deps = cc->GetDepends();
259 for (std::vector<std::string>::const_iterator i = deps.begin();
260 i != deps.end(); ++i) {
262 if (this->GetRealDependency(i->c_str(), this->GetConfigName(), dep))
263 ninjaDeps.push_back(ConvertToNinjaPath(dep.c_str()));
267 std::string cmLocalNinjaGenerator::BuildCommandLine(
268 const std::vector<std::string> &cmdLines)
270 // If we have no commands but we need to build a command anyway, use ":".
271 // This happens when building a POST_BUILD value for link targets that
272 // don't use POST_BUILD.
273 if (cmdLines.empty())
281 for (std::vector<std::string>::const_iterator li = cmdLines.begin();
282 li != cmdLines.end(); ++li) {
283 if (li != cmdLines.begin()) {
286 } else if (cmdLines.size() > 1) {
287 cmd << "cmd.exe /c ";
295 void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc,
296 std::vector<std::string> &cmdLines)
298 cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile);
299 if (ccg.GetNumberOfCommands() > 0) {
300 const char* wd = cc->GetWorkingDirectory();
302 wd = this->GetMakefile()->GetStartOutputDirectory();
304 cmOStringStream cdCmd;
305 cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL);
306 cmdLines.push_back(cdCmd.str());
308 for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) {
309 cmdLines.push_back(this->ConvertToOutputFormat(ccg.GetCommand(i).c_str(),
311 std::string& cmd = cmdLines.back();
312 ccg.AppendArguments(i, cmd);
317 cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
318 cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps)
320 if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc))
323 const std::vector<std::string> &outputs = cc->GetOutputs();
324 cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps;
326 std::transform(outputs.begin(), outputs.end(),
327 ninjaOutputs.begin(), MapToNinjaPath());
328 this->AppendCustomCommandDeps(cc, ninjaDeps);
330 for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();
332 this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i);
334 std::vector<std::string> cmdLines;
335 this->AppendCustomCommandLines(cc, cmdLines);
337 if (cmdLines.empty()) {
338 cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
339 "Phony custom command for " +
347 this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
348 this->BuildCommandLine(cmdLines),
349 this->ConstructComment(*cc),
350 "Custom command for " + ninjaOutputs[0],
357 void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc,
360 this->CustomCommandTargets[cc].insert(target);
363 void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
365 for (CustomCommandTargetMap::iterator i = this->CustomCommandTargets.begin();
366 i != this->CustomCommandTargets.end(); ++i) {
367 // A custom command may appear on multiple targets. However, some build
368 // systems exist where the target dependencies on some of the targets are
369 // overspecified, leading to a dependency cycle. If we assume all target
370 // dependencies are a superset of the true target dependencies for this
371 // custom command, we can take the set intersection of all target
372 // dependencies to obtain a correct dependency list.
374 // FIXME: This won't work in certain obscure scenarios involving indirect
376 std::set<cmTarget*>::iterator j = i->second.begin();
377 assert(j != i->second.end());
378 std::vector<std::string> ccTargetDeps;
379 this->AppendTargetDepends(*j, ccTargetDeps);
380 std::sort(ccTargetDeps.begin(), ccTargetDeps.end());
383 for (; j != i->second.end(); ++j) {
384 std::vector<std::string> jDeps, depsIntersection;
385 this->AppendTargetDepends(*j, jDeps);
386 std::sort(jDeps.begin(), jDeps.end());
387 std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(),
388 jDeps.begin(), jDeps.end(),
389 std::back_inserter(depsIntersection));
390 ccTargetDeps = depsIntersection;
393 this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps);