Skip to content

Make object order deterministic - #47

Open
bmwiedemann wants to merge 5 commits into
medek:masterfrom
bmwiedemann:deterministic-object-order
Open

Make object order deterministic#47
bmwiedemann wants to merge 5 commits into
medek:masterfrom
bmwiedemann:deterministic-object-order

Conversation

@bmwiedemann

Copy link
Copy Markdown

While working on reproducible builds for openSUSE, I found that our rav1e package varied in every build and the 1st commit in this PR helps to make the build result deterministic.
The 2nd commit was found along the way by Claude to increase robustness and even though my knowledge of rust is limited, it both looks good to me.

Without this patch, the parallel path of compile_objects_inner would
return OK with a reduced result list.
It logged the error, but that can easily be missed in a large
non-interactive compile log.
Then users could get missing symbols later at link time.
With this change, it fails early to make debugging problems easier.
Verifies that compile_objects_inner returns Err when a file fails to
compile, in both the parallel and the serial configuration.
The test fails without the previous commit.
The error from run() only says "nonzero exit status", which is not
enough when many files are compiled, especially in parallel where the
nasm output of several jobs is interleaved on stderr.
@bmwiedemann
bmwiedemann force-pushed the deterministic-object-order branch from 6a4e71f to dcd7942 Compare August 2, 2026 05:44
@bmwiedemann

bmwiedemann commented Aug 2, 2026

Copy link
Copy Markdown
Author

I split out the robustness improvement into #48 and rebased onto it.

Note: without the fix, the regression test failed on cargo test --features parallel

bmwiedemann and others added 2 commits August 2, 2026 08:10
With the `parallel` feature, compile_objects_inner() shares one work list
between the calling thread and the jobs the helper thread spawns, and then
concatenates the calling thread's results with the helper thread's. Which
file lands in which of the two vectors depends on jobserver token timing,
so the returned Vec comes out in a different order on every run. The
non-parallel path returns the input order, so the two paths disagree as
well.

Callers pass the result straight on to cc::Build::object() or to archive(),
so the order becomes the member order of the resulting .a, and from there
the layout of the linked binary. That makes the build unreproducible: in
rav1e, which assembles 39 files this way, two builds of the same source
placed the assembly routines at different addresses every time.

Carry each file's index through the work list and give the results one slot
per input file, so a job drops its object where the caller expects it
whatever order the jobs finish in.

Tested with a 40-file crate and a nasm stub that sleeps a random moment:
before, six builds produced six different orders; after, all six agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bmwiedemann
bmwiedemann force-pushed the deterministic-object-order branch from dcd7942 to 5634065 Compare August 2, 2026 06:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant