# Cargo Featalign ### Cargo features alignment tool. [![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Checks](https://github.com/hack-ink/cargo-featalign/actions/workflows/checks.yml/badge.svg?branch=main)](https://github.com/hack-ink/cargo-featalign/actions/workflows/checks.yml) [![Release](https://github.com/hack-ink/cargo-featalign/actions/workflows/release.yml/badge.svg)](https://github.com/hack-ink/cargo-featalign/actions/workflows/release.yml) [![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/hack-ink/cargo-featalign)](https://github.com/hack-ink/cargo-featalign/tags) [![GitHub code lines](https://tokei.rs/b1/github/hack-ink/cargo-featalign)](https://github.com/hack-ink/cargo-featalign) [![GitHub last commit](https://img.shields.io/github/last-commit/hack-ink/cargo-featalign?color=red&style=plastic)](https://github.com/hack-ink/cargo-featalign)
### Introduction The original version of this project can be found at [`subalfred check features`](https://github.com/hack-ink/subalfred). Upon further investigation, I have found that this tool is not only compatible with *Substrate* projects but also works for general *Cargo* projects, offering even more powerful features than before. Now, `cargo-featalign` stands out with its enhanced functionality. The `cargo-featalign` tool offers the following features: - Checking for missing features - Printing the dependency path - Performing a dry run before overwriting - Automatically aligning/fixing missing features - Sorting alphabetically while aligning ### Installation - From GitHub: [`github.com/hack-ink/cargo-featalign/releases/latest`](https://github.com/hack-ink/**cargo-featalign/releases/latest) - From Cargo: `cargo install cargo-featalign` ### Usage ```sh cargo featalign --help ``` ``` Cargo features alignment tool. Usage: cargo-featalign [OPTIONS] --features <[NAME]> [PATH] Arguments: [PATH] Root `Cargo.toml`'s path. If `Cargo.toml` is not provided, it will be searched for under the specified path. [default: ./Cargo.toml] Options: --features <[NAME]> Features to process --thread Number of threads to use. The default value is based on the number of logical cores. [default: 32] --mode Running mode. Check: Prints the analysis result. DryRun: Prints the resolved result without modifying the `Cargo.toml` file. DryRun2: creates a `*.cargo-featalign.swap` file. Overwrite: Overwrites the original `Cargo.toml` file. [default: overwrite] [possible values: check, dry-run, dry-run2, overwrite] --indent-symbol Use the given symbol for indentation [default: tab] [possible values: tab, whitespace] --indent-size The number of spaces used for indentation [default: 4] --workspace-only Determines whether to process only workspace members --default-std Determines whether to check default features. This option is useful when working in a no-std environment. This feature checks if you have set `default-features = false` while also having a `std = ["x/std"]` part to control it separately. --depth Depth of the dependency tree to process. Use `-1` to process the entire tree. !! Running with this flag under a large project, even with 128 threads configured, is incredibly challenging. [default: 0] --sort Wether to sort the required features while aligning --verbose Verbose output -h, --help Print help (see a summary with '-h') -V, --version Print version ``` ### Example #### Preparation ```sh cargo install cargo-featalign git clone https://github.com/hack-ink/cargo-featalign.git cd cargo-featalign ``` #### Only check the features of top-level workspace members ```sh cargo featalign mock --features std,runtime-benchmarks,try-runtime --workspace-only --default-std --depth -1 --mode check | jq ``` ```json { "mock-runtime 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock)": [ { "id": "general-c 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/general/c)", "alias": "", "dependency-path": "/mock-runtime", "problem": "default-features-enabled" }, { "id": "pallet-a 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/a)", "alias": "pallet-a", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "std" ] } }, { "id": "pallet-b 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/b)", "alias": "pallet-b", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "runtime-benchmarks" ] } }, { "id": "pallet-c 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/c)", "alias": "pallet-c", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "try-runtime" ] } }, { "id": "pallet-d 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/d)", "alias": "pallet-d", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "runtime-benchmarks", "std", "try-runtime" ] } } ] } ``` #### Check the features of workspace members recursively ```sh cargo featalign mock --features std,runtime-benchmarks,try-runtime --workspace-only --default-std --depth -1 --mode check | jq ``` ```json { "nested-a 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/a)": [ { "id": "nested-d 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/d)", "alias": "", "dependency-path": "/mock-runtime/primitive-a/nested-a", "problem": "default-features-enabled" }, { "id": "nested-b 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/b)", "alias": "nested-b", "dependency-path": "/mock-runtime/primitive-a/nested-a", "problem": { "missing-features": [ "std" ] } } ], "nested-b 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/b)": [ { "id": "nested-c 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/c)", "alias": "nested-c", "dependency-path": "/mock-runtime/primitive-a/nested-a/nested-b", "problem": { "missing-features": [ "std" ] } } ], "mock-runtime 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock)": [ { "id": "general-c 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/general/c)", "alias": "", "dependency-path": "/mock-runtime", "problem": "default-features-enabled" }, { "id": "pallet-a 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/a)", "alias": "pallet-a", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "std" ] } }, { "id": "pallet-b 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/b)", "alias": "pallet-b", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "runtime-benchmarks" ] } }, { "id": "pallet-c 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/c)", "alias": "pallet-c", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "try-runtime" ] } }, { "id": "pallet-d 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/pallet/d)", "alias": "pallet-d", "dependency-path": "/mock-runtime", "problem": { "missing-features": [ "runtime-benchmarks", "std", "try-runtime" ] } } ] } ``` #### Check the features of all dependencies recursively **!! Running this under a large project, even with 128 threads configured, is incredibly challenging.** ```sh cargo featalign . --features std --depth -1 --mode check | jq ``` ```json { "semver 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "serde 1.0.176 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "serde", "dependency-path": "/cargo-featalign/cargo_metadata/semver", "problem": { "missing-features": [ "std" ] } } ], "tracing-core 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "valuable 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "valuable", "dependency-path": "/cargo-featalign/color-eyre/color-spantrace/tracing-core", "problem": { "missing-features": [ "std" ] } }, { "id": "valuable 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "valuable", "dependency-path": "/cargo-featalign/color-eyre/tracing-error/tracing/tracing-core", "problem": { "missing-features": [ "std" ] } }, { "id": "valuable 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "valuable", "dependency-path": "/cargo-featalign/color-eyre/tracing-error/tracing-subscriber/tracing-core", "problem": { "missing-features": [ "std" ] } }, { "id": "valuable 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "valuable", "dependency-path": "/cargo-featalign/color-eyre/color-spantrace/tracing-error/tracing/tracing-core", "problem": { "missing-features": [ "std" ] } }, { "id": "valuable 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "valuable", "dependency-path": "/cargo-featalign/color-eyre/color-spantrace/tracing-error/tracing-subscriber/tracing-core", "problem": { "missing-features": [ "std" ] } } ], "backtrace 0.3.68 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "libc 0.2.147 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "libc", "dependency-path": "/cargo-featalign/color-eyre/backtrace", "problem": { "missing-features": [ "std" ] } } ], "rustix 0.38.4 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "libc 0.2.147 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "libc", "dependency-path": "/cargo-featalign/clap/clap_builder/anstream/is-terminal/rustix", "problem": { "missing-features": [ "std" ] } } ], "errno 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "libc 0.2.147 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "libc", "dependency-path": "/cargo-featalign/clap/clap_builder/anstream/is-terminal/rustix/errno", "problem": { "missing-features": [ "std" ] } } ], "time 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "libc 0.2.147 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "libc", "dependency-path": "/cargo-featalign/vergen/time", "problem": { "missing-features": [ "std" ] } }, { "id": "serde 1.0.176 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "serde", "dependency-path": "/cargo-featalign/vergen/time", "problem": { "missing-features": [ "std" ] } } ], "ahash 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "once_cell 1.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "once_cell", "dependency-path": "/cargo-featalign/imara-diff/ahash", "problem": { "missing-features": [ "std" ] } } ], "getrandom 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "libc 0.2.147 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "libc", "dependency-path": "/cargo-featalign/imara-diff/ahash/getrandom", "problem": { "missing-features": [ "std" ] } } ], "clap 4.3.19 (registry+https://github.com/rust-lang/crates.io-index)": [ { "id": "once_cell 1.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "alias": "once_cell", "dependency-path": "/cargo-featalign/clap", "problem": { "missing-features": [ "std" ] } } ] } ``` #### Dry run of aligning features for workspace members ```sh cargo featalign mock --features std,runtime-benchmarks,try-runtime --workspace-only --default-std --depth -1 --mode dry-run ``` ```diff nested-a 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/a) @@ -17,4 +17,5 @@ default = ["std"] std = [ "nested-d/std", + "nested-b/std", ] nested-b 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/b) @@ -14,4 +14,6 @@ [features] default = ["std"] -std = [] +std = [ + "nested-c/std", +] mock-runtime 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock) @@ -45,18 +45,24 @@ "pallet-c/std", # "pallet-d/std", "primitive-a/std", + "pallet-a/std", + "pallet-d/std", ] runtime-benchmarks = [ "pallet-a/runtime-benchmarks", # "pallet-b/runtime-benchmarks", "pallet-c/runtime-benchmarks", + "pallet-b/runtime-benchmarks", + "pallet-d/runtime-benchmarks", # "pallet-d/runtime-benchmarks", ] try-runtime = [ "pallet-a/try-runtime", "pallet-b/try-runtime", + "pallet-c/try-runtime", + "pallet-d/try-runtime", # "pallet-c/try-runtime", # "pallet-d/try-runtime", ] ``` #### Dry run V2 of aligning features for workspace members ```sh cargo featalign mock --features std,runtime-benchmarks,try-runtime --workspace-only --default-std --depth -1 --mode dry-run2 ``` ```sh diff mock/Cargo.toml mock/Cargo.toml.cargo-featalign.swap ``` #### Sorting ```sh cargo featalign mock --features std,runtime-benchmarks,try-runtime --workspace-only --default-std --depth -1 --mode dry-run --sort ``` ```diff @@ -16,5 +16,6 @@ [features] default = ["std"] std = [ + "nested-b/std", "nested-d/std", ] nested-b 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock/nested/b) @@ -14,4 +14,6 @@ [features] default = ["std"] -std = [] +std = [ + "nested-c/std", +] mock-runtime 0.0.0 (path+file:///root/code/hack-ink/cargo-featalign/mock) @@ -48,24 +48,34 @@ "pallet-b/std", "pallet-c/std", # "pallet-d/std", + "pallet-a/std", + "pallet-d/std", "primitive-a/std", ] runtime-benchmarks = [ "pallet-a/runtime-benchmarks", # "pallet-b/runtime-benchmarks", + "pallet-b/runtime-benchmarks", "pallet-c/runtime-benchmarks", + "pallet-d/runtime-benchmarks", # "pallet-d/runtime-benchmarks", ] try-runtime = [ "pallet-a/try-runtime", "pallet-b/try-runtime", + "pallet-c/try-runtime", + "pallet-d/try-runtime", # "pallet-c/try-runtime", # "pallet-d/try-runtime", ] -empty = [] +empty = [ + "primitive-b/empty", + "primitive-c/empty", + "primitive-d/empty", +] [workspace] resolver = "2" ```