OLD | NEW |
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/file_template.h" | 5 #include "tools/gn/file_template.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <iostream> | 8 #include <iostream> |
9 | 9 |
10 #include "tools/gn/escape.h" | 10 #include "tools/gn/escape.h" |
11 #include "tools/gn/filesystem_utils.h" | 11 #include "tools/gn/filesystem_utils.h" |
12 #include "tools/gn/string_utils.h" | 12 #include "tools/gn/string_utils.h" |
13 #include "tools/gn/target.h" | 13 #include "tools/gn/target.h" |
14 | 14 |
15 const char FileTemplate::kSource[] = "{{source}}"; | 15 const char FileTemplate::kSource[] = "{{source}}"; |
16 const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}"; | 16 const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}"; |
17 const char FileTemplate::kSourceFilePart[] = "{{source_file_part}}"; | 17 const char FileTemplate::kSourceFilePart[] = "{{source_file_part}}"; |
| 18 const char FileTemplate::kSourceDir[] = "{{source_dir}}"; |
| 19 const char FileTemplate::kRootRelDir[] = "{{source_root_relative_dir}}"; |
| 20 const char FileTemplate::kSourceGenDir[] = "{{source_gen_dir}}"; |
| 21 const char FileTemplate::kSourceOutDir[] = "{{source_out_dir}}"; |
18 | 22 |
19 const char kSourceExpansion_Help[] = | 23 const char kSourceExpansion_Help[] = |
20 "How Source Expansion Works\n" | 24 "How Source Expansion Works\n" |
21 "\n" | 25 "\n" |
22 " Source expansion is used for the action_foreach and copy target types\n" | 26 " Source expansion is used for the action_foreach and copy target types\n" |
23 " to map source file names to output file names or arguments.\n" | 27 " to map source file names to output file names or arguments.\n" |
24 "\n" | 28 "\n" |
25 " To perform source expansion in the outputs, GN maps every entry in the\n" | 29 " To perform source expansion in the outputs, GN maps every entry in the\n" |
26 " sources to every entry in the outputs list, producing the cross\n" | 30 " sources to every entry in the outputs list, producing the cross\n" |
27 " product of all combinations, expanding placeholders (see below).\n" | 31 " product of all combinations, expanding placeholders (see below).\n" |
28 "\n" | 32 "\n" |
29 " Source expansion in the args works similarly, but performing the\n" | 33 " Source expansion in the args works similarly, but performing the\n" |
30 " placeholder substitution produces a different set of arguments for\n" | 34 " placeholder substitution produces a different set of arguments for\n" |
31 " each invocation of the script.\n" | 35 " each invocation of the script.\n" |
32 "\n" | 36 "\n" |
33 " If no placeholders are found, the outputs or args list will be treated\n" | 37 " If no placeholders are found, the outputs or args list will be treated\n" |
34 " as a static list of literal file names that do not depend on the\n" | 38 " as a static list of literal file names that do not depend on the\n" |
35 " sources.\n" | 39 " sources.\n" |
36 "\n" | 40 "\n" |
37 " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n" | 41 " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n" |
38 " this is applied.\n" | 42 " this is applied.\n" |
39 "\n" | 43 "\n" |
40 "Placeholders\n" | 44 "Placeholders\n" |
41 "\n" | 45 "\n" |
42 " {{source}}\n" | 46 " {{source}}\n" |
43 " The name of the source file relative to the root build output\n" | 47 " The name of the source file relative to the root build output\n" |
44 " directory (which is the current directory when running compilers\n" | 48 " directory (which is the current directory when running compilers\n" |
45 " and scripts). This will generally be used for specifying inputs\n" | 49 " and scripts). This will generally be used for specifying inputs\n" |
46 " to a script in the \"args\" variable.\n" | 50 " to a script in the \"args\" variable.\n" |
| 51 " \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n" |
47 "\n" | 52 "\n" |
48 " {{source_file_part}}\n" | 53 " {{source_file_part}}\n" |
49 " The file part of the source including the extension. For the\n" | 54 " The file part of the source including the extension.\n" |
50 " source \"foo/bar.txt\" the source file part will be \"bar.txt\".\n" | 55 " \"//foo/bar/baz.txt\" => \"baz.txt\"\n" |
51 "\n" | 56 "\n" |
52 " {{source_name_part}}\n" | 57 " {{source_name_part}}\n" |
53 " The filename part of the source file with no directory or\n" | 58 " The filename part of the source file with no directory or\n" |
54 " extension. This will generally be used for specifying a\n" | 59 " extension. This will generally be used for specifying a\n" |
55 " transformation from a soruce file to a destination file with the\n" | 60 " transformation from a soruce file to a destination file with the\n" |
56 " same name but different extension. For the source \"foo/bar.txt\"\n" | 61 " same name but different extension.\n" |
57 " the source name part will be \"bar\".\n" | 62 " \"//foo/bar/baz.txt\" => \"baz\"\n" |
| 63 "\n" |
| 64 " {{source_dir}}\n" |
| 65 " The directory containing the source file, relative to the build\n" |
| 66 " directory, with no trailing slash.\n" |
| 67 " \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n" |
| 68 "\n" |
| 69 " {{source_root_relative_dir}}\n" |
| 70 " The path to the source file's directory relative to the source\n" |
| 71 " root, with no leading \"//\" or trailing slashes. If the path is\n" |
| 72 " system-absolute, (beginning in a single slash) this will just\n" |
| 73 " return the path with no trailing slash.\n" |
| 74 " \"//foo/bar/baz.txt\" => \"foo/bar\"\n" |
| 75 "\n" |
| 76 " {{source_gen_dir}}\n" |
| 77 " The generated file directory corresponding to the source file's\n" |
| 78 " path, relative to the build directory. This will be different than\n" |
| 79 " the target's generated file directory if the source file is in a\n" |
| 80 " different directory than the build.gn file. If the input path is\n" |
| 81 " system absolute, this will return the root generated file\n" |
| 82 " directory." |
| 83 " \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n" |
| 84 "\n" |
| 85 " {{source_out_dir}}\n" |
| 86 " The object file directory corresponding to the source file's\n" |
| 87 " path, relative to the build directory. this us be different than\n" |
| 88 " the target's out directory if the source file is in a different\n" |
| 89 " directory than the build.gn file. if the input path is system\n" |
| 90 " absolute, this will return the root generated file directory.\n" |
| 91 " \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n" |
58 "\n" | 92 "\n" |
59 "Examples\n" | 93 "Examples\n" |
60 "\n" | 94 "\n" |
61 " Non-varying outputs:\n" | 95 " Non-varying outputs:\n" |
62 " action(\"hardcoded_outputs\") {\n" | 96 " action(\"hardcoded_outputs\") {\n" |
63 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" | 97 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" |
64 " outputs = [ \"$target_out_dir/output1.dat\",\n" | 98 " outputs = [ \"$target_out_dir/output1.dat\",\n" |
65 " \"$target_out_dir/output2.dat\" ]\n" | 99 " \"$target_out_dir/output2.dat\" ]\n" |
66 " }\n" | 100 " }\n" |
67 " The outputs in this case will be the two literal files given.\n" | 101 " The outputs in this case will be the two literal files given.\n" |
68 "\n" | 102 "\n" |
69 " Varying outputs:\n" | 103 " Varying outputs:\n" |
70 " action_foreach(\"varying_outputs\") {\n" | 104 " action_foreach(\"varying_outputs\") {\n" |
71 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" | 105 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" |
72 " outputs = [ \"$target_out_dir/{{source_name_part}}.h\",\n" | 106 " outputs = [ \"$target_out_dir/{{source_name_part}}.h\",\n" |
73 " \"$target_out_dir/{{source_name_part}}.cc\" ]\n" | 107 " \"$target_out_dir/{{source_name_part}}.cc\" ]\n" |
74 " }\n" | 108 " }\n" |
75 " Performing source expansion will result in the following output names:\n" | 109 " Performing source expansion will result in the following output names:\n" |
76 " //out/Debug/obj/mydirectory/input1.h\n" | 110 " //out/Debug/obj/mydirectory/input1.h\n" |
77 " //out/Debug/obj/mydirectory/input1.cc\n" | 111 " //out/Debug/obj/mydirectory/input1.cc\n" |
78 " //out/Debug/obj/mydirectory/input2.h\n" | 112 " //out/Debug/obj/mydirectory/input2.h\n" |
79 " //out/Debug/obj/mydirectory/input2.cc\n"; | 113 " //out/Debug/obj/mydirectory/input2.cc\n"; |
80 | 114 |
81 FileTemplate::FileTemplate(const Value& t, Err* err) | 115 FileTemplate::FileTemplate(const Settings* settings, const Value& t, Err* err) |
82 : has_substitutions_(false) { | 116 : settings_(settings), |
| 117 has_substitutions_(false) { |
83 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); | 118 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); |
84 ParseInput(t, err); | 119 ParseInput(t, err); |
85 } | 120 } |
86 | 121 |
87 FileTemplate::FileTemplate(const std::vector<std::string>& t) | 122 FileTemplate::FileTemplate(const Settings* settings, |
88 : has_substitutions_(false) { | 123 const std::vector<std::string>& t) |
| 124 : settings_(settings), |
| 125 has_substitutions_(false) { |
89 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); | 126 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); |
90 for (size_t i = 0; i < t.size(); i++) | 127 for (size_t i = 0; i < t.size(); i++) |
91 ParseOneTemplateString(t[i]); | 128 ParseOneTemplateString(t[i]); |
92 } | 129 } |
93 | 130 |
94 FileTemplate::FileTemplate(const std::vector<SourceFile>& t) | 131 FileTemplate::FileTemplate(const Settings* settings, |
95 : has_substitutions_(false) { | 132 const std::vector<SourceFile>& t) |
| 133 : settings_(settings), |
| 134 has_substitutions_(false) { |
96 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); | 135 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); |
97 for (size_t i = 0; i < t.size(); i++) | 136 for (size_t i = 0; i < t.size(); i++) |
98 ParseOneTemplateString(t[i].value()); | 137 ParseOneTemplateString(t[i].value()); |
99 } | 138 } |
100 | 139 |
101 FileTemplate::~FileTemplate() { | 140 FileTemplate::~FileTemplate() { |
102 } | 141 } |
103 | 142 |
104 // static | 143 // static |
105 FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) { | 144 FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) { |
106 const Target::FileList& outputs = target->action_values().outputs(); | 145 const Target::FileList& outputs = target->action_values().outputs(); |
107 std::vector<std::string> output_template_args; | 146 std::vector<std::string> output_template_args; |
108 for (size_t i = 0; i < outputs.size(); i++) | 147 for (size_t i = 0; i < outputs.size(); i++) |
109 output_template_args.push_back(outputs[i].value()); | 148 output_template_args.push_back(outputs[i].value()); |
110 return FileTemplate(output_template_args); | 149 return FileTemplate(target->settings(), output_template_args); |
111 } | 150 } |
112 | 151 |
113 bool FileTemplate::IsTypeUsed(Subrange::Type type) const { | 152 bool FileTemplate::IsTypeUsed(Subrange::Type type) const { |
114 DCHECK(type > Subrange::LITERAL && type < Subrange::NUM_TYPES); | 153 DCHECK(type > Subrange::LITERAL && type < Subrange::NUM_TYPES); |
115 return types_required_[type]; | 154 return types_required_[type]; |
116 } | 155 } |
117 | 156 |
118 void FileTemplate::Apply(const Value& sources, | 157 void FileTemplate::Apply(const SourceFile& source, |
119 const ParseNode* origin, | 158 std::vector<std::string>* output) const { |
120 std::vector<Value>* dest, | |
121 Err* err) const { | |
122 if (!sources.VerifyTypeIs(Value::LIST, err)) | |
123 return; | |
124 dest->reserve(sources.list_value().size() * templates_.container().size()); | |
125 | |
126 // Temporary holding place, allocate outside to re-use- buffer. | |
127 std::vector<std::string> string_output; | |
128 | |
129 const std::vector<Value>& sources_list = sources.list_value(); | |
130 for (size_t i = 0; i < sources_list.size(); i++) { | |
131 string_output.clear(); | |
132 if (!sources_list[i].VerifyTypeIs(Value::STRING, err)) | |
133 return; | |
134 | |
135 ApplyString(sources_list[i].string_value(), &string_output); | |
136 for (size_t out_i = 0; out_i < string_output.size(); out_i++) | |
137 dest->push_back(Value(origin, string_output[out_i])); | |
138 } | |
139 } | |
140 | |
141 void FileTemplate::ApplyString(const std::string& str, | |
142 std::vector<std::string>* output) const { | |
143 // Compute all substitutions needed so we can just do substitutions below. | 159 // Compute all substitutions needed so we can just do substitutions below. |
144 // We skip the LITERAL one since that varies each time. | 160 // We skip the LITERAL one since that varies each time. |
145 std::string subst[Subrange::NUM_TYPES]; | 161 std::string subst[Subrange::NUM_TYPES]; |
146 for (int i = 1; i < Subrange::NUM_TYPES; i++) { | 162 for (int i = 1; i < Subrange::NUM_TYPES; i++) { |
147 if (types_required_[i]) | 163 if (types_required_[i]) { |
148 subst[i] = GetSubstitution(str, static_cast<Subrange::Type>(i)); | 164 subst[i] = |
| 165 GetSubstitution(settings_, source, static_cast<Subrange::Type>(i)); |
| 166 } |
149 } | 167 } |
150 | 168 |
151 size_t first_output_index = output->size(); | 169 size_t first_output_index = output->size(); |
152 output->resize(output->size() + templates_.container().size()); | 170 output->resize(output->size() + templates_.container().size()); |
153 for (size_t template_i = 0; | 171 for (size_t template_i = 0; |
154 template_i < templates_.container().size(); template_i++) { | 172 template_i < templates_.container().size(); template_i++) { |
155 const Template& t = templates_[template_i]; | 173 const Template& t = templates_[template_i]; |
156 std::string& cur_output = (*output)[first_output_index + template_i]; | 174 std::string& cur_output = (*output)[first_output_index + template_i]; |
157 for (size_t subrange_i = 0; subrange_i < t.container().size(); | 175 for (size_t subrange_i = 0; subrange_i < t.container().size(); |
158 subrange_i++) { | 176 subrange_i++) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 // argument. | 220 // argument. |
203 out << '"' << item_str << '"'; | 221 out << '"' << item_str << '"'; |
204 } else { | 222 } else { |
205 out << item_str; | 223 out << item_str; |
206 } | 224 } |
207 } | 225 } |
208 } | 226 } |
209 | 227 |
210 void FileTemplate::WriteNinjaVariablesForSubstitution( | 228 void FileTemplate::WriteNinjaVariablesForSubstitution( |
211 std::ostream& out, | 229 std::ostream& out, |
212 const std::string& source, | 230 const Settings* settings, |
| 231 const SourceFile& source, |
213 const EscapeOptions& escape_options) const { | 232 const EscapeOptions& escape_options) const { |
214 for (int i = 1; i < Subrange::NUM_TYPES; i++) { | 233 for (int i = 1; i < Subrange::NUM_TYPES; i++) { |
215 if (types_required_[i]) { | 234 if (types_required_[i]) { |
216 Subrange::Type type = static_cast<Subrange::Type>(i); | 235 Subrange::Type type = static_cast<Subrange::Type>(i); |
217 out << " " << GetNinjaVariableNameForType(type) << " = "; | 236 out << " " << GetNinjaVariableNameForType(type) << " = "; |
218 EscapeStringToStream(out, GetSubstitution(source, type), escape_options); | 237 EscapeStringToStream(out, GetSubstitution(settings, source, type), |
| 238 escape_options); |
219 out << std::endl; | 239 out << std::endl; |
220 } | 240 } |
221 } | 241 } |
222 } | 242 } |
223 | 243 |
224 // static | 244 // static |
225 const char* FileTemplate::GetNinjaVariableNameForType(Subrange::Type type) { | 245 const char* FileTemplate::GetNinjaVariableNameForType(Subrange::Type type) { |
226 switch (type) { | 246 switch (type) { |
227 case Subrange::SOURCE: | 247 case Subrange::SOURCE: |
228 return "source"; | 248 return "source"; |
229 case Subrange::NAME_PART: | 249 case Subrange::NAME_PART: |
230 return "source_name_part"; | 250 return "source_name_part"; |
231 case Subrange::FILE_PART: | 251 case Subrange::FILE_PART: |
232 return "source_file_part"; | 252 return "source_file_part"; |
| 253 case Subrange::SOURCE_DIR: |
| 254 return "source_dir"; |
| 255 case Subrange::ROOT_RELATIVE_DIR: |
| 256 return "source_root_rel_dir"; |
| 257 case Subrange::SOURCE_GEN_DIR: |
| 258 return "source_gen_dir"; |
| 259 case Subrange::SOURCE_OUT_DIR: |
| 260 return "source_out_dir"; |
| 261 |
233 default: | 262 default: |
234 NOTREACHED(); | 263 NOTREACHED(); |
235 } | 264 } |
236 return ""; | 265 return ""; |
237 } | 266 } |
238 | 267 |
239 // static | 268 // static |
240 std::string FileTemplate::GetSubstitution(const std::string& source, | 269 std::string FileTemplate::GetSubstitution(const Settings* settings, |
| 270 const SourceFile& source, |
241 Subrange::Type type) { | 271 Subrange::Type type) { |
242 switch (type) { | 272 switch (type) { |
243 case Subrange::SOURCE: | 273 case Subrange::SOURCE: |
244 return source; | 274 if (source.is_system_absolute()) |
| 275 return source.value(); |
| 276 return RebaseSourceAbsolutePath(source.value(), |
| 277 settings->build_settings()->build_dir()); |
| 278 |
245 case Subrange::NAME_PART: | 279 case Subrange::NAME_PART: |
246 return FindFilenameNoExtension(&source).as_string(); | 280 return FindFilenameNoExtension(&source.value()).as_string(); |
| 281 |
247 case Subrange::FILE_PART: | 282 case Subrange::FILE_PART: |
248 return FindFilename(&source).as_string(); | 283 return source.GetName(); |
| 284 |
| 285 case Subrange::SOURCE_DIR: |
| 286 if (source.is_system_absolute()) |
| 287 return DirectoryWithNoLastSlash(source.GetDir()); |
| 288 return RebaseSourceAbsolutePath( |
| 289 DirectoryWithNoLastSlash(source.GetDir()), |
| 290 settings->build_settings()->build_dir()); |
| 291 |
| 292 case Subrange::ROOT_RELATIVE_DIR: |
| 293 if (source.is_system_absolute()) |
| 294 return DirectoryWithNoLastSlash(source.GetDir()); |
| 295 return RebaseSourceAbsolutePath( |
| 296 DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//")); |
| 297 |
| 298 case Subrange::SOURCE_GEN_DIR: |
| 299 return RebaseSourceAbsolutePath( |
| 300 DirectoryWithNoLastSlash( |
| 301 GetGenDirForSourceDir(settings, source.GetDir())), |
| 302 settings->build_settings()->build_dir()); |
| 303 |
| 304 case Subrange::SOURCE_OUT_DIR: |
| 305 return RebaseSourceAbsolutePath( |
| 306 DirectoryWithNoLastSlash( |
| 307 GetOutputDirForSourceDir(settings, source.GetDir())), |
| 308 settings->build_settings()->build_dir()); |
| 309 |
249 default: | 310 default: |
250 NOTREACHED(); | 311 NOTREACHED(); |
251 } | 312 } |
252 return std::string(); | 313 return std::string(); |
253 } | 314 } |
254 | 315 |
255 void FileTemplate::ParseInput(const Value& value, Err* err) { | 316 void FileTemplate::ParseInput(const Value& value, Err* err) { |
256 switch (value.type()) { | 317 switch (value.type()) { |
257 case Value::STRING: | 318 case Value::STRING: |
258 ParseOneTemplateString(value.string_value()); | 319 ParseOneTemplateString(value.string_value()); |
(...skipping 22 matching lines...) Expand all Loading... |
281 // Pick up everything from the previous spot to here as a literal. | 342 // Pick up everything from the previous spot to here as a literal. |
282 if (next == std::string::npos) { | 343 if (next == std::string::npos) { |
283 if (cur != str.size()) | 344 if (cur != str.size()) |
284 t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur))); | 345 t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur))); |
285 break; | 346 break; |
286 } else if (next > cur) { | 347 } else if (next > cur) { |
287 t.container().push_back( | 348 t.container().push_back( |
288 Subrange(Subrange::LITERAL, str.substr(cur, next - cur))); | 349 Subrange(Subrange::LITERAL, str.substr(cur, next - cur))); |
289 } | 350 } |
290 | 351 |
| 352 // Given the name of the string constant and enum for a template parameter, |
| 353 // checks for it and stores it. Writing this as a function requires passing |
| 354 // the entire state of this function as arguments, so this actually ends |
| 355 // up being more clear. |
| 356 #define IF_MATCH_THEN_STORE(const_name, enum_name) \ |
| 357 if (str.compare(next, arraysize(const_name) - 1, const_name) == 0) { \ |
| 358 t.container().push_back(Subrange(Subrange::enum_name)); \ |
| 359 types_required_[Subrange::enum_name] = true; \ |
| 360 has_substitutions_ = true; \ |
| 361 cur = next + arraysize(const_name) - 1; \ |
| 362 } |
| 363 |
291 // Decode the template param. | 364 // Decode the template param. |
292 if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) { | 365 IF_MATCH_THEN_STORE(kSource, SOURCE) |
293 t.container().push_back(Subrange(Subrange::SOURCE)); | 366 else IF_MATCH_THEN_STORE(kSourceNamePart, NAME_PART) |
294 types_required_[Subrange::SOURCE] = true; | 367 else IF_MATCH_THEN_STORE(kSourceFilePart, FILE_PART) |
295 has_substitutions_ = true; | 368 else IF_MATCH_THEN_STORE(kSourceDir, SOURCE_DIR) |
296 cur = next + arraysize(kSource) - 1; | 369 else IF_MATCH_THEN_STORE(kRootRelDir, ROOT_RELATIVE_DIR) |
297 } else if (str.compare(next, arraysize(kSourceNamePart) - 1, | 370 else IF_MATCH_THEN_STORE(kSourceGenDir, SOURCE_GEN_DIR) |
298 kSourceNamePart) == 0) { | 371 else IF_MATCH_THEN_STORE(kSourceOutDir, SOURCE_OUT_DIR) |
299 t.container().push_back(Subrange(Subrange::NAME_PART)); | 372 else { |
300 types_required_[Subrange::NAME_PART] = true; | |
301 has_substitutions_ = true; | |
302 cur = next + arraysize(kSourceNamePart) - 1; | |
303 } else if (str.compare(next, arraysize(kSourceFilePart) - 1, | |
304 kSourceFilePart) == 0) { | |
305 t.container().push_back(Subrange(Subrange::FILE_PART)); | |
306 types_required_[Subrange::FILE_PART] = true; | |
307 has_substitutions_ = true; | |
308 cur = next + arraysize(kSourceFilePart) - 1; | |
309 } else { | |
310 // If it's not a match, treat it like a one-char literal (this will be | 373 // If it's not a match, treat it like a one-char literal (this will be |
311 // rare, so it's not worth the bother to add to the previous literal) so | 374 // rare, so it's not worth the bother to add to the previous literal) so |
312 // we can keep going. | 375 // we can keep going. |
313 t.container().push_back(Subrange(Subrange::LITERAL, "{")); | 376 t.container().push_back(Subrange(Subrange::LITERAL, "{")); |
314 cur = next + 1; | 377 cur = next + 1; |
315 } | 378 } |
| 379 |
| 380 #undef IF_MATCH_THEN_STORE |
316 } | 381 } |
317 } | 382 } |
OLD | NEW |