bnjbvr / cargo-machete Goto Github PK
View Code? Open in Web Editor NEWRemove unused Rust dependencies with this one weird trick!
License: MIT License
Remove unused Rust dependencies with this one weird trick!
License: MIT License
Note: this is purely from code inspection
It looks like if I ran this in a workspace root where a crate is in the root, it will search all other crates within the workspace
You can use cargo package --list
to see what will be put on crates.io. The downside is people might exclude files related to dev-dependencies
, like tests.
Hello there,
This action is using Node12 which is deprecated
https://github.com/bnjbvr/cargo-machete/blob/881ee93cb16f717244d963d9c46f2fd3884048bb/action.yml#L11C29-L11C29 .
Would you consider to replace it with another option like https://github.com/clechasseur/rs-cargo ?
Not sure if this is a known drawback of this crate, but my project has a rustfmt.toml that collapses all imports into one use/pub use
{}
block. This seems to cause all the crates to be returned as false positives.
pub use {async_trait, futures, reqwest};
cargo-machete found the following unused dependencies in /var/home/...
my_crate -- /var/home/ ... /Cargo.toml:
async_trait
futures
reqwest
I have a project that includes a git submodule for .proto
files.
Inside of the submodule there is an example rust crate that has nothing to do with the main repository I am running cargo machete
on.
How can I exclude this unrelated crate from the cargo machete
analysis?
I can explicitly pass the paths to the projects I want to include, but I just don't like having to do so.
I see multiple possible implementations:
This would allow running cargo-machete
in CI, if one can enter a set of known false positives. It would be nice to reuse the same format as cargo-udeps
for that: https://github.com/est31/cargo-udeps#ignoring-some-of-the-dependencies
When the machete process is launched from within another rust script, like it does in our ci tool, it seems to receive the wrong arguments. I made this little script that reproduces it.
use std::process::Command;
use std::process::Stdio;
use std::env;
type Besult = Result<(), String>;
fn run_cmd(program: &str, args: &[&str]) -> Besult {
run_cmd_with_env(program, args, false, std::iter::empty()).map(|_| ())
}
fn run_cmd_with_env<'env>(
program: &str,
args: &[&str],
capture_stdout: bool,
env: impl Iterator<Item = (&'env str, &'env str)>,
) -> Result<Option<String>, String> {
let mut cmd = Command::new(program);
cmd.envs(env);
cmd.args(args);
cmd.stderr(Stdio::inherit());
if capture_stdout {
cmd.stdout(Stdio::piped());
}
let child = cmd.spawn().map_err(|e| format!("failed to spawn: {e}"))?;
let output = child
.wait_with_output()
.map_err(|e| format!("failed to run: {e}"))?;
if output.status.success() {
if capture_stdout {
Ok(Some(String::from_utf8_lossy(&output.stdout).to_string()))
} else {
Ok(None)
}
} else {
Err(format!(
"running {program} returned with status {}",
output
.status
.code()
.map_or_else(|| "unknown".into(), |x| x.to_string())
))
}
}
fn cargo(args: &[&str]) -> Besult {
run_cmd("cargo", args)
}
// Run as `cargo run -- machete` to see broken behavior, just do cargo run to see working.
fn main() {
let args: Vec<String> = env::args().collect();
let _ = if args.len() > 1 && args[1] == "cargo" {
cargo(&["machete"])
} else if args.len() > 1 && args[1] == "standalone" {
run_cmd("cargo-machete", &[])
} else {
println!("Wrong argument, use standalone or cargo");
Err("Wrong argument".to_string())
};
}
When running I get
martin@puff:~/Code/broken-machete$ cargo run -- standalone
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/broken-machete standalone`
Analyzing dependencies of crates in this directory...
cargo-machete didn't find any unused dependencies in /home/martin/Code/broken-machete. Good job!
Done!
martin@puff:~/Code/broken-machete$ cargo run -- cargo
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/broken-machete cargo`
Analyzing dependencies of crates in machete...
error when walking over subdirectories: IO error for operation on machete: No such file or directory (os error 2)
cargo-machete didn't find any unused dependencies in machete. Good job!
Done!
This would be nice to have, then we could have it fail on our CI if someone added a dependency that is actually unused.
Probably the Github action could download the binary from the latest release, and then run machete. Maybe the action would have some parameters that are equivalent to the CLI flags and parameters.
Contributions would be appreciated :)
Hi I have a Cargo.toml
file without version and machete is failing
with
|
1 | [package]
| ^^^^^^^^^
missing field `version`
However, the version
field is optional and has been for some time already.
https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field
It's easy to write ignore = true
instead of ignored = true
in the machete metadata section in a Cargo.toml file. Right now it'll fail the analysis of that specific package, but not overall; it might be better to make this a hard error instead, that fails the whole analysis (after displaying the analysis results for other crates).
Is there a way to ignore check in a workspace member crate?
I'd recommend re-loading the manifest using toml_edit
and editing the manfiest in there. Soon we'll have cargo rm
in cargo (its the next in cargo-edit
to be merged) and you could shell out to that instead, if you want (trade offs of course)
It would be great to have an equivalent to [package.metadata.cargo-machete]
inside a workspace main Cargo.toml
to ignore false positive dependencies present in lots of packages of a same workspace.
Hi, folk. Thanks for this crate!
I got false positive error while using humantime_serde crate
pub struct Settings {
#[serde(with = "humantime_serde")]
some_duration: Duration,
}
output:
my_crate -- /builds/..my_crate/Cargo.toml:
humantime-serde
Is it possible to fix that?
It'd be really useful to have an official docker image along with your releases, that we could use in our CI pipeline.
// ...and the "machete" command if ran as cargo subcommand.
if i == 1 && arg == "machete" {
continue;
}
Since your command accepts positional arguments, this will not allow me to run cargo-machette machette
where machette
is a directory but I'd have to know to run cargo-machette machette machette
. Personally, I find it dubious to have a command be dual natured (run in either cargo or out) especially when accepting positional arguments.
For example, I have a repo that depends on parry3d-f64
, which does not expose the glam
conversion feature. But I am able to enable this feature by adding the nalgebra
dependency like this:
nalgebra = { version = "0.32", features = ["convert-glam022"] }
parry3d-f64 = { version = "0.13" }
cargo machete
will return a false positive for nalgebra
because I am not use
ing it anywhere, but it is nonetheless still a necessary dependency.
The output text should indicate that it has found unused dependencies, so people less familiar with the tool can understand it, if they run into it on CI.
When running trybuild
tests, that library creates new crates under target/tests
directory. Unfortunately, it does it without caring to remove unused dependencies; thus, we get a warning from cargo-machete
.
I was surprised to discover cargo-machete looks into target/
dir. We could have a CLI option or better a knob that we could specify via a config file or workspace-level metadata to skip checking some directories.
Also, looks like pub use ::crate_name reference doesn't seem to be picked up by cargo-machete
Any dependency where the package name is different from the lib name (use pkg::*;
) is marked as a false positive, this seems to be consistent across the board:
[package]
name = "package"
[lib]
name = "pkg"
path = "src/lib.rs"
$ cargo machete --version
0.6.1
Example:
https://github.com/maciejhirsz/tiny-bip39/blob/master/Cargo.toml#L1-L19
This is a feature request to add support for a --locked
flag which would cause cargo-machete to produce an error rather than inadvertently modify Cargo.toml files (currently only applicable when --with-metadata
is used).
Have a project depend on https://crates.io/crates/iban_validate and actually use it. Machete will detect it as unused
This tool doesnt appear to remove unneeded dev-dependencies.
Is that feasible to perform, perhaps opt-in?
I saw that version 0.6.0 has been prepared in a commit, and there is a corresponding tag on the repository. In case this version is considered complete and final, could we please see a release on crates.io? The latest version I see there is 0.5.0.
Newer versions of cargo let you specify crate metadata in the workspace and then inherit in each crate, but cargo-machete
fails with this error when we do that:
error when handling /XXXX/Cargo.toml: invalid type: map, expected a string for key `package.version` at line 3 column 21
See here for more details.
And thanks for a wonderful tool that's been super helpful!
Thanks for this tool, it's awesome! I'd really like to be able to skip subdirectories in a project I have.
The current setup is that there is a tests
directory and many of the tests are their own Cargo project, with a unique Cargo.toml that has unused deps. I don't want to edit the tests to ensure they have only used dependencies.
From what I can understand skipping specified subdirectories isn't currently supported. Is this feature something you would accept a contribution for?
Currently, cargo-machete doesn't check if the workspace Cargo.toml contains any unused deps or not. I think it should be straightforward as you only need to deal with Cargo.toml
files, although things never are.
I was trying out machete
on https://github.com/apollographql/apollo-rs and came upon a weird error: it tries to read fuzz/Cargo.toml
and prints an OS error about that file not existing, but it does exist.
I've managed to reduce the issue to the cargo_toml
manifest creation:
let path = std::path::Path::new("../apollo-rs/fuzz/Cargo.toml");
println!("path: {:?}, exists: {}", path, path.exists());
let manifest = cargo_toml::Manifest::from_path(path);
println!("file manifest: {:?}", manifest);
prints on this windows machine:
path: "../apollo-rs/fuzz/Cargo.toml", exists: true
file manifest: Err(Io(Os { code: 3, kind: NotFound, message: "Le chemin d’accès spécifié est introuvable." }))
So I guess parsing this specific manifest is trying to read other files, and opening them fails, but at this point the error lacks the context of which file it is ?
Seems like machette
doesn't optimize any dependencies in the build-dependencies
block of a Cargo.toml
. Would be a nice to have, since they add to the build times.
Right now all the build/test/bench/example/"normal" dependencies are bundled into one set of dependencies that are searched for the src/
directory. We ought to be more precise than that, at least for build, bench and example dependencies (tests can be included in the src/
directory).
Title speaks for itself :)
If cargo-machete
has been useful for you and has helped removing dependencies in your project, feel free to add a comment to this issue :)
I had cargo-machete
spit out a dep as removable that actually isn't.
Check out this repo and run cargo-machete
:
❯ cargo machete
Analyzing dependencies of crates in this directory...
cargo-machete found the following unused dependencies in /home/moritz/code/crates/truck:
truck-stepio -- /home/moritz/code/crates/truck/truck-stepio/Cargo.toml:
truck-geotrait
Done!
Stripping truck-geotrait
from truck-stepio/Cargo.toml
will break the build with a bunch of #derive
's not being found.
It would be awesome if cargo-machete
could provide pre-compiled binaries making it much faster to download from CI.
See typos
CLI that uses github Release to upload pre-compiled binaries of their typos
CLI.
When running cargo-machete
with workspace inheritance the CLI outputs errors. This happens when the package.edition
key is used in the workspace-level manifest and inherited in the package-level manifest. Inheriting other keys doesn't seem to break cargo machete
.
You may use the reproduction git repository:
git clone [email protected]:Veetaha/cargo-machete-workspace-inheritance-bug-repro.git
cd cargo-machete-workspace-inheritance-bug-repro
cargo machete
cargo machete --with-metadata
You'll see that both cargo machete
invocations fail
~/dev/cargo-machete-workspace-inheritance-bug-repro (master) $ cargo machete
Analyzing dependencies of crates in this directory...
error when handling /home/veetaha/dev/cargo-machete-workspace-inheritance-bug-repro/foo/Cargo.toml: value from workspace hasn't been set
cargo-machete didn't find any unused dependencies in /home/veetaha/dev/cargo-machete-workspace-inheritance-bug-repro. Good job!
Done!
~/dev/cargo-machete-workspace-inheritance-bug-repro (master) $ cargo machete --with-metadata
Analyzing dependencies of crates in this directory...
error when handling /home/veetaha/dev/cargo-machete-workspace-inheritance-bug-repro/foo/Cargo.toml: value from workspace hasn't been set
cargo-machete didn't find any unused dependencies in /home/veetaha/dev/cargo-machete-workspace-inheritance-bug-repro. Good job!
Done!
Or you can create the cargo workspace manually the following way.
Workspace-level Cargo.toml
manifest:
workspace.members = ["foo"]
workspace.package.edition = "2021"
Package-level Cargo.toml
manifest:
[package]
name = "foo"
version = "0.1.0"
edition = { workspace = true }
CLI version is 0.4.0
. Suprisingly, there is no --version
command in the CLI.
Bonne soirée!
I have a little present for you :)
Please please don't feel forced to use it. Do whatever you want, it's yours now ❤️
Crate A:
#[macro_export]
macro_rules! a_macro {
crate_c::macro(xxx)
}
Crate A's Cargo.toml:
[dependencies]
create_c = "xxx"
Crate B:
// use a_macro
a_macro();
Crate B's Cargo.toml:
[dependencies]
crate_a = "xxx"
crate_c = "xxx"
Machete thinks crate C is not used in crate B.
But only if we make crate C as a dependency of crate B can we compile crate B.
Hi I've just discovered, tried and adopted right away cargo-machete
❤️ Great crate !
When setting it up in Github workflow, I ran into some error on windows-latest
:
error: could not compile `cargo-machete` (bin "cargo-machete") due to 1 previous error
error: failed to compile `cargo-machete v0.6.1`, intermediate artifacts can be found at `C:\Users\RUNNER~1\AppData\Local\Temp\cargo-install5qeHSQ`.
To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path.
Error: The process 'C:\Users\runneradmin\.cargo\bin\cargo.exe' failed with exit code 101
➡️ full details available in Action.
💡 A simple alternative is to install it with taiki-e/install-action
, as in following commit.
note that here the workflow failed for another reason: that I had unused dependencies indeed 😄
Hope this helps !
I guess cargo-machete doesn't understand the renaming happening here: https://github.com/netvl/xml-rs/blob/9c82a7664520e9c54996aefefed48014ca5cc4f6/Cargo.toml#L14
Repro:
cargo new --lib repro && cd repro
cargo add xml-rs
echo "pub use xml::*" > src/lib.rs
cargo machete
clap
with it's derive feature is very easy to use and yields a great command line application, I would consider using this as more arguments and flags are getting added.
It is necessary to have a crate as dependency if a feature of the package enables a feature of the dependency, even if it is not used in the code.
Exemple: https://github.com/PureStake/moonbeam/blob/master/node/Cargo.toml#L36 (moonbeam-service
is flagged as unused).
Would be great to look at features and don't flag dependencies as unused if they are used in features.
If that sounds like a good idea I can probably work on a PR for it :)
https://doc.rust-lang.org/cargo/reference/workspaces.html#the-members-and-exclude-fields
For example if the workspace Cargo.toml has
[workspace]
members = ["crates/*", "crates_private/*"]
Then ideally cargo-machete only searches the crates/
and crates_private/
directories.
This is particularly useful in monorepos where there are a large number of files not included in the workspace.
Thanks for the tool - it's great!
// use rustc_rayon_core as rayon_core; // Correctly marked used
use { rustc_rayon_core as rayon_core }; // Incorrectly marked unused
pub fn notify(_: &rayon_core::Registry) {
}
fn main() {
println!("Hello, benjy bworld!");
}
Also: multiline compound use statements would be an issue as well.
Thanks @lqd for the report!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.