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 |
24 // Represents a set of tool types. Must be first since it is also shared by | |
25 // some helper functions in the anonymous namespace below. | |
26 class NinjaBinaryTargetWriter::SourceFileTypeSet { | |
27 public: | |
28 SourceFileTypeSet() { | |
29 memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES)); | |
30 } | |
31 | |
32 void Set(SourceFileType type) { | |
33 flags_[static_cast<int>(type)] = true; | |
34 } | |
35 bool Get(SourceFileType type) const { | |
36 return flags_[static_cast<int>(type)]; | |
37 } | |
38 | |
39 private: | |
40 bool flags_[static_cast<int>(SOURCE_NUMTYPES)]; | |
41 }; | |
42 | |
21 namespace { | 43 namespace { |
22 | 44 |
23 // Returns the proper escape options for writing compiler and linker flags. | 45 // Returns the proper escape options for writing compiler and linker flags. |
24 EscapeOptions GetFlagOptions() { | 46 EscapeOptions GetFlagOptions() { |
25 EscapeOptions opts; | 47 EscapeOptions opts; |
26 opts.mode = ESCAPE_NINJA_COMMAND; | 48 opts.mode = ESCAPE_NINJA_COMMAND; |
27 | 49 |
28 // Some flag strings are actually multiple flags that expect to be just | 50 // Some flag strings are actually multiple flags that expect to be just |
29 // added to the command line. We assume that quoting is done by the | 51 // added to the command line. We assume that quoting is done by the |
30 // buildfiles if it wants such things quoted. | 52 // buildfiles if it wants such things quoted. |
(...skipping 27 matching lines...) Expand all Loading... | |
58 const std::string& path = path_out.str(); | 80 const std::string& path = path_out.str(); |
59 if (path[0] == '"') | 81 if (path[0] == '"') |
60 out << " \"-I" << path.substr(1); | 82 out << " \"-I" << path.substr(1); |
61 else | 83 else |
62 out << " -I" << path; | 84 out << " -I" << path; |
63 } | 85 } |
64 | 86 |
65 PathOutput& path_output_; | 87 PathOutput& path_output_; |
66 }; | 88 }; |
67 | 89 |
90 // Computes the set of output files resulting from compiling the given source | |
91 // file. If the file can be compiled and the tool exists, fills the outputs in | |
92 // and writes the tool type to computed_tool_type. If the file is not | |
93 // compilable, returns false. | |
94 // | |
95 // The target that the source belongs to is passed as an argument. In the case | |
96 // of linking to source sets, this can be different than the target this class | |
97 // is currently writing. | |
98 // | |
99 // The function can succeed with a "NONE" tool type for object files which are | |
100 // just passed to the output. The output will always be overwritten, not | |
101 // appended to. | |
102 bool GetOutputFilesForSource(const Target* target, | |
brettw
2015/06/29 21:36:10
This was moved (unchanged) from the bottom of the
| |
103 const SourceFile& source, | |
104 Toolchain::ToolType* computed_tool_type, | |
105 std::vector<OutputFile>* outputs) { | |
106 outputs->clear(); | |
107 *computed_tool_type = Toolchain::TYPE_NONE; | |
108 | |
109 SourceFileType file_type = GetSourceFileType(source); | |
110 if (file_type == SOURCE_UNKNOWN) | |
111 return false; | |
112 if (file_type == SOURCE_O) { | |
113 // Object files just get passed to the output and not compiled. | |
114 outputs->push_back( | |
115 OutputFile(target->settings()->build_settings(), source)); | |
116 return true; | |
117 } | |
118 | |
119 *computed_tool_type = | |
120 target->toolchain()->GetToolTypeForSourceType(file_type); | |
121 if (*computed_tool_type == Toolchain::TYPE_NONE) | |
122 return false; // No tool for this file (it's a header file or something). | |
123 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); | |
124 if (!tool) | |
125 return false; // Tool does not apply for this toolchain.file. | |
126 | |
127 // Figure out what output(s) this compiler produces. | |
128 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
129 target, source, tool->outputs(), outputs); | |
130 return !outputs->empty(); | |
131 } | |
132 | |
133 // Returns the language-specific prefix/suffix for precomiled header files. | |
134 const char* GetPCHLangForToolType(Toolchain::ToolType type) { | |
135 switch (type) { | |
136 case Toolchain::TYPE_CC: | |
137 return "c"; | |
138 case Toolchain::TYPE_CXX: | |
139 return "cc"; | |
140 case Toolchain::TYPE_OBJC: | |
141 return "m"; | |
142 case Toolchain::TYPE_OBJCXX: | |
143 return "mm"; | |
144 default: | |
145 NOTREACHED() << "Not a valid PCH tool type type"; | |
146 return ""; | |
147 } | |
148 } | |
149 | |
150 // Returns the object files for the precompiled header of the given type (flag | |
151 // type and tool type must match). | |
152 void GetWindowsPCHObjectFiles(const Target* target, | |
153 Toolchain::ToolType tool_type, | |
154 std::vector<OutputFile>* outputs) { | |
155 outputs->clear(); | |
156 | |
157 // Compute the tool. This must use the tool type passed in rather than the | |
158 // detected file type of the precompiled source file since the same | |
159 // precompiled source file will be used for separate C/C++ compiles. | |
160 const Tool* tool = target->toolchain()->GetTool(tool_type); | |
161 if (!tool) | |
162 return; | |
163 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
164 target, target->config_values().precompiled_source(), | |
165 tool->outputs(), outputs); | |
166 | |
167 if (outputs->empty()) | |
168 return; | |
169 if (outputs->size() > 1) | |
170 outputs->resize(1); // Only link the first output from the compiler tool. | |
171 | |
172 // Need to annotate the obj files with the language type. For example: | |
173 // obj/foo/target_name.precompile.obj -> | |
174 // obj/foo/target_name.precompile.cc.obj | |
175 const char* lang_suffix = GetPCHLangForToolType(tool_type); | |
176 std::string& output_value = (*outputs)[0].value(); | |
177 size_t extension_offset = FindExtensionOffset(output_value); | |
178 if (extension_offset == std::string::npos) { | |
179 NOTREACHED() << "No extension found"; | |
180 } else { | |
181 DCHECK(extension_offset >= 1); | |
182 DCHECK(output_value[extension_offset - 1] == '.'); | |
183 output_value.insert(extension_offset - 1, "."); | |
184 output_value.insert(extension_offset, lang_suffix); | |
185 } | |
186 } | |
187 | |
188 // Appends the object files generated by the given source set to the given | |
189 // output vector. | |
190 void AddSourceSetObjectFiles(const Target* source_set, | |
191 UniqueVector<OutputFile>* obj_files) { | |
192 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. | |
193 NinjaBinaryTargetWriter::SourceFileTypeSet used_types; | |
194 | |
195 // Compute object files for all sources. Only link the first output from | |
196 // teh tool if there are more than one. | |
scottmg
2015/06/29 21:49:17
teh
| |
197 for (const auto& source : source_set->sources()) { | |
198 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | |
199 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs)) | |
200 obj_files->push_back(tool_outputs[0]); | |
201 | |
202 used_types.Set(GetSourceFileType(source)); | |
203 } | |
204 | |
205 // Precompiled header object files. | |
206 if (source_set->config_values().has_precompiled_headers()) { | |
207 if (used_types.Get(SOURCE_C)) { | |
208 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CC, &tool_outputs); | |
209 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
210 } | |
211 if (used_types.Get(SOURCE_CPP)) { | |
212 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs); | |
213 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
214 } | |
215 if (used_types.Get(SOURCE_M)) { | |
216 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs); | |
217 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
218 } | |
219 if (used_types.Get(SOURCE_MM)) { | |
220 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJCXX, | |
221 &tool_outputs); | |
222 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
223 } | |
224 } | |
225 } | |
226 | |
68 } // namespace | 227 } // namespace |
69 | 228 |
70 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, | 229 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, |
71 std::ostream& out) | 230 std::ostream& out) |
72 : NinjaTargetWriter(target, out), | 231 : NinjaTargetWriter(target, out), |
73 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) { | 232 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)), |
233 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) { | |
74 } | 234 } |
75 | 235 |
76 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { | 236 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { |
77 } | 237 } |
78 | 238 |
79 void NinjaBinaryTargetWriter::Run() { | 239 void NinjaBinaryTargetWriter::Run() { |
80 WriteCompilerVars(); | 240 // Figure out what source types are needed. |
81 | 241 SourceFileTypeSet used_types; |
242 for (const auto& source : target_->sources()) | |
243 used_types.Set(GetSourceFileType(source)); | |
244 | |
245 WriteCompilerVars(used_types); | |
246 | |
247 // The input dependencies will be an order-only dependency. This will cause | |
brettw
2015/06/29 21:36:10
This comment was moved unchanged from below.
| |
248 // Ninja to make sure the inputs are up-to-date before compiling this source, | |
249 // but changes in the inputs deps won't cause the file to be recompiled. | |
250 // | |
251 // This is important to prevent changes in unrelated actions that are | |
252 // upstream of this target from causing everything to be recompiled | |
253 // | |
254 // Why can we get away with this rather than using implicit deps ("|", which | |
255 // will force rebuilds when the inputs change)? For source code, the | |
256 // computed dependencies of all headers will be computed by the compiler, | |
257 // which will cause source rebuilds if any "real" upstream dependencies | |
258 // change. | |
259 // | |
260 // If a .cc file is generated by an input dependency, Ninja will see the | |
261 // input to the build rule doesn't exist, and that it is an output from a | |
262 // previous step, and build the previous step first. This is a "real" | |
263 // dependency and doesn't need | or || to express. | |
264 // | |
265 // The only case where this rule matters is for the first build where no .d | |
266 // files exist, and Ninja doesn't know what that source file depends on. In | |
267 // this case it's sufficient to ensure that the upstream dependencies are | |
268 // built first. This is exactly what Ninja's order-only dependencies | |
269 // expresses. | |
270 OutputFile order_only_dep = | |
271 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); | |
272 | |
273 std::vector<OutputFile> pch_obj_files; | |
274 WritePrecompiledHeaderCommands(used_types, order_only_dep, &pch_obj_files); | |
275 | |
276 // Treat all precompiled object files as explicit dependencies of all | |
277 // compiles. Some notes: | |
278 // | |
279 // - Technically only the language-specific one is required for any specific | |
280 // compile, but that's more difficult to express and the additional logic | |
281 // doesn't buy much reduced parallelism. Just list them all (there's | |
282 // usually only one anyway). | |
283 // | |
284 // - Technically the .pch file is the input to the compile, not the | |
285 // precompiled header's corresponding object file that we're using here. | |
286 // But Ninja's depslog doesn't support multiple outputs from the | |
287 // precompiled header compile step (it outputs both the .pch file and a | |
288 // corresponding .obj file). So we consistently list the .obj file and the | |
289 // .pch file we really need comes along with it. | |
82 std::vector<OutputFile> obj_files; | 290 std::vector<OutputFile> obj_files; |
83 std::vector<SourceFile> other_files; | 291 std::vector<SourceFile> other_files; |
84 WriteSources(&obj_files, &other_files); | 292 WriteSources(pch_obj_files, order_only_dep, &obj_files, &other_files); |
85 | 293 |
86 if (target_->output_type() == Target::SOURCE_SET) | 294 // Also link all pch object files. |
295 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); | |
296 | |
297 if (target_->output_type() == Target::SOURCE_SET) { | |
87 WriteSourceSetStamp(obj_files); | 298 WriteSourceSetStamp(obj_files); |
88 else | 299 #ifndef NDEBUG |
300 // Verify that the function that separately computes a source set's object | |
301 // files match the object files just computed. | |
302 UniqueVector<OutputFile> computed_obj; | |
303 AddSourceSetObjectFiles(target_, &computed_obj); | |
304 DCHECK_EQ(obj_files.size(), computed_obj.size()); | |
305 for (const auto& obj : obj_files) | |
306 DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj)); | |
307 #endif | |
308 } else { | |
89 WriteLinkerStuff(obj_files, other_files); | 309 WriteLinkerStuff(obj_files, other_files); |
90 } | 310 } |
91 | 311 } |
92 void NinjaBinaryTargetWriter::WriteCompilerVars() { | 312 |
313 void NinjaBinaryTargetWriter::WriteCompilerVars( | |
314 const SourceFileTypeSet& used_types) { | |
93 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); | 315 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); |
94 | 316 |
95 // Defines. | 317 // Defines. |
96 if (subst.used[SUBSTITUTION_DEFINES]) { | 318 if (subst.used[SUBSTITUTION_DEFINES]) { |
97 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; | 319 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; |
98 RecursiveTargetConfigToStream<std::string>( | 320 RecursiveTargetConfigToStream<std::string>( |
99 target_, &ConfigValues::defines, DefineWriter(), out_); | 321 target_, &ConfigValues::defines, DefineWriter(), out_); |
100 out_ << std::endl; | 322 out_ << std::endl; |
101 } | 323 } |
102 | 324 |
103 // Include directories. | 325 // Include directories. |
104 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { | 326 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { |
105 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; | 327 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; |
106 PathOutput include_path_output( | 328 PathOutput include_path_output( |
107 path_output_.current_dir(), | 329 path_output_.current_dir(), |
108 settings_->build_settings()->root_path_utf8(), | 330 settings_->build_settings()->root_path_utf8(), |
109 ESCAPE_NINJA_COMMAND); | 331 ESCAPE_NINJA_COMMAND); |
110 RecursiveTargetConfigToStream<SourceDir>( | 332 RecursiveTargetConfigToStream<SourceDir>( |
111 target_, &ConfigValues::include_dirs, | 333 target_, &ConfigValues::include_dirs, |
112 IncludeWriter(include_path_output), out_); | 334 IncludeWriter(include_path_output), out_); |
113 out_ << std::endl; | 335 out_ << std::endl; |
114 } | 336 } |
115 | 337 |
116 // C flags and friends. | 338 bool has_precompiled_headers = |
117 EscapeOptions flag_escape_options = GetFlagOptions(); | 339 target_->config_values().has_precompiled_headers(); |
118 #define WRITE_FLAGS(name, subst_enum) \ | |
119 if (subst.used[subst_enum]) { \ | |
120 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \ | |
121 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \ | |
122 flag_escape_options, out_); \ | |
123 out_ << std::endl; \ | |
124 } | |
125 | 340 |
126 WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS) | 341 // Some toolchains pass cflags to the assembler since it's the same command, |
127 WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C) | 342 // and cflags_c might also be sent to the objective C compiler. |
128 WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC) | 343 // |
129 WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC) | 344 // TODO(brettw) remove the SOURCE_M from the CFLAGS_C writing once the Chrome |
130 WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC) | 345 // Mac build is updated not to pass cflags_c to .m files. |
131 | 346 EscapeOptions opts = GetFlagOptions(); |
132 #undef WRITE_FLAGS | 347 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) || |
348 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) || | |
349 used_types.Get(SOURCE_ASM)) { | |
350 WriteOneFlag(SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE, | |
351 &ConfigValues::cflags, opts); | |
352 } | |
353 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_M) || | |
354 used_types.Get(SOURCE_ASM)) { | |
355 WriteOneFlag(SUBSTITUTION_CFLAGS_C, has_precompiled_headers, | |
356 Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts); | |
357 } | |
358 if (used_types.Get(SOURCE_CPP)) { | |
359 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, has_precompiled_headers, | |
360 Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts); | |
361 } | |
362 if (used_types.Get(SOURCE_M)) { | |
363 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers, | |
364 Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts); | |
365 } | |
366 if (used_types.Get(SOURCE_MM)) { | |
367 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers, | |
368 Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts); | |
369 } | |
133 | 370 |
134 WriteSharedVars(subst); | 371 WriteSharedVars(subst); |
135 } | 372 } |
136 | 373 |
374 void NinjaBinaryTargetWriter::WriteOneFlag( | |
375 SubstitutionType subst_enum, | |
376 bool has_precompiled_headers, | |
377 Toolchain::ToolType tool_type, | |
378 const std::vector<std::string>& (ConfigValues::* getter)() const, | |
379 EscapeOptions flag_escape_options) { | |
380 if (!target_->toolchain()->substitution_bits().used[subst_enum]) | |
381 return; | |
382 | |
383 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; | |
384 | |
385 if (has_precompiled_headers) { | |
386 const Tool* tool = target_->toolchain()->GetTool(tool_type); | |
387 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) { | |
388 // Name the .pch file. | |
389 out_ << " /Fp"; | |
390 path_output_.WriteFile(out_, GetWindowsPCHFile(tool_type)); | |
391 | |
392 // Enables precompiled headers and names the .h file. It's a string | |
393 // rather than a file name (so no need to rebase or use path_output_). | |
394 out_ << " /Yu" << target_->config_values().precompiled_header(); | |
395 } | |
396 } | |
397 | |
398 RecursiveTargetConfigStringsToStream(target_, getter, | |
399 flag_escape_options, out_); | |
400 out_ << std::endl; | |
401 } | |
402 | |
403 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommands( | |
404 const SourceFileTypeSet& used_types, | |
405 const OutputFile& order_only_dep, | |
406 std::vector<OutputFile>* object_files) { | |
407 if (!target_->config_values().has_precompiled_headers()) | |
408 return; | |
409 | |
410 const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC); | |
411 if (tool_c && | |
412 tool_c->precompiled_header_type() == Tool::PCH_MSVC && | |
413 used_types.Get(SOURCE_C)) { | |
414 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_C, | |
415 Toolchain::TYPE_CC, | |
416 order_only_dep, object_files); | |
417 } | |
418 const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX); | |
419 if (tool_cxx && | |
420 tool_cxx->precompiled_header_type() == Tool::PCH_MSVC && | |
421 used_types.Get(SOURCE_CPP)) { | |
422 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_CC, | |
423 Toolchain::TYPE_CXX, | |
424 order_only_dep, object_files); | |
425 } | |
426 } | |
427 | |
428 void NinjaBinaryTargetWriter::WriteWindowsPCHCommand( | |
429 SubstitutionType flag_type, | |
430 Toolchain::ToolType tool_type, | |
431 const OutputFile& order_only_dep, | |
432 std::vector<OutputFile>* object_files) { | |
433 // Compute the object file (it will be language-specific). | |
434 std::vector<OutputFile> outputs; | |
435 GetWindowsPCHObjectFiles(target_, tool_type, &outputs); | |
436 if (outputs.empty()) | |
437 return; | |
438 object_files->insert(object_files->end(), outputs.begin(), outputs.end()); | |
439 | |
440 // Build line to compile the file. | |
441 WriteCompilerBuildLine(target_->config_values().precompiled_source(), | |
442 std::vector<OutputFile>(), order_only_dep, tool_type, | |
443 outputs); | |
444 | |
445 // This build line needs a custom language-specific flags value. It needs to | |
446 // include the switch to generate the .pch file in addition to the normal | |
447 // ones. Rule-specific variables are just indented underneath the rule line, | |
448 // and this defines the new one in terms of the old value. | |
449 out_ << " " << kSubstitutionNinjaNames[flag_type] << " ="; | |
450 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}"; | |
451 | |
452 // Append the command to generate the .pch file. | |
453 out_ << " /Yc" << target_->config_values().precompiled_header(); | |
454 | |
455 // Write two blank lines to help separate the PCH build lines from the | |
456 // regular source build lines. | |
457 out_ << std::endl << std::endl; | |
458 } | |
459 | |
137 void NinjaBinaryTargetWriter::WriteSources( | 460 void NinjaBinaryTargetWriter::WriteSources( |
461 const std::vector<OutputFile>& extra_deps, | |
462 const OutputFile& order_only_dep, | |
138 std::vector<OutputFile>* object_files, | 463 std::vector<OutputFile>* object_files, |
139 std::vector<SourceFile>* other_files) { | 464 std::vector<SourceFile>* other_files) { |
140 object_files->reserve(target_->sources().size()); | 465 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 | 466 |
147 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. | 467 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. |
148 for (const auto& source : target_->sources()) { | 468 for (const auto& source : target_->sources()) { |
149 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | 469 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; |
150 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { | 470 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { |
151 if (GetSourceFileType(source) == SOURCE_DEF) | 471 if (GetSourceFileType(source) == SOURCE_DEF) |
152 other_files->push_back(source); | 472 other_files->push_back(source); |
153 continue; // No output for this source. | 473 continue; // No output for this source. |
154 } | 474 } |
155 | 475 |
156 if (tool_type != Toolchain::TYPE_NONE) { | 476 if (tool_type != Toolchain::TYPE_NONE) { |
157 out_ << "build"; | 477 WriteCompilerBuildLine(source, extra_deps, order_only_dep, tool_type, |
158 path_output_.WriteFiles(out_, tool_outputs); | 478 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 } | 479 } |
193 | 480 |
194 // It's theoretically possible for a compiler to produce more than one | 481 // It's theoretically possible for a compiler to produce more than one |
195 // output, but we'll only link to the first output. | 482 // output, but we'll only link to the first output. |
196 object_files->push_back(tool_outputs[0]); | 483 object_files->push_back(tool_outputs[0]); |
197 } | 484 } |
198 out_ << std::endl; | 485 out_ << std::endl; |
199 } | 486 } |
200 | 487 |
488 void NinjaBinaryTargetWriter::WriteCompilerBuildLine( | |
brettw
2015/06/29 21:36:10
This was factored out of WriteSources so it could
| |
489 const SourceFile& source, | |
490 const std::vector<OutputFile>& extra_deps, | |
491 const OutputFile& order_only_dep, | |
492 Toolchain::ToolType tool_type, | |
493 const std::vector<OutputFile>& outputs) { | |
494 out_ << "build"; | |
495 path_output_.WriteFiles(out_, outputs); | |
496 | |
497 out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type); | |
498 out_ << " "; | |
499 path_output_.WriteFile(out_, source); | |
500 | |
501 if (!extra_deps.empty()) { | |
502 out_ << " |"; | |
503 for (const OutputFile& dep : extra_deps) { | |
504 out_ << " "; | |
505 path_output_.WriteFile(out_, dep); | |
506 } | |
507 } | |
508 | |
509 if (!order_only_dep.value().empty()) { | |
510 out_ << " || "; | |
511 path_output_.WriteFile(out_, order_only_dep); | |
512 } | |
513 out_ << std::endl; | |
514 } | |
515 | |
201 void NinjaBinaryTargetWriter::WriteLinkerStuff( | 516 void NinjaBinaryTargetWriter::WriteLinkerStuff( |
202 const std::vector<OutputFile>& object_files, | 517 const std::vector<OutputFile>& object_files, |
203 const std::vector<SourceFile>& other_files) { | 518 const std::vector<SourceFile>& other_files) { |
204 std::vector<OutputFile> output_files; | 519 std::vector<OutputFile> output_files; |
205 SubstitutionWriter::ApplyListToLinkerAsOutputFile( | 520 SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
206 target_, tool_, tool_->outputs(), &output_files); | 521 target_, tool_, tool_->outputs(), &output_files); |
207 | 522 |
208 out_ << "build"; | 523 out_ << "build"; |
209 path_output_.WriteFiles(out_, output_files); | 524 path_output_.WriteFiles(out_, output_files); |
210 | 525 |
211 out_ << ": " | 526 out_ << ": " << rule_prefix_ |
212 << GetNinjaRulePrefixForToolchain(settings_) | |
213 << Toolchain::ToolTypeToName( | 527 << Toolchain::ToolTypeToName( |
214 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); | 528 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); |
215 | 529 |
216 UniqueVector<OutputFile> extra_object_files; | 530 UniqueVector<OutputFile> extra_object_files; |
217 UniqueVector<const Target*> linkable_deps; | 531 UniqueVector<const Target*> linkable_deps; |
218 UniqueVector<const Target*> non_linkable_deps; | 532 UniqueVector<const Target*> non_linkable_deps; |
219 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); | 533 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); |
220 | 534 |
221 // Object files. | 535 // Object files. |
222 for (const auto& obj : object_files) { | 536 path_output_.WriteFiles(out_, object_files); |
223 out_ << " "; | 537 path_output_.WriteFiles(out_, extra_object_files); |
224 path_output_.WriteFile(out_, obj); | |
225 } | |
226 for (const auto& obj : extra_object_files) { | |
227 out_ << " "; | |
228 path_output_.WriteFile(out_, obj); | |
229 } | |
230 | 538 |
539 // Dependencies. | |
231 std::vector<OutputFile> implicit_deps; | 540 std::vector<OutputFile> implicit_deps; |
232 std::vector<OutputFile> solibs; | 541 std::vector<OutputFile> solibs; |
233 | |
234 for (const Target* cur : linkable_deps) { | 542 for (const Target* cur : linkable_deps) { |
235 // All linkable deps should have a link output file. | 543 // All linkable deps should have a link output file. |
236 DCHECK(!cur->link_output_file().value().empty()) | 544 DCHECK(!cur->link_output_file().value().empty()) |
237 << "No link output file for " | 545 << "No link output file for " |
238 << target_->label().GetUserVisibleName(false); | 546 << target_->label().GetUserVisibleName(false); |
239 | 547 |
240 if (cur->dependency_output_file().value() != | 548 if (cur->dependency_output_file().value() != |
241 cur->link_output_file().value()) { | 549 cur->link_output_file().value()) { |
242 // This is a shared library with separate link and deps files. Save for | 550 // This is a shared library with separate link and deps files. Save for |
243 // later. | 551 // later. |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
435 // don't link at all. | 743 // don't link at all. |
436 bool can_link_libs = target_->IsFinal(); | 744 bool can_link_libs = target_->IsFinal(); |
437 | 745 |
438 if (dep->output_type() == Target::SOURCE_SET) { | 746 if (dep->output_type() == Target::SOURCE_SET) { |
439 // Source sets have their object files linked into final targets | 747 // Source sets have their object files linked into final targets |
440 // (shared libraries, executables, and complete static | 748 // (shared libraries, executables, and complete static |
441 // libraries). Intermediate static libraries and other source sets | 749 // libraries). Intermediate static libraries and other source sets |
442 // just forward the dependency, otherwise the files in the source | 750 // just forward the dependency, otherwise the files in the source |
443 // set can easily get linked more than once which will cause | 751 // set can easily get linked more than once which will cause |
444 // multiple definition errors. | 752 // multiple definition errors. |
445 if (can_link_libs) { | 753 if (can_link_libs) |
446 // Linking in a source set to an executable, shared library, or | 754 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 | 755 |
458 // Add the source set itself as a non-linkable dependency on the current | 756 // 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 | 757 // 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 | 758 // depends on (like data deps) are also built before the current target |
461 // can be complete. Otherwise, these will be skipped since this target | 759 // can be complete. Otherwise, these will be skipped since this target |
462 // will depend only on the source set's object files. | 760 // will depend only on the source set's object files. |
463 non_linkable_deps->push_back(dep); | 761 non_linkable_deps->push_back(dep); |
464 } else if (can_link_libs && dep->IsLinkable()) { | 762 } else if (can_link_libs && dep->IsLinkable()) { |
465 linkable_deps->push_back(dep); | 763 linkable_deps->push_back(dep); |
466 } else { | 764 } else { |
467 non_linkable_deps->push_back(dep); | 765 non_linkable_deps->push_back(dep); |
468 } | 766 } |
469 } | 767 } |
470 | 768 |
471 void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies( | 769 void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies( |
472 const UniqueVector<const Target*>& non_linkable_deps) { | 770 const UniqueVector<const Target*>& non_linkable_deps) { |
473 if (!non_linkable_deps.empty()) { | 771 if (!non_linkable_deps.empty()) { |
474 out_ << " ||"; | 772 out_ << " ||"; |
475 | 773 |
476 // Non-linkable targets. | 774 // Non-linkable targets. |
477 for (const auto& non_linkable_dep : non_linkable_deps) { | 775 for (const auto& non_linkable_dep : non_linkable_deps) { |
478 out_ << " "; | 776 out_ << " "; |
479 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file()); | 777 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file()); |
480 } | 778 } |
481 } | 779 } |
482 } | 780 } |
483 | 781 |
484 bool NinjaBinaryTargetWriter::GetOutputFilesForSource( | 782 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile( |
485 const Target* target, | 783 Toolchain::ToolType tool_type) const { |
486 const SourceFile& source, | 784 // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up |
487 Toolchain::ToolType* computed_tool_type, | 785 // looking like "obj/chrome/browser/browser.cc.pch" |
488 std::vector<OutputFile>* outputs) const { | 786 OutputFile ret = GetTargetOutputDirAsOutputFile(target_); |
489 outputs->clear(); | 787 ret.value().append(target_->label().name()); |
490 *computed_tool_type = Toolchain::TYPE_NONE; | 788 ret.value().push_back('_'); |
789 ret.value().append(GetPCHLangForToolType(tool_type)); | |
790 ret.value().append(".pch"); | |
491 | 791 |
492 SourceFileType file_type = GetSourceFileType(source); | 792 return ret; |
493 if (file_type == SOURCE_UNKNOWN) | |
494 return false; | |
495 if (file_type == SOURCE_O) { | |
496 // Object files just get passed to the output and not compiled. | |
497 outputs->push_back(OutputFile(settings_->build_settings(), source)); | |
498 return true; | |
499 } | |
500 | |
501 *computed_tool_type = | |
502 target->toolchain()->GetToolTypeForSourceType(file_type); | |
503 if (*computed_tool_type == Toolchain::TYPE_NONE) | |
504 return false; // No tool for this file (it's a header file or something). | |
505 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); | |
506 if (!tool) | |
507 return false; // Tool does not apply for this toolchain.file. | |
508 | |
509 // Figure out what output(s) this compiler produces. | |
510 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
511 target, source, tool->outputs(), outputs); | |
512 return !outputs->empty(); | |
513 } | 793 } |
OLD | NEW |