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
3 // found in the LICENSE file.
4
5 #include "gn/source_dir.h"
6
7 #include <string>
8
9 #include "base/logging.h"
10 #include "gn/filesystem_utils.h"
11 #include "gn/source_file.h"
12 #include "util/build_config.h"
13
14 namespace {
15
AssertValueSourceDirString(std::string_view s)16 void AssertValueSourceDirString(std::string_view s) {
17 if (!s.empty()) {
18 #if defined(OS_WIN)
19 DCHECK(s[0] == '/' ||
20 (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
21 #else
22 DCHECK(s[0] == '/');
23 #endif
24 DCHECK(EndsWithSlash(s)) << s;
25 }
26 }
27
28 // Validates input value (input_value) and sets proper error message.
29 // Note: Parameter blame_input is used only for generating error message.
ValidateResolveInput(bool as_file,const Value & blame_input_value,std::string_view input_value,Err * err)30 bool ValidateResolveInput(bool as_file,
31 const Value& blame_input_value,
32 std::string_view input_value,
33 Err* err) {
34 if (as_file) {
35 // It's an error to resolve an empty string or one that is a directory
36 // (indicated by a trailing slash) because this is the function that expects
37 // to return a file.
38 if (input_value.empty()) {
39 *err = Err(blame_input_value, "Empty file path.",
40 "You can't use empty strings as file paths.");
41 return false;
42 } else if (input_value[input_value.size() - 1] == '/') {
43 std::string help = "You specified the path\n ";
44 help.append(std::string(input_value));
45 help.append(
46 "\nand it ends in a slash, indicating you think it's a directory."
47 "\nBut here you're supposed to be listing a file.");
48 *err = Err(blame_input_value, "File path ends in a slash.", help);
49 return false;
50 }
51 } else if (input_value.empty()) {
52 *err = Err(blame_input_value, "Empty directory path.",
53 "You can't use empty strings as directories.");
54 return false;
55 }
56 return true;
57 }
58
SourceDirStringAtom(std::string_view s)59 static StringAtom SourceDirStringAtom(std::string_view s) {
60 if (EndsWithSlash(s)) { // Avoid allocation when possible.
61 AssertValueSourceDirString(s);
62 return StringAtom(s);
63 }
64
65 std::string str;
66 str.reserve(s.size() + 1);
67 str += s;
68 str.push_back('/');
69 AssertValueSourceDirString(str);
70 return StringAtom(str);
71 }
72
73 } // namespace
74
SourceDir(std::string_view s)75 SourceDir::SourceDir(std::string_view s) : value_(SourceDirStringAtom(s)) {}
76
ResolveRelativeAs(bool as_file,const Value & blame_input_value,std::string_view input_value,Err * err,std::string_view source_root) const77 std::string SourceDir::ResolveRelativeAs(bool as_file,
78 const Value& blame_input_value,
79 std::string_view input_value,
80 Err* err,
81 std::string_view source_root) const {
82 if (!ValidateResolveInput(as_file, blame_input_value, input_value, err)) {
83 return std::string();
84 }
85 return ResolveRelative(input_value, value_.str(), as_file, source_root);
86 }
87
ResolveRelativeFile(const Value & p,Err * err,std::string_view source_root) const88 SourceFile SourceDir::ResolveRelativeFile(const Value& p,
89 Err* err,
90 std::string_view source_root) const {
91 SourceFile ret;
92
93 if (!p.VerifyTypeIs(Value::STRING, err))
94 return ret;
95
96 const std::string& input_string = p.string_value();
97 if (!ValidateResolveInput(true, p, input_string, err))
98 return ret;
99
100 ret.SetValue(ResolveRelative(input_string, value_.str(), true, source_root));
101 return ret;
102 }
103
ResolveRelativeDir(const Value & blame_input_value,std::string_view input_value,Err * err,std::string_view source_root) const104 SourceDir SourceDir::ResolveRelativeDir(const Value& blame_input_value,
105 std::string_view input_value,
106 Err* err,
107 std::string_view source_root) const {
108 SourceDir ret;
109 ret.value_ = StringAtom(ResolveRelativeAs(false, blame_input_value,
110 input_value, err, source_root));
111 return ret;
112 }
113
ResolveRelativeAs(bool as_file,const Value & v,Err * err,std::string_view source_root,const std::string * v_value) const114 std::string SourceDir::ResolveRelativeAs(bool as_file,
115 const Value& v,
116 Err* err,
117 std::string_view source_root,
118 const std::string* v_value) const {
119 if (!v.VerifyTypeIs(Value::STRING, err))
120 return std::string();
121
122 if (!v_value) {
123 v_value = &v.string_value();
124 }
125 std::string result =
126 ResolveRelativeAs(as_file, v, *v_value, err, source_root);
127 if (!as_file)
128 AssertValueSourceDirString(result);
129 return result;
130 }
131
ResolveRelativeDir(const Value & v,Err * err,std::string_view source_root) const132 SourceDir SourceDir::ResolveRelativeDir(const Value& v,
133 Err* err,
134 std::string_view source_root) const {
135 if (!v.VerifyTypeIs(Value::STRING, err))
136 return SourceDir();
137
138 return ResolveRelativeDir(v, v.string_value(), err, source_root);
139 }
140
Resolve(const base::FilePath & source_root) const141 base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
142 return ResolvePath(value_.str(), false, source_root);
143 }
144