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

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