Giter VIP home page Giter VIP logo

cargo-unused-features's Introduction

Donate Latest Version MIT docs

Potential unused, enabled feature flag finder and pruner.

This cargo tool allows you to find and prune enabled, but, potentially unused feature flags from your project.

Use unused-features --help to fetch more details about available subcommands and their configurations.

1. How to use

Run cargo install cargo-unused-features or download the library and build it yourself.

  1. Step Analyzing enabled unused features.

You can control the compilation by supplying --bin, --lib, --examples, --benches, --tests flags. By default, libraries and binaries are compiled. If you want tests, examples, benches, be compiled in the analysis, make sure to supply the corresponding tags.

cd C:/some_path/
unused-features analyze

After it finished running, check the report.json in the project directory and use this for the next two steps.

  1. Generating a HTML report. (optional)

You can generate a simple HTML report from the json to make it easier to inspect results.

unused-features build-report --input "C:/some_path/report.json"

After it finished running, check the report.html in the project directory. You can choose to manually fix your dependencies or use the command in the next step.

  1. Applying suggested removals of feature flags.

It is possible to auto-apply the findings of the first command. But keep in mind the disclaimers.

unused-features prune --input "C:/some_path/report.json"

2. How it Works

This library works for both workspaces and individual crates. In the context of a workspace it will just iterate each crate in the workspace-definition and run the same process it does for a single crate.

For a single crate it removes a feature of a dependency and then compiles the project to see if it still compiles. If it does, the feature flag can possibly be removed, but it can be a false-positve (disclaimers.). Yes, recompiling for every feature-flag implies some overhead. However, this is a one-time thing and if you have a large project, just let it run for a while. I personally have ran it on a project with over 50 crates and it finished within an hour. The compiler will not perform a complete clean rebuild which is in our favor.

Furthermore, This library uses cargo_toml to remove or add features. It loads a TOML file into memory, modifies the dependency features, serializes the Manifest, and writes it back to the toml-file. Then it starts compiling, and after it finishes running, the original content is written back as if nothing had happened.

But before doing all of that, we need to know which features to remove in the first case. This library uses cargo-metadata to collect all enabled features from the dependencies. Features can be enabled in several ways. Manually by features = ['x', 'y'] tag, or by the default-features=false/true tag. Also, features can enable 0-n other features e.g default=[x,y]. So, this library collects all enabled features, whether they are implicitly or explicitly enabled. After it collects all enabled features for a dependency, it will remove them one-by-one and compile the project as described above.

During the process, a json report is updated for each crate to ensure that if it crashes the progress is not lost. Use the cargo unused-features build-report command to visualize this report.

Finally, this library also has the option to apply all suggestions automatically by running cargo unused-features prune command. For this task, toml-edit is used because it doesn't mess with formatting, comments, and spaces, in the TOML-file.

3. Some things to keep in mind

  • Sometimes feature flags can turn logic on and off without breaking the compilation and therefore this tool can mark a feature flag as removable, but essentially it would change the internal logic of a library. For this reason, this library offers 3 phases. Analyze, automatically apply suggestions, and generate a report. If you want to be more carefully inspect the HTML report to see more clearly what suggestions are given and manually update the dependencies yourself.
  • Given crate A and B, B depends on A and uses logic from a dependency of A that is hidden behind a feature flag enabled in A, but A itself does not use this code. In this scenario, the feature flag can be removed for A but not for B. So this can result in a false positive. I would recommend going through the suggestions on a crate by crate basis, or just running it on the full workspace, and fixing the compilation errors by adding the removed features.
  • Feature flags may only be used for a certain target-os. This project does not compile for each target, but instead, you can specify the target with --target x to the cargo unused-features command.

4. Report Bug

This tool is very new, and one can expect problems. If you have problems, please do the following:

  1. Open an issue with the problematic Cargo.toml file.
  2. Provide the --log debug flag to the cargo-unused-features command and post the logs in the issue as well.

Future

Would be nice to find ways to be more certain about when a feature can be removed. Potentially we would need to do some regex matches with features in the dependency code base and see how the feature is used. And with that get a more precise assumption. If you have an idea, feel free to open an issue or reach out to me!

