Unify cargo features (#9267)

When building only a single crate in the workspace to run its tests, we
often recompile a lot of other, unrelated crates. Whenever cargo has a
different set of crate features, it needs to recompile. By moving some
features (non-exhaustive for now) to the workspace level, we always
activate them an avoid recompiling.

The cargo docs mismatch the behavior of cargo around default-deps, so I
filed that upstream and left most `default-features` mismatches:
https://github.com/rust-lang/cargo/issues/14841.

Reference script:

```python
import tomllib
from collections import defaultdict
from pathlib import Path

uv = Path("/home/konsti/projects/uv")
skip_list = ["uv-trampoline", "uv-dev", "uv-performance-flate2-backend", "uv-performance-memory-allocator"]

root_feature_map = defaultdict(set)
root_default_features = defaultdict(bool)
cargo_toml = tomllib.loads(uv.joinpath("Cargo.toml").read_text())
for dep, declaration in cargo_toml["workspace"]["dependencies"].items():
    root_default_features[dep] = root_default_features[dep] or declaration.get("default-features", True)
    root_feature_map[dep].update(declaration.get("features", []))

feature_map = defaultdict(set)
default_features = defaultdict(bool)
for crate in uv.joinpath("crates").iterdir():
    if crate.name in skip_list:
        continue
    if not crate.joinpath("Cargo.toml").is_file():
        continue
    cargo_toml = tomllib.loads(crate.joinpath("Cargo.toml").read_text())
    for dep, declaration in cargo_toml.get("dependencies", {}).items():
        # If any item uses default features, they are used everywhere
        default_features[dep] = default_features[dep] or declaration.get("default-features", True)
        feature_map[dep].update(declaration.get("features", []))

for dep, features in sorted(feature_map.items()):
    features = features - root_feature_map.get(dep, set())
    if not features and default_features[dep] == root_default_features[dep]:
        continue
    print(dep, default_features[dep], sorted(features))
```
This commit is contained in:
konsti 2024-11-20 16:11:24 +01:00 committed by GitHub
parent 8ca8de8eaa
commit 2f5a64a8b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 13 additions and 13 deletions

View File

@ -37,7 +37,7 @@ uv-distribution = { path = "crates/uv-distribution" }
uv-distribution-filename = { path = "crates/uv-distribution-filename" } uv-distribution-filename = { path = "crates/uv-distribution-filename" }
uv-distribution-types = { path = "crates/uv-distribution-types" } uv-distribution-types = { path = "crates/uv-distribution-types" }
uv-extract = { path = "crates/uv-extract" } uv-extract = { path = "crates/uv-extract" }
uv-fs = { path = "crates/uv-fs" } uv-fs = { path = "crates/uv-fs", features = ["serde", "tokio"] }
uv-git = { path = "crates/uv-git" } uv-git = { path = "crates/uv-git" }
uv-globfilter = { path = "crates/uv-globfilter" } uv-globfilter = { path = "crates/uv-globfilter" }
uv-install-wheel = { path = "crates/uv-install-wheel", default-features = false } uv-install-wheel = { path = "crates/uv-install-wheel", default-features = false }
@ -72,7 +72,7 @@ uv-workspace = { path = "crates/uv-workspace" }
anstream = { version = "0.6.15" } anstream = { version = "0.6.15" }
anyhow = { version = "1.0.89" } anyhow = { version = "1.0.89" }
async-channel = { version = "2.3.1" } async-channel = { version = "2.3.1" }
async-compression = { version = "0.4.12" } async-compression = { version = "0.4.12", features = ["bzip2", "gzip", "xz", "zstd"] }
async-trait = { version = "0.1.82" } async-trait = { version = "0.1.82" }
async_http_range_reader = { version = "0.9.1" } async_http_range_reader = { version = "0.9.1" }
async_zip = { git = "https://github.com/charliermarsh/rs-async-zip", rev = "c909fda63fcafe4af496a07bfda28a5aae97e58d", features = ["deflate", "tokio"] } async_zip = { git = "https://github.com/charliermarsh/rs-async-zip", rev = "c909fda63fcafe4af496a07bfda28a5aae97e58d", features = ["deflate", "tokio"] }
@ -84,7 +84,7 @@ boxcar = { version = "0.2.5" }
bytecheck = { version = "0.8.0" } bytecheck = { version = "0.8.0" }
cachedir = { version = "0.3.1" } cachedir = { version = "0.3.1" }
cargo-util = { version = "0.2.14" } cargo-util = { version = "0.2.14" }
clap = { version = "4.5.17" } clap = { version = "4.5.17", features = ["derive", "env", "string", "wrap_help"] }
clap_complete_command = { version = "0.6.1" } clap_complete_command = { version = "0.6.1" }
configparser = { version = "3.1.0" } configparser = { version = "3.1.0" }
console = { version = "0.15.8", default-features = false } console = { version = "0.15.8", default-features = false }
@ -98,7 +98,7 @@ either = { version = "1.13.0" }
encoding_rs_io = { version = "0.1.7" } encoding_rs_io = { version = "0.1.7" }
etcetera = { version = "0.8.0" } etcetera = { version = "0.8.0" }
flate2 = { version = "1.0.33", default-features = false } flate2 = { version = "1.0.33", default-features = false }
fs-err = { version = "3.0.0" } fs-err = { version = "3.0.0", features = ["tokio"] }
fs2 = { version = "0.4.3" } fs2 = { version = "0.4.3" }
futures = { version = "0.3.30" } futures = { version = "0.3.30" }
glob = { version = "0.3.1" } glob = { version = "0.3.1" }
@ -120,7 +120,7 @@ krata-tokio-tar = { version = "0.4.2" }
mailparse = { version = "0.15.0" } mailparse = { version = "0.15.0" }
md-5 = { version = "0.10.6" } md-5 = { version = "0.10.6" }
memchr = { version = "2.7.4" } memchr = { version = "2.7.4" }
miette = { version = "7.2.0" } miette = { version = "7.2.0", features = ["fancy-no-backtrace"] }
nanoid = { version = "0.4.0" } nanoid = { version = "0.4.0" }
nix = { version = "0.29.0" } nix = { version = "0.29.0" }
owo-colors = { version = "4.1.0" } owo-colors = { version = "4.1.0" }
@ -136,7 +136,7 @@ rayon = { version = "1.10.0" }
reflink-copy = { version = "0.1.19" } reflink-copy = { version = "0.1.19" }
regex = { version = "1.10.6" } regex = { version = "1.10.6" }
regex-automata = { version = "0.4.8", default-features = false, features = ["dfa-build", "dfa-search", "perf", "std", "syntax"] } regex-automata = { version = "0.4.8", default-features = false, features = ["dfa-build", "dfa-search", "perf", "std", "syntax"] }
reqwest = { version = "0.12.7", default-features = false, features = ["json", "gzip", "stream", "rustls-tls", "rustls-tls-native-roots", "socks", "multipart", "http2"] } reqwest = { version = "0.12.7", default-features = false, features = ["json", "gzip", "stream", "rustls-tls", "rustls-tls-native-roots", "socks", "multipart", "http2", "blocking"] }
reqwest-middleware = { version = "0.4.0", features = ["multipart"] } reqwest-middleware = { version = "0.4.0", features = ["multipart"] }
reqwest-retry = { version = "0.7.0" } reqwest-retry = { version = "0.7.0" }
rkyv = { version = "0.8.8", features = ["bytecheck"] } rkyv = { version = "0.8.8", features = ["bytecheck"] }
@ -148,7 +148,7 @@ same-file = { version = "1.0.6" }
schemars = { version = "0.8.21", features = ["url"] } schemars = { version = "0.8.21", features = ["url"] }
seahash = { version = "4.1.0" } seahash = { version = "4.1.0" }
self-replace = { version = "1.5.0" } self-replace = { version = "1.5.0" }
serde = { version = "1.0.210", features = ["derive"] } serde = { version = "1.0.210", features = ["derive", "rc"] }
serde-untagged = { version = "0.1.6" } serde-untagged = { version = "0.1.6" }
serde_json = { version = "1.0.128" } serde_json = { version = "1.0.128" }
sha2 = { version = "0.10.8" } sha2 = { version = "0.10.8" }
@ -164,7 +164,7 @@ thiserror = { version = "2.0.0" }
tl = { git = "https://github.com/astral-sh/tl.git", rev = "6e25b2ee2513d75385101a8ff9f591ef51f314ec" } tl = { git = "https://github.com/astral-sh/tl.git", rev = "6e25b2ee2513d75385101a8ff9f591ef51f314ec" }
tokio = { version = "1.40.0", features = ["fs", "io-util", "macros", "process", "rt", "signal", "sync"] } tokio = { version = "1.40.0", features = ["fs", "io-util", "macros", "process", "rt", "signal", "sync"] }
tokio-stream = { version = "0.1.16" } tokio-stream = { version = "0.1.16" }
tokio-util = { version = "0.7.12", features = ["compat"] } tokio-util = { version = "0.7.12", features = ["compat", "io"] }
toml = { version = "0.8.19" } toml = { version = "0.8.19" }
toml_edit = { version = "0.22.21", features = ["serde"] } toml_edit = { version = "0.22.21", features = ["serde"] }
tracing = { version = "0.1.40" } tracing = { version = "0.1.40" }
@ -173,7 +173,7 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json", "re
tracing-tree = { version = "0.4.0" } tracing-tree = { version = "0.4.0" }
unicode-width = { version = "0.1.13" } unicode-width = { version = "0.1.13" }
unscanny = { version = "0.1.0" } unscanny = { version = "0.1.0" }
url = { version = "2.5.2" } url = { version = "2.5.2", features = ["serde"] }
urlencoding = { version = "2.1.3" } urlencoding = { version = "2.1.3" }
version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "57afc831bf2551f164617a10383cf288bf5d190d" } version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "57afc831bf2551f164617a10383cf288bf5d190d" }
walkdir = { version = "2.5.0" } walkdir = { version = "2.5.0" }

