Skip to content

beamPackages.buildMix: compile non-deterministic deps in $out/src - #506184

Open
ju1m wants to merge 1 commit into
NixOS:masterfrom
ju1m:beam-fix-surface
Open

beamPackages.buildMix: compile non-deterministic deps in $out/src#506184
ju1m wants to merge 1 commit into
NixOS:masterfrom
ju1m:beam-fix-surface

Conversation

@ju1m

@ju1m ju1m commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

This is necessary to support mix compile.surface or Fine.include_dir().

Used by #550073

Default erlangDeterministicBuilds to false
because it breaks apps using Surface
and does not improve determinism within a sandboxed nix build which always builds in the same path anyway.

EDIT: keep erlangDeterministicBuilds to true by default because it apparently increases the closure size.

Yet, disabling erlangDeterministicBuilds is not enough for mix compile.surface to work across packages, as it:

  • needs to access the content of the source path of the dependencies,
  • and, uses as source path the actual path where the dependencies were built.

Since beamCopySourceHook only copies $src into $out/src, that is not enough: module_info/1 will return a path in /build/source instead of a path in $out/src,
therefore build into $out/src, which will remain reachable after the build.

Issue: surface-ui/surface#762

Things done

  • Built on platform:
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • Tested, as applicable:
  • Ran nixpkgs-review on this PR. See nixpkgs-review usage.
  • Tested basic functionality of all binary files, usually in ./result/bin/.
  • Nixpkgs Release Notes
    • Package update: when the change is major or breaking.
  • NixOS Release Notes
    • Module addition: when adding a new NixOS module.
    • Module update: when the change is significant.
  • Fits CONTRIBUTING.md, pkgs/README.md, maintainers/README.md and other READMEs.

@ju1m ju1m changed the title beamPackages: build in $out/src and disable erlangDeterministicBuilds by default beamPackages: build in $out/src and disable erlangDeterministicBuilds by default Apr 3, 2026
@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 11-100 This PR causes between 11 and 100 packages to rebuild on Linux. 10.rebuild-darwin: 11-100 This PR causes between 11 and 100 packages to rebuild on Darwin. 6.topic: erlang General-purpose, concurrent, functional high-level programming language labels Apr 3, 2026
@ju1m
ju1m marked this pull request as ready for review May 1, 2026 12:09
@nixpkgs-ci
nixpkgs-ci Bot requested review from a team, dlesl and gleber May 1, 2026 12:14
@adamcstephens

Copy link
Copy Markdown
Contributor

Why is it necessary to change the global default for this? Can't surface, or other packages, disable deterministic individually?

@ju1m

ju1m commented May 2, 2026

Copy link
Copy Markdown
Contributor Author

@adamcstephens
My goal in this PR is to make a correct package the easiest thing to come up with.
My rationale is that nix's sandbox must be enabled
when one cares about determinism/reproducibility.
and erlangDeterministicBuilds == true is useless when using nix's sandbox:

deterministic - Omit the options and source tuples in the list returned by Module:module_info(compile), and reduce the paths in stack traces to the module name alone. This option will make it easier to achieve reproducible builds.

Source: https://www.erlang.org/doc/apps/compiler/compile.html

From my somewhat limited experience in packaging Elixir in Nix,
I don't know of any case where a Nix packager would need erlangDeterministicBuilds == true.
But, I do know first hand that erlangDeterministicBuilds == true as default subtly (and sometimes silently) breaks all packages relying on module_info(), laying a trap to nix packager who like me may have to spend a lot of debugging time to understand what's wrong in nix whereas it works fine out of nix.

That's why I propose to stick to Erlang's default, which is to not enable ERL_COMPILER_OPTIONS=deterministic by default, which becomes important with that PR now exposing the correct $out/src (instead of /build/source) in module_info().

@adamcstephens

adamcstephens commented May 2, 2026

Copy link
Copy Markdown
Contributor

This is going to need a full nixpkgs-review, and probably build some of the nixos tests for elixir apps in tree. Furthermore I’m not comfortable merging this until at least after branch off for 26.05, to avoid unexpected consequences. I’d also be worried that this will leave a bunch of extra artifacts in the output.

@Munksgaard

Copy link
Copy Markdown
Contributor

As I remember, quite a bit of effort was put into enabling erlangDeterministicBuilds in #271288. I don't understand the full impact of the proposed changes, but we should probably loop in @happysalada, @scvalex and @mattpolzin.

cc @NixOS/beam

@mattpolzin

Copy link
Copy Markdown
Contributor

I don't know of any case where a Nix packager would need erlangDeterministicBuilds == true

The short answer is that it's about not bloating the resulting derivation with unnecessary runtime dependencies.