cargo-unused-features's People

Contributors

cdown avatar jayvdb avatar orhun avatar timonpost avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

cargo-unused-features's Issues

Supporting workspace versioning

Describe the bug
Does this crate support workspace versioning? I'm getting an error when attempting to run it on prql-compiler. Here's the issue template:

❯ unused-features analyze --log-level debug
[2022-12-09T00:21:11Z INFO  unused_features::subcommands::analyze] /Users/maximilian/workspace/prql/prql-compiler
[2022-12-09T00:21:11Z DEBUG unused_features::cargo_project] Loading '/Users/maximilian/workspace/prql/prql-compiler/Cargo.toml' ...
[2022-12-09T00:21:11Z DEBUG unused_features::cargo_project] Successfully read the toml file.
[2022-12-09T00:21:11Z DEBUG unused_features::cargo_project] Parsing toml definition ...
[2022-12-09T00:21:11Z ERROR unused_features::subcommands::analyze] Failed to load '/Users/maximilian/workspace/prql/prql-compiler/Cargo.toml' crate. invalid type: map, expected a string for key `package.version` at line 8 column 21
[2022-12-09T00:21:11Z INFO  unused_features] Finished the process
  • The target-os and architecture used for building — MacOS, arm64
  • Any optional specified CLI configuration flags.

Thank you!

Using wildcard in workspaces does not work

Describe the bug
It is valid to use a wildcard in a workspace Cargo.toml. This tool does not accept it and gives an error such as:

[2024-01-15T11:26:54Z ERROR unused_features::subcommands::analyze] Failed to load '/xxxxx/servers/*' crate. No such file or directory (os error 2)

workspace]
resolver = "2"

members = [
  "mycrate",
  "servers/*",
  "types/*",
]

Missing Cargo.lock

Can you please add Cargo.lock to the repository and keep it up-to-date? It’s important especially for Linux distros for reproducible builds.

Debug Log contains wrong dependency progress

Describe the bug
The dependency progress in the debug log for finishing pruning features of a dependency uses the progress from the start instead of the end.

log::debug!(
"{}: Finished stripping feature flags from dependency {}.",
dependency_progress_str,
toml.crate_name()
);

Provide the following information
Debug Log:

[2023-11-25T14:32:21Z INFO  unused_features::subcommands::analyze] [85.7%]: Prune 'color' feature flag from 'clap'
[2023-11-25T14:32:21Z DEBUG unused_features::subcommands::analyze] [85.7%]: Try compiling without feature flag.
[2023-11-25T14:33:47Z DEBUG unused_features::subcommands::analyze] [85.7%]: Successfully compiled without feature.flag.
[2023-11-25T14:33:47Z INFO  unused_features::subcommands::analyze] [92.9%]: Prune 'derive' feature flag from 'clap'
[2023-11-25T14:33:47Z DEBUG unused_features::subcommands::analyze] [92.9%]: Try compiling without feature flag.
[2023-11-25T14:35:13Z DEBUG unused_features::subcommands::analyze] [92.9%]: Failed to compile without feature flag. error: Failed to compile toml document: 1 job failed
[2023-11-25T14:35:13Z DEBUG unused_features::subcommands::analyze] [50.0%]: Finished stripping feature flags from dependency cli.

For more exact details, refer to #17.

Report does not contain Crate if all Features are "Used"

Describe the bug
When removing all features of a specific crate fails with Failed to compile without feature flag. error: ..., the following code prevents the report from containing the crate.

