• 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(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