1 // Copyright 2014 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.
5 #include "tools/gn/substitution_pattern.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "tools/gn/build_settings.h"
9 #include "tools/gn/err.h"
10 #include "tools/gn/filesystem_utils.h"
11 #include "tools/gn/value.h"
13 SubstitutionPattern::Subrange::Subrange()
14 : type(SUBSTITUTION_LITERAL
) {
17 SubstitutionPattern::Subrange::Subrange(SubstitutionType t
,
23 SubstitutionPattern::Subrange::~Subrange() {
26 SubstitutionPattern::SubstitutionPattern() : origin_(nullptr) {
29 SubstitutionPattern::~SubstitutionPattern() {
32 bool SubstitutionPattern::Parse(const Value
& value
, Err
* err
) {
33 if (!value
.VerifyTypeIs(Value::STRING
, err
))
35 return Parse(value
.string_value(), value
.origin(), err
);
38 bool SubstitutionPattern::Parse(const std::string
& str
,
39 const ParseNode
* origin
,
41 DCHECK(ranges_
.empty()); // Should only be called once.
45 size_t next
= str
.find("{{", cur
);
47 // Pick up everything from the previous spot to here as a literal.
48 if (next
== std::string::npos
) {
49 if (cur
!= str
.size())
50 ranges_
.push_back(Subrange(SUBSTITUTION_LITERAL
, str
.substr(cur
)));
52 } else if (next
> cur
) {
54 Subrange(SUBSTITUTION_LITERAL
, str
.substr(cur
, next
- cur
)));
57 // Find which specific pattern this corresponds to.
58 bool found_match
= false;
59 for (size_t i
= SUBSTITUTION_FIRST_PATTERN
;
60 i
< SUBSTITUTION_NUM_TYPES
; i
++) {
61 const char* cur_pattern
= kSubstitutionNames
[i
];
62 size_t cur_len
= strlen(cur_pattern
);
63 if (str
.compare(next
, cur_len
, cur_pattern
) == 0) {
64 ranges_
.push_back(Subrange(static_cast<SubstitutionType
>(i
)));
71 // Expect all occurrances of {{ to resolve to a pattern.
73 // Could make this error message more friendly if it comes up a lot. But
74 // most people will not be writing substitution patterns and the code
75 // to exactly indicate the error location is tricky.
76 *err
= Err(origin
, "Unknown substitution pattern",
77 "Found a {{ at offset " +
78 base::SizeTToString(next
) +
79 " and did not find a known substitution following it.");
87 // Fill required types vector.
88 SubstitutionBits bits
;
89 FillRequiredTypes(&bits
);
90 bits
.FillVector(&required_types_
);
95 SubstitutionPattern
SubstitutionPattern::MakeForTest(const char* str
) {
97 SubstitutionPattern pattern
;
98 CHECK(pattern
.Parse(str
, nullptr, &err
)) << err
.message();
102 std::string
SubstitutionPattern::AsString() const {
104 for (const auto& elem
: ranges_
) {
105 if (elem
.type
== SUBSTITUTION_LITERAL
)
106 result
.append(elem
.literal
);
108 result
.append(kSubstitutionNames
[elem
.type
]);
113 void SubstitutionPattern::FillRequiredTypes(SubstitutionBits
* bits
) const {
114 for (const auto& elem
: ranges_
) {
115 if (elem
.type
!= SUBSTITUTION_LITERAL
)
116 bits
->used
[static_cast<size_t>(elem
.type
)] = true;
120 bool SubstitutionPattern::IsInOutputDir(const BuildSettings
* build_settings
,
122 if (ranges_
.empty()) {
123 *err
= Err(origin_
, "This is empty but I was expecting an output file.");
127 if (ranges_
[0].type
== SUBSTITUTION_LITERAL
) {
128 // If the first thing is a literal, it must start with the output dir.
129 if (!EnsureStringIsInOutputDir(
130 build_settings
->build_dir(),
131 ranges_
[0].literal
, origin_
, err
))
134 // Otherwise, the first subrange must be a pattern that expands to
135 // something in the output directory.
136 if (!SubstitutionIsInOutputDir(ranges_
[0].type
)) {
138 "File is not inside output directory.",
139 "The given file should be in the output directory. Normally you\n"
140 "would specify\n\"$target_out_dir/foo\" or "
141 "\"{{source_gen_dir}}/foo\".");