View File

@ -23,7 +23,7 @@ uv-pypi-types = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }
csv = { workspace = true } csv = { workspace = true }
flate2 = { workspace = true } flate2 = { workspace = true, default-features = false }
fs-err = { workspace = true } fs-err = { workspace = true }
globset = { workspace = true } globset = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }

View File

@ -24,7 +24,7 @@ uv-fs = { workspace = true }
uv-git = { workspace = true } uv-git = { workspace = true }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }
uv-pep440 = { workspace = true } uv-pep440 = { workspace = true }
uv-pep508 = { workspace = true, features = ["serde"] } uv-pep508 = { workspace = true }
uv-platform-tags = { workspace = true } uv-platform-tags = { workspace = true }
uv-pypi-types = { workspace = true } uv-pypi-types = { workspace = true }

View File

@ -39,7 +39,7 @@ clap = { workspace = true, optional = true }
configparser = { workspace = true } configparser = { workspace = true }
fs-err = { workspace = true, features = ["tokio"] } fs-err = { workspace = true, features = ["tokio"] }
futures = { workspace = true } futures = { workspace = true }
goblin = { workspace = true } goblin = { workspace = true, default-features = false }
itertools = { workspace = true } itertools = { workspace = true }
owo-colors = { workspace = true } owo-colors = { workspace = true }
regex = { workspace = true } regex = { workspace = true }

View File

@ -106,7 +106,7 @@ base64 = { version = "0.22.1" }
byteorder = { version = "1.5.0" } byteorder = { version = "1.5.0" }
etcetera = { workspace = true } etcetera = { workspace = true }
filetime = { version = "0.2.25" } filetime = { version = "0.2.25" }
flate2 = { workspace = true } flate2 = { workspace = true, default-features = false }
ignore = { version = "0.4.23" } ignore = { version = "0.4.23" }
indoc = { version = "2.0.5" } indoc = { version = "2.0.5" }
insta = { version = "1.40.0", features = ["filters", "json"] } insta = { version = "1.40.0", features = ["filters", "json"] }