Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / gn / ninja_helper.cc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/gn/ninja_helper.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/string_utils.h"
11 #include "tools/gn/target.h"
12
13 namespace {
14
15 const char kLibDirWithSlash[] = "lib/";
16 const char kObjectDirNoSlash[] = "obj";
17
18 }  // namespace
19
20 NinjaHelper::NinjaHelper(const BuildSettings* build_settings)
21     : build_settings_(build_settings) {
22   build_to_src_no_last_slash_ = build_settings->build_to_source_dir_string();
23   if (!build_to_src_no_last_slash_.empty() &&
24       build_to_src_no_last_slash_[build_to_src_no_last_slash_.size() - 1] ==
25           '/')
26     build_to_src_no_last_slash_.resize(build_to_src_no_last_slash_.size() - 1);
27
28   build_to_src_system_no_last_slash_ = build_to_src_no_last_slash_;
29 }
30
31 NinjaHelper::~NinjaHelper() {
32 }
33
34 std::string NinjaHelper::GetTopleveOutputDir() const {
35   return kObjectDirNoSlash;
36 }
37
38 OutputFile NinjaHelper::GetTargetOutputDir(const Target* target) const {
39   OutputFile ret(target->settings()->toolchain_output_subdir());
40   ret.value().append(kObjectDirNoSlash);
41   AppendStringPiece(&ret.value(),
42                     target->label().dir().SourceAbsoluteWithOneSlash());
43   return ret;
44 }
45
46 OutputFile NinjaHelper::GetNinjaFileForTarget(const Target* target) const {
47   OutputFile ret = GetTargetOutputDir(target);
48   ret.value().append(target->label().name());
49   ret.value().append(".ninja");
50   return ret;
51 }
52
53 OutputFile NinjaHelper::GetNinjaFileForToolchain(
54     const Settings* settings) const {
55   OutputFile ret;
56   ret.value().append(settings->toolchain_output_subdir().value());
57   ret.value().append("toolchain.ninja");
58   return ret;
59 }
60
61 // In Python, GypPathToUniqueOutput does the qualification. The only case where
62 // the Python version doesn't qualify the name is for target outputs, which we
63 // handle in a separate function.
64 OutputFile NinjaHelper::GetOutputFileForSource(
65     const Target* target,
66     const SourceFile& source,
67     SourceFileType type) const {
68   // Extract the filename and remove the extension (keep the dot).
69   base::StringPiece filename = FindFilename(&source.value());
70   std::string name(filename.data(), filename.size());
71   size_t extension_offset = FindExtensionOffset(name);
72   CHECK(extension_offset != std::string::npos);
73   name.resize(extension_offset);
74
75   // Append the new extension.
76   switch (type) {
77     case SOURCE_ASM:
78     case SOURCE_C:
79     case SOURCE_CC:
80     case SOURCE_M:
81     case SOURCE_MM:
82     case SOURCE_S:
83       name.append(target->settings()->IsWin() ? "obj" : "o");
84       break;
85
86     case SOURCE_RC:
87       name.append("res");
88       break;
89
90     // Pass .o/.obj files through unchanged.
91     case SOURCE_O: {
92       // System-absolute file names get preserved (they don't need to be
93       // rebased relative to the build dir).
94       if (source.is_system_absolute())
95         return OutputFile(source.value());
96
97       // Files that are already inside the build dir should not be made
98       // relative to the source tree. Doing so will insert an unnecessary
99       // "../.." into the path which won't match the corresponding target
100       // name in ninja.
101       CHECK(build_settings_->build_dir().is_source_absolute());
102       CHECK(source.is_source_absolute());
103       if (StartsWithASCII(source.value(),
104                           build_settings_->build_dir().value(),
105                           true)) {
106         return OutputFile(
107             source.value().substr(
108                 build_settings_->build_dir().value().size()));
109       }
110
111       // Construct the relative location of the file from the build dir.
112       OutputFile ret(build_to_src_no_last_slash());
113       source.SourceAbsoluteWithOneSlash().AppendToString(&ret.value());
114       return ret;
115     }
116
117     case SOURCE_H:
118     case SOURCE_UNKNOWN:
119       NOTREACHED();
120       return OutputFile();
121   }
122
123   // Use the scheme <path>/<target>.<name>.<extension> so that all output
124   // names are unique to different targets.
125
126   // This will look like "obj" or "toolchain_name/obj".
127   OutputFile ret(target->settings()->toolchain_output_subdir());
128   ret.value().append(kObjectDirNoSlash);
129
130   // Find the directory, assume it starts with two slashes, and trim to one.
131   base::StringPiece dir = FindDir(&source.value());
132   CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/')
133       << "Source file isn't in the source repo: " << dir;
134   AppendStringPiece(&ret.value(), dir.substr(1));
135
136   ret.value().append(target->label().name());
137   ret.value().append(".");
138   ret.value().append(name);
139   return ret;
140 }
141
142 OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const {
143   OutputFile ret;
144
145   // Use the output name if given, fall back to target name if not.
146   const std::string& name = target->output_name().empty() ?
147       target->label().name() : target->output_name();
148
149   // This is prepended to the output file name. Some platforms get "lib"
150   // prepended to library names. but be careful not to make a duplicate (e.g.
151   // some targets like "libxml" already have the "lib" in the name).
152   const char* prefix;
153   if (!target->settings()->IsWin() &&
154       (target->output_type() == Target::SHARED_LIBRARY ||
155        target->output_type() == Target::STATIC_LIBRARY) &&
156       name.compare(0, 3, "lib") != 0)
157     prefix = "lib";
158   else
159     prefix = "";
160
161   const char* extension;
162   if (target->output_extension().empty()) {
163     if (target->output_type() == Target::GROUP ||
164         target->output_type() == Target::SOURCE_SET ||
165         target->output_type() == Target::COPY_FILES ||
166         target->output_type() == Target::ACTION ||
167         target->output_type() == Target::ACTION_FOREACH) {
168       extension = "stamp";
169     } else {
170       extension = GetExtensionForOutputType(target->output_type(),
171                                             target->settings()->target_os());
172     }
173   } else {
174     extension = target->output_extension().c_str();
175   }
176
177   // Everything goes into the toolchain directory (which will be empty for the
178   // default toolchain, and will end in a slash otherwise).
179   ret.value().append(target->settings()->toolchain_output_subdir().value());
180
181   // Binaries and loadable libraries go into the toolchain root.
182   if (target->output_type() == Target::EXECUTABLE ||
183       (target->settings()->IsMac() &&
184           (target->output_type() == Target::SHARED_LIBRARY ||
185            target->output_type() == Target::STATIC_LIBRARY)) ||
186       (target->settings()->IsWin() &&
187        target->output_type() == Target::SHARED_LIBRARY)) {
188     // Generate a name like "<toolchain>/<prefix><name>.<extension>".
189     ret.value().append(prefix);
190     ret.value().append(name);
191     if (extension[0]) {
192       ret.value().push_back('.');
193       ret.value().append(extension);
194     }
195     return ret;
196   }
197
198   // Libraries go into the library subdirectory like
199   // "<toolchain>/lib/<prefix><name>.<extension>".
200   if (target->output_type() == Target::SHARED_LIBRARY) {
201     ret.value().append(kLibDirWithSlash);
202     ret.value().append(prefix);
203     ret.value().append(name);
204     if (extension[0]) {
205       ret.value().push_back('.');
206       ret.value().append(extension);
207     }
208     return ret;
209   }
210
211   // Everything else goes next to the target's .ninja file like
212   // "<toolchain>/obj/<path>/<name>.<extension>".
213   ret.value().append(kObjectDirNoSlash);
214   AppendStringPiece(&ret.value(),
215                     target->label().dir().SourceAbsoluteWithOneSlash());
216   ret.value().append(prefix);
217   ret.value().append(name);
218   if (extension[0]) {
219     ret.value().push_back('.');
220     ret.value().append(extension);
221   }
222   return ret;
223 }
224
225 std::string NinjaHelper::GetRulePrefix(const Settings* settings) const {
226   // Don't prefix the default toolchain so it looks prettier, prefix everything
227   // else.
228   if (settings->is_default())
229     return std::string();  // Default toolchain has no prefix.
230   return settings->toolchain_label().name() + "_";
231 }
232
233 std::string NinjaHelper::GetRuleForSourceType(const Settings* settings,
234                                               SourceFileType type) const {
235   // This function may be hot since it will be called for every source file
236   // in the tree. We could cache the results to avoid making a string for
237   // every invocation.
238   std::string prefix = GetRulePrefix(settings);
239
240   if (type == SOURCE_C)
241     return prefix + "cc";
242   if (type == SOURCE_CC)
243     return prefix + "cxx";
244   if (type == SOURCE_M)
245     return prefix + "objc";
246   if (type == SOURCE_MM)
247     return prefix + "objcxx";
248   if (type == SOURCE_RC)
249     return prefix + "rc";
250   if (type == SOURCE_S)
251     return prefix + "cc";  // Assembly files just get compiled by CC.
252
253   // TODO(brettw) asm files.
254
255   // .obj files have no rules to make them (they're already built) so we return
256   // the enpty string for SOURCE_O.
257   return std::string();
258 }