From 8bdef3ae032ed12b5bbec548e3a65cf020adf457 Mon Sep 17 00:00:00 2001 From: "Adam C. Stephens" Date: Mon, 15 Dec 2025 09:09:25 -0500 Subject: [PATCH] beamPackages.buildMix: update to use fixed point --- pkgs/development/beam-modules/build-mix.nix | 228 ++++++++---------- pkgs/development/beam-modules/default.nix | 8 + .../beam-modules/hooks/default.nix | 8 + .../beam-modules/hooks/mix-configure-hook.sh | 34 +++ .../beam-modules/mix-configure-hook.sh | 18 -- 5 files changed, 149 insertions(+), 147 deletions(-) create mode 100644 pkgs/development/beam-modules/hooks/default.nix create mode 100755 pkgs/development/beam-modules/hooks/mix-configure-hook.sh delete mode 100755 pkgs/development/beam-modules/mix-configure-hook.sh diff --git a/pkgs/development/beam-modules/build-mix.nix b/pkgs/development/beam-modules/build-mix.nix index d9ff7d120588..d867648abdfb 100644 --- a/pkgs/development/beam-modules/build-mix.nix +++ b/pkgs/development/beam-modules/build-mix.nix @@ -1,64 +1,46 @@ { - stdenv, - writeText, elixir, erlang, hex, lib, + mixConfigureHook, + stdenv, + writeText, }: -{ - name, - version, - src, - buildInputs ? [ ], - nativeBuildInputs ? [ ], - erlangCompilerOptions ? [ ], - # Deterministic Erlang builds remove full system paths from debug information - # among other things to keep builds more reproducible. See their docs for more: - # https://www.erlang.org/doc/man/compile - erlangDeterministicBuilds ? true, - beamDeps ? [ ], - propagatedBuildInputs ? [ ], - postPatch ? "", - compilePorts ? false, - meta ? { }, - enableDebugInfo ? false, - mixEnv ? "prod", - mixTarget ? "host", - removeConfig ? true, - # A config directory that is considered for all the dependencies of an app, typically in $src/config/ - # This was initially added, as some of Mobilizon's dependencies need to access the config at build time. - appConfigPath ? null, - ... -}@attrs: - -assert appConfigPath != null -> removeConfig; - -let - shell = - drv: - stdenv.mkDerivation { - name = "interactive-shell-${drv.name}"; - buildInputs = [ drv ]; - }; - - pkg = - self: - stdenv.mkDerivation ( - attrs - // { - name = "${name}-${version}"; - inherit version src; +lib.extendMkDerivation { + constructDrv = stdenv.mkDerivation; + excludeDrvArgNames = [ + "mixEnv" + ]; + extendDrvArgs = + finalAttrs: + { + nativeBuildInputs ? [ ], + propagatedBuildInputs ? [ ], + passthru ? { }, + mixEnv ? "prod", + mixTarget ? "host", + enableDebugInfo ? false, + # A handful of libraries require compile time configuration + appConfigPath ? null, + erlangCompilerOptions ? [ ], + # Deterministic Erlang builds remove full system paths from debug information + # among other things to keep builds more reproducible. See their docs for more: + # https://www.erlang.org/doc/man/compile + erlangDeterministicBuilds ? true, + beamDeps ? [ ], + ... + }@args: + { + name = "erlang-${erlang.version}-${args.name}-${finalAttrs.version}"; + env = { MIX_ENV = mixEnv; MIX_TARGET = mixTarget; MIX_BUILD_PREFIX = (if mixTarget == "host" then "" else "${mixTarget}_") + "${mixEnv}"; MIX_DEBUG = if enableDebugInfo then 1 else 0; HEX_OFFLINE = 1; - - __darwinAllowLocalNetworking = true; - ERL_COMPILER_OPTIONS = let options = erlangCompilerOptions ++ lib.optionals erlangDeterministicBuilds [ "deterministic" ]; @@ -67,93 +49,81 @@ let LANG = if stdenv.hostPlatform.isLinux then "C.UTF-8" else "C"; LC_CTYPE = if stdenv.hostPlatform.isLinux then "C.UTF-8" else "UTF-8"; + __darwinAllowLocalNetworking = true; + }; - # add to ERL_LIBS so other modules can find at runtime. - # http://erlang.org/doc/man/code.html#code-path - # Mix also searches the code path when compiling with the --no-deps-check flag - setupHook = attrs.setupHook or writeText "setupHook.sh" '' - addToSearchPath ERL_LIBS "$1/lib/erlang/lib" + # add to ERL_LIBS so other modules can find at runtime. + # http://erlang.org/doc/man/code.html#code-path + # Mix also searches the code path when compiling with the --no-deps-check flag + # This is used by package builders such as mixRelease + setupHook = writeText "setupHook.sh" '' + addToSearchPath ERL_LIBS "$1/lib/erlang/lib" + ''; + + nativeBuildInputs = nativeBuildInputs ++ [ + elixir + hex + mixConfigureHook + ]; + propagatedBuildInputs = propagatedBuildInputs ++ beamDeps; + + # We don't want to include whatever config a dependency brings; per + # https://hexdocs.pm/elixir/main/Config.html, config is application specific. + patchPhase = + args.patchPhase or ( + '' + runHook prePatch + + rm -rf config + '' + + lib.optionalString (!isNull appConfigPath) '' + cp -r ${appConfigPath} config + '' + + '' + runHook postPatch + '' + ); + + buildPhase = + args.buildPhase or '' + runHook preBuild + + export HEX_HOME="$TEMPDIR/hex" + export MIX_HOME="$TEMPDIR/mix" + mix compile --no-deps-check + + runHook postBuild ''; - buildInputs = buildInputs ++ [ ]; - nativeBuildInputs = nativeBuildInputs ++ [ - elixir - hex - ]; - propagatedBuildInputs = propagatedBuildInputs ++ beamDeps; + installPhase = + args.installPhase or '' + runHook preInstall - configurePhase = - attrs.configurePhase or '' - runHook preConfigure + # This uses the install path convention established by nixpkgs maintainers + # for all beam packages. Changing this will break compatibility with other + # builder functions like buildRebar3 and buildErlangMk. + mkdir -p "$out/lib/erlang/lib/${finalAttrs.name}-${finalAttrs.version}-${args.version}" - ${./mix-configure-hook.sh} - ${lib.optionalString (removeConfig && isNull appConfigPath) - # By default, we don't want to include whatever config a dependency brings; per - # https://hexdocs.pm/elixir/main/Config.html, config is application specific. - '' - rm -rf config - mkdir config - '' - } - ${lib.optionalString (!isNull appConfigPath) - # Some more tightly-coupled dependencies do depend on the config of the application - # they're being built for. - '' - rm -rf config - cp -r ${appConfigPath} config - '' - } + # Some packages like db_connection will use _build/shared instead of + # honoring the $MIX_ENV variable. + for reldir in _build/{$MIX_BUILD_PREFIX,shared}/lib/${finalAttrs.name}-${finalAttrs.version}/{src,ebin,priv,include} ; do + if test -d $reldir ; then + # Some builds produce symlinks (eg: phoenix priv dircetory). They must + # be followed with -H flag. + cp -Hrt "$out/lib/erlang/lib/${finalAttrs.name}-${finalAttrs.version}" "$reldir" + fi + done - runHook postConfigure - ''; + runHook postInstall + ''; - buildPhase = - attrs.buildPhase or '' - runHook preBuild - export HEX_HOME="$TEMPDIR/hex" - export MIX_HOME="$TEMPDIR/mix" - mix compile --no-deps-check - runHook postBuild - ''; + # stripping does not have any effect on beam files + # it is however needed for dependencies with NIFs like bcrypt for example + dontStrip = args.dontStrip or false; - installPhase = - attrs.installPhase or '' - runHook preInstall - - # This uses the install path convention established by nixpkgs maintainers - # for all beam packages. Changing this will break compatibility with other - # builder functions like buildRebar3 and buildErlangMk. - mkdir -p "$out/lib/erlang/lib/${name}-${version}" - - # Some packages like db_connection will use _build/shared instead of - # honoring the $MIX_ENV variable. - for reldir in _build/{$MIX_BUILD_PREFIX,shared}/lib/${name}/{src,ebin,priv,include} ; do - if test -d $reldir ; then - # Some builds produce symlinks (eg: phoenix priv dircetory). They must - # be followed with -H flag. - cp -Hrt "$out/lib/erlang/lib/${name}-${version}" "$reldir" - fi - done - - # Copy the source so it can be used by dependent packages. For example, - # phoenix applications need the source of phoenix and phoenix_html to - # build javascript and css assets. - mkdir -p $out/src - cp -r "$src/." "$out/src" - - runHook postInstall - ''; - - # stripping does not have any effect on beam files - # it is however needed for dependencies with NIFs like bcrypt for example - dontStrip = false; - - passthru = { - packageName = name; - env = shell self; - inherit beamDeps; - }; + passthru = { + inherit beamDeps; } - ); -in -lib.fix pkg + // passthru; + }; +} diff --git a/pkgs/development/beam-modules/default.nix b/pkgs/development/beam-modules/default.nix index 0bcee85e7fcc..4102f0a60031 100644 --- a/pkgs/development/beam-modules/default.nix +++ b/pkgs/development/beam-modules/default.nix @@ -11,6 +11,9 @@ let callPackageWithScope = scope: drv: args: lib.callPackageWith scope drv args; + callPackagesWithScope = + scope: drv: args: + lib.callPackagesWith scope drv args; mkScope = scope: pkgs // scope; packages = @@ -18,6 +21,7 @@ let let defaultScope = mkScope self; callPackage = drv: args: callPackageWithScope defaultScope drv args; + callPackages = drv: args: callPackagesWithScope defaultScope drv args; in rec { inherit callPackage erlang; @@ -95,6 +99,10 @@ let # without helper functions buildRebar3 and buildMix. hex = callPackage ./hex { }; webdriver = callPackage ./webdriver { }; + + inherit (callPackages ./hooks { }) + mixConfigureHook + ; }; in makeExtensible packages diff --git a/pkgs/development/beam-modules/hooks/default.nix b/pkgs/development/beam-modules/hooks/default.nix new file mode 100644 index 000000000000..9bf74f5dbbb3 --- /dev/null +++ b/pkgs/development/beam-modules/hooks/default.nix @@ -0,0 +1,8 @@ +{ + makeSetupHook, +}: +{ + mixConfigureHook = makeSetupHook { + name = "mix-configure-hook.sh"; + } ./mix-configure-hook.sh; +} diff --git a/pkgs/development/beam-modules/hooks/mix-configure-hook.sh b/pkgs/development/beam-modules/hooks/mix-configure-hook.sh new file mode 100755 index 000000000000..56851bc9fb9d --- /dev/null +++ b/pkgs/development/beam-modules/hooks/mix-configure-hook.sh @@ -0,0 +1,34 @@ +# shellcheck shell=bash +mixConfigureHook() { + echo "Executing mixConfigureHook" + + runHook preConfigure + + # Copy the source so it can be used by mix projects + # do this before building to avoid build artifacts but after patching + # to include any modifications + mkdir -p "$out/src" + cp -r "." "$out/src" + + # Symlink all dependencies found in ERL_LIBS since Elixir does not honor ERL_LIBS + mkdir -p _build/"$MIX_BUILD_PREFIX"/lib + while read -r -d ':' lib; do + for dir in "$lib"/*; do + # Strip version number for directory name if it exists, so naming of + # all libs matches what mix's expectation. + dest=$(basename "$dir" | cut -d '-' -f1) + build_dir="_build/$MIX_BUILD_PREFIX/lib/$dest" + ((MIX_DEBUG == 1)) && echo "Linking $dir to $build_dir" + # Symlink libs to _build so that mix can find them when compiling. + # This is what allows mix to compile the package without searching + # for dependencies over the network. + ln -s "$dir" "$build_dir" + done + done <<<"$ERL_LIBS:" + + runHook postConfigure +} + +if [ -z "${dontCargoConfigure-}" ] && [ -z "${configurePhase-}" ]; then + configurePhase=mixConfigureHook +fi diff --git a/pkgs/development/beam-modules/mix-configure-hook.sh b/pkgs/development/beam-modules/mix-configure-hook.sh deleted file mode 100755 index 8c0ee23d5a1c..000000000000 --- a/pkgs/development/beam-modules/mix-configure-hook.sh +++ /dev/null @@ -1,18 +0,0 @@ -# shellcheck shell=bash -# this hook will symlink all dependencies found in ERL_LIBS -# since Elixir 1.12.2 elixir does not look into ERL_LIBS for -# elixir depencencies anymore, so those have to be symlinked to the _build directory -mkdir -p _build/"$MIX_BUILD_PREFIX"/lib -while read -r -d ':' lib; do - for dir in "$lib"/*; do - # Strip version number for directory name if it exists, so naming of - # all libs matches what mix's expectation. - dest=$(basename "$dir" | cut -d '-' -f1) - build_dir="_build/$MIX_BUILD_PREFIX/lib/$dest" - ((MIX_DEBUG == 1)) && echo "Linking $dir to $build_dir" - # Symlink libs to _build so that mix can find them when compiling. - # This is what allows mix to compile the package without searching - # for dependencies over the network. - ln -s "$dir" "$build_dir" - done -done <<< "$ERL_LIBS:"