• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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