Repository navigation
ghc: deterministic link order + package.cache (reproducible Haskell) #277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,34 @@ cd "ghc-${MINIMAL_ARG_VERSION}" | |
| sed -i 's/extern void\* malloc();/extern void\* malloc(long unsigned int);/' utils/hp2ps/Utilities.c | ||
| sed -i 's/extern void \*realloc();/extern void \*realloc(void \*, long unsigned int);/' utils/hp2ps/Utilities.c | ||
|
|
||
| # Reproducibility: GHC iterates package/UnitId collections in hash-set order, | ||
| # making the linker arg / DT_NEEDED / .dynstr order in every binary AND the | ||
| # ghc-pkg package.cache non-deterministic. Code (.text/.rodata) is already | ||
| # byte-identical; this is pure ORDERING. Sort the two collections. | ||
| # | ||
| # (1) Link order — backport of upstream GHC #26838 / MR !15453 (merged for | ||
| # 10.0.1; NOT in 9.10.x; Debian ships this exact patch for 9.10.3). Restores | ||
| # the sorted-by-UnitId order GHC <= 9.6 had. | ||
| ghc_state=compiler/GHC/Unit/State.hs | ||
| grep -q 'import Data.List ( intersperse, partition, sortBy, isSuffixOf, sortOn )' "$ghc_state" \ | ||
| && grep -q 'let preload1 = nonDetKeysUniqMap (filterUniqMap (isJust . uv_explicit) vis_map)' "$ghc_state" \ | ||
| || { echo "ERROR: GHC link-order patch targets not found in $ghc_state — GHC source changed; revisit the #26838 backport." >&2; exit 1; } | ||
| sed -i 's/import Data.List ( intersperse, partition, sortBy, isSuffixOf, sortOn )/import Data.List ( intersperse, partition, sortBy, isSuffixOf, sortOn, sort )/' "$ghc_state" | ||
| sed -i 's/let preload1 = nonDetKeysUniqMap (filterUniqMap (isJust . uv_explicit) vis_map)/let preload1 = sort $ nonDetKeysUniqMap (filterUniqMap (isJust . uv_explicit) vis_map)/' "$ghc_state" | ||
|
|
||
| # (2) ghc-pkg package.cache — sort the .conf list before it is read + serialized | ||
| # so the post-build `ghc-pkg recache` emits a byte-identical cache regardless | ||
| # of filesystem readdir order. (No upstream fix exists; `sort` already | ||
| # imported in Main.hs.) | ||
| ghc_pkg_main=utils/ghc-pkg/Main.hs | ||
| grep -q 'confs = map (path </>) $ filter (".conf" `isSuffixOf`) fs' "$ghc_pkg_main" \ | ||
| || { echo "ERROR: ghc-pkg package.cache patch target not found in $ghc_pkg_main — GHC source changed." >&2; exit 1; } | ||
| # The `sort $` below needs `sort` in scope; 9.10.3's Main.hs imports it (line 78), | ||
| # but guard it so a future GHC import reorg fails here, not deep in the build. | ||
| grep -qE 'import Data.List \(.*\bsort\b' "$ghc_pkg_main" \ | ||
| || { echo "ERROR: 'sort' not imported in $ghc_pkg_main — patch (2) requires it (add a sort import)." >&2; exit 1; } | ||
| sed -i 's#confs = map (path </>) $ filter (".conf" `isSuffixOf`) fs#confs = map (path </>) $ sort $ filter (".conf" `isSuffixOf`) fs#' "$ghc_pkg_main" | ||
|
|
||
|
Comment on lines
+12
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the required build-twice reproducibility verification step. This script improves ordering determinism, but it still does not perform the required “build twice and compare two As per coding guidelines, “Verify reproducibility by building the package twice and comparing the two $OUTPUT_DIR trees for byte-for-byte identity.” 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| # Create stubs for tools ./configure / bootstrap.py checks for but aren't strictly needed | ||
| STUB_DIR="$PWD/../stub-bin" | ||
| mkdir -p "$STUB_DIR" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard gap: Unlike patch (1) which explicitly adds
sortto theData.Listimport viased, patch (2) relies on the assumption thatsortis already in scope inMain.hs. Thegrepguard here only verifies the target line exists — it doesn't verifysortis importable.If
sorthappens NOT to be in scope (e.g., a future GHC version reorganizes imports inghc-pkg/Main.hs), the build would fail with a Haskell compile error deep into the multi-hour Hadrian build rather than failing early at this guard.Consider adding a guard like:
Or, more robustly, mirror the patch (1) pattern: explicitly
sedin asortimport if it's not already present, so the patch is self-contained regardless of upstream import state.