Skip to content

Commit c5db5b5

Browse files
authored
Unrolled build for rust-lang#130427
Rollup merge of rust-lang#130427 - jieyouxu:rmake-symlink, r=Kobzol run_make_support: rectify symlink handling Avoid confusing Unix symlinks and Windows symlinks. Since their semantics are quite different, we should avoid trying to make it automagic in how symlinks are created and deleted. Notably, the tests should reflect what type of symlinks are to be created to match what std does to make it less surprising for test readers.
2 parents e2dc1a1 + 7d76428 commit c5db5b5

File tree

5 files changed

+158
-80
lines changed

5 files changed

+158
-80
lines changed

src/tools/run-make-support/src/fs.rs

+130-50
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,6 @@
11
use std::io;
22
use std::path::{Path, PathBuf};
33

4-
// FIXME(jieyouxu): modify create_symlink to panic on windows.
5-
6-
/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix.
7-
#[cfg(target_family = "windows")]
8-
pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
9-
if link.as_ref().exists() {
10-
std::fs::remove_dir(link.as_ref()).unwrap();
11-
}
12-
if original.as_ref().is_file() {
13-
std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!(
14-
"failed to create symlink {:?} for {:?}",
15-
link.as_ref().display(),
16-
original.as_ref().display(),
17-
));
18-
} else {
19-
std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()).expect(&format!(
20-
"failed to create symlink {:?} for {:?}",
21-
link.as_ref().display(),
22-
original.as_ref().display(),
23-
));
24-
}
25-
}
26-
27-
/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix.
28-
#[cfg(target_family = "unix")]
29-
pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
30-
if link.as_ref().exists() {
31-
std::fs::remove_dir(link.as_ref()).unwrap();
32-
}
33-
std::os::unix::fs::symlink(original.as_ref(), link.as_ref()).expect(&format!(
34-
"failed to create symlink {:?} for {:?}",
35-
link.as_ref().display(),
36-
original.as_ref().display(),
37-
));
38-
}
39-
404
/// Copy a directory into another.
415
pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
426
fn copy_dir_all_inner(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
@@ -50,7 +14,31 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
5014
if ty.is_dir() {
5115
copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?;
5216
} else if ty.is_symlink() {
53-
copy_symlink(entry.path(), dst.join(entry.file_name()))?;
17+
// Traverse symlink once to find path of target entity.
18+
let target_path = std::fs::read_link(entry.path())?;
19+
20+
let new_symlink_path = dst.join(entry.file_name());
21+
#[cfg(windows)]
22+
{
23+
use std::os::windows::fs::FileTypeExt;
24+
if ty.is_symlink_dir() {
25+
std::os::windows::fs::symlink_dir(&target_path, new_symlink_path)?;
26+
} else {
27+
// Target may be a file or another symlink, in any case we can use
28+
// `symlink_file` here.
29+
std::os::windows::fs::symlink_file(&target_path, new_symlink_path)?;
30+
}
31+
}
32+
#[cfg(unix)]
33+
{
34+
std::os::unix::fs::symlink(target_path, new_symlink_path)?;
35+
}
36+
#[cfg(not(any(windows, unix)))]
37+
{
38+
// Technically there's also wasi, but I have no clue about wasi symlink
39+
// semantics and which wasi targets / environment support symlinks.
40+
unimplemented!("unsupported target");
41+
}
5442
} else {
5543
std::fs::copy(entry.path(), dst.join(entry.file_name()))?;
5644
}
@@ -69,12 +57,6 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
6957
}
7058
}
7159

72-
fn copy_symlink<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
73-
let target_path = std::fs::read_link(from).unwrap();
74-
create_symlink(target_path, to);
75-
Ok(())
76-
}
77-
7860
/// Helper for reading entries in a given directory.
7961
pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F) {
8062
for entry in read_dir(dir) {
@@ -85,8 +67,17 @@ pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F
8567
/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message.
8668
#[track_caller]
8769
pub fn remove_file<P: AsRef<Path>>(path: P) {
88-
std::fs::remove_file(path.as_ref())
89-
.expect(&format!("the file in path \"{}\" could not be removed", path.as_ref().display()));
70+
if let Err(e) = std::fs::remove_file(path.as_ref()) {
71+
panic!("failed to remove file at `{}`: {e}", path.as_ref().display());
72+
}
73+
}
74+
75+
/// A wrapper around [`std::fs::remove_dir`] which includes the directory path in the panic message.
76+
#[track_caller]
77+
pub fn remove_dir<P: AsRef<Path>>(path: P) {
78+
if let Err(e) = std::fs::remove_dir(path.as_ref()) {
79+
panic!("failed to remove directory at `{}`: {e}", path.as_ref().display());
80+
}
9081
}
9182

9283
/// A wrapper around [`std::fs::copy`] which includes the file path in the panic message.
@@ -165,13 +156,32 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) {
165156
));
166157
}
167158