Take a look at the PR that introduced erlangDeterministicBuilds and the history it links to if you'd like a longer answer to your question: #271288

@adamcstephens

Copy link
Copy Markdown
Contributor

The short answer is that it's about not bloating the resulting derivation with unnecessary runtime dependencies.

This is the more correct way of stating what I hinted at in my comment about extra artifacts. I can see this being a real problem. For example a mixRelease package generally bundles erts in the package itself. If its closure also pulled in erlang and its dependencies, that could be 300-1300MB+ of extra unnecessary dependencies.

As I've reflected on this more, we shouldn't change the default on deterministic builds, but we could improve the support for those who want to disable them. That means potentially a modification to a hook, or an extra hook, that will ensure that we get a proper output when they're disabled.

@ju1m
ju1m force-pushed the beam-fix-surface branch from eede973 to be226f1 Compare June 11, 2026 22:57
@ju1m ju1m changed the title beamPackages: build in $out/src and disable erlangDeterministicBuilds by default beamPackages: build in $out/src Jun 11, 2026
@ju1m

ju1m commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author
  • Keep erlangDeterministicBuilds's default to false

@adamcstephens adamcstephens left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see us change sourceRoot only in the case of non-deterministicBuilds. mix doesn't necessarily keep the source directory clean of artifacts, especially when working with native code.

I expect src to be a clean copy of the source itself, without any build artifacts that mix may create, so I'd also consider it more correct for non-deterministicBuilds to delete and re-copy the original src after building is done. This would provide the same clean slate as the deterministicBuilds scenario. This is less of a blocker for me, but from what I've seen you probably don't want the artifacts mix leaves around.

@ju1m
ju1m marked this pull request as draft July 16, 2026 09:57
@ju1m
ju1m force-pushed the beam-fix-surface branch from be226f1 to 5ab752c Compare July 17, 2026 11:25
@ju1m

ju1m commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Turned this PR into a draft and pushed some changes to address latest concern raised, but cannot test yet.

  • Register old beamCopySourceHook or new beamCopySourceToSourceRootHook depending on erlangDeterministicBuilds
  • Keep ./config writable because some package write to it (eg. surface_form_helpers).

@adamcstephens

adamcstephens commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

surface writes to config?!? could they, not? that seems like a bad practice that we're enabling instead of pushing back on.

@ju1m
ju1m force-pushed the beam-fix-surface branch from 5ab752c to 4e1d122 Compare July 18, 2026 20:33
@ju1m

ju1m commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

surface writes to config?!? could they, not? that seems like a bad practice that we're enabling instead of pushing back on.

I don't know but the way Bonfire (not in Nixpkgs, yet) uses it,
surface_form_helpers tries to remove and generate .hooks.js files in appConfigPath:

$ nix -L build -f. bonfire.ember.mixNixDeps.surface_form_helpers
[…]
erlang27.3.4.7-surface_form_helpers> Generated surface_form_helpers app
erlang27.3.4.7-surface_form_helpers> <- Ran mix compile.app in 4ms
erlang27.3.4.7-surface_form_helpers> -> Running mix compile.surface --no-deps-check (inside Surface.Form.MixProject)
erlang27.3.4.7-surface_form_helpers> ** (File.Error) could not remove file "/nix/store/7ya12s6c62v1kq3bv18skii86lwrcg6w-erlang27.3.4.7-surface_form_helpers-0.2.0/src/config/current_flavour/assets/hooks/Bonfire.Editor.Milkdown.hooks.js": permission denied
erlang27.3.4.7-surface_form_helpers>     (elixir 1.18.4) lib/file.ex:1222: File.rm!/1
erlang27.3.4.7-surface_form_helpers>     (elixir 1.18.4) lib/enum.ex:987: Enum."-each/2-lists^foreach/1-0-"/2
erlang27.3.4.7-surface_form_helpers>     (surface 0.12.3) lib/mix/tasks/compile/surface.asset_generator.ex:312: Mix.Tasks.Compile.Surface.AssetGenerator.delete_unused_hooks_files!/2
erlang27.3.4.7-surface_form_helpers>     (surface 0.12.3) lib/mix/tasks/compile/surface.asset_generator.ex:191: Mix.Tasks.Compile.Surface.AssetGenerator.generate_js_files/2
erlang27.3.4.7-surface_form_helpers>     (surface 0.12.3) lib/mix/tasks/compile/surface.asset_generator.ex:35: Mix.Tasks.Compile.Surface.AssetGenerator.do_run/2
erlang27.3.4.7-surface_form_helpers>     (surface 0.12.3) lib/mix/tasks/compile/surface.ex:198: Mix.Tasks.Compile.Surface.do_run/1
erlang27.3.4.7-surface_form_helpers>     (mix 1.18.4) lib/mix/task.ex:495: anonymous fn/3 in Mix.Task.run_task/5
erlang27.3.4.7-surface_form_helpers>     (stdlib 6.2.2.2) timer.erl:595: :timer.tc/2

