Skip to content

Commit 0237953

Browse files
committed
Auto merge of #6817 - thomwiggers:fix-2748, r=ehuss
Handle symlinks to directories When cargo encounters a link, check if it's a directory when considering it for further processing. I'm not sure how this interacts with things such as submodules – it allowed me to publish the crate [pqcrypto-kyber768](https://github.com/rustpq/pqcrypto) which uses a link to a submodule. Fixes #2748. This PR: * Add new tests that demonstrate that links to dirs can't be packaged * Fixes those tests * Enabled symlink tests to run on Windows * Fix a bug in the handling of symlinks * Add new test runner behaviour on Windows (it will ignore symlink-related tests and instead run them if you specify --ignored.)
2 parents b21602c + d0f7c0e commit 0237953

File tree

8 files changed

+140
-24
lines changed

8 files changed

+140
-24
lines changed

src/cargo/core/features.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
//! 3. To actually perform the feature gate, you'll want to have code that looks
2222
//! like:
2323
//!
24-
//! ```rust,ignore
24+
//! ```rust,compile_fail
2525
//! use core::{Feature, Features};
2626
//!
2727
//! let feature = Feature::launch_into_space();

src/cargo/sources/path.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,17 @@ impl<'cfg> PathSource<'cfg> {
219219
// the untracked files are often part of a build and may become relevant
220220
// as part of a future commit.
221221
let index_files = index.iter().map(|entry| {
222-
use libgit2_sys::GIT_FILEMODE_COMMIT;
223-
let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
224-
(join(root, &entry.path), Some(is_dir))
222+
use libgit2_sys::{GIT_FILEMODE_COMMIT, GIT_FILEMODE_LINK};
223+
// ``is_dir`` is an optimization to avoid calling
224+
// ``fs::metadata`` on every file.
225+
let is_dir = if entry.mode == GIT_FILEMODE_LINK as u32 {
226+
// Let the code below figure out if this symbolic link points
227+
// to a directory or not.
228+
None
229+
} else {
230+
Some(entry.mode == GIT_FILEMODE_COMMIT as u32)
231+
};
232+
(join(root, &entry.path), is_dir)
225233
});
226234
let mut opts = git2::StatusOptions::new();
227235
opts.include_untracked(true);

src/cargo/util/network.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ fn maybe_spurious(err: &Error) -> bool {
7373
///
7474
/// # Examples
7575
///
76-
/// ```ignore
77-
/// use util::network;
78-
/// cargo_result = network::with_retry(&config, || something.download());
76+
/// ```
77+
/// # use crate::cargo::util::{CargoResult, Config};
78+
/// # let download_something = || return Ok(());
79+
/// # let config = Config::default().unwrap();
80+
/// use cargo::util::network;
81+
/// let cargo_result = network::with_retry(&config, || download_something());
7982
/// ```
8083
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
8184
where

tests/testsuite/build.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ use std::io::prelude::*;
44

55
use crate::support::paths::{root, CargoPathExt};
66
use crate::support::registry::Package;
7-
use crate::support::ProjectBuilder;
87
use crate::support::{
9-
basic_bin_manifest, basic_lib_manifest, basic_manifest, rustc_host, sleep_ms,
8+
basic_bin_manifest, basic_lib_manifest, basic_manifest, main_file, project, rustc_host,
9+
sleep_ms, symlink_supported, Execs, ProjectBuilder,
1010
};
11-
use crate::support::{main_file, project, Execs};
1211
use cargo::util::paths::dylib_path_envvar;
1312

1413
#[cargo_test]
@@ -1495,9 +1494,12 @@ package `test v0.0.0 ([CWD])`",
14951494
}
14961495

14971496
#[cargo_test]
1497+
/// Make sure broken symlinks don't break the build
1498+
///
1499+
/// This test requires you to be able to make symlinks.
1500+
/// For windows, this may require you to enable developer mode.
14981501
fn ignore_broken_symlinks() {
1499-
// windows and symlinks don't currently agree that well
1500-
if cfg!(windows) {
1502+
if !symlink_supported() {
15011503
return;
15021504
}
15031505

tests/testsuite/package.rs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use std::fs::File;
33
use std::io::prelude::*;
44
use std::path::Path;
55

6-
use crate::support::cargo_process;
76
use crate::support::paths::CargoPathExt;
87
use crate::support::registry::Package;
98
use crate::support::{
10-
basic_manifest, git, path2url, paths, project, publish::validate_crate_contents, registry,
9+
basic_manifest, cargo_process, git, path2url, paths, project, publish::validate_crate_contents,
10+
registry, symlink_supported,
1111
};
1212
use git2;
1313

@@ -504,6 +504,56 @@ fn package_git_submodule() {
504504
.run();
505505
}
506506

507+
#[cargo_test]
508+
/// Tests if a symlink to a git submodule is properly handled.
509+
///
510+
/// This test requires you to be able to make symlinks.
511+
/// For windows, this may require you to enable developer mode.
512+
fn package_symlink_to_submodule() {
513+
#[cfg(unix)]
514+
use std::os::unix::fs::symlink;
515+
#[cfg(windows)]
516+
use std::os::windows::fs::symlink_dir as symlink;
517+
518+
if !symlink_supported() {
519+
return;
520+
}
521+
522+
let project = git::new("foo", |project| {
523+
project.file("src/lib.rs", "pub fn foo() {}")
524+
})
525+
.unwrap();
526+
527+
let library = git::new("submodule", |library| {
528+
library.no_manifest().file("Makefile", "all:")
529+
})
530+
.unwrap();
531+
532+
let repository = git2::Repository::open(&project.root()).unwrap();
533+
let url = path2url(library.root()).to_string();
534+
git::add_submodule(&repository, &url, Path::new("submodule"));
535+
t!(symlink(
536+
&project.root().join("submodule"),
537+
&project.root().join("submodule-link")
538+
));
539+
git::add(&repository);
540+
git::commit(&repository);
541+
542+
let repository = git2::Repository::open(&project.root().join("submodule")).unwrap();
543+
repository
544+
.reset(
545+
&repository.revparse_single("HEAD").unwrap(),
546+
git2::ResetType::Hard,
547+
None,
548+
)
549+
.unwrap();
550+
551+
project
552+
.cargo("package --no-verify -v")
553+
.with_stderr_contains("[ARCHIVING] submodule/Makefile")
554+
.run();
555+
}
556+
507557
#[cargo_test]
508558
fn no_duplicates_from_modified_tracked_files() {
509559
let root = paths::root().join("all");
@@ -660,9 +710,19 @@ See [..]
660710
}
661711

662712
#[cargo_test]
663-
#[cfg(unix)]
713+
/// Tests if a broken symlink is properly handled when packaging.
714+
///
715+
/// This test requires you to be able to make symlinks.
716+
/// For windows, this may require you to enable developer mode.
664717
fn broken_symlink() {
665-
use std::os::unix::fs;
718+
#[cfg(unix)]
719+
use std::os::unix::fs::symlink;
720+
#[cfg(windows)]
721+
use std::os::windows::fs::symlink_dir as symlink;
722+
723+
if !symlink_supported() {
724+
return;
725+
}
666726

667727
let p = project()
668728
.file(
@@ -681,7 +741,7 @@ fn broken_symlink() {
681741
)
682742
.file("src/main.rs", r#"fn main() { println!("hello"); }"#)
683743
.build();
684-
t!(fs::symlink("nowhere", &p.root().join("src/foo.rs")));
744+
t!(symlink("nowhere", &p.root().join("src/foo.rs")));
685745

686746
p.cargo("package -v")
687747
.with_status(101)
@@ -699,6 +759,26 @@ Caused by:
699759
.run();
700760
}
701761

762+
#[cargo_test]
763+
/// Tests if a symlink to a directory is proberly included.
764+
///
765+
/// This test requires you to be able to make symlinks.
766+
/// For windows, this may require you to enable developer mode.
767+
fn package_symlink_to_dir() {
768+
if !symlink_supported() {
769+
return;
770+
}
771+
772+
project()
773+
.file("src/main.rs", r#"fn main() { println!("hello"); }"#)
774+
.file("bla/Makefile", "all:")
775+
.symlink_dir("bla", "foo")
776+
.build()
777+
.cargo("package -v")
778+
.with_stderr_contains("[ARCHIVING] foo/Makefile")
779+
.run();
780+
}
781+
702782
#[cargo_test]
703783
fn do_not_package_if_repository_is_dirty() {
704784
let p = project().build();

tests/testsuite/small_fd_limits.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,6 @@ fn use_git_gc() {
9898
}
9999

100100
#[cargo_test]
101-
// it looks like this test passes on some windows machines but not others,
102-
// notably not on AppVeyor's machines. Sounds like another but for another day.
103-
#[cfg_attr(windows, ignore)]
104101
fn avoid_using_git() {
105102
let path = env::var_os("PATH").unwrap_or_default();
106103
let mut paths = env::split_paths(&path).collect::<Vec<_>>();

tests/testsuite/support/mod.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,24 @@ impl FileBuilder {
178178
struct SymlinkBuilder {
179179
dst: PathBuf,
180180
src: PathBuf,
181+
src_is_dir: bool,
181182
}
182183

183184
impl SymlinkBuilder {
184185
pub fn new(dst: PathBuf, src: PathBuf) -> SymlinkBuilder {
185-
SymlinkBuilder { dst, src }
186+
SymlinkBuilder {
187+
dst,
188+
src,
189+
src_is_dir: false,
190+
}
191+
}
192+
193+
pub fn new_dir(dst: PathBuf, src: PathBuf) -> SymlinkBuilder {
194+
SymlinkBuilder {
195+
dst,
196+
src,
197+
src_is_dir: true,
198+
}
186199
}
187200

188201
#[cfg(unix)]
@@ -194,7 +207,11 @@ impl SymlinkBuilder {
194207
#[cfg(windows)]
195208
fn mk(&self) {
196209
self.dirname().mkdir_p();
197-
t!(os::windows::fs::symlink_file(&self.dst, &self.src));
210+
if self.src_is_dir {
211+
t!(os::windows::fs::symlink_dir(&self.dst, &self.src));
212+
} else {
213+
t!(os::windows::fs::symlink_file(&self.dst, &self.src));
214+
}
198215
}
199216

200217
fn dirname(&self) -> &Path {
@@ -252,7 +269,7 @@ impl ProjectBuilder {
252269
.push(FileBuilder::new(self.root.root().join(path), body));
253270
}
254271

255-
/// Adds a symlink to the project.
272+
/// Adds a symlink to a file to the project.
256273
pub fn symlink<T: AsRef<Path>>(mut self, dst: T, src: T) -> Self {
257274
self.symlinks.push(SymlinkBuilder::new(
258275
self.root.root().join(dst),
@@ -261,6 +278,15 @@ impl ProjectBuilder {
261278
self
262279
}
263280

281+
/// Create a symlink to a directory
282+
pub fn symlink_dir<T: AsRef<Path>>(mut self, dst: T, src: T) -> Self {
283+
self.symlinks.push(SymlinkBuilder::new_dir(
284+
self.root.root().join(dst),
285+
self.root.root().join(src),
286+
));
287+
self
288+
}
289+
264290
pub fn no_manifest(mut self) -> Self {
265291
self.no_manifest = true;
266292
self

tests/testsuite/support/paths.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ impl CargoPathExt for Path {
153153
where
154154
F: Fn(i64, u32) -> ((i64, u32)),
155155
{
156-
let stat = t!(path.metadata());
156+
let stat = t!(path.symlink_metadata());
157157

158158
let mtime = FileTime::from_last_modification_time(&stat);
159159

0 commit comments

Comments
 (0)