OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/gn/ninja_binary_target_writer.h" | 5 #include "tools/gn/ninja_binary_target_writer.h" |
6 | 6 |
7 #include <cstring> | |
7 #include <set> | 8 #include <set> |
8 #include <sstream> | 9 #include <sstream> |
9 | 10 |
10 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
11 #include "tools/gn/config_values_extractors.h" | 12 #include "tools/gn/config_values_extractors.h" |
12 #include "tools/gn/deps_iterator.h" | 13 #include "tools/gn/deps_iterator.h" |
13 #include "tools/gn/err.h" | 14 #include "tools/gn/err.h" |
14 #include "tools/gn/escape.h" | 15 #include "tools/gn/escape.h" |
16 #include "tools/gn/filesystem_utils.h" | |
15 #include "tools/gn/ninja_utils.h" | 17 #include "tools/gn/ninja_utils.h" |
16 #include "tools/gn/settings.h" | 18 #include "tools/gn/settings.h" |
19 #include "tools/gn/source_file_type.h" | |
17 #include "tools/gn/string_utils.h" | 20 #include "tools/gn/string_utils.h" |
18 #include "tools/gn/substitution_writer.h" | 21 #include "tools/gn/substitution_writer.h" |
19 #include "tools/gn/target.h" | 22 #include "tools/gn/target.h" |
20 | 23 |
21 namespace { | 24 namespace { |
22 | 25 |
23 // Returns the proper escape options for writing compiler and linker flags. | 26 // Returns the proper escape options for writing compiler and linker flags. |
24 EscapeOptions GetFlagOptions() { | 27 EscapeOptions GetFlagOptions() { |
25 EscapeOptions opts; | 28 EscapeOptions opts; |
26 opts.mode = ESCAPE_NINJA_COMMAND; | 29 opts.mode = ESCAPE_NINJA_COMMAND; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
58 const std::string& path = path_out.str(); | 61 const std::string& path = path_out.str(); |
59 if (path[0] == '"') | 62 if (path[0] == '"') |
60 out << " \"-I" << path.substr(1); | 63 out << " \"-I" << path.substr(1); |
61 else | 64 else |
62 out << " -I" << path; | 65 out << " -I" << path; |
63 } | 66 } |
64 | 67 |
65 PathOutput& path_output_; | 68 PathOutput& path_output_; |
66 }; | 69 }; |
67 | 70 |
71 // Returns the language-specific prefix/suffix for precomiled header files. | |
72 const char* GetPCHLangForFlagType(SubstitutionType type) { | |
73 switch (type) { | |
74 case SUBSTITUTION_CFLAGS_C: | |
75 return "c"; | |
76 case SUBSTITUTION_CFLAGS_CC: | |
77 return "cc"; | |
78 case SUBSTITUTION_CFLAGS_OBJC: | |
79 return "m"; | |
80 case SUBSTITUTION_CFLAGS_OBJCC: | |
81 return "mm"; | |
82 default: | |
83 NOTREACHED() << "Not a valid language type"; | |
84 return ""; | |
85 } | |
86 } | |
87 | |
68 } // namespace | 88 } // namespace |
69 | 89 |
90 // Represents a set of tool types. | |
91 class NinjaBinaryTargetWriter::SourceFileTypeSet { | |
92 public: | |
93 SourceFileTypeSet() { | |
94 memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES)); | |
95 } | |
96 | |
97 void Set(SourceFileType type) { | |
98 flags_[static_cast<int>(type)] = true; | |
99 } | |
100 bool Get(SourceFileType type) const { | |
101 return flags_[static_cast<int>(type)]; | |
102 } | |
103 | |
104 private: | |
105 bool flags_[static_cast<int>(SOURCE_NUMTYPES)]; | |
106 }; | |
107 | |
70 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, | 108 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, |
71 std::ostream& out) | 109 std::ostream& out) |
72 : NinjaTargetWriter(target, out), | 110 : NinjaTargetWriter(target, out), |
73 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) { | 111 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)), |
112 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) { | |
74 } | 113 } |
75 | 114 |
76 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { | 115 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { |
77 } | 116 } |
78 | 117 |
79 void NinjaBinaryTargetWriter::Run() { | 118 void NinjaBinaryTargetWriter::Run() { |
80 WriteCompilerVars(); | 119 // Figure out what source types are needed. |
120 SourceFileTypeSet used_types; | |
121 for (const auto& source : target_->sources()) | |
122 used_types.Set(GetSourceFileType(source)); | |
81 | 123 |
124 WriteCompilerVars(used_types); | |
125 | |
126 // The input dependencies will be an order-only dependency. This will cause | |
127 // Ninja to make sure the inputs are up-to-date before compiling this source, | |
128 // but changes in the inputs deps won't cause the file to be recompiled. | |
129 // | |
130 // This is important to prevent changes in unrelated actions that are | |
131 // upstream of this target from causing everything to be recompiled | |
132 // | |
133 // Why can we get away with this rather than using implicit deps ("|", which | |
134 // will force rebuilds when the inputs change)? For source code, the | |
135 // computed dependencies of all headers will be computed by the compiler, | |
136 // which will cause source rebuilds if any "real" upstream dependencies | |
137 // change. | |
138 // | |
139 // If a .cc file is generated by an input dependency, Ninja will see the | |
140 // input to the build rule doesn't exist, and that it is an output from a | |
141 // previous step, and build the previous step first. This is a "real" | |
142 // dependency and doesn't need | or || to express. | |
143 // | |
144 // The only case where this rule matters is for the first build where no .d | |
145 // files exist, and Ninja doesn't know what that source file depends on. In | |
146 // this case it's sufficient to ensure that the upstream dependencies are | |
147 // built first. This is exactly what Ninja's order-only dependencies | |
148 // expresses. | |
149 OutputFile order_only_dep = | |
150 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); | |
151 | |
152 std::vector<OutputFile> pch_obj_files; | |
153 WritePrecompiledHeaderCommands(used_types, order_only_dep, &pch_obj_files); | |
154 | |
155 // Treat all precompiled object files as explicit dependencies of all | |
156 // compiles. Some notes: | |
157 // | |
158 // - Technically only the language-specific one is required for any specific | |
159 // compile, but that's more difficult to express and the additional logic | |
160 // doesn't buy much reduced parallelism. Just list them all (there's | |
161 // usually only one anyway). | |
162 // | |
163 // - Technically the .pch file is the input to the compile, not the | |
164 // precompiled header's corresponding object file that we're using here. | |
165 // But Ninja's depslog doesn't support multiple outputs from the | |
166 // precompiled header compile step (it outputs both the .pch file and a | |
167 // corresponding .obj file). So we consistently list the .obj file and the | |
168 // .pch file we really need comes along with it. | |
82 std::vector<OutputFile> obj_files; | 169 std::vector<OutputFile> obj_files; |
83 std::vector<SourceFile> other_files; | 170 std::vector<SourceFile> other_files; |
84 WriteSources(&obj_files, &other_files); | 171 WriteSources(pch_obj_files, order_only_dep, &obj_files, &other_files); |
85 | 172 |
86 if (target_->output_type() == Target::SOURCE_SET) | 173 // Also link all pch object files. |
174 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); | |
175 | |
176 if (target_->output_type() == Target::SOURCE_SET) { | |
87 WriteSourceSetStamp(obj_files); | 177 WriteSourceSetStamp(obj_files); |
88 else | 178 #ifndef NDEBUG |
179 // Verify that the function that separately computes a source set's object | |
180 // files match the object files just computed. | |
181 UniqueVector<OutputFile> computed_obj; | |
182 AddSourceSetObjectFiles(target_, &computed_obj); | |
183 DCHECK_EQ(obj_files.size(), computed_obj.size()); | |
184 for (const auto& obj : obj_files) | |
185 DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj)); | |
186 #endif | |
187 } else { | |
89 WriteLinkerStuff(obj_files, other_files); | 188 WriteLinkerStuff(obj_files, other_files); |
189 } | |
90 } | 190 } |
91 | 191 |
92 void NinjaBinaryTargetWriter::WriteCompilerVars() { | 192 void NinjaBinaryTargetWriter::WriteCompilerVars( |
193 const SourceFileTypeSet& used_types) { | |
93 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); | 194 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); |
94 | 195 |
95 // Defines. | 196 // Defines. |
96 if (subst.used[SUBSTITUTION_DEFINES]) { | 197 if (subst.used[SUBSTITUTION_DEFINES]) { |
97 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; | 198 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; |
98 RecursiveTargetConfigToStream<std::string>( | 199 RecursiveTargetConfigToStream<std::string>( |
99 target_, &ConfigValues::defines, DefineWriter(), out_); | 200 target_, &ConfigValues::defines, DefineWriter(), out_); |
100 out_ << std::endl; | 201 out_ << std::endl; |
101 } | 202 } |
102 | 203 |
103 // Include directories. | 204 // Include directories. |
104 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { | 205 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { |
105 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; | 206 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; |
106 PathOutput include_path_output( | 207 PathOutput include_path_output( |
107 path_output_.current_dir(), | 208 path_output_.current_dir(), |
108 settings_->build_settings()->root_path_utf8(), | 209 settings_->build_settings()->root_path_utf8(), |
109 ESCAPE_NINJA_COMMAND); | 210 ESCAPE_NINJA_COMMAND); |
110 RecursiveTargetConfigToStream<SourceDir>( | 211 RecursiveTargetConfigToStream<SourceDir>( |
111 target_, &ConfigValues::include_dirs, | 212 target_, &ConfigValues::include_dirs, |
112 IncludeWriter(include_path_output), out_); | 213 IncludeWriter(include_path_output), out_); |
113 out_ << std::endl; | 214 out_ << std::endl; |
114 } | 215 } |
115 | 216 |
116 // C flags and friends. | 217 // Some toolchains pass cflags to the assembler since it's the same command, |
117 EscapeOptions flag_escape_options = GetFlagOptions(); | 218 // and cflags_c might also be sent to the objective C compiler. |
118 #define WRITE_FLAGS(name, subst_enum) \ | 219 // |
119 if (subst.used[subst_enum]) { \ | 220 // TODO(brettw) remove the SOURCE_M from the CFLAGS_C writing once the Chrome |
120 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \ | 221 // Mac build is updated not to pass cflags_c to .m files. |
121 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \ | 222 EscapeOptions opts = GetFlagOptions(); |
122 flag_escape_options, out_); \ | 223 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CC) || |
123 out_ << std::endl; \ | 224 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) || |
124 } | 225 used_types.Get(SOURCE_ASM)) |
125 | 226 WriteOneFlag(SUBSTITUTION_CFLAGS, &ConfigValues::cflags, opts); |
126 WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS) | 227 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_M) || |
127 WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C) | 228 used_types.Get(SOURCE_ASM)) |
128 WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC) | 229 WriteOneFlag(SUBSTITUTION_CFLAGS_C, &ConfigValues::cflags_c, opts); |
129 WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC) | 230 if (used_types.Get(SOURCE_CC)) |
130 WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC) | 231 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, &ConfigValues::cflags_cc, opts); |
131 | 232 if (used_types.Get(SOURCE_M)) |
132 #undef WRITE_FLAGS | 233 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, &ConfigValues::cflags_objc, opts); |
234 if (used_types.Get(SOURCE_MM)) | |
235 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJCC, &ConfigValues::cflags_objcc, opts); | |
133 | 236 |
134 WriteSharedVars(subst); | 237 WriteSharedVars(subst); |
135 } | 238 } |
136 | 239 |
240 void NinjaBinaryTargetWriter::WriteOneFlag( | |
241 SubstitutionType subst_enum, | |
242 const std::vector<std::string>& (ConfigValues::* getter)() const, | |
243 EscapeOptions flag_escape_options) { | |
244 if (!target_->toolchain()->substitution_bits().used[subst_enum]) | |
245 return; | |
246 | |
247 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; | |
248 | |
249 // Write precompiled header flags. | |
250 if (target_->config_values().has_precompiled_headers() && | |
251 (subst_enum == SUBSTITUTION_CFLAGS_C || | |
252 subst_enum == SUBSTITUTION_CFLAGS_CC || | |
253 subst_enum == SUBSTITUTION_CFLAGS_OBJC || | |
254 subst_enum == SUBSTITUTION_CFLAGS_OBJCC)) { | |
255 // Name the .pch file. | |
256 out_ << " /Fp"; | |
257 path_output_.WriteFile(out_, GetWindowsPCHFile(subst_enum)); | |
258 | |
259 // Enables precompiled headers and names the .h file. It's a string rather | |
260 // than a file name that needs rebasing. | |
261 out_ << " /Yu" << target_->config_values().precompiled_header(); | |
262 } | |
263 | |
264 RecursiveTargetConfigStringsToStream(target_, getter, | |
265 flag_escape_options, out_); | |
266 out_ << std::endl; | |
267 } | |
268 | |
269 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommands( | |
270 const SourceFileTypeSet& used_types, | |
271 const OutputFile& order_only_dep, | |
272 std::vector<OutputFile>* object_files) { | |
273 if (!target_->config_values().has_precompiled_headers()) | |
274 return; | |
275 | |
276 if (used_types.Get(SOURCE_C)) { | |
277 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_C, | |
278 Toolchain::TYPE_CC, | |
279 order_only_dep, object_files); | |
280 } | |
281 if (used_types.Get(SOURCE_CC)) { | |
282 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_CC, | |
283 Toolchain::TYPE_CXX, | |
284 order_only_dep, object_files); | |
285 } | |
286 if (used_types.Get(SOURCE_M)) { | |
287 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_OBJC, | |
288 Toolchain::TYPE_OBJC, | |
289 order_only_dep, object_files); | |
290 } | |
291 if (used_types.Get(SOURCE_MM)) { | |
292 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_OBJCC, | |
293 Toolchain::TYPE_OBJCXX, | |
294 order_only_dep, object_files); | |
295 } | |
296 } | |
297 | |
298 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommand( | |
299 SubstitutionType flag_type, | |
300 Toolchain::ToolType tool_type, | |
301 const OutputFile& order_only_dep, | |
302 std::vector<OutputFile>* object_files) { | |
303 // Compute the object file (it will be language-specific). | |
304 std::vector<OutputFile> outputs; | |
305 GetWindowsPCHObjectFiles(target_, flag_type, tool_type, &outputs); | |
306 if (outputs.empty()) | |
307 return; | |
308 object_files->insert(object_files->end(), outputs.begin(), outputs.end()); | |
309 | |
310 // Build line to compile the file. | |
311 WriteCompilerBuildLine(target_->config_values().precompiled_source(), | |
312 std::vector<OutputFile>(), order_only_dep, tool_type, | |
313 outputs); | |
314 | |
315 // This build line needs a custom language-specific flags value. It needs to | |
316 // include the switch to generate the .pch file in addition to the normal | |
317 // ones. Rule-specific variables are just indented underneath the rule line, | |
318 // and this defines the new one in terms of the old value. | |
319 out_ << " " << kSubstitutionNinjaNames[flag_type] << " ="; | |
320 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}"; | |
321 | |
322 // Append the command to generate the .pch file. | |
323 out_ << " /Yc" << target_->config_values().precompiled_header(); | |
scottmg
2015/06/26 22:30:13
Don't you need /Fp here somewhere?
brettw
2015/06/29 21:36:10
No because this line will look like:
cflags_cc =
| |
324 out_ << std::endl; | |
325 } | |
326 | |
137 void NinjaBinaryTargetWriter::WriteSources( | 327 void NinjaBinaryTargetWriter::WriteSources( |
328 const std::vector<OutputFile>& extra_deps, | |
329 const OutputFile& order_only_dep, | |
138 std::vector<OutputFile>* object_files, | 330 std::vector<OutputFile>* object_files, |
139 std::vector<SourceFile>* other_files) { | 331 std::vector<SourceFile>* other_files) { |
140 object_files->reserve(target_->sources().size()); | 332 object_files->reserve(object_files->size() + target_->sources().size()); |
141 | |
142 OutputFile input_dep = | |
143 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); | |
144 | |
145 std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_); | |
146 | 333 |
147 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. | 334 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. |
148 for (const auto& source : target_->sources()) { | 335 for (const auto& source : target_->sources()) { |
149 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | 336 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; |
150 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { | 337 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { |
151 if (GetSourceFileType(source) == SOURCE_DEF) | 338 if (GetSourceFileType(source) == SOURCE_DEF) |
152 other_files->push_back(source); | 339 other_files->push_back(source); |
153 continue; // No output for this source. | 340 continue; // No output for this source. |
154 } | 341 } |
155 | 342 |
156 if (tool_type != Toolchain::TYPE_NONE) { | 343 if (tool_type != Toolchain::TYPE_NONE) { |
157 out_ << "build"; | 344 WriteCompilerBuildLine(source, extra_deps, order_only_dep, tool_type, |
158 path_output_.WriteFiles(out_, tool_outputs); | 345 tool_outputs); |
159 | |
160 out_ << ": " << rule_prefix << Toolchain::ToolTypeToName(tool_type); | |
161 out_ << " "; | |
162 path_output_.WriteFile(out_, source); | |
163 if (!input_dep.value().empty()) { | |
164 // Write out the input dependencies as an order-only dependency. This | |
165 // will cause Ninja to make sure the inputs are up-to-date before | |
166 // compiling this source, but changes in the inputs deps won't cause | |
167 // the file to be recompiled. | |
168 // | |
169 // This is important to prevent changes in unrelated actions that | |
170 // are upstream of this target from causing everything to be recompiled. | |
171 // | |
172 // Why can we get away with this rather than using implicit deps ("|", | |
173 // which will force rebuilds when the inputs change)? For source code, | |
174 // the computed dependencies of all headers will be computed by the | |
175 // compiler, which will cause source rebuilds if any "real" upstream | |
176 // dependencies change. | |
177 // | |
178 // If a .cc file is generated by an input dependency, Ninja will see | |
179 // the input to the build rule doesn't exist, and that it is an output | |
180 // from a previous step, and build the previous step first. This is a | |
181 // "real" dependency and doesn't need | or || to express. | |
182 // | |
183 // The only case where this rule matters is for the first build where | |
184 // no .d files exist, and Ninja doesn't know what that source file | |
185 // depends on. In this case it's sufficient to ensure that the upstream | |
186 // dependencies are built first. This is exactly what Ninja's order- | |
187 // only dependencies expresses. | |
188 out_ << " || "; | |
189 path_output_.WriteFile(out_, input_dep); | |
190 } | |
191 out_ << std::endl; | |
192 } | 346 } |
193 | 347 |
194 // It's theoretically possible for a compiler to produce more than one | 348 // It's theoretically possible for a compiler to produce more than one |
195 // output, but we'll only link to the first output. | 349 // output, but we'll only link to the first output. |
196 object_files->push_back(tool_outputs[0]); | 350 object_files->push_back(tool_outputs[0]); |
197 } | 351 } |
198 out_ << std::endl; | 352 out_ << std::endl; |
199 } | 353 } |
200 | 354 |
355 void NinjaBinaryTargetWriter::WriteCompilerBuildLine( | |
356 const SourceFile& source, | |
357 const std::vector<OutputFile>& extra_deps, | |
358 const OutputFile& order_only_dep, | |
359 Toolchain::ToolType tool_type, | |
360 const std::vector<OutputFile>& outputs) { | |
361 out_ << "build"; | |
362 path_output_.WriteFiles(out_, outputs); | |
363 | |
364 out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type); | |
365 out_ << " "; | |
366 path_output_.WriteFile(out_, source); | |
367 | |
368 if (!extra_deps.empty()) { | |
369 out_ << " |"; | |
370 for (const OutputFile& dep : extra_deps) { | |
371 out_ << " "; | |
372 path_output_.WriteFile(out_, dep); | |
373 } | |
374 } | |
375 | |
376 if (!order_only_dep.value().empty()) { | |
377 out_ << " || "; | |
378 path_output_.WriteFile(out_, order_only_dep); | |
379 } | |
380 out_ << std::endl; | |
381 } | |
382 | |
201 void NinjaBinaryTargetWriter::WriteLinkerStuff( | 383 void NinjaBinaryTargetWriter::WriteLinkerStuff( |
202 const std::vector<OutputFile>& object_files, | 384 const std::vector<OutputFile>& object_files, |
203 const std::vector<SourceFile>& other_files) { | 385 const std::vector<SourceFile>& other_files) { |
204 std::vector<OutputFile> output_files; | 386 std::vector<OutputFile> output_files; |
205 SubstitutionWriter::ApplyListToLinkerAsOutputFile( | 387 SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
206 target_, tool_, tool_->outputs(), &output_files); | 388 target_, tool_, tool_->outputs(), &output_files); |
207 | 389 |
208 out_ << "build"; | 390 out_ << "build"; |
209 path_output_.WriteFiles(out_, output_files); | 391 path_output_.WriteFiles(out_, output_files); |
210 | 392 |
211 out_ << ": " | 393 out_ << ": " << rule_prefix_ |
212 << GetNinjaRulePrefixForToolchain(settings_) | |
213 << Toolchain::ToolTypeToName( | 394 << Toolchain::ToolTypeToName( |
214 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); | 395 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); |
215 | 396 |
216 UniqueVector<OutputFile> extra_object_files; | 397 UniqueVector<OutputFile> extra_object_files; |
217 UniqueVector<const Target*> linkable_deps; | 398 UniqueVector<const Target*> linkable_deps; |
218 UniqueVector<const Target*> non_linkable_deps; | 399 UniqueVector<const Target*> non_linkable_deps; |
219 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); | 400 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); |
220 | 401 |
221 // Object files. | 402 // Object files. |
222 for (const auto& obj : object_files) { | 403 for (const auto& obj : object_files) { |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
435 // don't link at all. | 616 // don't link at all. |
436 bool can_link_libs = target_->IsFinal(); | 617 bool can_link_libs = target_->IsFinal(); |
437 | 618 |
438 if (dep->output_type() == Target::SOURCE_SET) { | 619 if (dep->output_type() == Target::SOURCE_SET) { |
439 // Source sets have their object files linked into final targets | 620 // Source sets have their object files linked into final targets |
440 // (shared libraries, executables, and complete static | 621 // (shared libraries, executables, and complete static |
441 // libraries). Intermediate static libraries and other source sets | 622 // libraries). Intermediate static libraries and other source sets |
442 // just forward the dependency, otherwise the files in the source | 623 // just forward the dependency, otherwise the files in the source |
443 // set can easily get linked more than once which will cause | 624 // set can easily get linked more than once which will cause |
444 // multiple definition errors. | 625 // multiple definition errors. |
445 if (can_link_libs) { | 626 if (can_link_libs) |
446 // Linking in a source set to an executable, shared library, or | 627 AddSourceSetObjectFiles(dep, extra_object_files); |
447 // complete static library, so copy its object files. | |
448 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. | |
449 for (const auto& source : dep->sources()) { | |
450 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | |
451 if (GetOutputFilesForSource(dep, source, &tool_type, &tool_outputs)) { | |
452 // Only link the first output if there are more than one. | |
453 extra_object_files->push_back(tool_outputs[0]); | |
454 } | |
455 } | |
456 } | |
457 | 628 |
458 // Add the source set itself as a non-linkable dependency on the current | 629 // Add the source set itself as a non-linkable dependency on the current |
459 // target. This will make sure that anything the source set's stamp file | 630 // target. This will make sure that anything the source set's stamp file |
460 // depends on (like data deps) are also built before the current target | 631 // depends on (like data deps) are also built before the current target |
461 // can be complete. Otherwise, these will be skipped since this target | 632 // can be complete. Otherwise, these will be skipped since this target |
462 // will depend only on the source set's object files. | 633 // will depend only on the source set's object files. |
463 non_linkable_deps->push_back(dep); | 634 non_linkable_deps->push_back(dep); |
464 } else if (can_link_libs && dep->IsLinkable()) { | 635 } else if (can_link_libs && dep->IsLinkable()) { |
465 linkable_deps->push_back(dep); | 636 linkable_deps->push_back(dep); |
466 } else { | 637 } else { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
504 return false; // No tool for this file (it's a header file or something). | 675 return false; // No tool for this file (it's a header file or something). |
505 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); | 676 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); |
506 if (!tool) | 677 if (!tool) |
507 return false; // Tool does not apply for this toolchain.file. | 678 return false; // Tool does not apply for this toolchain.file. |
508 | 679 |
509 // Figure out what output(s) this compiler produces. | 680 // Figure out what output(s) this compiler produces. |
510 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | 681 SubstitutionWriter::ApplyListToCompilerAsOutputFile( |
511 target, source, tool->outputs(), outputs); | 682 target, source, tool->outputs(), outputs); |
512 return !outputs->empty(); | 683 return !outputs->empty(); |
513 } | 684 } |
685 | |
686 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile( | |
687 SubstitutionType flag_type) const { | |
688 // Use "obj/{dir}/{target_name}.{lang}.pch" which ends up | |
689 // looking like "obj/chrome/browser/browser.cc.precompile.h.pch" | |
690 OutputFile ret = GetTargetOutputDirAsOutputFile(target_); | |
691 ret.value().append(target_->label().name()); | |
692 ret.value().push_back('.'); | |
693 ret.value().append(GetPCHLangForFlagType(flag_type)); | |
694 ret.value().append(".pch"); | |
695 | |
696 return ret; | |
697 } | |
698 | |
699 void NinjaBinaryTargetWriter::GetWindowsPCHObjectFiles( | |
700 const Target* target, | |
701 SubstitutionType flag_type, | |
702 Toolchain::ToolType tool_type, | |
703 std::vector<OutputFile>* outputs) const { | |
704 outputs->clear(); | |
705 | |
706 // Compute the tool. This must use the tool type passed in rather than the | |
707 // detected file type of the precompiled source file since the same | |
708 // precompiled source file will be used for separate C/C++ compiles. | |
709 const Tool* tool = target->toolchain()->GetTool(tool_type); | |
710 if (!tool) | |
711 return; | |
712 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
713 target, target->config_values().precompiled_source(), | |
714 tool->outputs(), outputs); | |
715 | |
716 if (outputs->empty()) | |
717 return; | |
718 if (outputs->size() > 1) | |
719 outputs->resize(1); // Only link the first output from the compiler tool. | |
720 | |
721 // Need to annotate the obj files with the language type. For example: | |
722 // obj/foo/target_name.precompile.obj -> | |
723 // obj/foo/target_name.precompile.cc.obj | |
724 const char* lang_suffix = GetPCHLangForFlagType(flag_type); | |
725 std::string& output_value = (*outputs)[0].value(); | |
726 size_t extension_offset = FindExtensionOffset(output_value); | |
727 if (extension_offset == std::string::npos) { | |
728 NOTREACHED() << "No extension found"; | |
729 } else { | |
730 DCHECK(extension_offset >= 1); | |
731 DCHECK(output_value[extension_offset - 1] == '.'); | |
732 output_value.insert(extension_offset - 1, "."); | |
733 output_value.insert(extension_offset, lang_suffix); | |
734 } | |
735 } | |
736 | |
737 void NinjaBinaryTargetWriter::AddSourceSetObjectFiles( | |
738 const Target* source_set, | |
739 UniqueVector<OutputFile>* obj_files) const { | |
740 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. | |
741 SourceFileTypeSet used_types; | |
742 | |
743 // Compute object files for all sources. Only link the first output from | |
744 // teh tool if there are more than one. | |
745 for (const auto& source : source_set->sources()) { | |
746 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | |
747 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs)) | |
748 obj_files->push_back(tool_outputs[0]); | |
749 | |
750 used_types.Set(GetSourceFileType(source)); | |
751 } | |
752 | |
753 // Precompiled header object files. | |
754 if (source_set->config_values().has_precompiled_headers()) { | |
755 if (used_types.Get(SOURCE_C)) { | |
756 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_C, | |
757 Toolchain::TYPE_CC, &tool_outputs); | |
758 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
759 } | |
760 if (used_types.Get(SOURCE_CC)) { | |
761 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_CC, | |
762 Toolchain::TYPE_CXX, &tool_outputs); | |
763 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
764 } | |
765 if (used_types.Get(SOURCE_M)) { | |
766 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_OBJC, | |
767 Toolchain::TYPE_OBJC, &tool_outputs); | |
768 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
769 } | |
770 if (used_types.Get(SOURCE_MM)) { | |
771 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_OBJCC, | |
772 Toolchain::TYPE_OBJCXX, &tool_outputs); | |
773 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
774 } | |
775 } | |
776 } | |
OLD | NEW |