cargo-about
cargo-about
is a cargo plugin that lets you generate license information for all crates in a dependency graph.
Quickstart
Installs cargo-about, initializes your project with a default configuration, then generates a licenses.html file.
cargo install --locked cargo-about && cargo about init && cargo about generate -o licenses.html about.hbs
Command Line Interface
cargo-about is intended to be used as a Command Line Tool, see the link for the available commands and options.
API
cargo-about is primarily meant to be used as a cargo plugin, but a majority of its functionality is within a library whose docs you may view on docs.rs
Commands
common
-L, --log-level
The log level for messages, only log messages at or above the level will be emitted.
Possible values:
off
- No output will be emittederror
warn
(default)info
debug
trace
--color
Whether coloring is applied to human-formatted output, using it on JSON output has no effect.
Possible values:
auto
(default) - Coloring is applied if the output stream is a TTYalways
- Coloring is always appliednever
- No coloring is applied for any output
init
Initializes an about.toml configuration.
Flags
--no-handlebars
Disables the generation of the default handlebars template.
--overwrite
Overwrites an existing about.toml
file with the default configuration.
generate
The generate subcommand is the primary subcommand of cargo-about
. It attempts to find and satisfy all license requirements for a crate's or workspace's dependency graph and generate licensing output based on one or more handlebar templates.
Flags
--all-features
(single crate or workspace)
Enables all features when determining which crates to consider. Works for both single crates and workspaces.
--no-default-features
(single crate only)
Disables the default
feature for a crate when determining which crates to consider.
--workspace
Scan licenses for the entire workspace, not just the active package.
--fail
Exits with a non-zero exit code if any crate's license cannot be reasonably determined
Options
-c, --config
Path to the config to use. Will default to <manifest_root/about.toml>
if not specified.
--features
(single crate only)
Space-separated list of features to enable when determining which crates to consider.
-i, --include-local
Include local crates beneath one or more directories, local crates are disregarded by default.
-m, --manifest-path
The path of the Cargo.toml for the root crate, defaults to the current crate or workspace in the current working directory.
-n, --name
The name of the template to use when rendering. If only passing a single template file to templates
this is not used.
-o, --output-file
A file to write the generated output to. Typically an .html
file.
--threshold
(default: 0.8)
The confidence threshold required for license files to be positively identified: 0.0 - 1.0
--format <json|handlebars>
(default: handlebars
)
The format to output the license + crate data in.
Args
<templates>
The template(s) or template directory to use. Must either be a .hbs
file, or have at least one .hbs
file in it if it is a directory. Required if --format = handlebars
(the default).
Config
Contains all of the configuration options used when running generate
The accepted
field
Priority list of all the accepted licenses for a project. cargo-about
will try to satisfy the licenses in the order that they are declared in this list. So in the below example, if a crate is licensed with the typical Apache-2.0 OR MIT
license expression, only the Apache-2.0
license would be used as it has higher priority than MIT
only one of them is required. This list applies globally to all crates. The licenses specified here are used to satisfy the license expressions for every crate, if they can't be satisfied then cargo-about
will emit an error for why.
accepted = [
"Apache-2.0",
"MIT",
]
The targets
field (optional)
A list of targets that are actually building for. Crates which are only included via cfg()
expressions that don't match one or more of the listed targets will be ignored. Note that currently the targets are evaluated all at once, so there might be cases where a crate is included that is actually impossible for any one target alone.
targets = [
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-pc-windows-msvc",
"x86_64-apple-darwin",
]
The ignore-build-dependencies
field (optional)
If true, all crates that are only used as build dependencies will be ignored.
ignore-build-dependencies = true
The ignore-dev-dependencies
field (optional)
If true, all crates that are only used as dev dependencies will be ignored.
ignore-dev-dependencies = true
The ignore-transitive-dependencies
field (optional)
If true, only direct dependencies of crates in the workspace will be included in the graph, transitive dependencies (dependencies of dependencies) will be ignored.
ignore-transitive-dependencies = true
The no-clearly-defined
field (optional)
If true, will not attempt to lookup licensing information for any crate from https://clearlydefined.io, only user clarifications, workarounds, and local file scanning will be used to determine licensing information.
The filter-noassertion
field (optional)
If using https://clearlydefined.io to gather license information, that service will conservatively add NOASSERTION
to the expression for files that contain license like data, but an SPDX license ID could not be confidently ascribed to it. This can result in the license expression for the crate to contain 1 or more NOASSERTION
identifiers, which would require the user to accept that (not really valid) ID to pass the license check. By setting this field to true
, files that have a NOASSERTION
id will instead be scanned locally, which will generally either figure out the license, or else skip that file.
For a real world example of what this looks like, webpki:0.22.0
's LICENSE file is an ISC license, however it has a preamble that is not part of the ISC license that trips up clearly defined's inspection, causing it to be attributed with ISC AND NOASSERTION
. Locally scanning the file will be more tolerant and just attribute it with ISC
.
The workarounds
field (optional)
Unfortunately, not all crates properly package their licenses, or if they do, sometimes in a non-machine readable format, or in a few cases, are slightly wrong. These can be clarified manually via configuration, but some crates that are widely used in the Rust ecosystem have these issues, and rather than require that every cargo-about user who happens to have a dependency on one or more of these crates specify the same config to get it working, cargo-about instead includes a few built-in clarifications that can be opted into with a single config entry rather than redoing work.
See Workarounds for a list of the workarounds currently built-in to cargo-about
workarounds = [
"ring",
"rustls",
]
The private
field (optional)
It's often not useful or wanted to check for licenses in your own private workspace crates. So the private field allows you to do so.
The ignore
field
If true
, workspace members will not have their license expression checked, if they are not published.
# Cargo.toml
[package]
name = "sekret"
license = "¯\_(ツ)_/¯"
publish = false # "private"!
# about.toml
# The sekret package would be ignored now
private = { ignore = true }
The registries
field
A list of private registries you may publish your workspace crates to. If a workspace member only publishes to private registries, it will also be ignored if private.ignore = true
# Cargo.toml
[package]
name = "sekret"
license = "¯\_(ツ)_/¯"
publish = ["sauce"]
# about.toml
# Still ignored!
private = { ignore = true, registries = ["sauce"] }
Crate configuration
Along with the global options, crates can be individually configured as well, using the name of the crate as the key. Crate specific configuration must come last in the config file.
The accepted
field (optional)
Just as with the global accepted
field, this accepts specific licenses for the crate. These licenses are appended to the global list, and are again in priority order. So for example, if the global accept was like this:
accepted = ["MIT", "ISC"]
And we are using ring
, which also is licensed under the OpenSSL
license, we could use the following configuration to satisfy the license requirements of ring
.
accepted = ["MIT", "ISC"]
[ring]
accepted = ["OpenSSL"]
The clarify
field (optional)
As noted in the workarounds
, some crates have complicated or incomplete licensing that messes up the harvesting of the license info in an automated fashion. While the workarounds
exists for popular crates (and can always be expanded with PRs!) there are often going to be crates that you will need to clarify yourself until a new release of the crate, etc, which is the purpose of the clarify
field, to specify exactly what the license information is, and how to verify that the license terms are still the same as when they were clarified, using hashes of the input files.
Note that since clarifications are human supplied in your project's own configuration, they take precedence over all other methods. If a crate is clarified, it will not be retrieved from clearlydefined.io nor via local file harvesting.
The license
field
This is the top level SPDX expression for the crate as a whole. It should be noted that this actually overrides the license
expression of the crate itself if it exists, though in most cases this will be the same as the stated license
, it is simply required so that you can't accidentally forget it in the cases where it does differ.
[ring.clarify]
license = "ISC AND MIT AND OpenSSL"
The override-git-commit
field (optional)
When clarifying a crate with files pulled from its source git repository, cargo-about will normally read the contents of the .cargo_vcs_info.json
file that is usually part of a published crate's contents, which includes the full commit hash at the time the crate was published. However, this file is not guaranteed to be present (eg, if the crate is published with cargo publish --allow-dirty
), so in that case the git ref to pull the license file contents must be supplied. This can either be a full git revision, or a git tag.
[core-graphics-types.clarify]
override-git-commit = "3841d2bb3aa76dec2ea6319e757603fb923b5a50"
The files
and/or git
field
When clarifying the license of a crate, it is required to give a source of truth for the licenses in the expression, to prevent drift between the clarification and the actual licensing of the crate in question. For example, if a crate uses the Zlib
license, then changes between releases to use the MIT
license instead, the source of truth (eg. the LICENSE
file) would also (hopefully...) change resulting in a hash mismatch that means the clarification would not be used.
We'll be using this example for the ring
crate
[ring.clarify]
license = "ISC AND MIT AND OpenSSL"
[[ring.clarify.files]]
path = 'LICENSE'
license = 'OpenSSL'
checksum = '53552a9b197cd0db29bd085d81253e67097eedd713706e8cd2a3cc6c29850ceb'
start = '/* ===================================================================='
end = '\n * Hudson (tjh@cryptsoft.com).\n *\n */'
The path
field
This is the relative path to the file from the root. For files
this is the root of the crate, but for git
this is the repo root.
The license
field (optional)
In a multiple license situation it can be useful to supply the exact license for the file. If this is not supplied the parent clarify.license
expression is used instead.
The checksum
field
This is the full sha-256 checksum of the contents. If this doesn't match the computed checksum, the clarification will not be used.
The start
field (optional)
In some cases, crates concatenate multiple licenses together into a single file, which confuses machine readers, and makes splatting the license text into the final generated template a pain, so in those cases you need to supply a place in the text that a license starts and/or ends from. This is just a simple substring find.
The end
field (optional)
Just as with start, this is just a simple substring find, however, it will only match text that comes after the position the start text (or beginning of the file) was found.
Workarounds
Here's a list of the current set of workarounds, the crates they apply to, and why the workaround is needed
bitvec
The bitvec
crate and one of its dependencies by the same author don't include the license information in the crate source.
chrono
The chrono
crate puts both the Apache-2.0
and MIT
license texts in the same file, which confuses askalono
and also means the SPDX expression is not machine readable.
cocoa
Some of the crates published from https://github.com/servo/core-foundation-rs do not properly package the license text when publishing the crate.
gtk
The various gtk
crates don't include the license text in older versions, though versions published after 2021-10-21 will have the license information in the packaged source so the workaround is not needed for those versions.
prost
The various prost
crates don't include the license text in the published crates.
ring
The ring
crate puts puts 4 different licenses in a single file which confuses tools and also doesn't declare its expression in the Cargo.toml manifest.
rustls
The rustls
crate puts puts 3 different licenses in a single file which confuses tools. This should be fixed in later versions.
sentry
None of the crates published from https://github.com/getsentry/sentry-rust include the license text.
sentry-actix
sentry-anyhow
sentry-backtrace
sentry-contexts
sentry-core
sentry-debug-images
sentry-log
sentry-panic
sentry-slog
sentry-tower
sentry-tracing
sentry-types
sentry
tonic
None of the crates published from https://github.com/hyperium/tonic include the license text.
tract
None of the crates published from https://github.com/sonos/tract included the license text previous to versions 0.15.4. Versions after this do include the license text and this workaround is not needed
tract-data
tract-linalg
tract-core
tract-pulse
tract-pulse-opl
tract-hir
tract-nnef
tract-tensorflow
tract-onnx-opl
tract-onnx
tract-kaldi
tract-cli
wasmtime
The crates around wasmtime
and cranelift
, many but not all of which are published from https://github.com/bytecodealliance/wasmtime, use the Apache-2.0 WITH LLVM-exception
, and the license text reflects this. However, neither clearlydefined.io
nor askalono
report the inclusion of the LLVM-exception
, so this workaround just clarifies that.
cranelift-bforest
cranelift-codegen
cranelift-codegen-meta
cranelift-codegen-shared
cranelift-entity
cranelift-frontend
cranelift-native
cranelift-wasm
regalloc
wasi-cap-std-sync
wasi-common
wasmparser
wasmtime-environ
wasmtime-jit
wasmtime-runtime
wasmtime-types
wasmtime-wasi
wast
wiggle
wiggle-generate
wiggle-macro
winx
Output template
cargo-about uses handlebars templates to take the output of license gathering and transform it into your desired output. See handlebars for how handlebar templates work generally.
Types
LicenseSet
count
- The number of times the license was used to satisfy a license expression for a cratename
- The name of the licenseid
- Theid
of the license
License
name
- The full name of the licenseid
- The SPDX identifiertext
- The full license textsource_path
- The path of the license if it was pulled from the source code of the crateused_by
A list ofUsedBy
UsedBy
crate
- Metadata for a cargo packagepath
- Optional path of the dependency that is being used by the license
Variables
These are the variables that are exposed to the templates
overview
- A list ofLicenseSet
licenses
- A list ofLicense
Example
<ul class="licenses-overview">
{{#each overview}}
<li><a href="#{{id}}">{{name}}</a> ({{count}})</li>
{{/each}}
</ul>
Preview of the default about.hbs
You can view the full license here.
clarify
Computes a clarification for a file
Options
-s, --subsections
One or more subsections in the file which is itself its own license. Uses !!
as the separator between the start and end of the subsection.
--threshold
(default: 0.8)
The minimum confidence score a license must have
Args
<path>
The relative path from the root of the source of the file to clarify.
Subcommands
crate
Retrieves the file from the git repository and commit associated with the specified crate and version.
Args
<spec>
The crate's <name>-<version>
spec to retrieve. The crate source must already be downloaded.
repo
Pulls the file from a git repository rather than the file system.
Args
<rev>
The git revision to retrieve. Can either be a commit hash or a tag.
<repo>
The full URL to the git repo. Only github.com
, gitlab.com
, and bitbucket.org
are currently supported.
path
Reads the license information from a path on disk.
Args
<root>
The path root.