Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: tools/gn/ninja_binary_target_writer.cc

Issue 1207903002: Windows precompiled header support in GN (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698