rust-lang / rust-lang/cargo

`cargo build --features "X"` doesn't update binary mtime when list of features change

Open
#15,313 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-build-system A-rebuild-detection C-bug S-needs-design
Dominant language
Rust
Stars
15.5k
Forks
3k
Avg merge
23h 30m
Merged PRs (30d)
51

Description

Problem

Cargo doesn't appear to be updating the mtime on the binary in the ./target/debug directory when grabbing that file from the cache. This is breaking my using cargo inside a Makefile because the Makefile believes the built binary is older than it actually is.

Steps
  1. Create a repo with the following source

    # Cargo.toml
    [package]
    name = "minrepro"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
    [features]
    feature_a = []
    feature_b = []
    
    // src/main.rs
    fn main() {
        #[cfg(feature = "feature_a")]
        println!("Hello, Feature A!");
        #[cfg(feature = "feature_b")]
        println!("Hello, Feature B!");
    
        println!("Hello, world!");
    }
    
    # Makefile to show the problem
    FEATURES :=
    
    ifeq ($(FEATURE),A)
    FEATURES += "feature_a"
    endif
    
    ifeq ($(FEATURE),B)
    FEATURES += "feature_b"
    endif
    
    .PHONY: all FORCE
    
    all: bin
    	./bin
    
    bin: target/debug/minrepro
    	cp target/debug/minrepro bin
    
    target/debug/minrepro: FORCE
    	cargo build --features="$(FEATURES)"
    
  2. Check the current time

    bash$ date
    Fri Mar 14 10:55:00 PM PDT 202
    
  3. Build the crate with feature a

    cargo build --features feature_a
      Compiling minrepro v0.1.0 (/home/matsch/dev/minrepro)
      Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
    
  4. stat the built binary

    bash$ stat ./target/debug/minrepro
    stat ./target/debug/minrepro
    File: ./target/debug/minrepro
    Size: 3946232   	Blocks: 7712       IO Block: 4096   regular file
    Device: 253,1	Inode: 58468456    Links: 2
    Access: (0755/-rwxr-xr-x)  Uid: (1155901/  matsch)   Gid: (89939/primarygroup)
    Access: 2025-03-14 22:55:51.930800853 -0700
    Modify: 2025-03-14 22:55:51.930800853 -0700
    Change: 2025-03-14 22:55:51.938800874 -0700
    Birth: 2025-03-14 22:55:51.874800711 -0700
    
  5. Build the crate with feature b

    cargo build --features feature_b
      Compiling minrepro v0.1.0 (/home/matsch/dev/minrepro)
      Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
    
  6. stat the built binary again

    stat ./target/debug/minrepro
    File: ./target/debug/minrepro
    Size: 3946216   	Blocks: 7712       IO Block: 4096   regular file
    Device: 253,1	Inode: 58470933    Links: 2
    Access: (0755/-rwxr-xr-x)  Uid: (1155901/  matsch)   Gid: (89939/primarygroup)
    Access: 2025-03-14 23:01:12.287611878 -0700
    Modify: 2025-03-14 23:01:12.287611878 -0700
    Change: 2025-03-14 23:01:12.295611898 -0700
    Birth: 2025-03-14 23:01:12.231611736 -0700
    
  7. Build the crate with feature a again

    cargo build --features feature_a
      Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
    
  8. stat the built binary again

    stat ./target/debug/minrepro
    File: ./target/debug/minrepro
    Size: 3946232   	Blocks: 7712       IO Block: 4096   regular file
    Device: 253,1	Inode: 58468456    Links: 2
    Access: (0755/-rwxr-xr-x)  Uid: (1155901/  matsch)   Gid: (89939/primarygroup)
    Access: 2025-03-14 22:55:51.930800853 -0700
    Modify: 2025-03-14 22:55:51.930800853 -0700
    Change: 2025-03-14 23:02:43.063840777 -0700
    Birth: 2025-03-14 22:55:51.874800711 -0700
    

Notice that the "Modify" time is now older than step 5, even though the file on disk has been updated since step "5"

Using the Makefile, I can produce the same issue:

# The first time it works
bash$ FEATURE=B make
cargo build --features=""feature_b""
   Compiling minrepro v0.1.0 (/home/matsch/dev/minrepro)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
cp target/debug/minrepro bin
./bin
Hello, Feature B!
Hello, world!

# Same with feature A
bash$ FEATURE=A make
cargo build --features=""feature_a""
   Compiling minrepro v0.1.0 (/home/matsch/dev/minrepro)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
cp target/debug/minrepro bin
./bin
Hello, Feature A!
Hello, world!

# But now we go back to feature B and it still outputs feature A
# This is because make has determined the `cp` is not needed because the mtime of `./target/debug/minrepro` is older than `bin`
bash$ FEATURE=B make
cargo build --features=""feature_b""
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
./bin
Hello, Feature A!
Hello, world!
Possible Solution(s)

No response

Notes

No response

Version
cargo --version --verbose
cargo 1.85.0 (d73d2caf9 2024-12-31)
release: 1.85.0
commit-hash: d73d2caf9e41a39daf2a8d6ce60ec80bf354d2a7
commit-date: 2024-12-31
host: x86_64-unknown-linux-gnu
libgit2: 1.8.1 (sys:0.19.0 vendored)
libcurl: 8.9.0-DEV (sys:0.4.74+curl-8.9.0 vendored ssl:OpenSSL/1.1.1w)
ssl: OpenSSL 1.1.1w  11 Sep 2023
os: Debian n/a (rodete) [64-bit]

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the provided minimal crate and alternating feature builds, then trace how Cargo handles cached artifacts and the target/debug binary. Done means switching between feature_a and feature_b updates the binary's mtime when the cached artifact is restored, so the Makefile copies and runs the current build.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.