]>
git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/metadata/rust-imports.cc
6fe2277be7339b986f551ebdc43bd6bf5834d105
1 // Copyright (C) 2020-2023 Free Software Foundation, Inc.
3 // This file is part of GCC.
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
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
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/>.
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"
31 // The list of paths we search for import files.
32 static std::vector
<std::string
> search_path
;
34 // Add a directory to the search path. This is called from the option
35 // handling language hook.
37 add_search_path (const std::string
&path
)
39 search_path
.push_back (path
);
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.
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.
49 // When FILENAME does start with ./ or ../, we use
50 // RELATIVE_IMPORT_PATH as a prefix.
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.
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.
66 Import::open_package (const std::string
&filename
, Location location
,
67 const std::string
&relative_import_path
)
70 if (IS_ABSOLUTE_PATH (filename
))
72 else if (filename
[0] == '.'
73 && (filename
[1] == '\0' || IS_DIR_SEPARATOR (filename
[1])))
75 else if (filename
[0] == '.' && filename
[1] == '.'
76 && (filename
[2] == '\0' || IS_DIR_SEPARATOR (filename
[2])))
81 std::string fn
= filename
;
82 if (is_local
&& !IS_ABSOLUTE_PATH (filename
)
83 && !relative_import_path
.empty ())
88 fn
= relative_import_path
;
90 else if (fn
[0] == '.' && fn
[1] == '.'
91 && (fn
[2] == '\0' || IS_DIR_SEPARATOR (fn
[2])))
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
100 for (index
= relative_import_path
.length () - 1;
101 index
> 0 && !IS_DIR_SEPARATOR (relative_import_path
[index
]);
105 fn
= relative_import_path
.substr (0, index
) + fn
.substr (2);
107 fn
= relative_import_path
+ '/' + fn
;
110 fn
= relative_import_path
+ '/' + fn
;
116 for (std::vector
<std::string
>::const_iterator p
= search_path
.begin ();
117 p
!= search_path
.end (); ++p
)
119 std::string indir
= *p
;
120 if (!indir
.empty () && indir
[indir
.size () - 1] != '/')
123 Stream
*s
= Import::try_package_in_directory (indir
, location
);
129 Stream
*s
= Import::try_package_in_directory (fn
, location
);
136 // Try to find the export data for FILENAME.
139 Import::try_package_in_directory (const std::string
&filename
,
142 std::string found_filename
= filename
;
143 int fd
= open (found_filename
.c_str (), O_RDONLY
| O_BINARY
);
148 if (fstat (fd
, &s
) >= 0 && S_ISDIR (s
.st_mode
))
158 if (errno
!= ENOENT
&& errno
!= EISDIR
)
159 rust_warning_at (location
, 0, "%s: %m", filename
.c_str ());
161 fd
= Import::try_suffixes (&found_filename
);
166 // The export data may not be in this file.
167 Stream
*s
= Import::find_export_data (found_filename
, fd
, location
);
173 rust_error_at (location
, "%s exists but does not contain any Go export data",
174 found_filename
.c_str ());
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.
184 Import::try_suffixes (std::string
*pfilename
)
186 std::string filename
= *pfilename
+ ".rox";
187 int fd
= open (filename
.c_str (), O_RDONLY
| O_BINARY
);
190 *pfilename
= filename
;
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
);
200 *pfilename
= filename
;
204 filename
= pfilename
->substr (0, basename_pos
) + "lib" + basename
+ ".a";
205 fd
= open (filename
.c_str (), O_RDONLY
| O_BINARY
);
208 *pfilename
= filename
;
212 filename
= *pfilename
+ ".o";
213 fd
= open (filename
.c_str (), O_RDONLY
| O_BINARY
);
216 *pfilename
= filename
;
223 // Look for export data in the file descriptor FD.
226 Import::find_export_data (const std::string
&filename
, int fd
,
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
);
235 const int len
= sizeof (Metadata::kMagicHeader
);
236 if (lseek (fd
, 0, SEEK_SET
) < 0)
238 rust_error_at (location
, "lseek %s failed: %m", filename
.c_str ());
243 ssize_t c
= ::read (fd
, buf
, len
);
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)
252 // FIXME we need to work out a better header
254 if (memcmp (buf
, Metadata::kMagicHeader
, sizeof (Metadata::kMagicHeader
))
256 return new Stream_from_file (fd
);
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
);
265 // Look for export data in an object file.
268 Import::find_object_export_data (const std::string
&filename
, int fd
,
269 off_t offset
, Location location
)
274 const char *errmsg
= rust_read_export_data (fd
, offset
, &buf
, &len
, &err
);
278 rust_error_at (location
, "%s: %s", filename
.c_str (), errmsg
);
280 rust_error_at (location
, "%s: %s: %s", filename
.c_str (), errmsg
,
288 return new Stream_from_buffer (buf
, len
);
293 // Construct an Import object. We make the builtin_types_ vector
294 // large enough to hold all the builtin types.
296 Import::Import (Stream
*stream
, Location location
)
297 : stream_ (stream
), location_ (location
)
300 // Import the data in the associated stream.
302 // Read LENGTH bytes from the stream.
305 Import::read (size_t length
, std::string
*out
)
308 if (!this->stream_
->peek (length
, &data
))
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 ("");
317 *out
= std::string (data
, length
);
318 this->advance (length
);
321 // Class Import::Stream.
323 Import::Stream::Stream () : pos_ (0), saw_error_ (false) {}
325 Import::Stream::~Stream () {}
327 // Return the next character to come from the stream.
330 Import::Stream::peek_char ()
333 if (!this->do_peek (1, &read
))
335 // Make sure we return an unsigned char, so that we don't get
337 unsigned char ret
= *read
;
341 // Return true if the next LENGTH characters from the stream match
345 Import::Stream::match_bytes (const char *bytes
, size_t length
)
348 if (!this->do_peek (length
, &read
))
350 return memcmp (bytes
, read
, length
) == 0;
353 // Require that the next LENGTH bytes from the stream match BYTES.
356 Import::Stream::require_bytes (Location location
, const char *bytes
,
360 if (!this->do_peek (length
, &read
) || memcmp (bytes
, read
, length
) != 0)
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;
368 this->advance (length
);
371 // Class Stream_from_file.
373 Stream_from_file::Stream_from_file (int fd
) : fd_ (fd
), data_ ()
375 if (lseek (fd
, 0, SEEK_SET
) != 0)
377 rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
378 this->set_saw_error ();
382 Stream_from_file::~Stream_from_file () { close (this->fd_
); }
387 Stream_from_file::do_peek (size_t length
, const char **bytes
)
389 if (this->data_
.length () >= length
)
391 *bytes
= this->data_
.data ();
395 this->data_
.resize (length
);
396 ssize_t got
= ::read (this->fd_
, &this->data_
[0], length
);
400 if (!this->saw_error ())
401 rust_fatal_error (Linemap::unknown_location (), "read failed: %m");
402 this->set_saw_error ();
406 if (lseek (this->fd_
, -got
, SEEK_CUR
) < 0)
408 if (!this->saw_error ())
409 rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
410 this->set_saw_error ();
414 if (static_cast<size_t> (got
) < length
)
417 *bytes
= this->data_
.data ();
424 Stream_from_file::do_advance (size_t skip
)
426 if (lseek (this->fd_
, skip
, SEEK_CUR
) < 0)
428 if (!this->saw_error ())
429 rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
430 this->set_saw_error ();
432 if (!this->data_
.empty ())
434 if (this->data_
.length () > skip
)
435 this->data_
.erase (0, skip
);
437 this->data_
.clear ();