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 kLibDirWithSlash
[] = "lib/";
16 const char kObjectDirNoSlash
[] = "obj";
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] ==
26 build_to_src_no_last_slash_
.resize(build_to_src_no_last_slash_
.size() - 1);
28 build_to_src_system_no_last_slash_
= build_to_src_no_last_slash_
;
31 NinjaHelper::~NinjaHelper() {
34 std::string
NinjaHelper::GetTopleveOutputDir() const {
35 return kObjectDirNoSlash
;
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());
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");
53 OutputFile
NinjaHelper::GetNinjaFileForToolchain(
54 const Settings
* settings
) const {
56 ret
.value().append(settings
->toolchain_output_subdir().value());
57 ret
.value().append("toolchain.ninja");
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(
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
);
75 // Append the new extension.
83 name
.append(target
->settings()->IsWin() ? "obj" : "o");
90 // Pass .o/.obj files through unchanged.
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());
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
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(),
107 source
.value().substr(
108 build_settings_
->build_dir().value().size()));
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());
123 // Use the scheme <path>/<target>.<name>.<extension> so that all output
124 // names are unique to different targets.
126 // This will look like "obj" or "toolchain_name/obj".
127 OutputFile
ret(target
->settings()->toolchain_output_subdir());
128 ret
.value().append(kObjectDirNoSlash
);
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));
136 ret
.value().append(target
->label().name());
137 ret
.value().append(".");
138 ret
.value().append(name
);
142 OutputFile
NinjaHelper::GetTargetOutputFile(const Target
* target
) const {
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();
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).
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)
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
) {
170 extension
= GetExtensionForOutputType(target
->output_type(),
171 target
->settings()->target_os());
174 extension
= target
->output_extension().c_str();
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());
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
);
192 ret
.value().push_back('.');
193 ret
.value().append(extension
);
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
);
205 ret
.value().push_back('.');
206 ret
.value().append(extension
);
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
);
219 ret
.value().push_back('.');
220 ret
.value().append(extension
);
225 std::string
NinjaHelper::GetRulePrefix(const Settings
* settings
) const {
226 // Don't prefix the default toolchain so it looks prettier, prefix everything
228 if (settings
->is_default())
229 return std::string(); // Default toolchain has no prefix.
230 return settings
->toolchain_label().name() + "_";
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
238 std::string prefix
= GetRulePrefix(settings
);
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.
253 // TODO(brettw) asm files.
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();