168-
/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message.
159+
/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. Note
160+
/// that this will traverse symlinks and will return metadata about the target file. Use
161+
/// [`symlink_metadata`] if you don't want to traverse symlinks.
162+
///
163+
/// See [`std::fs::metadata`] docs for more details.
169164
#[track_caller]
170165
pub fn metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata {
171-
std::fs::metadata(path.as_ref()).expect(&format!(
172-
"the file's metadata in path \"{}\" could not be read",
173-
path.as_ref().display()
174-
))
166+
match std::fs::metadata(path.as_ref()) {
167+
Ok(m) => m,
168+
Err(e) => panic!("failed to read file metadata at `{}`: {e}", path.as_ref().display()),
169+
}
170+
}
171+
172+
/// A wrapper around [`std::fs::symlink_metadata`] which includes the file path in the panic
173+
/// message. Note that this will not traverse symlinks and will return metadata about the filesystem
174+
/// entity itself. Use [`metadata`] if you want to traverse symlinks.
175+
///
176+
/// See [`std::fs::symlink_metadata`] docs for more details.
177+
#[track_caller]
178+
pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata {
179+
match std::fs::symlink_metadata(path.as_ref()) {
180+
Ok(m) => m,
181+
Err(e) => {
182+
panic!("failed to read file metadata (shallow) at `{}`: {e}", path.as_ref().display())
183+
}
184+
}
175185
}
176186

177187
/// A wrapper around [`std::fs::rename`] which includes the file path in the panic message.
@@ -205,3 +215,73 @@ pub fn shallow_find_dir_entries<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
205215
}
206216
output
207217
}
218+
219+
/// Create a new symbolic link to a directory.
220+
///
221+
/// # Removing the symlink
222+
///
223+
/// - On Windows, a symlink-to-directory needs to be removed with a corresponding [`fs::remove_dir`]
224+
/// and not [`fs::remove_file`].
225+
/// - On Unix, remove the symlink with [`fs::remove_file`].
226+
///
227+
/// [`fs::remove_dir`]: crate::fs::remove_dir
228+
/// [`fs::remove_file`]: crate::fs::remove_file
229+
pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
230+
#[cfg(unix)]
231+
{
232+
if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) {
233+
panic!(
234+
"failed to create symlink: original=`{}`, link=`{}`: {e}",
235+
original.as_ref().display(),
236+
link.as_ref().display()
237+
);
238+
}
239+
}
240+
#[cfg(windows)]
241+
{
242+
if let Err(e) = std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()) {
243+
panic!(
244+
"failed to create symlink-to-directory: original=`{}`, link=`{}`: {e}",
245+
original.as_ref().display(),
246+
link.as_ref().display()
247+
);
248+
}
249+
}
250+
#[cfg(not(any(windows, unix)))]
251+
{
252+
unimplemented!("target family not currently supported")
253+
}
254+
}
255+
256+
/// Create a new symbolic link to a file.
257+
///
258+
/// # Removing the symlink
259+
///
260+
/// On both Windows and Unix, a symlink-to-file needs to be removed with a corresponding
261+
/// [`fs::remove_file`](crate::fs::remove_file) and not [`fs::remove_dir`](crate::fs::remove_dir).
262+
pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
263+
#[cfg(unix)]
264+
{
265+
if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) {
266+
panic!(
267+
"failed to create symlink: original=`{}`, link=`{}`: {e}",
268+
original.as_ref().display(),
269+
link.as_ref().display()
270+
);
271+
}
272+
}
273+
#[cfg(windows)]
274+
{
275+
if let Err(e) = std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()) {
276+
panic!(
277+
"failed to create symlink-to-file: original=`{}`, link=`{}`: {e}",
278+
original.as_ref().display(),
279+
link.as_ref().display()
280+
);
281+
}
282+
}
283+
#[cfg(not(any(windows, unix)))]
284+
{
285+
unimplemented!("target family not currently supported")
286+
}
287+
}

tests/run-make/invalid-symlink-search-path/rmake.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
// In this test, the symlink created is invalid (valid relative to the root, but not
2-
// relatively to where it is located), and used to cause an internal
3-
// compiler error (ICE) when passed as a library search path. This was fixed in #26044,
4-
// and this test checks that the invalid symlink is instead simply ignored.
1+
// In this test, the symlink created is invalid (valid relative to the root, but not relatively to
2+
// where it is located), and used to cause an internal compiler error (ICE) when passed as a library
3+
// search path. This was fixed in #26044, and this test checks that the invalid symlink is instead
4+
// simply ignored.
5+
//
56
// See https://github.com/rust-lang/rust/issues/26006
67