if !feature_buffer.successfully_removed_features.is_empty() {

Although the behaviour is fine, there should be indication through the CLI output that no features could be pruned so it wasn't included in the output. Especially in workspaces, this may lead to confusion as the report json is written but does not contain the crate.

Provide the following information

Tests

Describe the bug
Some tests are needed.

Running on binary project gives wrong results [fixed]

From reddit.

Probably not a minimal example, but this fails for me:

cargo new bug
cd ./bug/
cargo add async-std
echo "fn main() { let _ = async_std::task::yield_now(); }" > ./src/main.rs
unused-features analyze
unused-features prune --input .\report.json
cargo check

Failing to prune from `report.json`

Describe the bug
Unable to prune from report.json

❯ unused-features prune --input report.json
A
B
[2022-10-28T09:30:29Z INFO  unused_features::subcommands::prune] Executing prune command.
[2022-10-28T09:30:29Z INFO  unused_features::report] Loading report from report.json.
[2022-10-28T09:30:29Z ERROR unused_features] Failed to deserialize report from report.json, maybe an old report? Current version is 0, make sure this is the same one in the report.

Only change I have made to the report.json file is removing from reqwest's successfully_remove_features the features rustls-tls and rustls-tls-webpki-roots

Provide the following information
Cargo.toml

[dependencies]
clap = { version = "4.0.15", features = ["derive"] }
regex = "1.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.11", default-features = false, features = ["stream", "rustls-tls"] }
tokio = { version = "1.16.1", features = ["full"] }
anyhow = "1.0.52"
indicatif = "0.16.2"
futures-util = "0.3.14"
dirs = "4.0.0"
cfg-if = "1.0"
tracing = "0.1"
tracing-subscriber = "0.2"
yansi = "0.5.1"
  • The debug level logs: the default ones, didnt specify anything
  • The target-os and architecture used for building: Arch linux x86

0.2.0 fails to build with rustc 1.71.1

% rustc --version
rustc 1.71.1 (eb26296b5 2023-08-03)
% cargo install cargo-unused-features
[...]
error[E0599]: `Vec<u8>` is not an iterator
   --> /home/lin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-0.68.0/src/cargo/core/compiler/future_incompat.rs:228:18
    |
227 | /             strip_ansi_escapes::strip(&to_display)
228 | |                 .map(|v| String::from_utf8(v).expect("utf8"))
    | |                 -^^^ `Vec<u8>` is not an iterator; try calling `.into_iter()` or `.iter()`
    | |_________________|
    | 
    |
   ::: /home/lin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:396:1
    |
396 |   pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
    |   ------------------------------------------------------------------------------------------------ doesn't satisfy `Vec<u8>: Iterator`
    |
    = note: the following trait bounds were not satisfied:
            `Vec<u8>: Iterator`
            which is required by `&mut Vec<u8>: Iterator`
            `[u8]: Iterator`
            which is required by `&mut [u8]: Iterator`

error[E0599]: `Vec<u8>` is not an iterator
    --> /home/lin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-0.68.0/src/cargo/core/compiler/mod.rs:1514:26
     |
1513 | /                     strip_ansi_escapes::strip(&msg.rendered)
1514 | |                         .map(|v| String::from_utf8(v).expect("utf8"))
     | |                         -^^^ `Vec<u8>` is not an iterator; try calling `.into_iter()` or `.iter()`
     | |_________________________|
     | 
     |
    ::: /home/lin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:396:1
     |
396  |   pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
     |   ------------------------------------------------------------------------------------------------ doesn't satisfy `Vec<u8>: Iterator`
     |
     = note: the following trait bounds were not satisfied:
             `Vec<u8>: Iterator`
             which is required by `&mut Vec<u8>: Iterator`
             `[u8]: Iterator`
             which is required by `&mut [u8]: Iterator`

error[E0599]: `Vec<u8>` is not an iterator
    --> /home/lin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-0.68.0/src/cargo/core/compiler/mod.rs:1548:22
     |
1547 |                   error.rendered = strip_ansi_escapes::strip(&error.rendered)
     |  __________________________________-
1548 | |                     .map(|v| String::from_utf8(v).expect("utf8"))
     | |                     -^^^ `Vec<u8>` is not an iterator; try calling `.into_iter()` or `.iter()`
     | |_____________________|
     | 
     |
    ::: /home/lin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:396:1
     |
396  |   pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
     |   ------------------------------------------------------------------------------------------------ doesn't satisfy `Vec<u8>: Iterator`
     |
     = note: the following trait bounds were not satisfied:
             `Vec<u8>: Iterator`
             which is required by `&mut Vec<u8>: Iterator`
             `[u8]: Iterator`
             which is required by `&mut [u8]: Iterator`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `cargo` (lib) due to 3 previous errors
error: failed to compile `cargo-unused-features v0.2.0`, intermediate artifacts can be found at `/tmp/cargo-installQnrMA3`

