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("")
31 this->WindowsShell = true;
33 this->TargetImplib = "$TARGET_IMPLIB";
36 //----------------------------------------------------------------------------
37 // Virtual public methods.
39 cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
43 void cmLocalNinjaGenerator::Generate()
45 this->SetConfigName();
47 this->WriteProcessedMakefile(this->GetBuildFileStream());
48 #ifdef NINJA_GEN_VERBOSE_FILES
49 this->WriteProcessedMakefile(this->GetRulesFileStream());
52 this->WriteBuildFileTop();
54 cmTargets& targets = this->GetMakefile()->GetTargets();
55 for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
57 cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(&t->second);
61 // Add the target to "all" if required.
62 if (!this->GetGlobalNinjaGenerator()->IsExcluded(
63 this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0],
65 this->GetGlobalNinjaGenerator()->AddDependencyToAll(&t->second);
70 this->WriteCustomCommandBuildStatements();
74 // cmLocalUnixMakefileGenerator3.
76 // Source/cmMakefile.cxx
77 // Source/cmGlobalGenerator.cxx
78 void cmLocalNinjaGenerator::Configure()
80 // Compute the path to use when referencing the current output
81 // directory from the top output directory.
82 this->HomeRelativeOutputPath =
83 this->Convert(this->Makefile->GetStartOutputDirectory(), HOME_OUTPUT);
84 if(this->HomeRelativeOutputPath == ".")
86 this->HomeRelativeOutputPath = "";
88 this->cmLocalGenerator::Configure();
92 // TODO: Picked up from cmLocalUnixMakefileGenerator3. Refactor it.
93 std::string cmLocalNinjaGenerator
94 ::GetTargetDirectory(cmTarget const& target) const
96 std::string dir = cmake::GetCMakeFilesDirectoryPostSlash();
97 dir += target.GetName();
106 //----------------------------------------------------------------------------
107 // Non-virtual public methods.
109 const cmGlobalNinjaGenerator*
110 cmLocalNinjaGenerator::GetGlobalNinjaGenerator() const
113 static_cast<const cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
116 cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator()
118 return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
121 //----------------------------------------------------------------------------
122 // Virtual protected methods.
125 cmLocalNinjaGenerator::ConvertToLinkReference(std::string const& lib)
127 return this->Convert(lib.c_str(), HOME_OUTPUT, SHELL);
131 cmLocalNinjaGenerator::ConvertToIncludeReference(std::string const& path)
133 return this->Convert(path.c_str(), HOME_OUTPUT, SHELL);
136 //----------------------------------------------------------------------------
139 cmGeneratedFileStream& cmLocalNinjaGenerator::GetBuildFileStream() const
141 return *this->GetGlobalNinjaGenerator()->GetBuildFileStream();
144 cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const
146 return *this->GetGlobalNinjaGenerator()->GetRulesFileStream();
149 const cmake* cmLocalNinjaGenerator::GetCMakeInstance() const
151 return this->GetGlobalGenerator()->GetCMakeInstance();
154 cmake* cmLocalNinjaGenerator::GetCMakeInstance()
156 return this->GetGlobalGenerator()->GetCMakeInstance();
159 bool cmLocalNinjaGenerator::isRootMakefile() const
161 return (strcmp(this->Makefile->GetCurrentDirectory(),
162 this->GetCMakeInstance()->GetHomeDirectory()) == 0);
165 void cmLocalNinjaGenerator::WriteBuildFileTop()
167 // We do that only once for the top CMakeLists.txt file.
168 if(!this->isRootMakefile())
171 // For the build file.
172 this->WriteProjectHeader(this->GetBuildFileStream());
173 this->WriteNinjaFilesInclusion(this->GetBuildFileStream());
175 // For the rule file.
176 this->WriteProjectHeader(this->GetRulesFileStream());
179 void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os)
181 cmGlobalNinjaGenerator::WriteDivider(os);
183 << "# Project: " << this->GetMakefile()->GetProjectName() << std::endl
184 << "# Configuration: " << this->ConfigName << std::endl
186 cmGlobalNinjaGenerator::WriteDivider(os);
189 void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
191 cmGlobalNinjaGenerator::WriteDivider(os);
193 << "# Include auxiliary files.\n"
196 cmGlobalNinjaGenerator::WriteInclude(os,
197 cmGlobalNinjaGenerator::NINJA_RULES_FILE,
198 "Include rules file.");
202 void cmLocalNinjaGenerator::SetConfigName()
204 // Store the configuration name that will be generated.
205 if(const char* config =
206 this->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"))
208 // Use the build type given by the user.
209 this->ConfigName = config;
213 // No configuration type given.
214 this->ConfigName = "";
218 void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
220 cmGlobalNinjaGenerator::WriteDivider(os);
222 << "# Write statements declared in CMakeLists.txt:" << std::endl
223 << "# " << this->Makefile->GetCurrentListFile() << std::endl
225 if(this->isRootMakefile())
226 os << "# Which is the root file." << std::endl;
227 cmGlobalNinjaGenerator::WriteDivider(os);
231 std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path)
233 std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT);
235 cmSystemTools::ReplaceString(convPath, "/", "\\");
241 cmLocalNinjaGenerator
242 ::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
244 this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs);
248 cmLocalNinjaGenerator
249 ::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
251 this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs);
254 void cmLocalNinjaGenerator::AppendCustomCommandDeps(const cmCustomCommand *cc,
255 cmNinjaDeps &ninjaDeps)
257 const std::vector<std::string> &deps = cc->GetDepends();
258 for (std::vector<std::string>::const_iterator i = deps.begin();
259 i != deps.end(); ++i) {
261 if (this->GetRealDependency(i->c_str(), this->GetConfigName(), dep))
262 ninjaDeps.push_back(ConvertToNinjaPath(dep.c_str()));
266 std::string cmLocalNinjaGenerator::BuildCommandLine(
267 const std::vector<std::string> &cmdLines)
269 // If we have no commands but we need to build a command anyway, use ":".
270 // This happens when building a POST_BUILD value for link targets that
271 // don't use POST_BUILD.
272 if (cmdLines.empty())
280 for (std::vector<std::string>::const_iterator li = cmdLines.begin();
281 li != cmdLines.end(); ++li) {
282 if (li != cmdLines.begin()) {
285 } else if (cmdLines.size() > 1) {
286 cmd << "cmd.exe /c ";
294 void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc,
295 std::vector<std::string> &cmdLines)
297 cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile);
298 if (ccg.GetNumberOfCommands() > 0) {
299 const char* wd = cc->GetWorkingDirectory();
301 wd = this->GetMakefile()->GetStartOutputDirectory();
303 cmOStringStream cdCmd;
305 std::string cdStr = "cd /D ";
307 std::string cdStr = "cd ";
309 cdCmd << cdStr << this->ConvertToOutputFormat(wd, SHELL);
310 cmdLines.push_back(cdCmd.str());
312 for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) {
313 cmdLines.push_back(this->ConvertToOutputFormat(ccg.GetCommand(i).c_str(),
315 std::string& cmd = cmdLines.back();
316 ccg.AppendArguments(i, cmd);
321 cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
322 cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps)
324 if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc))
327 const std::vector<std::string> &outputs = cc->GetOutputs();
328 cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps;
330 std::transform(outputs.begin(), outputs.end(),
331 ninjaOutputs.begin(), MapToNinjaPath());
332 this->AppendCustomCommandDeps(cc, ninjaDeps);
334 for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();
336 this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i);
338 std::vector<std::string> cmdLines;
339 this->AppendCustomCommandLines(cc, cmdLines);
341 if (cmdLines.empty()) {
342 this->GetGlobalNinjaGenerator()->WritePhonyBuild(
343 this->GetBuildFileStream(),
344 "Phony custom command for " +
352 this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
353 this->BuildCommandLine(cmdLines),
354 this->ConstructComment(*cc),
355 "Custom command for " + ninjaOutputs[0],
362 void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc,
365 this->CustomCommandTargets[cc].insert(target);
368 void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
370 for (CustomCommandTargetMap::iterator i = this->CustomCommandTargets.begin();
371 i != this->CustomCommandTargets.end(); ++i) {
372 // A custom command may appear on multiple targets. However, some build
373 // systems exist where the target dependencies on some of the targets are
374 // overspecified, leading to a dependency cycle. If we assume all target
375 // dependencies are a superset of the true target dependencies for this
376 // custom command, we can take the set intersection of all target
377 // dependencies to obtain a correct dependency list.
379 // FIXME: This won't work in certain obscure scenarios involving indirect
381 std::set<cmTarget*>::iterator j = i->second.begin();
382 assert(j != i->second.end());
383 std::vector<std::string> ccTargetDeps;
384 this->AppendTargetDepends(*j, ccTargetDeps);
385 std::sort(ccTargetDeps.begin(), ccTargetDeps.end());
388 for (; j != i->second.end(); ++j) {
389 std::vector<std::string> jDeps, depsIntersection;
390 this->AppendTargetDepends(*j, jDeps);
391 std::sort(jDeps.begin(), jDeps.end());
392 std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(),
393 jDeps.begin(), jDeps.end(),
394 std::back_inserter(depsIntersection));
395 ccTargetDeps = depsIntersection;
398 this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps);