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(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)44 void WriteOneFlag(const Target* target,
45 const Substitution* subst_enum,
46 bool has_precompiled_headers,
47 const char* tool_name,
48 const std::vector<std::string>& (ConfigValues::*getter)()
49 const,
50 EscapeOptions flag_escape_options,
51 PathOutput& path_output,
52 std::ostream& out,
53 bool write_substitution) {
54 if (!target->toolchain()->substitution_bits().used.count(subst_enum))
55 return;
56
57 if (write_substitution)
58 out << subst_enum->ninja_name << " =";
59
60 if (has_precompiled_headers) {
61 const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
62 if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
63 // Name the .pch file.
64 out << " /Fp";
65 path_output.WriteFile(out, GetWindowsPCHFile(target, tool_name));
66
67 // Enables precompiled headers and names the .h file. It's a string
68 // rather than a file name (so no need to rebase or use path_output).
69 out << " /Yu" << target->config_values().precompiled_header();
70 RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
71 out);
72 } else if (tool && tool->precompiled_header_type() == CTool::PCH_GCC) {
73 // The targets to build the .gch files should omit the -include flag
74 // below. To accomplish this, each substitution flag is overwritten in
75 // the target rule and these values are repeated. The -include flag is
76 // omitted in place of the required -x <header lang> flag for .gch
77 // targets.
78 RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
79 out);
80
81 // Compute the gch file (it will be language-specific).
82 std::vector<OutputFile> outputs;
83 GetPCHOutputFiles(target, tool_name, &outputs);
84 if (!outputs.empty()) {
85 // Trim the .gch suffix for the -include flag.
86 // e.g. for gch file foo/bar/target.precompiled.h.gch:
87 // -include foo/bar/target.precompiled.h
88 std::string pch_file = outputs[0].value();
89 pch_file.erase(pch_file.length() - 4);
90 out << " -include " << pch_file;
91 }
92 } else {
93 RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
94 out);
95 }
96 } else {
97 RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
98 out);
99 }
100
101 if (write_substitution)
102 out << std::endl;
103 }
104
GetPCHOutputFiles(const Target * target,const char * tool_name,std::vector<OutputFile> * outputs)105 void GetPCHOutputFiles(const Target* target,
106 const char* tool_name,
107 std::vector<OutputFile>* outputs) {
108 outputs->clear();
109
110 // Compute the tool. This must use the tool type passed in rather than the
111 // detected file type of the precompiled source file since the same
112 // precompiled source file will be used for separate C/C++ compiles.
113 const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
114 if (!tool)
115 return;
116 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
117 target, target->config_values().precompiled_source(), tool->outputs(),
118 outputs);
119
120 if (outputs->empty())
121 return;
122 if (outputs->size() > 1)
123 outputs->resize(1); // Only link the first output from the compiler tool.
124
125 std::string& output_value = (*outputs)[0].value();
126 size_t extension_offset = FindExtensionOffset(output_value);
127 if (extension_offset == std::string::npos) {
128 // No extension found.
129 return;
130 }
131 DCHECK(extension_offset >= 1);
132 DCHECK(output_value[extension_offset - 1] == '.');
133
134 std::string output_extension;
135 CTool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
136 switch (header_type) {
137 case CTool::PCH_MSVC:
138 output_extension = GetWindowsPCHObjectExtension(
139 tool_name, output_value.substr(extension_offset - 1));
140 break;
141 case CTool::PCH_GCC:
142 output_extension = GetGCCPCHOutputExtension(tool_name);
143 break;
144 case CTool::PCH_NONE:
145 NOTREACHED() << "No outputs for no PCH type.";
146 break;
147 }
148 output_value.replace(extension_offset - 1, std::string::npos,
149 output_extension);
150 }
151
GetGCCPCHOutputExtension(const char * tool_name)152 std::string GetGCCPCHOutputExtension(const char* tool_name) {
153 const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
154 std::string result = ".";
155 // For GCC, the output name must have a .gch suffix and be annotated with
156 // the language type. For example:
157 // obj/foo/target_name.header.h ->
158 // obj/foo/target_name.header.h-cc.gch
159 // In order for the compiler to pick it up, the output name (minus the .gch
160 // suffix MUST match whatever is passed to the -include flag).
161 result += "h-";
162 result += lang_suffix;
163 result += ".gch";
164 return result;
165 }
166
GetWindowsPCHObjectExtension(const char * tool_name,const std::string & obj_extension)167 std::string GetWindowsPCHObjectExtension(const char* tool_name,
168 const std::string& obj_extension) {
169 const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
170 std::string result = ".";
171 // For MSVC, annotate the obj files with the language type. For example:
172 // obj/foo/target_name.precompile.obj ->
173 // obj/foo/target_name.precompile.cc.obj
174 result += lang_suffix;
175 result += obj_extension;
176 return result;
177 }
178