Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cargo fmt does not handle workspace member "Cargo.toml" being a symlink; gives error "Failed to find targets" #6184

Open
tmfink opened this issue Jun 5, 2024 · 5 comments · May be fixed by #6189

Comments

@tmfink
Copy link
Contributor

tmfink commented Jun 5, 2024

Problem

If a cargo workspace has a member project whose Cargo.toml is a symlink to a valid Cargo.toml, then running cargo fmt from the member directory fails with:

$ cargo fmt
Failed to find targets
This utility formats all bin and lib files of the current crate using rustfmt.

Usage: cargo fmt [OPTIONS] [-- <rustfmt_options>...]

Arguments:
  [rustfmt_options]...  Options passed to rustfmt

Options:
  -q, --quiet
          No output printed to stdout
  -v, --verbose
          Use verbose output
      --version
          Print rustfmt version and exit
  -p, --package <package>...
          Specify package to format
      --manifest-path <manifest-path>
          Specify path to Cargo.toml
      --message-format <message-format>
          Specify message-format: short|json|human
      --all
          Format all packages, and also their local path-based dependencies
      --check
          Run rustfmt in check mode
  -h, --help
          Print help

Here's a diff with a simple workspace that reproduces the issue.
You can git apply in an empty Git repo:

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..b7695d7
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[workspace]
+resolver = "2"
+
+# lists member "concrete" crates that are in the virtual workspace
+members = [
+    "lib_a",
+    "main",
+]
diff --git a/lib_a/Cargo.toml b/lib_a/Cargo.toml
new file mode 120000
index 0000000..01b4380
--- /dev/null
+++ b/lib_a/Cargo.toml
@@ -0,0 +1 @@
+../lib_a_Cargo.toml
\ No newline at end of file
diff --git a/lib_a/src/lib.rs b/lib_a/src/lib.rs
new file mode 100644
index 0000000..211bfff
--- /dev/null
+++ b/lib_a/src/lib.rs
@@ -0,0 +1,3 @@
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
diff --git a/lib_a_Cargo.toml b/lib_a_Cargo.toml
new file mode 100644
index 0000000..bab3991
--- /dev/null
+++ b/lib_a_Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "lib_a"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/main/Cargo.toml b/main/Cargo.toml
new file mode 100644
index 0000000..bd9e3fa
--- /dev/null
+++ b/main/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "main"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+lib_a = { path = "../lib_a" }
diff --git a/main/src/main.rs b/main/src/main.rs
new file mode 100644
index 0000000..e7a11a9
--- /dev/null
+++ b/main/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..e7a11a9
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}

Steps

  1. Create cargo workspace project
  2. Convert one member project's Cargo.toml to a symlink
  3. cd into member project directory from previous step
  4. run cargo fmt

Notes

Interestingly, even if all of the Cargo.toml files are symlinks, the issue does not occur when running cargo fmt from the root of the workspace.

Version

This occurs on the latest stable and nightly versions of rustfmt as of submitting:

$ cargo fmt --version
rustfmt 1.7.0-stable (9b00956 2024-04-29)

$ cargo +nightly fmt --version
rustfmt 1.7.0-nightly (a330e49 2024-06-04)

(Migrated from rust-lang/cargo#14012 since cargo-fmt lives in rust repo, not cargo repo)

@ehuss
Copy link
Contributor

ehuss commented Jun 5, 2024

Rustfmt has its own repository.

@bors transfer rustfmt

@ehuss
Copy link
Contributor

ehuss commented Jun 5, 2024

@rustbot transfer rustfmt

@rustbot rustbot transferred this issue from rust-lang/rust Jun 5, 2024
@tmfink
Copy link
Contributor Author

tmfink commented Jun 6, 2024

The issue occurs due to use of camino::Utf8Path::canonicalize() on the Cargo.toml path, which resolves symlinks:

Returns the canonical, absolute form of the path with all intermediate components normalized and symbolic links resolved.

The use is in get_targets_root_only() in src/cargo-fmt/main.rs:

|| PathBuf::from(&p.manifest_path)
.canonicalize()

The get_targets_root_only() function tries to enumerate targets where the canonicalized path of the Cargo.toml is the same as the current directory joined with Cargo.toml.
However, if Cargo.toml is a symlink, then those paths will never equal and the targets are never found.
Hence, the error Failed to find targets.

tmfink added a commit to tmfink/rustfmt that referenced this issue Jun 7, 2024
The canonicalize() function resolves symlinks, which would cause targets
whose "Cargo.toml" is a symlink to be filtered out. This would cause
`cargo-fmt` to print an error "Failed to find targets".

Resolves rust-lang#6184
@tmfink tmfink linked a pull request Jun 7, 2024 that will close this issue
@epage
Copy link
Contributor

epage commented Sep 4, 2024

Unrelated to fixing this, I'm curious what use cases people have for symlinking Cargo.toml. Was considering changing Cargo's behavior around it (resolving the symlink early, possibly changing where the TARGET_DIR is) and wanting to understand the potential impact.

@tmfink
Copy link
Contributor Author

tmfink commented Sep 4, 2024

Unrelated to fixing this, I'm curious what use cases people have for symlinking Cargo.toml.

I have a comment on the "path_bases" RFC that goes into detail: rust-lang/rfcs#3529 (comment)
The "path_bases" RFC would solve the problem as well, but it is not yet implemented.

My company has a custom external build system that calls cargo. The Rust projects need to be able to consume "published" Rust sources that comes from a path that is only known at build time. However, cargo does not support expanding environment variables in the Cargo.toml.

To workaround this, our build system currently:

  1. Generates Cargo.toml from a template (variables get expanded)
  2. Writes the generated Cargo.toml to the "object directory" since the build system does not like writing into the "source directory"
  3. Creates a Cargo.toml symlink in the Rust project's directory to the generated Cargo.toml (which is in the "object directory")

Example Cargo.toml template expansion

Input template:

[dependencies]
foo = { path = "${MY_PUBLISH}/path/to/rust/crate/foo" }

Output Cargo.toml:

[dependencies]
foo = { path = "/very/long/path/to/publish/2024-01-01/path/to/rust/crate/foo" }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants