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

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

Issue 1292983004: [GN]: Precompiled header support for GCC. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 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
« no previous file with comments | « tools/gn/ninja_binary_target_writer.h ('k') | tools/gn/ninja_binary_target_writer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 <cstring>
8 #include <set> 8 #include <set>
9 #include <sstream> 9 #include <sstream>
10 10
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); 125 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type);
126 if (!tool) 126 if (!tool)
127 return false; // Tool does not apply for this toolchain.file. 127 return false; // Tool does not apply for this toolchain.file.
128 128
129 // Figure out what output(s) this compiler produces. 129 // Figure out what output(s) this compiler produces.
130 SubstitutionWriter::ApplyListToCompilerAsOutputFile( 130 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
131 target, source, tool->outputs(), outputs); 131 target, source, tool->outputs(), outputs);
132 return !outputs->empty(); 132 return !outputs->empty();
133 } 133 }
134 134
135 // Returns the language-specific prefix/suffix for precomiled header files. 135 // Returns the language-specific suffix for precompiled header files.
136 const char* GetPCHLangForToolType(Toolchain::ToolType type) { 136 const char* GetPCHSuffixForToolType(Toolchain::ToolType type) {
137 switch (type) { 137 switch (type) {
138 case Toolchain::TYPE_CC: 138 case Toolchain::TYPE_CC:
139 return "c"; 139 return "c";
140 case Toolchain::TYPE_CXX: 140 case Toolchain::TYPE_CXX:
141 return "cc"; 141 return "cc";
142 case Toolchain::TYPE_OBJC: 142 case Toolchain::TYPE_OBJC:
143 return "m"; 143 return "m";
144 case Toolchain::TYPE_OBJCXX: 144 case Toolchain::TYPE_OBJCXX:
145 return "mm"; 145 return "mm";
146 default: 146 default:
147 NOTREACHED() << "Not a valid PCH tool type type"; 147 NOTREACHED() << "Not a valid PCH tool type: " << type;
148 return ""; 148 return "";
149 } 149 }
150 } 150 }
151 151
152 // Returns the object files for the precompiled header of the given type (flag 152 // Returns the language-specific lang recognized by gcc’s -x flag for
153 // type and tool type must match). 153 // precompiled header files.
154 void GetWindowsPCHObjectFiles(const Target* target, 154 const char* GetPCHLangForToolType(Toolchain::ToolType type) {
155 Toolchain::ToolType tool_type, 155 switch (type) {
156 std::vector<OutputFile>* outputs) { 156 case Toolchain::TYPE_CC:
157 return "c-header";
158 case Toolchain::TYPE_CXX:
159 return "c++-header";
160 case Toolchain::TYPE_OBJC:
161 return "objective-c-header";
162 case Toolchain::TYPE_OBJCXX:
163 return "objective-c++-header";
164 default:
165 NOTREACHED() << "Not a valid PCH tool type: " << type;
166 return "";
167 }
168 }
169
170 // Returns the object or gch files for the precompiled header of the given type
171 // (flag type and tool type must match).
172 void GetPCHOutputFiles(const Target* target,
173 Toolchain::ToolType tool_type,
174 std::vector<OutputFile>* outputs) {
157 outputs->clear(); 175 outputs->clear();
158 176
159 // Compute the tool. This must use the tool type passed in rather than the 177 // Compute the tool. This must use the tool type passed in rather than the
160 // detected file type of the precompiled source file since the same 178 // detected file type of the precompiled source file since the same
161 // precompiled source file will be used for separate C/C++ compiles. 179 // precompiled source file will be used for separate C/C++ compiles.
162 const Tool* tool = target->toolchain()->GetTool(tool_type); 180 const Tool* tool = target->toolchain()->GetTool(tool_type);
163 if (!tool) 181 if (!tool)
164 return; 182 return;
165 SubstitutionWriter::ApplyListToCompilerAsOutputFile( 183 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
166 target, target->config_values().precompiled_source(), 184 target, target->config_values().precompiled_source(),
167 tool->outputs(), outputs); 185 tool->outputs(), outputs);
168 186
169 if (outputs->empty()) 187 if (outputs->empty())
170 return; 188 return;
171 if (outputs->size() > 1) 189 if (outputs->size() > 1)
172 outputs->resize(1); // Only link the first output from the compiler tool. 190 outputs->resize(1); // Only link the first output from the compiler tool.
173 191
174 // Need to annotate the obj files with the language type. For example:
175 // obj/foo/target_name.precompile.obj ->
176 // obj/foo/target_name.precompile.cc.obj
177 const char* lang_suffix = GetPCHLangForToolType(tool_type);
178 std::string& output_value = (*outputs)[0].value(); 192 std::string& output_value = (*outputs)[0].value();
179 size_t extension_offset = FindExtensionOffset(output_value); 193 size_t extension_offset = FindExtensionOffset(output_value);
180 if (extension_offset == std::string::npos) { 194 if (extension_offset == std::string::npos) {
brettw 2015/09/14 23:56:27 This new code will crash if there is no extension.
Bons 2015/09/15 15:08:02 Done.
181 NOTREACHED() << "No extension found"; 195 NOTREACHED() << "No extension found";
182 } else { 196 }
183 DCHECK(extension_offset >= 1); 197 DCHECK(extension_offset >= 1);
184 DCHECK(output_value[extension_offset - 1] == '.'); 198 DCHECK(output_value[extension_offset - 1] == '.');
199 const char* lang_suffix = GetPCHSuffixForToolType(tool_type);
200
201 if (tool->precompiled_header_type() == Tool::PCH_MSVC) {
202 // For MSVC, annotate the obj files with the language type. For example:
203 // obj/foo/target_name.precompile.o ->
204 // obj/foo/target_name.precompile.cc.o
185 output_value.insert(extension_offset - 1, "."); 205 output_value.insert(extension_offset - 1, ".");
186 output_value.insert(extension_offset, lang_suffix); 206 output_value.insert(extension_offset, lang_suffix);
207 } else if (tool->precompiled_header_type() == Tool::PCH_GCC) {
208 // For GCC, the output name must have a .gch suffix and be annotated with
209 // the language type. For example:
210 // obj/foo/target_name.header.o ->
brettw 2015/09/14 23:56:28 ".o" -> ".h"
Bons 2015/09/15 15:08:02 Done.
211 // obj/foo/target_name.header.h-cc.gch
212 // In order for the compiler to pick it up, the output name (minus the .gch
213 // suffix MUST match whatever is passed to the -include flag).
214 std::string output_suffix;
215 output_suffix += "h-";
216 output_suffix += lang_suffix;
217 output_suffix += ".gch";
218 output_value.replace(extension_offset, std::string::npos, output_suffix);
219 } else {
220 NOTREACHED() << "No outputs for no (or unknown) PCH type.";
187 } 221 }
188 } 222 }
189 223
190 // Appends the object files generated by the given source set to the given 224 // Appends the object files generated by the given source set to the given
191 // output vector. 225 // output vector.
192 void AddSourceSetObjectFiles(const Target* source_set, 226 void AddSourceSetObjectFiles(const Target* source_set,
193 UniqueVector<OutputFile>* obj_files) { 227 UniqueVector<OutputFile>* obj_files) {
194 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. 228 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
195 NinjaBinaryTargetWriter::SourceFileTypeSet used_types; 229 NinjaBinaryTargetWriter::SourceFileTypeSet used_types;
196 230
197 // Compute object files for all sources. Only link the first output from 231 // Compute object files for all sources. Only link the first output from
198 // the tool if there are more than one. 232 // the tool if there are more than one.
199 for (const auto& source : source_set->sources()) { 233 for (const auto& source : source_set->sources()) {
200 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; 234 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
201 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs)) 235 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs))
202 obj_files->push_back(tool_outputs[0]); 236 obj_files->push_back(tool_outputs[0]);
203 237
204 used_types.Set(GetSourceFileType(source)); 238 used_types.Set(GetSourceFileType(source));
205 } 239 }
206 240
207 // Precompiled header object files. 241 // Add MSVC precompiled header object files. GCC .gch files are not object
242 // files so they are omitted.
208 if (source_set->config_values().has_precompiled_headers()) { 243 if (source_set->config_values().has_precompiled_headers()) {
209 if (used_types.Get(SOURCE_C)) { 244 if (used_types.Get(SOURCE_C)) {
210 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CC, &tool_outputs); 245 const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CC);
211 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); 246 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
247 GetPCHOutputFiles(source_set, Toolchain::TYPE_CC, &tool_outputs);
248 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
249 }
212 } 250 }
213 if (used_types.Get(SOURCE_CPP)) { 251 if (used_types.Get(SOURCE_CPP)) {
214 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs); 252 const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CXX);
215 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); 253 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
254 GetPCHOutputFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs);
255 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
256 }
216 } 257 }
217 if (used_types.Get(SOURCE_M)) { 258 if (used_types.Get(SOURCE_M)) {
218 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs); 259 const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_OBJC);
219 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); 260 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
261 GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs);
262 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
263 }
220 } 264 }
221 if (used_types.Get(SOURCE_MM)) { 265 if (used_types.Get(SOURCE_MM)) {
222 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJCXX, 266 const Tool* tool = source_set->toolchain()->GetTool(
223 &tool_outputs); 267 Toolchain::TYPE_OBJCXX);
224 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); 268 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
269 GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJCXX, &tool_outputs);
270 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
271 }
225 } 272 }
226 } 273 }
227 } 274 }
228 275
229 } // namespace 276 } // namespace
230 277
231 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, 278 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
232 std::ostream& out) 279 std::ostream& out)
233 : NinjaTargetWriter(target, out), 280 : NinjaTargetWriter(target, out),
234 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)), 281 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)),
235 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) { 282 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {
236 } 283 }
237 284
238 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { 285 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
239 } 286 }
240 287
241 void NinjaBinaryTargetWriter::Run() { 288 void NinjaBinaryTargetWriter::Run() {
242 // Figure out what source types are needed. 289 // Figure out what source types are needed.
243 SourceFileTypeSet used_types; 290 SourceFileTypeSet used_types;
244 for (const auto& source : target_->sources()) 291 for (const auto& source : target_->sources())
245 used_types.Set(GetSourceFileType(source)); 292 used_types.Set(GetSourceFileType(source));
246 293
247 WriteCompilerVars(used_types); 294 WriteCompilerVars(used_types);
248 295
249 // The input dependencies will be an order-only dependency. This will cause 296 // The input dependencies will be an order-only dependency. This will cause
250 // Ninja to make sure the inputs are up-to-date before compiling this source, 297 // Ninja to make sure the inputs are up-to-date before compiling this source,
251 // but changes in the inputs deps won't cause the file to be recompiled. 298 // but changes in the inputs deps won't cause the file to be recompiled.
252 // 299 //
253 // This is important to prevent changes in unrelated actions that are 300 // This is important to prevent changes in unrelated actions that are
254 // upstream of this target from causing everything to be recompiled 301 // upstream of this target from causing everything to be recompiled.
255 // 302 //
256 // Why can we get away with this rather than using implicit deps ("|", which 303 // Why can we get away with this rather than using implicit deps ("|", which
257 // will force rebuilds when the inputs change)? For source code, the 304 // will force rebuilds when the inputs change)? For source code, the
258 // computed dependencies of all headers will be computed by the compiler, 305 // computed dependencies of all headers will be computed by the compiler,
259 // which will cause source rebuilds if any "real" upstream dependencies 306 // which will cause source rebuilds if any "real" upstream dependencies
260 // change. 307 // change.
261 // 308 //
262 // If a .cc file is generated by an input dependency, Ninja will see the 309 // If a .cc file is generated by an input dependency, Ninja will see the
263 // input to the build rule doesn't exist, and that it is an output from a 310 // input to the build rule doesn't exist, and that it is an output from a
264 // previous step, and build the previous step first. This is a "real" 311 // previous step, and build the previous step first. This is a "real"
265 // dependency and doesn't need | or || to express. 312 // dependency and doesn't need | or || to express.
266 // 313 //
267 // The only case where this rule matters is for the first build where no .d 314 // The only case where this rule matters is for the first build where no .d
268 // files exist, and Ninja doesn't know what that source file depends on. In 315 // files exist, and Ninja doesn't know what that source file depends on. In
269 // this case it's sufficient to ensure that the upstream dependencies are 316 // this case it's sufficient to ensure that the upstream dependencies are
270 // built first. This is exactly what Ninja's order-only dependencies 317 // built first. This is exactly what Ninja's order-only dependencies
271 // expresses. 318 // expresses.
272 OutputFile order_only_dep = 319 OutputFile order_only_dep =
273 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); 320 WriteInputDepsStampAndGetDep(std::vector<const Target*>());
274 321
322 // For GCC builds, the .gch files are not object files, but still need to be
323 // added as explicit dependencies below. The .gch output files are placed in
324 // |pch_other_files|. This is to prevent linking against them.
275 std::vector<OutputFile> pch_obj_files; 325 std::vector<OutputFile> pch_obj_files;
276 WritePrecompiledHeaderCommands(used_types, order_only_dep, &pch_obj_files); 326 std::vector<OutputFile> pch_other_files;
327 if (!WritePCHCommands(used_types, order_only_dep,
328 &pch_obj_files, &pch_other_files)) {
329 return;
330 }
331 std::vector<OutputFile>* pch_files = !pch_obj_files.empty() ?
332 &pch_obj_files : &pch_other_files;
277 333
278 // Treat all precompiled object files as explicit dependencies of all 334 // Treat all pch output files as explicit dependencies of all
279 // compiles. Some notes: 335 // compiles. Some notes:
280 // 336 //
281 // - Technically only the language-specific one is required for any specific 337 // - Only the language-specific one is required for any specific compile, but
282 // compile, but that's more difficult to express and the additional logic 338 // that's more difficult to express and the additional logic doesn't buy
283 // doesn't buy much reduced parallelism. Just list them all (there's 339 // much reduced parallelism. Just list them all (there's usually only one
284 // usually only one anyway). 340 // anyway).
285 // 341 //
286 // - Technically the .pch file is the input to the compile, not the 342 // - On Windows, the .pch file is the input to the compile, not the
287 // precompiled header's corresponding object file that we're using here. 343 // precompiled header's corresponding object file that we're using here.
288 // But Ninja's depslog doesn't support multiple outputs from the 344 // But Ninja's depslog doesn't support multiple outputs from the
289 // precompiled header compile step (it outputs both the .pch file and a 345 // precompiled header compile step (it outputs both the .pch file and a
290 // corresponding .obj file). So we consistently list the .obj file and the 346 // corresponding .obj file). So we consistently list the .obj file and the
291 // .pch file we really need comes along with it. 347 // .pch file we really need comes along with it.
348 //
349 // - GCC .gch files are not object files, therefore they are not added to the
350 // object file list.
292 std::vector<OutputFile> obj_files; 351 std::vector<OutputFile> obj_files;
293 std::vector<SourceFile> other_files; 352 std::vector<SourceFile> other_files;
294 WriteSources(pch_obj_files, order_only_dep, &obj_files, &other_files); 353 WriteSources(*pch_files, order_only_dep, &obj_files, &other_files);
295 354
296 // Also link all pch object files. 355 // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
297 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); 356 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
298
299 if (!CheckForDuplicateObjectFiles(obj_files)) 357 if (!CheckForDuplicateObjectFiles(obj_files))
300 return; 358 return;
301 359
302 if (target_->output_type() == Target::SOURCE_SET) { 360 if (target_->output_type() == Target::SOURCE_SET) {
303 WriteSourceSetStamp(obj_files); 361 WriteSourceSetStamp(obj_files);
304 #ifndef NDEBUG 362 #ifndef NDEBUG
305 // Verify that the function that separately computes a source set's object 363 // Verify that the function that separately computes a source set's object
306 // files match the object files just computed. 364 // files match the object files just computed.
307 UniqueVector<OutputFile> computed_obj; 365 UniqueVector<OutputFile> computed_obj;
308 AddSourceSetObjectFiles(target_, &computed_obj); 366 AddSourceSetObjectFiles(target_, &computed_obj);
(...skipping 27 matching lines...) Expand all
336 ESCAPE_NINJA_COMMAND); 394 ESCAPE_NINJA_COMMAND);
337 RecursiveTargetConfigToStream<SourceDir>( 395 RecursiveTargetConfigToStream<SourceDir>(
338 target_, &ConfigValues::include_dirs, 396 target_, &ConfigValues::include_dirs,
339 IncludeWriter(include_path_output), out_); 397 IncludeWriter(include_path_output), out_);
340 out_ << std::endl; 398 out_ << std::endl;
341 } 399 }
342 400
343 bool has_precompiled_headers = 401 bool has_precompiled_headers =
344 target_->config_values().has_precompiled_headers(); 402 target_->config_values().has_precompiled_headers();
345 403
346 // Some toolchains pass cflags to the assembler since it's the same command, 404 // Some toolchains pass cflags to the assembler since it's the same command.
347 // and cflags_c might also be sent to the objective C compiler.
348 //
349 // TODO(brettw) remove the SOURCE_M from the CFLAGS_C writing once the Chrome
350 // Mac build is updated not to pass cflags_c to .m files.
351 EscapeOptions opts = GetFlagOptions(); 405 EscapeOptions opts = GetFlagOptions();
352 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) || 406 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) ||
353 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) || 407 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) ||
354 used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) { 408 used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) {
355 WriteOneFlag(SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE, 409 WriteOneFlag(SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
356 &ConfigValues::cflags, opts); 410 &ConfigValues::cflags, opts);
357 } 411 }
358 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_M) || 412 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_S) ||
359 used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) { 413 used_types.Get(SOURCE_ASM)) {
360 WriteOneFlag(SUBSTITUTION_CFLAGS_C, has_precompiled_headers, 414 WriteOneFlag(SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
361 Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts); 415 Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts);
362 } 416 }
363 if (used_types.Get(SOURCE_CPP)) { 417 if (used_types.Get(SOURCE_CPP)) {
364 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, has_precompiled_headers, 418 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
365 Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts); 419 Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts);
366 } 420 }
367 if (used_types.Get(SOURCE_M)) { 421 if (used_types.Get(SOURCE_M)) {
368 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers, 422 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
369 Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts); 423 Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts);
(...skipping 10 matching lines...) Expand all
380 SubstitutionType subst_enum, 434 SubstitutionType subst_enum,
381 bool has_precompiled_headers, 435 bool has_precompiled_headers,
382 Toolchain::ToolType tool_type, 436 Toolchain::ToolType tool_type,
383 const std::vector<std::string>& (ConfigValues::* getter)() const, 437 const std::vector<std::string>& (ConfigValues::* getter)() const,
384 EscapeOptions flag_escape_options) { 438 EscapeOptions flag_escape_options) {
385 if (!target_->toolchain()->substitution_bits().used[subst_enum]) 439 if (!target_->toolchain()->substitution_bits().used[subst_enum])
386 return; 440 return;
387 441
388 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; 442 out_ << kSubstitutionNinjaNames[subst_enum] << " =";
389 443
444 bool flag_values_written = false;
445
390 if (has_precompiled_headers) { 446 if (has_precompiled_headers) {
391 const Tool* tool = target_->toolchain()->GetTool(tool_type); 447 const Tool* tool = target_->toolchain()->GetTool(tool_type);
392 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) { 448 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
393 // Name the .pch file. 449 // Name the .pch file.
394 out_ << " /Fp"; 450 out_ << " /Fp";
395 path_output_.WriteFile(out_, GetWindowsPCHFile(tool_type)); 451 path_output_.WriteFile(out_, GetWindowsPCHFile(tool_type));
396 452
397 // Enables precompiled headers and names the .h file. It's a string 453 // Enables precompiled headers and names the .h file. It's a string
398 // rather than a file name (so no need to rebase or use path_output_). 454 // rather than a file name (so no need to rebase or use path_output_).
399 out_ << " /Yu" << target_->config_values().precompiled_header(); 455 out_ << " /Yu" << target_->config_values().precompiled_header();
400 } 456 } else if (tool && tool->precompiled_header_type() == Tool::PCH_GCC) {
401 } 457 // The targets to build the .gch files should omit the -include flag
402 458 // below. To accomplish this, each substitution flag is overwritten in the
403 RecursiveTargetConfigStringsToStream(target_, getter, 459 // target rule and these values are repeated. The -include flag is omitted
404 flag_escape_options, out_); 460 // in place of the required -x <header lang> flag for .gch targets.
461 RecursiveTargetConfigStringsToStream(target_, getter,
462 flag_escape_options, out_);
463 flag_values_written = true;
464
465 // Compute the gch file (it will be language-specific).
466 std::vector<OutputFile> outputs;
467 GetPCHOutputFiles(target_, tool_type, &outputs);
468 if (outputs.empty())
469 return;
470
471 // Trim the .gch suffix for the -include flag.
472 // e.g. for gch file foo/bar/target.precompiled.h.gch:
473 // -include foo/bar/target.precompiled.h
474 std::string pch_file = outputs[0].value();
475 pch_file.erase(pch_file.length() - 4);
476 out_ << " -include " << pch_file;
477 }
478 }
479 if (!flag_values_written) {
480 RecursiveTargetConfigStringsToStream(target_, getter,
481 flag_escape_options, out_);
482 }
405 out_ << std::endl; 483 out_ << std::endl;
406 } 484 }
407 485
408 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommands( 486 bool NinjaBinaryTargetWriter::WritePCHCommands(
409 const SourceFileTypeSet& used_types, 487 const SourceFileTypeSet& used_types,
410 const OutputFile& order_only_dep, 488 const OutputFile& order_only_dep,
411 std::vector<OutputFile>* object_files) { 489 std::vector<OutputFile>* object_files,
490 std::vector<OutputFile>* other_files) {
412 if (!target_->config_values().has_precompiled_headers()) 491 if (!target_->config_values().has_precompiled_headers())
413 return; 492 return true;
414 493
415 const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC); 494 const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC);
416 if (tool_c && 495 if (tool_c &&
417 tool_c->precompiled_header_type() == Tool::PCH_MSVC && 496 tool_c->precompiled_header_type() != Tool::PCH_NONE &&
418 used_types.Get(SOURCE_C)) { 497 used_types.Get(SOURCE_C)) {
419 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_C, 498 if (!WritePCHCommand(SUBSTITUTION_CFLAGS_C,
420 Toolchain::TYPE_CC, 499 Toolchain::TYPE_CC,
421 order_only_dep, object_files); 500 tool_c->precompiled_header_type(),
501 order_only_dep, object_files, other_files)) {
502 return false;
503 }
422 } 504 }
423 const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX); 505 const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX);
424 if (tool_cxx && 506 if (tool_cxx &&
425 tool_cxx->precompiled_header_type() == Tool::PCH_MSVC && 507 tool_cxx->precompiled_header_type() != Tool::PCH_NONE &&
426 used_types.Get(SOURCE_CPP)) { 508 used_types.Get(SOURCE_CPP)) {
427 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_CC, 509 if (!WritePCHCommand(SUBSTITUTION_CFLAGS_CC,
428 Toolchain::TYPE_CXX, 510 Toolchain::TYPE_CXX,
429 order_only_dep, object_files); 511 tool_cxx->precompiled_header_type(),
430 } 512 order_only_dep, object_files, other_files)) {
513 return false;
514 }
515 }
516
517 const Tool* tool_objc = target_->toolchain()->GetTool(Toolchain::TYPE_OBJC);
518 if (tool_objc &&
519 tool_objc->precompiled_header_type() == Tool::PCH_GCC &&
520 used_types.Get(SOURCE_M)) {
521 if (!WritePCHCommand(SUBSTITUTION_CFLAGS_OBJC,
522 Toolchain::TYPE_OBJC,
523 tool_objc->precompiled_header_type(),
524 order_only_dep, object_files, other_files)) {
525 return false;
526 }
527 }
528
529 const Tool* tool_objcxx =
530 target_->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
531 if (tool_objcxx &&
532 tool_objcxx->precompiled_header_type() == Tool::PCH_GCC &&
533 used_types.Get(SOURCE_MM)) {
534 if (!WritePCHCommand(SUBSTITUTION_CFLAGS_OBJCC,
535 Toolchain::TYPE_OBJCXX,
536 tool_objcxx->precompiled_header_type(),
537 order_only_dep, object_files, other_files)) {
538 return false;
539 }
540 }
541 return true;
431 } 542 }
432 543
433 void NinjaBinaryTargetWriter::WriteWindowsPCHCommand( 544 bool NinjaBinaryTargetWriter::WritePCHCommand(
434 SubstitutionType flag_type, 545 SubstitutionType flag_type,
435 Toolchain::ToolType tool_type, 546 Toolchain::ToolType tool_type,
547 Tool::PrecompiledHeaderType header_type,
436 const OutputFile& order_only_dep, 548 const OutputFile& order_only_dep,
437 std::vector<OutputFile>* object_files) { 549 std::vector<OutputFile>* object_files,
438 // Compute the object file (it will be language-specific). 550 std::vector<OutputFile>* other_files) {
551 // With the GCC toolset, ensure the precompiled source and precompiled header
552 // are the same target.
553 if (header_type == Tool::PCH_GCC) {
554 SourceFile source = target_->config_values().precompiled_source();
555 std::string header_str = target_->config_values().precompiled_header();
556 header_str.insert(0, "//");
557 SourceFile header = SourceFile(SourceFile::SWAP_IN, &header_str);
558 if (source != header) {
559 Err err(
560 target_->defined_from(),
561 "GCC precompiled source and header are different files",
562 "The target " + target_->label().GetUserVisibleName(false) +
563 "\nspecifies a precompiled source:\n " +
564 source.value() + "\n"
565 "that is different than the precompiled header:\n " +
566 header_str + "\n"
567 "\n"
568 "For GCC toolchains, the two must be equal, for instance:\n"
569 " precompiled_header = \"build/precompile.h\""
570 " precompiled_source = \"//build/precompile.h\"");
571 g_scheduler->FailWithError(err);
572 return false;
573 }
574 }
575
576 // Compute the pch output file (it will be language-specific).
439 std::vector<OutputFile> outputs; 577 std::vector<OutputFile> outputs;
440 GetWindowsPCHObjectFiles(target_, tool_type, &outputs); 578 GetPCHOutputFiles(target_, tool_type, &outputs);
441 if (outputs.empty()) 579 if (outputs.empty())
442 return; 580 return true;
443 object_files->insert(object_files->end(), outputs.begin(), outputs.end()); 581 switch (header_type) {
582 case Tool::PCH_MSVC:
583 object_files->insert(object_files->end(), outputs.begin(), outputs.end());
584 break;
585 case Tool::PCH_GCC:
586 // .gch files are not object files.
587 other_files->insert(other_files->end(), outputs.begin(), outputs.end());
588 break;
589 case Tool::PCH_NONE:
590 NOTREACHED() << "Cannot write a PCH command with no PCH header type";
591 break;
592 }
444 593
445 // Build line to compile the file. 594 // Build line to compile the file.
446 WriteCompilerBuildLine(target_->config_values().precompiled_source(), 595 WriteCompilerBuildLine(target_->config_values().precompiled_source(),
447 std::vector<OutputFile>(), order_only_dep, tool_type, 596 std::vector<OutputFile>(), order_only_dep, tool_type,
448 outputs); 597 outputs);
449 598
450 // This build line needs a custom language-specific flags value. It needs to 599 // This build line needs a custom language-specific flags value. Rule-specific
451 // include the switch to generate the .pch file in addition to the normal 600 // variables are just indented underneath the rule line.
452 // ones. Rule-specific variables are just indented underneath the rule line,
453 // and this defines the new one in terms of the old value.
454 out_ << " " << kSubstitutionNinjaNames[flag_type] << " ="; 601 out_ << " " << kSubstitutionNinjaNames[flag_type] << " =";
455 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}"; 602
456 603 switch (header_type) {
457 // Append the command to generate the .pch file. 604 case Tool::PCH_MSVC:
458 out_ << " /Yc" << target_->config_values().precompiled_header(); 605 // Append the command to generate the .pch file.
606 // This adds the value to the existing flag instead of overwriting it.
607 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}";
608 out_ << " /Yc" << target_->config_values().precompiled_header();
609 break;
610 case Tool::PCH_GCC: {
611 // Each substitution flag is overwritten in the target rule to replace the
612 // -include flag with the -x <header lang> flag required for .gch targets.
613 EscapeOptions opts = GetFlagOptions();
614 if (header_type == Tool::PCH_GCC) {
615 if (tool_type == Toolchain::TYPE_CC) {
616 RecursiveTargetConfigStringsToStream(target_,
617 &ConfigValues::cflags_c, opts, out_);
618 } else if (tool_type == Toolchain::TYPE_CXX) {
619 RecursiveTargetConfigStringsToStream(target_,
620 &ConfigValues::cflags_cc, opts, out_);
621 } else if (tool_type == Toolchain::TYPE_OBJC) {
622 RecursiveTargetConfigStringsToStream(target_,
623 &ConfigValues::cflags_objc, opts, out_);
624 } else if (tool_type == Toolchain::TYPE_OBJCXX) {
625 RecursiveTargetConfigStringsToStream(target_,
626 &ConfigValues::cflags_objcc, opts, out_);
627 }
628 }
629 // Append the command to specify the language of the .gch file.
630 out_ << " -x " << GetPCHLangForToolType(tool_type);
631 break;
632 }
633 case Tool::PCH_NONE:
634 NOTREACHED() << "Cannot write a PCH command with no PCH header type";
635 break;
636 }
459 637
460 // Write two blank lines to help separate the PCH build lines from the 638 // Write two blank lines to help separate the PCH build lines from the
461 // regular source build lines. 639 // regular source build lines.
462 out_ << std::endl << std::endl; 640 out_ << std::endl << std::endl;
641
642 return true;
463 } 643 }
464 644
465 void NinjaBinaryTargetWriter::WriteSources( 645 void NinjaBinaryTargetWriter::WriteSources(
466 const std::vector<OutputFile>& extra_deps, 646 const std::vector<OutputFile>& pch_deps,
467 const OutputFile& order_only_dep, 647 const OutputFile& order_only_dep,
468 std::vector<OutputFile>* object_files, 648 std::vector<OutputFile>* object_files,
469 std::vector<SourceFile>* other_files) { 649 std::vector<SourceFile>* other_files) {
470 object_files->reserve(object_files->size() + target_->sources().size()); 650 object_files->reserve(object_files->size() + target_->sources().size());
471 651
472 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. 652 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
653 std::vector<OutputFile> deps;
473 for (const auto& source : target_->sources()) { 654 for (const auto& source : target_->sources()) {
655 // Clear the vector but maintain the max capacity to prevent reallocations.
656 deps.resize(0);
474 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; 657 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
475 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { 658 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) {
476 if (GetSourceFileType(source) == SOURCE_DEF) 659 if (GetSourceFileType(source) == SOURCE_DEF)
477 other_files->push_back(source); 660 other_files->push_back(source);
478 continue; // No output for this source. 661 continue; // No output for this source.
479 } 662 }
480 663
481 if (tool_type != Toolchain::TYPE_NONE) { 664 if (tool_type != Toolchain::TYPE_NONE) {
482 WriteCompilerBuildLine(source, extra_deps, order_only_dep, tool_type, 665 // Only include PCH deps that correspond to the tool type, for instance,
666 // do not specify target_name.precompile.cc.o (a CXX PCH file) as a dep
667 // for the output of a C tool type.
668 //
669 // This makes the assumption that pch_deps only contains pch output files
670 // with the naming scheme specified in GetPCHOutputFiles.
671 const Tool* tool = target_->toolchain()->GetTool(tool_type);
672 for (const auto& dep : pch_deps) {
673 const std::string& output_value = dep.value();
674 const char* lang_suffix = nullptr;
675 if (tool->precompiled_header_type() != Tool::PCH_NONE)
676 lang_suffix = GetPCHSuffixForToolType(tool_type);
677 std::string output_suffix;
678 switch (tool->precompiled_header_type()) {
679 case Tool::PCH_MSVC:
680 // MSVC: target_name.precompile.cc.o
681 output_suffix += ".";
682 output_suffix += lang_suffix;
683 output_suffix += ".o";
684 break;
685 case Tool::PCH_GCC:
686 // GCC: target_name.header.h-cc.gch
687 output_suffix += ".h-";
brettw 2015/09/14 23:56:28 I need to get into this a little bit more, I didn'
Bons 2015/09/15 15:08:02 Done.
688 output_suffix += lang_suffix;
689 output_suffix += ".gch";
690 break;
691 case Tool::PCH_NONE:
692 // Do nothing.
693 break;
694 }
695 if (output_value.compare(output_value.size() - output_suffix.size(),
696 output_suffix.size(), output_suffix) == 0) {
697 deps.push_back(dep);
698 }
699 }
700 WriteCompilerBuildLine(source, deps, order_only_dep, tool_type,
483 tool_outputs); 701 tool_outputs);
484 } 702 }
485 703
486 // It's theoretically possible for a compiler to produce more than one 704 // It's theoretically possible for a compiler to produce more than one
487 // output, but we'll only link to the first output. 705 // output, but we'll only link to the first output.
488 object_files->push_back(tool_outputs[0]); 706 object_files->push_back(tool_outputs[0]);
489 } 707 }
490 out_ << std::endl; 708 out_ << std::endl;
491 } 709 }
492 710
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after
781 for (const auto& non_linkable_dep : non_linkable_deps) { 999 for (const auto& non_linkable_dep : non_linkable_deps) {
782 out_ << " "; 1000 out_ << " ";
783 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file()); 1001 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
784 } 1002 }
785 } 1003 }
786 } 1004 }
787 1005
788 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile( 1006 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile(
789 Toolchain::ToolType tool_type) const { 1007 Toolchain::ToolType tool_type) const {
790 // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up 1008 // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
791 // looking like "obj/chrome/browser/browser.cc.pch" 1009 // looking like "obj/chrome/browser/browser_cc.pch"
792 OutputFile ret = GetTargetOutputDirAsOutputFile(target_); 1010 OutputFile ret = GetTargetOutputDirAsOutputFile(target_);
793 ret.value().append(target_->label().name()); 1011 ret.value().append(target_->label().name());
794 ret.value().push_back('_'); 1012 ret.value().push_back('_');
795 ret.value().append(GetPCHLangForToolType(tool_type)); 1013 ret.value().append(GetPCHSuffixForToolType(tool_type));
796 ret.value().append(".pch"); 1014 ret.value().append(".pch");
797 1015
798 return ret; 1016 return ret;
799 } 1017 }
800 1018
801 bool NinjaBinaryTargetWriter::CheckForDuplicateObjectFiles( 1019 bool NinjaBinaryTargetWriter::CheckForDuplicateObjectFiles(
802 const std::vector<OutputFile>& files) const { 1020 const std::vector<OutputFile>& files) const {
803 base::hash_set<std::string> set; 1021 base::hash_set<std::string> set;
804 for (const auto& file : files) { 1022 for (const auto& file : files) {
805 if (!set.insert(file.value()).second) { 1023 if (!set.insert(file.value()).second) {
(...skipping 11 matching lines...) Expand all
817 "\n" 1035 "\n"
818 "In the latter case, either rename one of the files or move one of\n" 1036 "In the latter case, either rename one of the files or move one of\n"
819 "the sources to a separate source_set to avoid them both being in\n" 1037 "the sources to a separate source_set to avoid them both being in\n"
820 "the same target."); 1038 "the same target.");
821 g_scheduler->FailWithError(err); 1039 g_scheduler->FailWithError(err);
822 return false; 1040 return false;
823 } 1041 }
824 } 1042 }
825 return true; 1043 return true;
826 } 1044 }
OLDNEW
« no previous file with comments | « tools/gn/ninja_binary_target_writer.h ('k') | tools/gn/ninja_binary_target_writer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698