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 "tools/gn/filesystem_utils.h"
9 #include "tools/gn/string_utils.h"
10 #include "tools/gn/target.h"
14 const char kLibDirWithSlash
[] = "lib/";
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_
;
28 ConvertPathToSystem(&build_to_src_system_no_last_slash_
);
31 NinjaHelper::~NinjaHelper() {
34 std::string
NinjaHelper::GetTopleveOutputDir() const {
35 return kObjectDirNoSlash
;
38 std::string
NinjaHelper::GetTargetOutputDir(const Target
* target
) const {
39 return kObjectDirNoSlash
+ target
->label().dir().SourceAbsoluteWithOneSlash();
42 OutputFile
NinjaHelper::GetNinjaFileForTarget(const Target
* target
) const {
43 OutputFile
ret(target
->settings()->toolchain_output_subdir());
44 ret
.value().append(kObjectDirNoSlash
);
45 AppendStringPiece(&ret
.value(),
46 target
->label().dir().SourceAbsoluteWithOneSlash());
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");
95 // Use the scheme <path>/<target>.<name>.<extension> so that all output
96 // names are unique to different targets.
98 // This will look like "obj" or "toolchain_name/obj".
99 OutputFile
ret(target
->settings()->toolchain_output_subdir());
100 ret
.value().append(kObjectDirNoSlash
);
102 // Find the directory, assume it starts with two slashes, and trim to one.
103 base::StringPiece dir
= FindDir(&source
.value());
104 CHECK(dir
.size() >= 2 && dir
[0] == '/' && dir
[1] == '/')
105 << "Source file isn't in the source repo: " << dir
;
106 AppendStringPiece(&ret
.value(), dir
.substr(1));
108 ret
.value().append(target
->label().name());
109 ret
.value().append(".");
110 ret
.value().append(name
);
114 OutputFile
NinjaHelper::GetTargetOutputFile(const Target
* target
) const {
117 // Use the output name if given, fall back to target name if not.
118 const std::string
& name
= target
->output_name().empty() ?
119 target
->label().name() : target
->output_name();
121 // This is prepended to the output file name. Some platforms get "lib"
122 // prepended to library names. but be careful not to make a duplicate (e.g.
123 // some targets like "libxml" already have the "lib" in the name).
125 if (!target
->settings()->IsWin() &&
126 (target
->output_type() == Target::SHARED_LIBRARY
||
127 target
->output_type() == Target::STATIC_LIBRARY
) &&
128 name
.compare(0, 3, "lib") != 0)
133 const char* extension
;
134 if (target
->output_type() == Target::GROUP
||
135 target
->output_type() == Target::SOURCE_SET
||
136 target
->output_type() == Target::COPY_FILES
||
137 target
->output_type() == Target::CUSTOM
) {
140 extension
= GetExtensionForOutputType(target
->output_type(),
141 target
->settings()->target_os());
144 // Everything goes into the toolchain directory (which will be empty for the
145 // default toolchain, and will end in a slash otherwise).
146 ret
.value().append(target
->settings()->toolchain_output_subdir().value());
148 // Binaries and loadable libraries go into the toolchain root.
149 if (target
->output_type() == Target::EXECUTABLE
||
150 (target
->settings()->IsMac() &&
151 (target
->output_type() == Target::SHARED_LIBRARY
||
152 target
->output_type() == Target::STATIC_LIBRARY
)) ||
153 (target
->settings()->IsWin() &&
154 target
->output_type() == Target::SHARED_LIBRARY
)) {
155 // Generate a name like "<toolchain>/<prefix><name>.<extension>".
156 ret
.value().append(prefix
);
157 ret
.value().append(name
);
159 ret
.value().push_back('.');
160 ret
.value().append(extension
);
165 // Libraries go into the library subdirectory like
166 // "<toolchain>/lib/<prefix><name>.<extension>".
167 if (target
->output_type() == Target::SHARED_LIBRARY
) {
168 ret
.value().append(kLibDirWithSlash
);
169 ret
.value().append(prefix
);
170 ret
.value().append(name
);
172 ret
.value().push_back('.');
173 ret
.value().append(extension
);
178 // Everything else goes next to the target's .ninja file like
179 // "<toolchain>/obj/<path>/<name>.<extension>".
180 ret
.value().append(kObjectDirNoSlash
);
181 AppendStringPiece(&ret
.value(),
182 target
->label().dir().SourceAbsoluteWithOneSlash());
183 ret
.value().append(prefix
);
184 ret
.value().append(name
);
186 ret
.value().push_back('.');
187 ret
.value().append(extension
);
192 std::string
NinjaHelper::GetRulePrefix(const Settings
* settings
) const {
193 // Don't prefix the default toolchain so it looks prettier, prefix everything
195 if (settings
->is_default())
196 return std::string(); // Default toolchain has no prefix.
197 return settings
->toolchain_label().name() + "_";
200 std::string
NinjaHelper::GetRuleForSourceType(const Settings
* settings
,
201 SourceFileType type
) const {
202 // This function may be hot since it will be called for every source file
203 // in the tree. We could cache the results to avoid making a string for
205 std::string prefix
= GetRulePrefix(settings
);
207 if (type
== SOURCE_C
)
208 return prefix
+ "cc";
209 if (type
== SOURCE_CC
)
210 return prefix
+ "cxx";
212 // TODO(brettw) asm files.
214 if (settings
->IsMac()) {
215 if (type
== SOURCE_M
)
216 return prefix
+ "objc";
217 if (type
== SOURCE_MM
)
218 return prefix
+ "objcxx";
221 if (settings
->IsWin()) {
222 if (type
== SOURCE_RC
)
223 return prefix
+ "rc";
225 if (type
== SOURCE_S
)
226 return prefix
+ "cc"; // Assembly files just get compiled by CC.
229 return std::string();