#cargo #features #dependencies #feature-unification #visualization

bin+lib cargo-hackerman

Workspace hack management and package/feature query

5 releases

0.2.5 May 10, 2022
0.2.4 May 10, 2022
0.2.3 May 10, 2022
0.2.2 May 9, 2022
0.2.1 May 8, 2022

#255 in Cargo plugins

Download history 468/week @ 2023-01-24 325/week @ 2023-01-31 329/week @ 2023-02-07 485/week @ 2023-02-14 321/week @ 2023-02-21 317/week @ 2023-02-28 257/week @ 2023-03-07 334/week @ 2023-03-14 286/week @ 2023-03-21 278/week @ 2023-03-28 347/week @ 2023-04-04 407/week @ 2023-04-11 171/week @ 2023-04-18 196/week @ 2023-04-25 210/week @ 2023-05-02 287/week @ 2023-05-09

934 downloads per month


2.5K SLoC

Hackerman solves following problems

Current functions include the following.

Hackerman hack / check / restore

As a part of working with workspaces cargo performs feature unification: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification

What does this mean?

Suppose you have a workspace

members = [ "mega", "potato" ]

With two members: mega

name = "mega"

potatoer = { version = "0.2.1", features = ["mega"] }

And potato

name = "potato"

potatoer = { version = "0.2.1", features = ["potato"] }

Both of which depend on a common third party crate potatoer but with different features: mega is interested in "mega" aspect, potato is interested in "potato" one.

when running different commands you end up requiring several different versions of potatoer crate.

  • Whole workspace commands will use version with unified features:
    cargo check # this will use potatoer with both "mega" and "potato"
  • Commands operating on a single crate will use versions without unification:
    cargo check -p mega           # this will use potatoer with "mega" feature
    cargo check -p potatoer       # this will use potatoer with "potato" feature
    cargo check -p mega -p potato # this will require both "mega" and "potato"

If a dependency with required combination is not present - cargo will compile it.

One way to avoid this problem is to make sure that if members of a workspace depend on a crate - they depend on it with the same set of features. Maintaining it by hand is error prone and that's when hackerman hack and hackerman restore come in.

When used with --lock option hackerman will take a checksum of all the dependencies and will save it inside Cargo.toml file under ["package.metadata.hackerman.lock"] and subsequent calls to check will confirm that this checksum is still valid.

This is required to make sure that original (unhacked) dependencies are saved and can be restored at a later point.

It is possible to hardcode --lock option in a Cargo.toml file that defines the workspace:

lock = true

At the moment unification is performed for current target only and without crosscompilation support. Automatic update for workspace toml files might not work if you are specifying dependencies using syntax different than by version or {}:

potato = "3.14"               # this is okay
banana = { version = "3.14" } # this is also okay

Hackerman explain

With large amount of dependencies it might be difficult to tell why exactly some sub-sub-sub dependency is included. hackerman explain solves this problem:

explain starts at a given crate/feature and follows reverse dependency links until it reaches all the crossing points with the workspace but without entering the workspace itself.

White nodes represent workspace members, round nodes represent features, octagonal nodes represent base crates. Dotted line represents dev-only dependency, dashed line - both dev and normal but with different features across them. Target is usually highlighted. By default hackerman expands packages info feature nodes which can be reverted with -P and tries to reduce transitive dependencies to keep the tree more readable - this can be reverted with -T.

If a crate is present in several versions you can specify version of the one you are interested in but it's optional.

You can also specify which feature to look for, otherwise hackerman will be looking for all of them.

Note, hackerman uses xdot by default. If it's not available - it is possible to install hackerman without "spawn_xdot" feature to produce .dot file to stdout


cargo hackerman explain rand 0.8.4
cargo hackerman explain serde_json preserve_order

Hackerman tree

One different problem is figuring out what some crates require. Welcome to hackerman tree

tree starts at a given crate/feature and follows dependencies links all the way to the end.

If a crate is present in several versions you need to specify the version of the one you are interested in, otherwise it's optional.

You can also specify which feature to look for, otherwise hackerman will be looking for all of them.


cargo hackerman tree rand 0.8.4
cargo hackerman explain serde_json preserve_order

Hackerman dupes

cargo hackerman dupes list all the packages used in workspace dependencies present in at least two versions:

cargo hackerman dupes

Hackerman show

cargo hackerman show aims to get more information about the dependency. Currently the details include:

  • crate manifest file for exact version used
  • crate documentation - for exact version used
  • crate repository if specified

Hackerman mergetool

Resolves merge and rebase conflicts for Cargo.toml files changed by hackerman

To use it you want something like this

global .gitconfig or local .git/config.

[merge "hackerman"]
    name = merge restored files with hackerman
    driver = cargo hackerman merge %O %A %B %P

gitattributes file, could be local per project or global

Cargo.toml merge=hackerman

To create a global gitattributes file you need to specify a path to it inside the global git config:

    attributesfile = ~/.gitattributes

Hackerman vs no hack vs single hack crate

Here I'm comparing hackerman's hack vs single crate with manually unified dependencies vs just using cargo as is. Time used - user time. package a is included in package b which is included in package c, etc. Last line performs `cargo check over the whole workspace.

Just running cargo clean ; time cargo check takes 672.80s

command no hack hackerman manual hack
check -p a 0.86s 0.80s 215.39s
check -p b 211.30s 240.15s 113.56s
check -p c 362.69s 233.38s 176.73s
check -p d 36.16s 0.24s 0.25s
check -p e 385.35s 66.34s 375.22s
check 267.06s 93.29s 81.50s
total 1263.42 634.20 962.65


~320K SLoC