OLD | NEW |
| (Empty) |
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 "tools/gn/filesystem_utils.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/strings/utf_string_conversions.h" | |
9 #include "build/build_config.h" | |
10 #include "tools/gn/location.h" | |
11 #include "tools/gn/source_dir.h" | |
12 | |
13 namespace { | |
14 | |
15 enum DotDisposition { | |
16 // The given dot is just part of a filename and is not special. | |
17 NOT_A_DIRECTORY, | |
18 | |
19 // The given dot is the current directory. | |
20 DIRECTORY_CUR, | |
21 | |
22 // The given dot is the first of a double dot that should take us up one. | |
23 DIRECTORY_UP | |
24 }; | |
25 | |
26 // When we find a dot, this function is called with the character following | |
27 // that dot to see what it is. The return value indicates what type this dot is | |
28 // (see above). This code handles the case where the dot is at the end of the | |
29 // input. | |
30 // | |
31 // |*consumed_len| will contain the number of characters in the input that | |
32 // express what we found. | |
33 DotDisposition ClassifyAfterDot(const std::string& path, | |
34 size_t after_dot, | |
35 size_t* consumed_len) { | |
36 if (after_dot == path.size()) { | |
37 // Single dot at the end. | |
38 *consumed_len = 1; | |
39 return DIRECTORY_CUR; | |
40 } | |
41 if (path[after_dot] == '/') { | |
42 // Single dot followed by a slash. | |
43 *consumed_len = 2; // Consume the slash | |
44 return DIRECTORY_CUR; | |
45 } | |
46 | |
47 if (path[after_dot] == '.') { | |
48 // Two dots. | |
49 if (after_dot + 1 == path.size()) { | |
50 // Double dot at the end. | |
51 *consumed_len = 2; | |
52 return DIRECTORY_UP; | |
53 } | |
54 if (path[after_dot + 1] == '/') { | |
55 // Double dot folowed by a slash. | |
56 *consumed_len = 3; | |
57 return DIRECTORY_UP; | |
58 } | |
59 } | |
60 | |
61 // The dots are followed by something else, not a directory. | |
62 *consumed_len = 1; | |
63 return NOT_A_DIRECTORY; | |
64 } | |
65 | |
66 } // namesapce | |
67 | |
68 SourceFileType GetSourceFileType(const SourceFile& file, | |
69 Settings::TargetOS os) { | |
70 base::StringPiece extension = FindExtension(&file.value()); | |
71 if (extension == "cc" || extension == "cpp" || extension == "cxx") | |
72 return SOURCE_CC; | |
73 if (extension == "h") | |
74 return SOURCE_H; | |
75 if (extension == "c") | |
76 return SOURCE_C; | |
77 | |
78 switch (os) { | |
79 case Settings::MAC: | |
80 if (extension == "m") | |
81 return SOURCE_M; | |
82 if (extension == "mm") | |
83 return SOURCE_MM; | |
84 break; | |
85 | |
86 case Settings::WIN: | |
87 if (extension == "rc") | |
88 return SOURCE_RC; | |
89 break; | |
90 | |
91 default: | |
92 break; | |
93 } | |
94 | |
95 // TODO(brettw) asm files. | |
96 // TODO(brettw) weird thing with .S on non-Windows platforms. | |
97 return SOURCE_UNKNOWN; | |
98 } | |
99 | |
100 const char* GetExtensionForOutputType(Target::OutputType type, | |
101 Settings::TargetOS os) { | |
102 switch (os) { | |
103 case Settings::WIN: | |
104 switch (type) { | |
105 case Target::NONE: | |
106 NOTREACHED(); | |
107 return ""; | |
108 case Target::EXECUTABLE: | |
109 return "exe"; | |
110 case Target::SHARED_LIBRARY: | |
111 return "dll.lib"; // Extension of import library. | |
112 case Target::STATIC_LIBRARY: | |
113 return "lib"; | |
114 case Target::LOADABLE_MODULE: | |
115 return "dll"; // TODO(brettw) what's this? | |
116 default: | |
117 NOTREACHED(); | |
118 } | |
119 break; | |
120 | |
121 default: | |
122 NOTREACHED(); | |
123 } | |
124 return ""; | |
125 } | |
126 | |
127 std::string FilePathToUTF8(const base::FilePath& path) { | |
128 #if defined(OS_WIN) | |
129 return WideToUTF8(path.value()); | |
130 #else | |
131 return path.value(); | |
132 #endif | |
133 } | |
134 | |
135 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) { | |
136 #if defined(OS_WIN) | |
137 return base::FilePath(UTF8ToWide(sp)); | |
138 #else | |
139 return base::FilePath(sp.as_string()); | |
140 #endif | |
141 } | |
142 | |
143 size_t FindExtensionOffset(const std::string& path) { | |
144 for (int i = static_cast<int>(path.size()); i >= 0; i--) { | |
145 if (path[i] == '/') | |
146 break; | |
147 if (path[i] == '.') | |
148 return i + 1; | |
149 } | |
150 return std::string::npos; | |
151 } | |
152 | |
153 base::StringPiece FindExtension(const std::string* path) { | |
154 size_t extension_offset = FindExtensionOffset(*path); | |
155 if (extension_offset == std::string::npos) | |
156 return base::StringPiece(); | |
157 return base::StringPiece(&path->data()[extension_offset], | |
158 path->size() - extension_offset); | |
159 } | |
160 | |
161 size_t FindFilenameOffset(const std::string& path) { | |
162 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) { | |
163 if (path[i] == '/') | |
164 return i + 1; | |
165 } | |
166 return 0; // No filename found means everything was the filename. | |
167 } | |
168 | |
169 base::StringPiece FindFilename(const std::string* path) { | |
170 size_t filename_offset = FindFilenameOffset(*path); | |
171 if (filename_offset == 0) | |
172 return base::StringPiece(*path); // Everything is the file name. | |
173 return base::StringPiece(&(*path).data()[filename_offset], | |
174 path->size() - filename_offset); | |
175 } | |
176 | |
177 base::StringPiece FindFilenameNoExtension(const std::string* path) { | |
178 if (path->empty()) | |
179 return base::StringPiece(); | |
180 size_t filename_offset = FindFilenameOffset(*path); | |
181 size_t extension_offset = FindExtensionOffset(*path); | |
182 | |
183 size_t name_len; | |
184 if (extension_offset == std::string::npos) | |
185 name_len = path->size() - filename_offset; | |
186 else | |
187 name_len = extension_offset - filename_offset - 1; | |
188 | |
189 return base::StringPiece(&(*path).data()[filename_offset], name_len); | |
190 } | |
191 | |
192 void RemoveFilename(std::string* path) { | |
193 path->resize(FindFilenameOffset(*path)); | |
194 } | |
195 | |
196 bool EndsWithSlash(const std::string& s) { | |
197 return !s.empty() && s[s.size() - 1] == '/'; | |
198 } | |
199 | |
200 base::StringPiece FindDir(const std::string* path) { | |
201 size_t filename_offset = FindFilenameOffset(*path); | |
202 if (filename_offset == 0u) | |
203 return base::StringPiece(); | |
204 return base::StringPiece(path->data(), filename_offset); | |
205 } | |
206 | |
207 bool EnsureStringIsInOutputDir(const SourceDir& dir, | |
208 const std::string& str, | |
209 const Value& originating, | |
210 Err* err) { | |
211 // The last char of the dir will be a slash. We don't care if the input ends | |
212 // in a slash or not, so just compare up until there. | |
213 // | |
214 // This check will be wrong for all proper prefixes "e.g. "/output" will | |
215 // match "/out" but we don't really care since this is just a sanity check. | |
216 const std::string& dir_str = dir.value(); | |
217 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1) | |
218 != 0) { | |
219 *err = Err(originating, "File not inside output directory.", | |
220 "The given file should be in the output directory. Normally you would " | |
221 "specify\n\"$target_output_dir/foo\" or " | |
222 "\"$target_gen_dir/foo\". I interpreted this as\n\"" | |
223 + str + "\"."); | |
224 return false; | |
225 } | |
226 return true; | |
227 } | |
228 | |
229 std::string InvertDir(const SourceDir& path) { | |
230 const std::string value = path.value(); | |
231 if (value.empty()) | |
232 return std::string(); | |
233 | |
234 DCHECK(value[0] == '/'); | |
235 size_t begin_index = 1; | |
236 | |
237 // If the input begins with two slashes, skip over both (this is a | |
238 // source-relative dir). | |
239 if (value.size() > 1 && value[1] == '/') | |
240 begin_index = 2; | |
241 | |
242 std::string ret; | |
243 for (size_t i = begin_index; i < value.size(); i++) { | |
244 if (value[i] == '/') | |
245 ret.append("../"); | |
246 } | |
247 return ret; | |
248 } | |
249 | |
250 void NormalizePath(std::string* path) { | |
251 char* pathbuf = path->empty() ? NULL : &(*path)[0]; | |
252 | |
253 // top_index is the first character we can modify in the path. Anything | |
254 // before this indicates where the path is relative to. | |
255 size_t top_index = 0; | |
256 bool is_relative = true; | |
257 if (!path->empty() && pathbuf[0] == '/') { | |
258 is_relative = false; | |
259 | |
260 if (path->size() > 1 && pathbuf[1] == '/') { | |
261 // Two leading slashes, this is a path into the source dir. | |
262 top_index = 2; | |
263 } else { | |
264 // One leading slash, this is a system-absolute path. | |
265 top_index = 1; | |
266 } | |
267 } | |
268 | |
269 size_t dest_i = top_index; | |
270 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) { | |
271 if (pathbuf[src_i] == '.') { | |
272 if (src_i == 0 || pathbuf[src_i - 1] == '/') { | |
273 // Slash followed by a dot, see if it's something special. | |
274 size_t consumed_len; | |
275 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) { | |
276 case NOT_A_DIRECTORY: | |
277 // Copy the dot to the output, it means nothing special. | |
278 pathbuf[dest_i++] = pathbuf[src_i++]; | |
279 break; | |
280 case DIRECTORY_CUR: | |
281 // Current directory, just skip the input. | |
282 src_i += consumed_len; | |
283 break; | |
284 case DIRECTORY_UP: | |
285 // Back up over previous directory component. If we're already | |
286 // at the top, preserve the "..". | |
287 if (dest_i > top_index) { | |
288 // The previous char was a slash, remove it. | |
289 dest_i--; | |
290 } | |
291 | |
292 if (dest_i == top_index) { | |
293 if (is_relative) { | |
294 // We're already at the beginning of a relative input, copy the | |
295 // ".." and continue. We need the trailing slash if there was | |
296 // one before (otherwise we're at the end of the input). | |
297 pathbuf[dest_i++] = '.'; | |
298 pathbuf[dest_i++] = '.'; | |
299 if (consumed_len == 3) | |
300 pathbuf[dest_i++] = '/'; | |
301 | |
302 // This also makes a new "root" that we can't delete by going | |
303 // up more levels. Otherwise "../.." would collapse to | |
304 // nothing. | |
305 top_index = dest_i; | |
306 } | |
307 // Otherwise we're at the beginning of an absolute path. Don't | |
308 // allow ".." to go up another level and just eat it. | |
309 } else { | |
310 // Just find the previous slash or the beginning of input. | |
311 while (dest_i > 0 && pathbuf[dest_i - 1] != '/') | |
312 dest_i--; | |
313 } | |
314 src_i += consumed_len; | |
315 } | |
316 } else { | |
317 // Dot not preceeded by a slash, copy it literally. | |
318 pathbuf[dest_i++] = pathbuf[src_i++]; | |
319 } | |
320 } else if (pathbuf[src_i] == '/') { | |
321 if (src_i > 0 && pathbuf[src_i - 1] == '/') { | |
322 // Two slashes in a row, skip over it. | |
323 src_i++; | |
324 } else { | |
325 // Just one slash, copy it. | |
326 pathbuf[dest_i++] = pathbuf[src_i++]; | |
327 } | |
328 } else { | |
329 // Input nothing special, just copy it. | |
330 pathbuf[dest_i++] = pathbuf[src_i++]; | |
331 } | |
332 } | |
333 path->resize(dest_i); | |
334 } | |
335 | |
336 void ConvertPathToSystem(std::string* path) { | |
337 #if defined(OS_WIN) | |
338 for (size_t i = 0; i < path->size(); i++) { | |
339 if ((*path)[i] == '/') | |
340 (*path)[i] = '\\'; | |
341 } | |
342 #endif | |
343 } | |
344 | |
345 std::string PathToSystem(const std::string& path) { | |
346 std::string ret(path); | |
347 ConvertPathToSystem(&ret); | |
348 return ret; | |
349 } | |
350 | |
OLD | NEW |