I can gate that behind a appConfigPathWritable if you prefer?

@adamcstephens

Copy link
Copy Markdown
Contributor

Are you sure that’s not a misconfiguration or maybe can be configured to write elsewhere? I wouldn’t expect assets, js or really any content to generate into config.

@adamcstephens

adamcstephens commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

https://surface.hexdocs.pm/Mix.Tasks.Compile.Surface.html#module-options

hooks_output_dir is the config key that changes this.

Why it's trying to delete project files is a bit unclear to me.

Can you share your build target? Is it https://github.com/bonfire-networks/bonfire-app on main?

@ju1m

ju1m commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

@adamcstephens I'm currently packaging Bonfire in NGIpkgs here: ngi-nix/ngipkgs#2155
See profiles/pkgs/development/beam-modules/build-mix/mix-app-config-patch-hook.sh to uncomment cp and comment ln:

    #cp --no-preserve=mode -r "${appConfigPath}" config
    ln -sf "${appConfigPath}" config

But do no expect it to work besides bonfire.ember.mixNixDeps.surface_form_helpers, I'm still struggling to make mixNixDeps.exla and mixNixDeps.evision updates work.
NGIpkgs has been archived, hence this PR to prepare a possible migration of Bonfire to Nixpkgs.

@adamcstephens

Copy link
Copy Markdown
Contributor

Have you considered using mixFodDeps instead? I wonder if this would make your life easier, at the cost of rebuild efficiency.

@ju1m

ju1m commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Have you considered using mixFodDeps instead? I wonder if this would make your life easier, at the cost of rebuild efficiency.

Ah, yes, it would, mixFodDeps is what was used by previous attempts to package Bonfire:
https://github.com/bonfire-networks/bonfire-app/blob/03c6cc1ddbec1dfe99a7967c9444cb7bdb040b73/nix/package.nix#L16-L33

But I've been struggling/able to avoid it up to now, because it undermines the added value of using nix wrt. Docker
to precisely blame reproducibility problems and build from source instead of pulling dozens of binaryNativeCode
(C and Rust NIFs, xla, opencv, …),
sometimes not even free software without upstream developers being aware.

@ju1m
ju1m force-pushed the beam-fix-surface branch from 4e1d122 to 31c4f5e Compare July 18, 2026 23:38
@ju1m ju1m mentioned this pull request Aug 7, 2026
17 tasks
@ju1m ju1m added this to NGI Aug 7, 2026
@github-project-automation github-project-automation Bot moved this to Needs Refinement in NGI Aug 7, 2026
@ju1m ju1m moved this from Needs Refinement to In Review in NGI Aug 7, 2026
@ju1m
ju1m marked this pull request as ready for review August 18, 2026 08:58
Comment thread pkgs/development/beam-modules/hooks/mix-app-config-patch-hook.sh Outdated
@ju1m
ju1m force-pushed the beam-fix-surface branch from 31c4f5e to 6259bc1 Compare August 20, 2026 02:24
@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. and removed 10.rebuild-linux: 11-100 This PR causes between 11 and 100 packages to rebuild on Linux. 10.rebuild-darwin: 11-100 This PR causes between 11 and 100 packages to rebuild on Darwin. labels Aug 20, 2026
@adamcstephens adamcstephens changed the title beamPackages: build in $out/src beamPackages.buildMix: compile non-deterministic deps in $out/src Aug 21, 2026
@adamcstephens

Copy link
Copy Markdown
Contributor

I've taken a stab at a new PR title. Can you update the commit to something similar?

@nixpkgs-ci nixpkgs-ci Bot added the 12.approvals: 1 This PR was reviewed and approved by one person. label Aug 21, 2026
`mix compile.surface` or `Fine.include_dir()`
use `module_info/1` to get source files of BEAM dependencies.

To work this requires:

1. `erlangDeterministicBuilds` set to `false`
   to preserve the build directory in `.beam` files.
2. to build dependencies in a directory persisting
   across nix builds: `$out/src` instead of `/build/source`.

Issue: surface-ui/surface#762
@ju1m
ju1m force-pushed the beam-fix-surface branch from 6259bc1 to 1b113c3 Compare August 24, 2026 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: erlang General-purpose, concurrent, functional high-level programming language 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 12.approvals: 1 This PR was reviewed and approved by one person.

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

4 participants