1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gn/ninja_target_command_util.h"
6
7 #include <string.h>
8
9 #include "gn/c_tool.h"
10 #include "gn/substitution_writer.h"
11
12 namespace {
13
14 // Returns the language-specific suffix for precompiled header files.
GetPCHLangSuffixForToolType(const char * name)15 const char* GetPCHLangSuffixForToolType(const char* name) {
16 if (name == CTool::kCToolCc)
17 return "c";
18 if (name == CTool::kCToolCxx)
19 return "cc";
20 if (name == CTool::kCToolObjC)
21 return "m";
22 if (name == CTool::kCToolObjCxx)
23 return "mm";
24 NOTREACHED() << "Not a valid PCH tool type: " << name;
25 return "";
26 }
27
28 } // namespace
29
30 // Returns the computed name of the Windows .pch file for the given
31 // tool type. The tool must support precompiled headers.
GetWindowsPCHFile(const Target * target,const char * tool_name)32 OutputFile GetWindowsPCHFile(const Target* target, const char* tool_name) {
33 // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
34 // looking like "obj/chrome/browser/browser_cc.pch"
35 OutputFile ret = GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ);
36 ret.value().append(target->label().name());
37 ret.value().push_back('_');
38 ret.value().append(GetPCHLangSuffixForToolType(tool_name));
39 ret.value().append(".pch");
40
41 return ret;
42 }
43
WriteOneFlag(RecursiveWriterConfig config,const Target * target,const Substitution * subst_enum,bool has_precompiled_headers,const char * tool_name,const std::vector<std::string> & (ConfigValues::* getter)()const,EscapeOptions flag_escape_options,PathOutput & path_output,std::ostream & out,bool write_substitution,bool indent)44 void WriteOneFlag(RecursiveWriterConfig config,
45 const Target* target,
46 const Substitution* subst_enum,
47 bool has_precompiled_headers,
48 const char* tool_name,
49 const std::vector<std::string>& (ConfigValues::*getter)()
50 const,
51 EscapeOptions flag_escape_options,
52 PathOutput& path_output,
53 std::ostream& out,
54 bool write_substitution,
55 bool indent) {
56 if (!target->toolchain()->substitution_bits().used.count(subst_enum))
57 return;
58
59 if (indent)
60 out << " ";
61 if (write_substitution)
62 out << subst_enum->ninja_name << " =";
63
64 if (has_precompiled_headers) {
65 const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
66 if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
67 // Name the .pch file.
68 out << " /Fp";
69 path_output.WriteFile(out, GetWindowsPCHFile(target, tool_name));
70
71 // Enables precompiled headers and names the .h file. It's a string
72 // rather than a file name (so no need to rebase or use path_output).
73 out << " /Yu" << target->config_values().precompiled_header();
74 RecursiveTargetConfigStringsToStream(config, target, getter,
75 flag_escape_options, out);
76 } else if (tool && tool->precompiled_header_type() == CTool::PCH_GCC) {
77 // The targets to build the .gch files should omit the -include flag
78 // below. To accomplish this, each substitution flag is overwritten in
79 // the target rule and these values are repeated. The -include flag is
80 // omitted in place of the required -x <header lang> flag for .gch
81 // targets.
82 RecursiveTargetConfigStringsToStream(config, target, getter,
83 flag_escape_options, out);
84
85 // Compute the gch file (it will be language-specific).
86 std::vector<OutputFile> outputs;
87 GetPCHOutputFiles(target, tool_name, &outputs);
88 if (!outputs.empty()) {
89 // Trim the .gch suffix for the -include flag.
90 // e.g. for gch file foo/bar/target.precompiled.h.gch:
91 // -include foo/bar/target.precompiled.h
92 std::string pch_file = outputs[0].value();
93 pch_file.erase(pch_file.length() - 4);
94 out << " -include " << pch_file;
95 }
96 } else {
97 RecursiveTargetConfigStringsToStream(config, target, getter,
98 flag_escape_options, out);
99 }
100 } else {
101 RecursiveTargetConfigStringsToStream(config, target, getter,
102 flag_escape_options, out);
103 }
104
105 if (write_substitution)
106 out << std::endl;
107 }
108
GetPCHOutputFiles(const Target * target,const char * tool_name,std::vector<OutputFile> * outputs)109 void GetPCHOutputFiles(const Target* target,
110 const char* tool_name,
111 std::vector<OutputFile>* outputs) {
112 outputs->clear();
113
114 // Compute the tool. This must use the tool type passed in rather than the
115 // detected file type of the precompiled source file since the same
116 // precompiled source file will be used for separate C/C++ compiles.
117 const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
118 if (!tool)
119 return;
120 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
121 target, target->config_values().precompiled_source(), tool->outputs(),
122 outputs);
123
124 if (outputs->empty())
125 return;
126 if (outputs->size() > 1)
127 outputs->resize(1); // Only link the first output from the compiler tool.
128
129 std::string& output_value = (*outputs)[0].value();
130 size_t extension_offset = FindExtensionOffset(output_value);
131 if (extension_offset == std::string::npos) {
132 // No extension found.
133 return;
134 }
135 DCHECK(extension_offset >= 1);
136 DCHECK(output_value[extension_offset - 1] == '.');
137
138 std::string output_extension;
139 CTool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
140 switch (header_type) {
141 case CTool::PCH_MSVC:
142 output_extension = GetWindowsPCHObjectExtension(
143 tool_name, output_value.substr(extension_offset - 1));
144 break;
145 case CTool::PCH_GCC:
146 output_extension = GetGCCPCHOutputExtension(tool_name);
147 break;
148 case CTool::PCH_NONE:
149 NOTREACHED() << "No outputs for no PCH type.";
150 break;
151 }
152 output_value.replace(extension_offset - 1, std::string::npos,
153 output_extension);
154 }
155
GetGCCPCHOutputExtension(const char * tool_name)156 std::string GetGCCPCHOutputExtension(const char* tool_name) {
157 const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
158 std::string result = ".";
159 // For GCC, the output name must have a .gch suffix and be annotated with
160 // the language type. For example:
161 // obj/foo/target_name.header.h ->
162 // obj/foo/target_name.header.h-cc.gch
163 // In order for the compiler to pick it up, the output name (minus the .gch
164 // suffix MUST match whatever is passed to the -include flag).
165 result += "h-";
166 result += lang_suffix;
167 result += ".gch";
168 return result;
169 }
170
GetWindowsPCHObjectExtension(const char * tool_name,const std::string & obj_extension)171 std::string GetWindowsPCHObjectExtension(const char* tool_name,
172 const std::string& obj_extension) {
173 const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
174 std::string result = ".";
175 // For MSVC, annotate the obj files with the language type. For example:
176 // obj/foo/target_name.precompile.obj ->
177 // obj/foo/target_name.precompile.cc.obj
178 result += lang_suffix;
179 result += obj_extension;
180 return result;
181 }
182