Hive Hive
Sign in

Cargo target derivation ignores feature resolution: excluded packages become build targets

GitHub issue · Open

Metadata
Source
tuist/once #216
Updated
Aug 1, 2026
Domains
Once
Details

What happened

once query targets derives Cargo targets from Cargo.lock without applying Cargo’s feature resolution. Every package present in the lockfile becomes a rust_crate target, including packages that the enabled feature set excludes from the build.

For a crate depending on sqlx with default-features = false and only the postgres driver enabled, Once emits build targets for the SQLite and MySQL drivers. cargo build never compiles these.

This is not cosmetic: once build then tries to compile sqlx-sqlite, which fails, so no target that depends on sqlx can be built at all.

Reproduction

Cargo.toml:

[package]
name = "demo"
version = "0.1.0"
edition = "2021"
[dependencies]
sqlx = { version = "0.8.6", default-features = false, features = ["runtime-tokio-rustls", "postgres", "uuid", "time", "macros", "migrate", "json"] }
uuid = { version = "1", features = ["v4"] }

src/main.rs:

fn main() {
println!("{}", std::any::type_name::<sqlx::PgPool>());
}

Then:

cargo generate-lockfile
once query targets | grep -iE 'sqlx-(sqlite|mysql)|libsqlite3'

Actual

libsqlite3-sys-0.30.1 (rust_crate) [build]
libsqlite3-sys-0.30.1-host (rust_crate) [build]
sqlx-mysql-0.8.6 (rust_crate) [build]
sqlx-mysql-0.8.6-host (rust_crate) [build]
sqlx-sqlite-0.8.6 (rust_crate) [build]
sqlx-sqlite-0.8.6-host (rust_crate) [build]

Cargo disagrees — these are not part of the build:

$ cargo tree -i libsqlite3-sys
warning: nothing to print.
$ cargo tree -i sqlx-mysql
warning: nothing to print.

They are in Cargo.lock (the migrate feature pulls them into the resolve graph) but the feature set excludes them from compilation.

Expected

Targets are emitted only for packages that the resolved feature set actually builds, matching cargo tree -e normal.

Impact

Target counts for the reproduction above:

count
once query targets (rust_crate) 311
packages in Cargo.lock 203
crates Cargo actually builds 136

The extra packages are not merely listed — they are reachable as build dependencies of the binary:

$ once query "MATCH (t)-[:DEPENDS_ON*]->(d) WHERE t.name = 'cargo_demo_bin_demo' RETURN d.name" | grep -iE 'sqlite|mysql'
sqlx-mysql-0.8.6
sqlx-sqlite-0.8.6
libsqlite3-sys-0.30.1
sqlx-mysql-0.8.6-host
sqlx-sqlite-0.8.6-host
libsqlite3-sys-0.30.1-host

Transitive dependency count for that binary: 306 in Once vs 132 that Cargo builds.

So Once schedules roughly 2.3× the necessary compilation work, and the surplus includes a package with a bundled C library (libsqlite3-sys) that the enabled feature set is specifically meant to avoid.

I have not been able to observe the resulting compile failure end-to-end, because once build currently stops earlier on an unrelated issue (build scripts, filed separately) before reaching these targets. The graph query above is the reliable evidence.

Environment

  • once 0.42.0 (x86_64-unknown-linux-gnu release build)
  • Linux 6.8, x86_64
Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments
P
productdevbook Aug 1, 2026

Following up with the precise mechanism, since cargo metadata alone is ambiguous here.

In cargo metadata --locked, an optional dependency stays in resolve.nodes[].deps whether or not it is enabled. What decides whether it is built is the features list on the same node:

$ cargo metadata --locked --filter-platform x86_64-unknown-linux-gnu
# node for `sqlx`:
sqlx-postgres feature enabled? True -> built
sqlx-sqlite feature enabled? False -> not built
deps: [sqlx-core, sqlx-macros, sqlx-mysql, sqlx-postgres, sqlx-sqlite]

Both drivers appear in deps; only postgres appears in features. Cargo agrees:

$ cargo tree -i sqlx-sqlite
warning: nothing to print.

So the fix is to intersect deps with the enabling feature on the parent node, rather than lowering every entry of deps to a target.

Note this also affects cargo_dependencies, whose schema already exposes features / no_default_features / all_features — those knobs cannot currently express “drop the optional dep that the feature set disables”, because the exclusion is derived, not declared.