Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmLocalNinjaGenerator.cxx
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>
5
6   Distributed under the OSI-approved BSD License (the "License");
7   see accompanying file Copyright.txt for details.
8
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"
21 #include "cmake.h"
22
23 #include <assert.h>
24
25 cmLocalNinjaGenerator::cmLocalNinjaGenerator()
26   : cmLocalGenerator()
27   , ConfigName("")
28   , HomeRelativeOutputPath("")
29 {
30   this->IsMakefileGenerator = true;
31 #ifdef _WIN32
32   this->WindowsShell = true;
33 #endif
34   this->TargetImplib = "$TARGET_IMPLIB";
35 }
36
37 //----------------------------------------------------------------------------
38 // Virtual public methods.
39
40 cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
41 {
42 }
43
44 void cmLocalNinjaGenerator::Generate()
45 {
46   this->SetConfigName();
47
48   this->WriteProcessedMakefile(this->GetBuildFileStream());
49 #ifdef NINJA_GEN_VERBOSE_FILES
50   this->WriteProcessedMakefile(this->GetRulesFileStream());
51 #endif
52
53   this->WriteBuildFileTop();
54
55   cmTargets& targets = this->GetMakefile()->GetTargets();
56   for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t)
57     {
58     cmNinjaTargetGenerator* tg = cmNinjaTargetGenerator::New(&t->second);
59     if(tg)
60       {
61       tg->Generate();
62       // Add the target to "all" if required.
63       if (!this->GetGlobalNinjaGenerator()->IsExcluded(
64             this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0],
65             t->second))
66         this->GetGlobalNinjaGenerator()->AddDependencyToAll(&t->second);
67       delete tg;
68       }
69     }
70
71   this->WriteCustomCommandBuildStatements();
72 }
73
74 // Implemented in:
75 //   cmLocalUnixMakefileGenerator3.
76 // Used in:
77 //   Source/cmMakefile.cxx
78 //   Source/cmGlobalGenerator.cxx
79 void cmLocalNinjaGenerator::Configure()
80 {
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 == ".")
86     {
87     this->HomeRelativeOutputPath = "";
88     }
89   this->cmLocalGenerator::Configure();
90
91 }
92
93 // TODO: Picked up from cmLocalUnixMakefileGenerator3.  Refactor it.
94 std::string cmLocalNinjaGenerator
95 ::GetTargetDirectory(cmTarget const& target) const
96 {
97   std::string dir = cmake::GetCMakeFilesDirectoryPostSlash();
98   dir += target.GetName();
99 #if defined(__VMS)
100   dir += "_dir";
101 #else
102   dir += ".dir";
103 #endif
104   return dir;
105 }
106
107 //----------------------------------------------------------------------------
108 // Non-virtual public methods.
109
110 const cmGlobalNinjaGenerator*
111 cmLocalNinjaGenerator::GetGlobalNinjaGenerator() const
112 {
113   return
114     static_cast<const cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
115 }
116
117 cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator()
118 {
119   return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator());
120 }
121
122 //----------------------------------------------------------------------------
123 // Virtual protected methods.
124
125 std::string
126 cmLocalNinjaGenerator::ConvertToLinkReference(std::string const& lib)
127 {
128   return this->Convert(lib.c_str(), HOME_OUTPUT, SHELL);
129 }
130
131 std::string
132 cmLocalNinjaGenerator::ConvertToIncludeReference(std::string const& path)
133 {
134   return this->Convert(path.c_str(), HOME_OUTPUT, SHELL);
135 }
136
137 //----------------------------------------------------------------------------
138 // Private methods.
139
140 cmGeneratedFileStream& cmLocalNinjaGenerator::GetBuildFileStream() const
141 {
142   return *this->GetGlobalNinjaGenerator()->GetBuildFileStream();
143 }
144
145 cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const
146 {
147   return *this->GetGlobalNinjaGenerator()->GetRulesFileStream();
148 }
149
150 const cmake* cmLocalNinjaGenerator::GetCMakeInstance() const
151 {
152   return this->GetGlobalGenerator()->GetCMakeInstance();
153 }
154
155 cmake* cmLocalNinjaGenerator::GetCMakeInstance()
156 {
157   return this->GetGlobalGenerator()->GetCMakeInstance();
158 }
159
160 bool cmLocalNinjaGenerator::isRootMakefile() const
161 {
162   return (strcmp(this->Makefile->GetCurrentDirectory(),
163                  this->GetCMakeInstance()->GetHomeDirectory()) == 0);
164 }
165
166 void cmLocalNinjaGenerator::WriteBuildFileTop()
167 {
168   // We do that only once for the top CMakeLists.txt file.
169   if(!this->isRootMakefile())
170     return;
171
172   // For the build file.
173   this->WriteProjectHeader(this->GetBuildFileStream());
174   this->WriteNinjaFilesInclusion(this->GetBuildFileStream());
175
176   // For the rule file.
177   this->WriteProjectHeader(this->GetRulesFileStream());
178 }
179
180 void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os)
181 {
182   cmGlobalNinjaGenerator::WriteDivider(os);
183   os
184     << "# Project: " << this->GetMakefile()->GetProjectName() << std::endl
185     << "# Configuration: " << this->ConfigName << std::endl
186     ;
187   cmGlobalNinjaGenerator::WriteDivider(os);
188 }
189
190 void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
191 {
192   cmGlobalNinjaGenerator::WriteDivider(os);
193   os
194     << "# Include auxiliary files.\n"
195     << "\n"
196     ;
197   cmGlobalNinjaGenerator::WriteInclude(os,
198                                       cmGlobalNinjaGenerator::NINJA_RULES_FILE,
199                                        "Include rules file.");
200   os << "\n";
201 }
202
203 void cmLocalNinjaGenerator::SetConfigName()
204 {
205   // Store the configuration name that will be generated.
206   if(const char* config =
207        this->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE"))
208     {
209     // Use the build type given by the user.
210     this->ConfigName = config;
211     }
212   else
213     {
214     // No configuration type given.
215     this->ConfigName = "";
216     }
217 }
218
219 void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
220 {
221   cmGlobalNinjaGenerator::WriteDivider(os);
222   os
223     << "# Write statements declared in CMakeLists.txt:" << std::endl
224     << "# " << this->Makefile->GetCurrentListFile() << std::endl
225     ;
226   if(this->isRootMakefile())
227     os << "# Which is the root file." << std::endl;
228   cmGlobalNinjaGenerator::WriteDivider(os);
229   os << std::endl;
230 }
231
232 std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const char *path)
233 {
234   std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT);
235 #ifdef _WIN32
236   cmSystemTools::ReplaceString(convPath, "/", "\\");
237 #endif
238   return convPath;
239 }
240
241 void
242 cmLocalNinjaGenerator
243 ::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
244 {
245   this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs);
246 }
247
248 void
249 cmLocalNinjaGenerator
250 ::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
251 {
252   this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs);
253 }
254
255 void cmLocalNinjaGenerator::AppendCustomCommandDeps(const cmCustomCommand *cc,
256                                                     cmNinjaDeps &ninjaDeps)
257 {
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) {
261     std::string dep;
262     if (this->GetRealDependency(i->c_str(), this->GetConfigName(), dep))
263       ninjaDeps.push_back(ConvertToNinjaPath(dep.c_str()));
264   }
265 }
266
267 std::string cmLocalNinjaGenerator::BuildCommandLine(
268                                     const std::vector<std::string> &cmdLines)
269 {
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())
274 #ifdef _WIN32
275     return "cd .";
276 #else
277     return ":";
278 #endif
279
280   cmOStringStream cmd;
281   for (std::vector<std::string>::const_iterator li = cmdLines.begin();
282        li != cmdLines.end(); ++li) {
283     if (li != cmdLines.begin()) {
284       cmd << " && ";
285 #ifdef _WIN32
286     } else if (cmdLines.size() > 1) {
287       cmd << "cmd.exe /c ";
288 #endif
289     }
290     cmd << *li;
291   }
292   return cmd.str();
293 }
294
295 void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc,
296                                             std::vector<std::string> &cmdLines)
297 {
298   cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile);
299   if (ccg.GetNumberOfCommands() > 0) {
300     const char* wd = cc->GetWorkingDirectory();
301     if (!wd)
302       wd = this->GetMakefile()->GetStartOutputDirectory();
303
304     cmOStringStream cdCmd;
305     cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL);
306     cmdLines.push_back(cdCmd.str());
307   }
308   for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) {
309     cmdLines.push_back(this->ConvertToOutputFormat(ccg.GetCommand(i).c_str(),
310                                                    SHELL));
311     std::string& cmd = cmdLines.back();
312     ccg.AppendArguments(i, cmd);
313   }
314 }
315
316 void
317 cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
318   cmCustomCommand const *cc, const cmNinjaDeps& orderOnlyDeps)
319 {
320   if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc))
321     return;
322
323   const std::vector<std::string> &outputs = cc->GetOutputs();
324   cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps;
325
326   std::transform(outputs.begin(), outputs.end(),
327                  ninjaOutputs.begin(), MapToNinjaPath());
328   this->AppendCustomCommandDeps(cc, ninjaDeps);
329
330   for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();
331        ++i)
332     this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(*i);
333
334   std::vector<std::string> cmdLines;
335   this->AppendCustomCommandLines(cc, cmdLines);
336
337   if (cmdLines.empty()) {
338     cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
339                                             "Phony custom command for " +
340                                               ninjaOutputs[0],
341                                             ninjaOutputs,
342                                             ninjaDeps,
343                                             cmNinjaDeps(),
344                                             orderOnlyDeps,
345                                             cmNinjaVars());
346   } else {
347     this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
348       this->BuildCommandLine(cmdLines),
349       this->ConstructComment(*cc),
350       "Custom command for " + ninjaOutputs[0],
351       ninjaOutputs,
352       ninjaDeps,
353       orderOnlyDeps);
354   }
355 }
356
357 void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc,
358                                                    cmTarget* target)
359 {
360   this->CustomCommandTargets[cc].insert(target);
361 }
362
363 void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
364 {
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.
373     //
374     // FIXME: This won't work in certain obscure scenarios involving indirect
375     // dependencies.
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());
381     ++j;
382
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;
391     }
392
393     this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps);
394   }
395 }