78
//@ needs-symlink
89
//Reason: symlink requires elevated permission in Windows
910

10-
use run_make_support::{rfs, rustc};
11+
use run_make_support::{path, rfs, rustc};
1112

1213
fn main() {
1314
// We create two libs: `bar` which depends on `foo`. We need to compile `foo` first.
@@ -20,9 +21,9 @@ fn main() {
2021
.metadata("foo")
2122
.output("out/foo/libfoo.rlib")
2223
.run();
23-
rfs::create_dir("out/bar");
24-
rfs::create_dir("out/bar/deps");
25-
rfs::create_symlink("out/foo/libfoo.rlib", "out/bar/deps/libfoo.rlib");
24+
rfs::create_dir_all("out/bar/deps");
25+
rfs::symlink_file(path("out/foo/libfoo.rlib"), path("out/bar/deps/libfoo.rlib"));
26+
2627
// Check that the invalid symlink does not cause an ICE
2728
rustc()
2829
.input("in/bar/lib.rs")
+12-12
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
// Crates that are resolved normally have their path canonicalized and all
2-
// symlinks resolved. This did not happen for paths specified
3-
// using the --extern option to rustc, which could lead to rustc thinking
4-
// that it encountered two different versions of a crate, when it's
5-
// actually the same version found through different paths.
6-
// See https://github.com/rust-lang/rust/pull/16505
7-
8-
// This test checks that --extern and symlinks together
9-
// can result in successful compilation.
1+
// Crates that are resolved normally have their path canonicalized and all symlinks resolved. This
2+
// did not happen for paths specified using the `--extern` option to rustc, which could lead to
3+
// rustc thinking that it encountered two different versions of a crate, when it's actually the same
4+
// version found through different paths.
5+
//
6+
// This test checks that `--extern` and symlinks together can result in successful compilation.
7+
//
8+
// See <https://github.com/rust-lang/rust/pull/16505>.
109

1110
//@ ignore-cross-compile
1211
//@ needs-symlink
1312

14-
use run_make_support::{cwd, rfs, rustc};
13+
use run_make_support::{cwd, path, rfs, rustc};
1514

1615
fn main() {
1716
rustc().input("foo.rs").run();
1817
rfs::create_dir_all("other");
19-
rfs::create_symlink("libfoo.rlib", "other");
18+
rfs::symlink_file(path("libfoo.rlib"), path("other").join("libfoo.rlib"));
19+
2020
rustc().input("bar.rs").library_search_path(cwd()).run();
21-
rustc().input("baz.rs").extern_("foo", "other").library_search_path(cwd()).run();
21+
rustc().input("baz.rs").extern_("foo", "other/libfoo.rlib").library_search_path(cwd()).run();
2222
}
+6-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
// When a directory and a symlink simultaneously exist with the same name,
2-
// setting that name as the library search path should not cause rustc
3-
// to avoid looking in the symlink and cause an error. This test creates
4-
// a directory and a symlink named "other", and places the library in the symlink.
5-
// If it succeeds, the library was successfully found.
6-
// See https://github.com/rust-lang/rust/issues/12459
1+
// Avoid erroring on symlinks pointing to the same file that are present in the library search path.
2+
//
3+
// See <https://github.com/rust-lang/rust/issues/12459>.
74

85
//@ ignore-cross-compile
96
//@ needs-symlink
107

11-
use run_make_support::{dynamic_lib_name, rfs, rustc};
8+
use run_make_support::{cwd, dynamic_lib_name, path, rfs, rustc};
129

1310
fn main() {
1411
rustc().input("foo.rs").arg("-Cprefer-dynamic").run();
1512
rfs::create_dir_all("other");
16-
rfs::create_symlink(dynamic_lib_name("foo"), "other");
17-
rustc().input("bar.rs").library_search_path("other").run();
13+
rfs::symlink_file(dynamic_lib_name("foo"), path("other").join(dynamic_lib_name("foo")));
14+
rustc().input("bar.rs").library_search_path(cwd()).library_search_path("other").run();
1815
}

tests/run-make/symlinked-rlib/rmake.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ use run_make_support::{cwd, rfs, rustc};
1212

1313
fn main() {
1414
rustc().input("foo.rs").crate_type("rlib").output("foo.xxx").run();
15-
rfs::create_symlink("foo.xxx", "libfoo.rlib");
15+
rfs::symlink_file("foo.xxx", "libfoo.rlib");
1616
rustc().input("bar.rs").library_search_path(cwd()).run();
1717
}

0 commit comments

Comments
 (0)