cargo-hackerman 0.2.8

Workspace hack management and package/feature query
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# cargo-hackerman
  ![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)
  [![cargo-hackerman on crates.io](https://img.shields.io/crates/v/cargo-hackerman)](https://crates.io/crates/cargo-hackerman)
  [![cargo-hackerman on docs.rs](https://docs.rs/cargo-hackerman/badge.svg)](https://docs.rs/cargo-hackerman)
  [![Source Code Repository](https://img.shields.io/badge/Code-On%20github.com-blue)](https://github.com/pacak/hackerman)
  [![cargo-hackerman on deps.rs](https://deps.rs/repo/github/pacak/cargo-hackerman/status.svg)](https://deps.rs/repo/github/pacak/hackerman)



# Hackerman solves following problems

- [Avoiding unnecessary recompilations]#cargo-hackerman-hack
- [Explaining why workspace depends on a certain crate]#cargo-hackerman-explain
- [Explaining what crates are needed for a certain crate]#cargo-hackerman-tree
- [Finding crates that a workspace needs in multiple versions]#cargo-hackerman-dupes
- [Quick lookup for crate documentation, homepage, etc]#cargo-hackerman-show


[Command line summary](#command-summary)

# Feature unification, what does this mean for me as a user?

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

```toml
[workspace]
members = [ "mega", "potato" ]
```

With two members: `mega`

```toml
[package]
name = "mega"

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

And `potato`

```toml
[package]
name = "potato"

[dependencies]
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"` feature, `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:

  ```sh
  cargo check # this will use potatoer with both "mega" and "potato"
  ```

- Commands operating on a single crate will use versions without unification:

  ```sh
  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:

```toml
[workspace.metadata.hackerman]
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 `{}`:

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

### 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`.

```text
[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

```text
Cargo.toml merge=hackerman
```

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

```text
[core]
    attributesfile = ~/.gitattributes
```

### Hackerman vs no hack vs single hack crate

Here I'm comparing effects of different approaches to unification on a workspace. Without any
changes clean check over the whole workspace that involves compiling of all the external
dependencies takes 672 seconds.

Workspace contains a bunch of crates, from which I selected crates `a`, `b`, `c`, etc, such
that crate `b` imports crate `a`, crate `c` imports crate `b`, etc. crate `a` contains no
external dependencies, other crates to.

- _no hack_ - checks are done without any hacks.
- _hackerman_ - hack was generated with `cargo hackerman hack` command and new dependencies are
  added to every crate
- _manual hack_ - hack consists of a single crate with all the crates that have different
  combinations of features and this new crate is included as a dependency to every crate in the
  workspace

Before runnining the command I clean the compilation results then commands for each column
sequentially

| 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      |





# Command summary

  * [`cargo hackerman`]#cargo-hackerman
  * [`cargo hackerman hack`]#cargo-hackerman-hack
  * [`cargo hackerman restore`]#cargo-hackerman-restore
  * [`cargo hackerman check`]#cargo-hackerman-check
  * [`cargo hackerman merge`]#cargo-hackerman-merge
  * [`cargo hackerman explain`]#cargo-hackerman-explain
  * [`cargo hackerman dupes`]#cargo-hackerman-dupes
  * [`cargo hackerman tree`]#cargo-hackerman-tree
  * [`cargo hackerman show`]#cargo-hackerman-show

# cargo hackerman

A collection of tools that help your workspace to compile fast

**Usage**: **`cargo hackerman`** _`COMMAND ...`_

**Available options:**
- **`-h`**, **`--help`** &mdash; 
  Prints help information
- **`-V`**, **`--version`** &mdash; 
  Prints version information



**Available commands:**
- **`hack`** &mdash; 
  Unify crate dependencies across individual crates in the workspace
- **`restore`** &mdash; 
  Remove crate dependency unification added by the `hack` command
- **`check`** &mdash; 
  Check if unification is required and if checksums are correct
- **`merge`** &mdash; 
  Restore files and merge with the default merge driver
- **`explain`** &mdash; 
  Explain why some dependency is present. Both feature and version are optional
- **`dupes`** &mdash; 
  Lists all the duplicates in the workspace
- **`tree`** &mdash; 
  Make a tree out of dependencies
- **`show`** &mdash; 
  Show crate manifest, readme, repository or documentation



You can pass **`--help`** twice for more detailed help


# cargo hackerman hack

Unify crate dependencies across individual crates in the workspace

**Usage**: **`cargo hackerman`** **`hack`** _`CARGO_OPTS`_ \[**`--dry`**\] \[**`--lock`**\] \[**`-D`**\]

You can undo those changes using `cargo hackerman restore`.

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available options:**
- **`    --dry`** &mdash; 
  Don't perform action, only display it
- **`    --lock`** &mdash; 
  Include dependencies checksum into stash

  This helps to ensure you can go back to original (unhacked) dependencies: to be able to restore the original dependencies hackerman needs to have them stashed in `Cargo.toml` file. If CI detects checksum mismatch this means dependencies were updated on hacked sources. You should instead restore them, update and hack again.

  You can make locking the default behavior by adding this to `Cargo.toml` in the workspace

  
  ```text
  [workspace.metadata.hackerman]
  lock = true
  ```

- **`-D`**, **`--no-dev`** &mdash; 
  Don't unify dev dependencies
- **`-h`**, **`--help`** &mdash; 
  Prints help information



`cargo-hackerman hack` calculates and adds a minimal set of extra dependencies to all the workspace members such that features of all the dependencies of this crate stay the same when it is used as part of the whole workspace or by itself.

Once dependencies are hacked you should restore them before making any changes.


# cargo hackerman restore

Remove crate dependency unification added by the `hack` command

**Usage**: **`cargo hackerman`** **`restore`** _`CARGO_OPTS`_ \[_`TOML`_\]...

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available positional items:**
- _`TOML`_ &mdash; 
  Restore individual files instead of the whole workspace



**Available options:**
- **`-h`**, **`--help`** &mdash; 
  Prints help information


# cargo hackerman check

Check if unification is required and if checksums are correct

Similar to `cargo-hackerman hack --dry`, but also sets exit status to 1 so you can use it as part of CI process

**Usage**: **`cargo hackerman`** **`check`** _`CARGO_OPTS`_ \[**`-D`**\]

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available options:**
- **`-D`**, **`--no-dev`** &mdash; 
  Don't unify dev dependencies
- **`-h`**, **`--help`** &mdash; 
  Prints help information


# cargo hackerman merge

Restore files and merge with the default merge driver

**Usage**: **`cargo hackerman`** **`merge`** _`BASE`_ _`LOCAL`_ _`REMOTE`_ _`RESULT`_

**Available options:**
- **`-h`**, **`--help`** &mdash; 
  Prints help information



To use it you would add something like this to `~/.gitconfig` or `.git/config`

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


And something like this to `.git/gitattributes`

  ```text
  Cargo.toml merge=hackerman
  ```

# cargo hackerman explain

Explain why some dependency is present. Both feature and version are optional

**Usage**: **`cargo hackerman`** **`explain`** _`CARGO_OPTS`_ \[**`-T`**\] \[**`-P`**\] \[**`-s`**\] _`CRATE`_ \[_`FEATURE`_\] \[_`VERSION`_\]

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available options:**
- **`-T`**, **`--no-transitive-opt`** &mdash; 
  Don't strip redundant links
- **`-P`**, **`--package-nodes`** &mdash; 
  Use package nodes instead of feature nodes
- **`-s`**, **`--stdout`** &mdash; 
  Print dot file to stdout instead of spawning `xdot`
- **`-h`**, **`--help`** &mdash; 
  Prints help information



 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 by tracing the dependency chain from the target and to the workspace.

`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.


# cargo hackerman dupes

Lists all the duplicates in the workspace

**Usage**: **`cargo hackerman`** **`dupes`** _`CARGO_OPTS`_

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available options:**
- **`-h`**, **`--help`** &mdash; 
  Prints help information


# cargo hackerman tree

Make a tree out of dependencies

**Usage**: **`cargo hackerman`** **`tree`** _`CARGO_OPTS`_ \[**`-T`**\] \[**`-D`**\] \[**`-P`**\] \[**`-w`**\] \[**`-s`**\] \[_`CRATE`_\] \[_`FEATURE`_\] \[_`VERSION`_\]

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available options:**
- **`-T`**, **`--no-transitive-opt`** &mdash; 
  Don't strip redundant links
- **`-D`**, **`--no-dev`** &mdash; 
  Don't include dev dependencies
- **`-P`**, **`--package-nodes`** &mdash; 
  Use package nodes instead of feature nodes
- **`-w`**, **`--workspace`** &mdash; 
  Keep within the workspace
- **`-s`**, **`--stdout`** &mdash; 
  Print dot file to stdout instead of spawning `xdot`
- **`-h`**, **`--help`** &mdash; 
  Prints help information



Examples:

  ```sh
  cargo hackerman tree rand 0.8.4
  cargo hackerman tree serde_json preserve_order
  ```

# cargo hackerman show

Show crate manifest, readme, repository or documentation

**Usage**: **`cargo hackerman`** **`show`** _`CARGO_OPTS`_ \[**`-m`** | **`-r`** | **`-d`** | **`-R`**\] _`CRATE`_ \[_`VERSION`_\]

**Cargo options:**
- **`    --manifest-path`**=_`PATH`_ &mdash; 
  Path to Cargo.toml file
- **`    --frozen`** &mdash; 
  Require Cargo.lock and cache are up to date
- **`    --locked`** &mdash; 
  Require Cargo.lock is up to date
- **`    --offline`** &mdash; 
  Run without accessing the network
- **`-v`**, **`--verbose`** &mdash; 
  increase verbosity, can be used several times



**Available options:**
- **`-m`**, **`--manifest`** &mdash; 
  Show crate manifest
- **`-r`**, **`--readme`** &mdash; 
  Show crate readme
- **`-d`**, **`--doc`** &mdash; 
  Open documentation URL
- **`-R`**, **`--repository`** &mdash; 
  Repository
- **`-h`**, **`--help`** &mdash; 
  Prints help information



Examples:

  ```sh
  cargo hackerman show --repository syn
  ```