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.
5 #include "tools/gn/ninja_helper.h"
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"
15 const char kObjectDirNoSlash[] = "obj";
19 NinjaHelper::NinjaHelper(const BuildSettings* build_settings)
20 : build_settings_(build_settings) {
21 build_to_src_no_last_slash_ = build_settings->build_to_source_dir_string();
22 if (!build_to_src_no_last_slash_.empty() &&
23 build_to_src_no_last_slash_[build_to_src_no_last_slash_.size() - 1] ==
25 build_to_src_no_last_slash_.resize(build_to_src_no_last_slash_.size() - 1);
27 build_to_src_system_no_last_slash_ = build_to_src_no_last_slash_;
30 NinjaHelper::~NinjaHelper() {
33 std::string NinjaHelper::GetTopleveOutputDir() const {
34 return kObjectDirNoSlash;
37 OutputFile NinjaHelper::GetTargetOutputDir(const Target* target) const {
38 OutputFile ret(target->settings()->toolchain_output_subdir());
39 ret.value().append(kObjectDirNoSlash);
40 AppendStringPiece(&ret.value(),
41 target->label().dir().SourceAbsoluteWithOneSlash());
45 OutputFile NinjaHelper::GetNinjaFileForTarget(const Target* target) const {
46 OutputFile ret = GetTargetOutputDir(target);
47 ret.value().append(target->label().name());
48 ret.value().append(".ninja");
52 OutputFile NinjaHelper::GetNinjaFileForToolchain(
53 const Settings* settings) const {
55 ret.value().append(settings->toolchain_output_subdir().value());
56 ret.value().append("toolchain.ninja");
60 // In Python, GypPathToUniqueOutput does the qualification. The only case where
61 // the Python version doesn't qualify the name is for target outputs, which we
62 // handle in a separate function.
63 OutputFile NinjaHelper::GetOutputFileForSource(
65 const SourceFile& source,
66 SourceFileType type) const {
67 // Extract the filename and remove the extension (keep the dot).
68 base::StringPiece filename = FindFilename(&source.value());
69 std::string name(filename.data(), filename.size());
70 size_t extension_offset = FindExtensionOffset(name);
71 CHECK(extension_offset != std::string::npos);
72 name.resize(extension_offset);
74 // Append the new extension.
82 name.append(target->settings()->IsWin() ? "obj" : "o");
89 // Pass .o/.obj files through unchanged.
91 // System-absolute file names get preserved (they don't need to be
92 // rebased relative to the build dir).
93 if (source.is_system_absolute())
94 return OutputFile(source.value());
96 // Files that are already inside the build dir should not be made
97 // relative to the source tree. Doing so will insert an unnecessary
98 // "../.." into the path which won't match the corresponding target
100 CHECK(build_settings_->build_dir().is_source_absolute());
101 CHECK(source.is_source_absolute());
102 if (StartsWithASCII(source.value(),
103 build_settings_->build_dir().value(),
106 source.value().substr(
107 build_settings_->build_dir().value().size()));
110 // Construct the relative location of the file from the build dir.
111 OutputFile ret(build_to_src_no_last_slash());
112 source.SourceAbsoluteWithOneSlash().AppendToString(&ret.value());
122 // Use the scheme <path>/<target>.<name>.<extension> so that all output
123 // names are unique to different targets.
125 // This will look like "obj" or "toolchain_name/obj".
126 OutputFile ret(target->settings()->toolchain_output_subdir());
127 ret.value().append(kObjectDirNoSlash);
129 // Find the directory, assume it starts with two slashes, and trim to one.
130 base::StringPiece dir = FindDir(&source.value());
131 CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/')
132 << "Source file isn't in the source repo: " << dir;
133 AppendStringPiece(&ret.value(), dir.substr(1));
135 ret.value().append(target->label().name());
136 ret.value().append(".");
137 ret.value().append(name);
141 OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const {
144 // Use the output name if given, fall back to target name if not.
145 const std::string& name = target->output_name().empty() ?
146 target->label().name() : target->output_name();
148 // This is prepended to the output file name. Some platforms get "lib"
149 // prepended to library names. but be careful not to make a duplicate (e.g.
150 // some targets like "libxml" already have the "lib" in the name).
152 if (!target->settings()->IsWin() &&
153 (target->output_type() == Target::SHARED_LIBRARY ||
154 target->output_type() == Target::STATIC_LIBRARY) &&
155 name.compare(0, 3, "lib") != 0)
160 const char* extension;
161 if (target->output_extension().empty()) {
162 if (target->output_type() == Target::GROUP ||
163 target->output_type() == Target::SOURCE_SET ||
164 target->output_type() == Target::COPY_FILES ||
165 target->output_type() == Target::ACTION ||
166 target->output_type() == Target::ACTION_FOREACH) {
169 extension = GetExtensionForOutputType(target->output_type(),
170 target->settings()->target_os());
173 extension = target->output_extension().c_str();
176 // Everything goes into the toolchain directory (which will be empty for the
177 // default toolchain, and will end in a slash otherwise).
178 ret.value().append(target->settings()->toolchain_output_subdir().value());
180 // Binaries and shared libraries go into the toolchain root.
181 if (target->output_type() == Target::EXECUTABLE ||
182 target->output_type() == Target::SHARED_LIBRARY) {
183 // Generate a name like "<toolchain>/<prefix><name>.<extension>".
184 ret.value().append(prefix);
185 ret.value().append(name);
187 ret.value().push_back('.');
188 ret.value().append(extension);
193 // Everything else goes next to the target's .ninja file like
194 // "<toolchain>/obj/<path>/<name>.<extension>".
195 ret.value().append(kObjectDirNoSlash);
196 AppendStringPiece(&ret.value(),
197 target->label().dir().SourceAbsoluteWithOneSlash());
198 ret.value().append(prefix);
199 ret.value().append(name);
201 ret.value().push_back('.');
202 ret.value().append(extension);
207 std::string NinjaHelper::GetRulePrefix(const Settings* settings) const {
208 // Don't prefix the default toolchain so it looks prettier, prefix everything
210 if (settings->is_default())
211 return std::string(); // Default toolchain has no prefix.
212 return settings->toolchain_label().name() + "_";
215 std::string NinjaHelper::GetRuleForSourceType(const Settings* settings,
216 SourceFileType type) const {
217 // This function may be hot since it will be called for every source file
218 // in the tree. We could cache the results to avoid making a string for
220 std::string prefix = GetRulePrefix(settings);
222 if (type == SOURCE_C)
223 return prefix + "cc";
224 if (type == SOURCE_CC)
225 return prefix + "cxx";
226 if (type == SOURCE_M)
227 return prefix + "objc";
228 if (type == SOURCE_MM)
229 return prefix + "objcxx";
230 if (type == SOURCE_RC)
231 return prefix + "rc";
232 if (type == SOURCE_S)
233 return prefix + "cc"; // Assembly files just get compiled by CC.
235 // TODO(brettw) asm files.
237 // .obj files have no rules to make them (they're already built) so we return
238 // the enpty string for SOURCE_O.
239 return std::string();