Empty reports when ran in workspace

Running unused-features analyze --log-level debug on https://github.com/manforowicz/gday generated empty reports for each crate:

{
  "version": 0,
  "root_name": "Workspace",
  "workspace_crates": {}
}

The logs looked normal, other than this being outputted repeatedly:

[2024-03-24T20:39:29Z DEBUG unused_features::subcommands::analyze] [90.0%]: Failed to compile without feature flag. error: Failed to compile toml document: failed to select a version for `env_logger`.
        ... required by package `gday v0.1.0 (/home/marcin/Documents/gday/gday)`
    versions that meet the requirements `^0.11.3` (locked to 0.11.3) are: 0.11.3
    
    the package `gday` depends on `env_logger`, with features: `anstream` but `env_logger` does not have these features.
     It has an optional dependency with that name, but that dependency uses the "dep:" syntax in the features table, so it does not have an implicit feature with that name.

Does not work properly with Cargo's sparse index protocol

When the Cargo registry sparse protocol is enabled as described here, unused-features generates an empty report.

The debug logs indicate:

Failed to compile without feature flag. error: Failed to compile toml document: failed to get `[dependency]` as a dependency of package `[package]`

A common fix for this issue is to delete ~/.cargo/registry/index and retry, however this did not fix my issue.

In order to see more details of the problem, I patched the Anyhow error message to display the backtrace.

diff --git a/src/cargo_project.rs b/src/cargo_project.rs
index 67390f3..4c2aff1 100644
--- a/src/cargo_project.rs
+++ b/src/cargo_project.rs
@@ -204,7 +204,7 @@ impl CargoProject {
         let workspace = Workspace::new(&self.toml_path(), &config)?;

         cargo::ops::compile(&workspace, &compile_options)
-            .map_err(|e| anyhow::anyhow!("Failed to compile toml document: {}", e))?;
+            .map_err(|e| anyhow::anyhow!("Failed to compile toml document: {e:?}"))?;

         Ok(())
     }

This indicated the source of the problem.

    Caused by:
        0: failed to load source for dependency `[dependency]`
        1: Unable to update registry `crates-io`
        2: usage of sparse registries requires `-Z sparse-registry`

Once the root cause was known, the workaround is to disable the sparse protocol in ~/.cargo/config.toml.

I'm just opening this issue in case others run into the same problem. I don't see any way to pass cargo flags (-Z) to unused-features analyze. I would also suggest updating Anyhow errors to include the backtrace to make identifying issues like this easier.

Mere presence of any `workspace` settings in `Cargo.toml` triggers analysis of workspace members

I am working on a project which uses a cargo-dist-generated workspace section, e.g.,

# Config for 'cargo dist'
[workspace.metadata.dist]
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.17.0"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]
# Publish jobs to run in CI
pr-run-mode = "plan"
# Whether to install an updater program
install-updater = true

The project has no workspace members, so if I run analyze on the project nothing gets analyzed (similar to #21).

invalid type: map, expected a string for key `package.version`

I'm using this code in Cargo.toml:

[package]
name = "app"
version.workspace = true
edition.workspace = true
publish.workspace = true

but when I run unused-features analyze it errors with:

A
B
[2022-12-16T17:08:45Z INFO  unused_features::subcommands::analyze] C:\projects\app
[2022-12-16T17:08:45Z ERROR unused_features::subcommands::analyze] Failed to load 'C:\projects\app/bin/app' crate. invalid type: map, expected a string for key `package.version` at line 3 column 21

Windows 10, rustc 1.65.0 (897e37553 2022-11-02), cargo 1.65.0 (4bc8f24d3 2022-10-20)

run analyze suddenly deletes the section [lints.clippy]

Provide the following information

  • The Cargo.toml file of your project (strip sensitive information If needed, dependencies are only relevant)
[package]
name = "twinet-samples"
version = "0.3.0"
authors = ["skip"]
description = "skip"
edition = "2021"
publish = false
rust-version = "1.57"

[lints.rust]
unsafe_code = "warn"

[lints.clippy]
useless_attribute = "allow"
pedantic = "warn"

[dependencies]
rand = "0.8"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.