]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/rust/metadata/rust-imports.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / metadata / rust-imports.cc
CommitLineData
a945c346 1// Copyright (C) 2020-2024 Free Software Foundation, Inc.
509e4c32
PH
2
3// This file is part of GCC.
4
5// GCC is free software; you can redistribute it and/or modify it under
6// the terms of the GNU General Public License as published by the Free
7// Software Foundation; either version 3, or (at your option) any later
8// version.
9
10// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11// WARRANTY; without even the implied warranty of MERCHANTABILITY or
12// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13// for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with GCC; see the file COPYING3. If not see
17// <http://www.gnu.org/licenses/>.
18
19#include "rust-system.h"
20#include "rust-diagnostics.h"
21#include "rust-imports.h"
22#include "rust-object-export.h"
23#include "rust-export-metadata.h"
24
25#ifndef O_BINARY
26#define O_BINARY 0
27#endif
28
29namespace Rust {
30
31// The list of paths we search for import files.
32static std::vector<std::string> search_path;
33
34// Add a directory to the search path. This is called from the option
35// handling language hook.
36void
37add_search_path (const std::string &path)
38{
39 search_path.push_back (path);
40}
41
42// Find import data. This searches the file system for FILENAME and
43// returns a pointer to a Stream object to read the data that it
44// exports. If the file is not found, it returns NULL.
45
46// When FILENAME is not an absolute path and does not start with ./ or
47// ../, we use the search path provided by -I and -L options.
48
49// When FILENAME does start with ./ or ../, we use
50// RELATIVE_IMPORT_PATH as a prefix.
51
52// When FILENAME does not exist, we try modifying FILENAME to find the
53// file. We use the first of these which exists:
54// * We append ".gox".
55// * We turn the base of FILENAME into libFILENAME.so.
56// * We turn the base of FILENAME into libFILENAME.a.
57// * We append ".o".
58
59// When using a search path, we apply each of these transformations at
60// each entry on the search path before moving on to the next entry.
61// If the file exists, but does not contain any Go export data, we
62// stop; we do not keep looking for another file with the same name
63// later in the search path.
64
65Import::Stream *
66Import::open_package (const std::string &filename, Location location,
67 const std::string &relative_import_path)
68{
69 bool is_local;
70 if (IS_ABSOLUTE_PATH (filename))
71 is_local = true;
72 else if (filename[0] == '.'
73 && (filename[1] == '\0' || IS_DIR_SEPARATOR (filename[1])))
74 is_local = true;
75 else if (filename[0] == '.' && filename[1] == '.'
76 && (filename[2] == '\0' || IS_DIR_SEPARATOR (filename[2])))
77 is_local = true;
78 else
79 is_local = false;
80
81 std::string fn = filename;
82 if (is_local && !IS_ABSOLUTE_PATH (filename)
83 && !relative_import_path.empty ())
84 {
85 if (fn == ".")
86 {
87 // A special case.
88 fn = relative_import_path;
89 }
90 else if (fn[0] == '.' && fn[1] == '.'
91 && (fn[2] == '\0' || IS_DIR_SEPARATOR (fn[2])))
92 {
93 // We are going to join relative_import_path and fn, and it
94 // will look like DIR/../PATH. But DIR does not necessarily
95 // exist in this case, and if it doesn't the use of .. will
96 // fail although it shouldn't. The gc compiler uses
97 // path.Join here, which cleans up the .., so we need to do
98 // the same.
99 size_t index;
100 for (index = relative_import_path.length () - 1;
101 index > 0 && !IS_DIR_SEPARATOR (relative_import_path[index]);
102 index--)
103 ;
104 if (index > 0)
105 fn = relative_import_path.substr (0, index) + fn.substr (2);
106 else
107 fn = relative_import_path + '/' + fn;
108 }
109 else
110 fn = relative_import_path + '/' + fn;
111 is_local = false;
112 }
113
114 if (!is_local)
115 {
116 for (std::vector<std::string>::const_iterator p = search_path.begin ();
117 p != search_path.end (); ++p)
118 {
119 std::string indir = *p;
120 if (!indir.empty () && indir[indir.size () - 1] != '/')
121 indir += '/';
122 indir += fn;
123 Stream *s = Import::try_package_in_directory (indir, location);
124 if (s != NULL)
125 return s;
126 }
127 }
128
129 Stream *s = Import::try_package_in_directory (fn, location);
130 if (s != NULL)
131 return s;
132
133 return NULL;
134}
135
136// Try to find the export data for FILENAME.
137
138Import::Stream *
139Import::try_package_in_directory (const std::string &filename,
140 Location location)
141{
142 std::string found_filename = filename;
143 int fd = open (found_filename.c_str (), O_RDONLY | O_BINARY);
144
145 if (fd >= 0)
146 {
147 struct stat s;
148 if (fstat (fd, &s) >= 0 && S_ISDIR (s.st_mode))
149 {
150 close (fd);
151 fd = -1;
152 errno = EISDIR;
153 }
154 }
155
156 if (fd < 0)
157 {
158 if (errno != ENOENT && errno != EISDIR)
159 rust_warning_at (location, 0, "%s: %m", filename.c_str ());
160
161 fd = Import::try_suffixes (&found_filename);
162 if (fd < 0)
163 return NULL;
164 }
165
166 // The export data may not be in this file.
167 Stream *s = Import::find_export_data (found_filename, fd, location);
168 if (s != NULL)
169 return s;
170
171 close (fd);
172
173 rust_error_at (location, "%s exists but does not contain any Go export data",
174 found_filename.c_str ());
175
176 return NULL;
177}
178
179// Given import "*PFILENAME", where *PFILENAME does not exist, try
180// various suffixes. If we find one, set *PFILENAME to the one we
181// found. Return the open file descriptor.
182
183int
184Import::try_suffixes (std::string *pfilename)
185{
186 std::string filename = *pfilename + ".rox";
187 int fd = open (filename.c_str (), O_RDONLY | O_BINARY);
188 if (fd >= 0)
189 {
190 *pfilename = filename;
191 return fd;
192 }
193
194 const char *basename = lbasename (pfilename->c_str ());
195 size_t basename_pos = basename - pfilename->c_str ();
196 filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".so";
197 fd = open (filename.c_str (), O_RDONLY | O_BINARY);
198 if (fd >= 0)
199 {
200 *pfilename = filename;
201 return fd;
202 }
203
204 filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".a";
205 fd = open (filename.c_str (), O_RDONLY | O_BINARY);
206 if (fd >= 0)
207 {
208 *pfilename = filename;
209 return fd;
210 }
211
212 filename = *pfilename + ".o";
213 fd = open (filename.c_str (), O_RDONLY | O_BINARY);
214 if (fd >= 0)
215 {
216 *pfilename = filename;
217 return fd;
218 }
219
220 return -1;
221}
222
223// Look for export data in the file descriptor FD.
224
225Import::Stream *
226Import::find_export_data (const std::string &filename, int fd,
227 Location location)
228{
229 // See if we can read this as an object file.
230 Import::Stream *stream
231 = Import::find_object_export_data (filename, fd, 0, location);
232 if (stream != NULL)
233 return stream;
234
235 const int len = sizeof (Metadata::kMagicHeader);
236 if (lseek (fd, 0, SEEK_SET) < 0)
237 {
238 rust_error_at (location, "lseek %s failed: %m", filename.c_str ());
239 return NULL;
240 }
241
242 char buf[len];
243 ssize_t c = ::read (fd, buf, len);
244 if (c < len)
245 return NULL;
246
247 // Check for a file containing nothing but Go export data.
248 // if (memcmp (buf, Export::cur_magic, Export::magic_len) == 0
249 // || memcmp (buf, Export::v1_magic, Export::magic_len) == 0
250 // || memcmp (buf, Export::v2_magic, Export::magic_len) == 0)
251 //
252 // FIXME we need to work out a better header
253 //
254 if (memcmp (buf, Metadata::kMagicHeader, sizeof (Metadata::kMagicHeader))
255 == 0)
256 return new Stream_from_file (fd);
257
258 // See if we can read this as an archive.
259 if (Import::is_archive_magic (buf))
260 return Import::find_archive_export_data (filename, fd, location);
261
262 return NULL;
263}
264
265// Look for export data in an object file.
266
267Import::Stream *
268Import::find_object_export_data (const std::string &filename, int fd,
269 off_t offset, Location location)
270{
271 char *buf;
272 size_t len;
273 int err;
274 const char *errmsg = rust_read_export_data (fd, offset, &buf, &len, &err);
275 if (errmsg != NULL)
276 {
277 if (err == 0)
278 rust_error_at (location, "%s: %s", filename.c_str (), errmsg);
279 else
280 rust_error_at (location, "%s: %s: %s", filename.c_str (), errmsg,
281 xstrerror (err));
282 return NULL;
283 }
284
285 if (buf == NULL)
286 return NULL;
287
288 return new Stream_from_buffer (buf, len);
289}
290
291// Class Import.
292
293// Construct an Import object. We make the builtin_types_ vector
294// large enough to hold all the builtin types.
295
296Import::Import (Stream *stream, Location location)
297 : stream_ (stream), location_ (location)
298{}
299
300// Import the data in the associated stream.
301
302// Read LENGTH bytes from the stream.
303
304void
305Import::read (size_t length, std::string *out)
306{
307 const char *data;
308 if (!this->stream_->peek (length, &data))
309 {
310 if (!this->stream_->saw_error ())
311 rust_error_at (this->location_, "import error at %d: expected %d bytes",
312 this->stream_->pos (), static_cast<int> (length));
313 this->stream_->set_saw_error ();
314 *out = std::string ("");
315 return;
316 }
317 *out = std::string (data, length);
318 this->advance (length);
319}
320
321// Class Import::Stream.
322
323Import::Stream::Stream () : pos_ (0), saw_error_ (false) {}
324
325Import::Stream::~Stream () {}
326
327// Return the next character to come from the stream.
328
329int
330Import::Stream::peek_char ()
331{
332 const char *read;
333 if (!this->do_peek (1, &read))
334 return -1;
335 // Make sure we return an unsigned char, so that we don't get
336 // confused by \xff.
337 unsigned char ret = *read;
338 return ret;
339}
340
341// Return true if the next LENGTH characters from the stream match
342// BYTES
343
344bool
345Import::Stream::match_bytes (const char *bytes, size_t length)
346{
347 const char *read;
348 if (!this->do_peek (length, &read))
349 return false;
350 return memcmp (bytes, read, length) == 0;
351}
352
353// Require that the next LENGTH bytes from the stream match BYTES.
354
355void
356Import::Stream::require_bytes (Location location, const char *bytes,
357 size_t length)
358{
359 const char *read;
360 if (!this->do_peek (length, &read) || memcmp (bytes, read, length) != 0)
361 {
362 if (!this->saw_error_)
363 rust_error_at (location, "import error at %d: expected %<%.*s%>",
364 this->pos (), static_cast<int> (length), bytes);
365 this->saw_error_ = true;
366 return;
367 }
368 this->advance (length);
369}
370
371// Class Stream_from_file.
372
373Stream_from_file::Stream_from_file (int fd) : fd_ (fd), data_ ()
374{
375 if (lseek (fd, 0, SEEK_SET) != 0)
376 {
377 rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
378 this->set_saw_error ();
379 }
380}
381
382Stream_from_file::~Stream_from_file () { close (this->fd_); }
383
384// Read next bytes.
385
386bool
387Stream_from_file::do_peek (size_t length, const char **bytes)
388{
389 if (this->data_.length () >= length)
390 {
391 *bytes = this->data_.data ();
392 return true;
393 }
394
395 this->data_.resize (length);
396 ssize_t got = ::read (this->fd_, &this->data_[0], length);
397
398 if (got < 0)
399 {
400 if (!this->saw_error ())
401 rust_fatal_error (Linemap::unknown_location (), "read failed: %m");
402 this->set_saw_error ();
403 return false;
404 }
405
406 if (lseek (this->fd_, -got, SEEK_CUR) < 0)
407 {
408 if (!this->saw_error ())
409 rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
410 this->set_saw_error ();
411 return false;
412 }
413
414 if (static_cast<size_t> (got) < length)
415 return false;
416
417 *bytes = this->data_.data ();
418 return true;
419}
420
421// Advance.
422
423void
424Stream_from_file::do_advance (size_t skip)
425{
426 if (lseek (this->fd_, skip, SEEK_CUR) < 0)
427 {
428 if (!this->saw_error ())
429 rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
430 this->set_saw_error ();
431 }
432 if (!this->data_.empty ())
433 {
434 if (this->data_.length () > skip)
435 this->data_.erase (0, skip);
436 else
437 this->data_.clear ();
438 }
439}
440
441} // namespace Rust