fix: serialize sources in build info ordered by source unit ID#354
Open
fix: serialize sources in build info ordered by source unit ID#354
Conversation
Fixes #178 The `sources` key in build info was ordered alphabetically by path instead of by source unit ID, which broke compatibility with solc's JSON output and Hardhat's build format. This PR: 1. Adds a custom serde serializer for `CompilerOutput.sources` that orders entries by their source unit ID instead of path 2. Enables `preserve_order` feature for `serde_json` to maintain insertion order when converting to `serde_json::Value` 3. Adds a regression test to verify sources are ordered by ID Amp-Thread-ID: https://ampcode.com/threads/T-019be4d8-6ecf-72ef-94ba-ce4726744e00 Co-authored-by: Amp <amp@ampcode.com>
41a977e to
e8887b5
Compare
- Empty sources - Single source file - Non-sequential IDs with gaps (5, 50, 100) - Roundtrip serialization/deserialization - Many sources (50 files)
e8887b5 to
dfc5e7b
Compare
Adds an integration test that: 1. Creates a project with 3 Solidity files (Z_Last, A_First, M_Middle) 2. Compiles the project with build_info enabled 3. Reads the generated build info JSON 4. Verifies sources appear in order of source unit ID in the output
The fix was only applied to foundry_compilers::compilers::CompilerOutput, but not to foundry_compilers_artifacts::CompilerOutput. Both structs need the custom serializer to ensure sources are ordered by ID. Also improved the E2E test to correctly search for source entry patterns in the JSON output (avoiding false matches in AST content).
Move the sources_by_id serialization module to a single location in foundry_compilers_artifacts::serde_helpers and use it from both: - foundry_compilers_artifacts::CompilerOutput - foundry_compilers::compilers::CompilerOutput This removes ~110 lines of duplicated code.
zerosnacks
reviewed
Jan 22, 2026
| semver = { version = "1.0", features = ["serde"] } | ||
| serde = { version = "1", features = ["derive", "rc"] } | ||
| serde_json = "1.0" | ||
| serde_json = { version = "1.0", features = ["preserve_order"] } |
Member
There was a problem hiding this comment.
This introduces some level of overhead but should be relatively small. I am unsure however if this causes any unexpected side effects.
Member
There was a problem hiding this comment.
Without preserve_order, serde_json::to_value() captures into a BTreeMap-backed Map that re-sorts alphabetically, losing the ordering from the custom serializer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #178
Summary
The
sourceskey in build info was ordered alphabetically by path instead of by source unit ID, which broke compatibility with solc's JSON output and Hardhat's build format. Tools that rely on the index of a source path insourcesmatching its source unit ID (e.g., for source map resolution) would fail.Changes
CompilerOutput.sources- Added asources_by_idserde module that serializes the sources map ordered by source unit ID rather than by pathpreserve_orderforserde_json- This ensures that when the output is converted toserde_json::Value(inRawBuildInfo::new), the insertion order is preservedBefore
{"output": {"sources": {"a_first.sol": {"id": 2}, "m_middle.sol": {"id": 1}, "z_last.sol": {"id": 0}}}}After
{"output": {"sources": {"z_last.sol": {"id": 0}, "m_middle.sol": {"id": 1}, "a_first.sol": {"id": 2}}}}Note: The input sources remain alphabetically ordered as that's the input to the compiler. The output sources are now ordered by ID to match solc's output.