Skip to content

Fix compiler when using ERL_COMPILER_OPTIONS=deterministic #762

Open

Description

@msaraiva

According to the Erlang docs, Using the deterministic options will:

Omit the options and source tuples in the list returned by Module:module_info(compile)

Currently, the Surface compiler uses module_info/1 to retrieve the related source file in order to generate JS hooks. Instead, we could add/retrieve this information directly to/from the beam chunks as an custom attribute so we're able to use deterministic.

Activity

  1. ju1m commented on Nov 25, 2025

    @ju1m

    The current approach assumes something more than not having ERL_COMPILER_OPTIONS=deterministic, namely that the source given by module_info still exists, which is not the case when building each mix app in ephemeral sandboxes like nix does by default.
    See in nix the source is deterministically in /build/source/, but actually exists only during the build of the mix app:

    iex(1)> Bonfire.UI.Me.Stickyheader.module_info() |> get_in([:compile])
    [
      version: ~c"9.0.2",
      options: [:no_spawn_compiler_process, :from_core, :no_core_prepare,
       :no_auto_import],
      source: ~c"/build/source/lib/components/profile/stickyheader.ex"
    ]
    

    So that by the time the dependency (Bonfire.UI.Me.Stickyheader) is used (through $ERL_LIBS), its /build/source/ is long gone (actually it's now the /build/source/ of the current mix app using that dependency).

  2. ju1m commented on Nov 25, 2025

    @ju1m

    When using nixpkgs's buildMix, a workaround is to build into $out/src/ instead of /build/source/, so that module_info returns a path inside $out, which, being the final output path of the package into the Nix store, still exists when building dependent packages:

    buildMix {
       erlangDeterministicBuilds = false;
    
       # Explanation: sadly 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 buildMix's installPhase only copies $src into $out/src,
       # that is not enough: module_info/1 will return /build/source instead of $out/src,
       # so build into $out/src, which will remain reachable after the build.
       # Issue: https://github.com/surface-ui/surface/issues/762#issuecomment-3577030748
       postUnpack = previousArgs.postUnpack or "" + ''
         mkdir -p $out
         mv $sourceRoot $out/src
         sourceRoot=$out/src
         src=$(mktemp -d)
       '';
       postInstall = previousArgs.postInstall or "" + ''
         src=$out/src
         rm -rf _build
       '';
    
      […snip…]
    }

    Which now records an existing source:, enabling compile.surface to correctly generate all the .hooks.js of the dependencies:

    iex(1)> Bonfire.UI.Me.Stickyheader.module_info() |> get_in([:compile])
    [
      version: ~c"9.0.2",
      options: [:no_spawn_compiler_process, :from_core, :no_core_prepare,
       :no_auto_import],
      source: ~c"/nix/store/xhkblhd0jnw3ynvyk66jf9cglyh2h5lb-bonfire_ui_me-0.1.0/src/lib/components/profile/stickyheader.ex"
    ]
    
  3. added a commit that references this issue on Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions