fix: match artifact by profile when writing extra output files#350
Merged
zerosnacks merged 12 commits intomainfrom Jan 20, 2026
Merged
fix: match artifact by profile when writing extra output files#350zerosnacks merged 12 commits intomainfrom
zerosnacks merged 12 commits intomainfrom
Conversation
When using `--extra-output-files bin` with multiple compiler profiles (via `additional_compiler_profiles` and `compilation_restrictions`), only a single `.bin` file was generated instead of one per profile. The root cause was that `Artifacts::find_artifact()` only matched by file, contract name, and version, but not by profile. When the same contract was compiled with multiple profiles (same version), the lookup would return the first match for all profiles, causing extra output files to be written to the same path and overwriting each other. This fix: - Adds `find_artifact_with_profile()` that includes profile matching - Updates `ConfigurableArtifacts::handle_artifacts()` to use the new method, ensuring each profile's extra output files are written to the correct path (e.g., `Counter.bin` and `Counter.optimized-runs.bin`) Fixes: foundry-rs/foundry#13057
Adds a test that verifies when using --extra-output-files with multiple compiler profiles, separate .bin files are generated for each profile (e.g., Counter.bin and Counter.optimized.bin). This test would fail before the fix because find_artifact() only matched by version, not profile, causing all profiles to write to the same .bin file path.
- Remove redundant .clone() on contract_path - Remove redundant .clone() on id.path (simplified to just collect profiles) - Inline format args in assert! macro
The test now uses a shared library (Lib.sol) that is imported by both: - Default.sol (compiles with default profile) - Optimized.sol (compiles with optimized profile via restriction) This ensures Lib.sol is compiled with BOTH profiles, which is the scenario that triggers the bug where only one .bin file was generated.
- Shorten comment to fit line length - Remove redundant .clone() on id.profile - Use HashSet<&str> instead of Vec<String> for cleaner code
Changed test to use the same pattern as test_settings_restrictions: - Default profile uses Paris EVM version - Cancun profile uses Cancun EVM version - Cancun.sol uses tstore which requires Cancun EVM version - This forces Lib.sol to be compiled with both profiles The optimizer_runs restriction wasn't triggering multi-profile compilation because the default profile didn't have explicit optimizer_runs set.
Copy the working test setup exactly to ensure the multi-profile compilation is triggered correctly.
Empty contracts produce no bytecode, so no .bin file is written.
The bug only manifests when: 1. Same solc version 2. Same contract file/name 3. Different profiles (with different settings like optimizer_runs) The previous test used different EVM versions which caused different solc versions to be selected, so the version check in find_artifact() worked correctly and the test passed even without the fix. This test now: - Fixes solc to 0.8.26 for both profiles - Uses optimizer_runs restriction (10000) to force one contract to optimized profile - Verifies Counter.sol is compiled with same version but different profiles - Checks that 2 .bin files exist (would be 1 without the fix)
onbjerg
approved these changes
Jan 20, 2026
zerosnacks
approved these changes
Jan 20, 2026
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.
Summary
When using
forge build --extra-output-files binwith multiple compiler profiles (viaadditional_compiler_profilesandcompilation_restrictions), only a single.binfile was generated instead of one per profile. The.jsonartifacts were correctly generated per profile (e.g.,Counter.jsonandCounter.optimized.json), but only one.binfile was created, containing bytecode from whichever profile was processed last.Root Cause
Artifacts::find_artifact()only matches by:When the same contract is compiled with multiple profiles using the same solc version,
find_artifact()returns the first match for all profiles. This causeswrite_extras()to write all profiles' extra output files to the same path, with later profiles overwriting earlier ones.Fix
find_artifact_with_profile()that includes profile matchingConfigurableArtifacts::handle_artifacts()to use the new methodThis ensures each profile's extra output files are written to the correct path with the profile suffix (e.g.,
Counter.binandCounter.optimized.bin).Expected Behavior After Fix
Evidence
Without the fix
CI Run: https://github.com/foundry-rs/compilers/actions/runs/21180139935
Only 1
.binfile was created becausefind_artifact()returns the same artifact for both profiles (matches by version only), causing the second write to overwrite the first.With the fix
CI Run: https://github.com/foundry-rs/compilers/actions/runs/21179524355
Test passes - 2
.binfiles are created becausefind_artifact_with_profile()correctly matches by profile.Fixes: foundry-rs/foundry#13057