diff --git a/pkgs/development/beam-modules/build-mix.nix b/pkgs/development/beam-modules/build-mix.nix index d9ff7d120588..52c0ce1ad1f8 100644 --- a/pkgs/development/beam-modules/build-mix.nix +++ b/pkgs/development/beam-modules/build-mix.nix @@ -1,55 +1,50 @@ { - stdenv, - writeText, elixir, erlang, hex, + beamCopySourceHook, + beamModuleInstallHook, + mixBuildDirHook, + mixCompileHook, + mixAppConfigPatchHook, + lib, + 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: +lib.extendMkDerivation { + constructDrv = stdenv.mkDerivation; + excludeDrvArgNames = [ + "mixEnv" + ]; + extendDrvArgs = + finalAttrs: + { + beamDeps ? [ ], + mixEnv ? "prod", + mixTarget ? "host", -assert appConfigPath != null -> removeConfig; + # Allow passing compile time config instead of an empty config + appConfigPath ? null, -let - shell = - drv: - stdenv.mkDerivation { - name = "interactive-shell-${drv.name}"; - buildInputs = [ drv ]; - }; + enableDebugInfo ? false, + 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, + ... + }@args: + { + name = "erlang${erlang.version}-${args.name}-${finalAttrs.version}"; - pkg = - self: - stdenv.mkDerivation ( - attrs - // { - name = "${name}-${version}"; - inherit version src; + env = { + ERL_COMPILER_OPTIONS = + let + options = erlangCompilerOptions ++ lib.optionals erlangDeterministicBuilds [ "deterministic" ]; + in + "[${lib.concatStringsSep "," options}]"; MIX_ENV = mixEnv; MIX_TARGET = mixTarget; @@ -57,103 +52,38 @@ let MIX_DEBUG = if enableDebugInfo then 1 else 0; HEX_OFFLINE = 1; - __darwinAllowLocalNetworking = true; - - ERL_COMPILER_OPTIONS = - let - options = erlangCompilerOptions ++ lib.optionals erlangDeterministicBuilds [ "deterministic" ]; - in - "[${lib.concatStringsSep "," options}]"; - LANG = if stdenv.hostPlatform.isLinux then "C.UTF-8" else "C"; LC_CTYPE = if stdenv.hostPlatform.isLinux then "C.UTF-8" else "UTF-8"; - # 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" - ''; + # some hooks need name-version, but we've overridden name above for the nix package + beamModuleName = args.name; + }; - buildInputs = buildInputs ++ [ ]; - nativeBuildInputs = nativeBuildInputs ++ [ - elixir - hex - ]; - propagatedBuildInputs = propagatedBuildInputs ++ beamDeps; + __darwinAllowLocalNetworking = true; - configurePhase = - attrs.configurePhase or '' - runHook preConfigure + # 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" + ''; - ${./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 - '' - } + nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ + elixir + hex - runHook postConfigure - ''; + beamCopySourceHook + beamModuleInstallHook + mixBuildDirHook + mixCompileHook + mixAppConfigPatchHook + ]; - buildPhase = - attrs.buildPhase or '' - runHook preBuild - export HEX_HOME="$TEMPDIR/hex" - export MIX_HOME="$TEMPDIR/mix" - mix compile --no-deps-check - runHook postBuild - ''; + propagatedBuildInputs = (args.propagatedBuildInputs or [ ]) ++ beamDeps; - 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; - }; - } - ); -in -lib.fix pkg + passthru = (args.passthru or { }) // { + inherit beamDeps; + }; + }; +} diff --git a/pkgs/development/beam-modules/default.nix b/pkgs/development/beam-modules/default.nix index 0bcee85e7fcc..0fec22df3458 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,14 @@ let # without helper functions buildRebar3 and buildMix. hex = callPackage ./hex { }; webdriver = callPackage ./webdriver { }; + + inherit (callPackages ./hooks { }) + beamCopySourceHook + beamModuleInstallHook + mixBuildDirHook + mixCompileHook + mixAppConfigPatchHook + ; }; in makeExtensible packages diff --git a/pkgs/development/beam-modules/hooks/beam-copy-source-hook.sh b/pkgs/development/beam-modules/hooks/beam-copy-source-hook.sh new file mode 100755 index 000000000000..a4ffa9fd1413 --- /dev/null +++ b/pkgs/development/beam-modules/hooks/beam-copy-source-hook.sh @@ -0,0 +1,16 @@ +# shellcheck shell=bash +# +# Copy the source so it can be used by mix projects to assemble `deps` +# do this before building to avoid build artifacts but after patching +# to include any user modifications to the source + +beamCopySourceHook() { + echo "Executing beamCopySourceHook" + + mkdir -p "$out/src" + cp -r "." "$out/src" + + echo "Finished beamCopySourceHook" +} + +postPatchHooks+=(beamCopySourceHook) diff --git a/pkgs/development/beam-modules/hooks/beam-module-install-hook.sh b/pkgs/development/beam-modules/hooks/beam-module-install-hook.sh new file mode 100644 index 000000000000..6591f4bb70f7 --- /dev/null +++ b/pkgs/development/beam-modules/hooks/beam-module-install-hook.sh @@ -0,0 +1,25 @@ +# shellcheck shell=bash +# +# 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. + +beamModuleInstallHook() { + echo "Executing beamModuleInstallHook" + + mkdir -p "$out/lib/erlang/lib/${beamModuleName}-${version}" + + for reldir in _build/{$MIX_BUILD_PREFIX,shared}/lib/${beamModuleName}/{src,ebin,priv,include}; do + if test -d "$reldir"; then + # Some builds produce symlinks (eg: phoenix priv directory). They must + # be followed with -H flag. + cp -vHrt "$out/lib/erlang/lib/${beamModuleName}-${version}" "$reldir" + fi + done + + echo "Finished beamModuleInstallHook" +} + +if [ -z "${dontBeamModuleInstall-}" ] && [ -z "${installPhase-}" ]; then + installPhase=beamModuleInstallHook +fi diff --git a/pkgs/development/beam-modules/hooks/default.nix b/pkgs/development/beam-modules/hooks/default.nix new file mode 100644 index 000000000000..000363c9d150 --- /dev/null +++ b/pkgs/development/beam-modules/hooks/default.nix @@ -0,0 +1,22 @@ +{ makeSetupHook }: +{ + beamCopySourceHook = makeSetupHook { + name = "beam-copy-source-hook.sh"; + } ./beam-copy-source-hook.sh; + + beamModuleInstallHook = makeSetupHook { + name = "beam-module-install-hook.sh"; + } ./beam-module-install-hook.sh; + + mixBuildDirHook = makeSetupHook { + name = "mix-configure-hook.sh"; + } ./mix-build-dir-hook.sh; + + mixCompileHook = makeSetupHook { + name = "mix-compile-hook.sh"; + } ./mix-compile-hook.sh; + + mixAppConfigPatchHook = makeSetupHook { + name = "mix-config-patch-hook.sh"; + } ./mix-app-config-patch-hook.sh; +} diff --git a/pkgs/development/beam-modules/hooks/mix-app-config-patch-hook.sh b/pkgs/development/beam-modules/hooks/mix-app-config-patch-hook.sh new file mode 100644 index 000000000000..e2153db58b4c --- /dev/null +++ b/pkgs/development/beam-modules/hooks/mix-app-config-patch-hook.sh @@ -0,0 +1,19 @@ +# shellcheck shell=bash +# +# We don't want to include whatever config a dependency brings; per +# https://hexdocs.pm/elixir/main/Config.html, config is application specific. + +mixAppConfigPatchHook() { + echo "Executing mixAppConfigPatchHook" + + rm -rvf config + + # But we still need a way to provide config for deps that need compile time config + if [ ! -z "${appConfigPath}" ]; then + ln -sf "${appConfigPath}" config + fi + + echo "Finished mixAppConfigPatchHook" +} + +prePatchHooks+=(mixAppConfigPatchHook) diff --git a/pkgs/development/beam-modules/hooks/mix-build-dir-hook.sh b/pkgs/development/beam-modules/hooks/mix-build-dir-hook.sh new file mode 100755 index 000000000000..c353aaeabc4d --- /dev/null +++ b/pkgs/development/beam-modules/hooks/mix-build-dir-hook.sh @@ -0,0 +1,26 @@ +# shellcheck shell=bash +# +# Symlink all dependencies found in ERL_LIBS since Elixir does not honor ERL_LIBS + +mixBuildDirHook() { + echo "Executing mixBuildDirHook" + + 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" + + # 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 -sv "$dir" "$build_dir" + done + done <<<"$ERL_LIBS:" + + echo "Finished mixBuildDirHook" +} + +preConfigureHooks+=(mixBuildDirHook) diff --git a/pkgs/development/beam-modules/hooks/mix-compile-hook.sh b/pkgs/development/beam-modules/hooks/mix-compile-hook.sh new file mode 100755 index 000000000000..19b27c8a94f8 --- /dev/null +++ b/pkgs/development/beam-modules/hooks/mix-compile-hook.sh @@ -0,0 +1,20 @@ +# shellcheck shell=bash + +mixCompileHook() { + echo "Executing mixCompileHook" + + runHook preBuild + + local flagsArray=() + concatTo flagsArray mixCompileFlags + + mix compile --no-deps-check "${flagsArray[@]}" + + runHook postBuild + + echo "Finished mixCompileHook" +} + +if [ -z "${dontMixCompile-}" ] && [ -z "${buildPhase-}" ]; then + buildPhase=mixCompileHook +fi diff --git a/pkgs/development/beam-modules/mix-configure-hook.sh b/pkgs/development/beam-modules/mix-configure-hook.sh index 8c0ee23d5a1c..80a395b8a6a0 100755 --- a/pkgs/development/beam-modules/mix-configure-hook.sh +++ b/pkgs/development/beam-modules/mix-configure-hook.sh @@ -1,18 +1,5 @@ -# 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:" +# Added 2026-02-12 for external users +echo "mix-configure-hook.sh has been removed and is now a setup hook. + Add 'beamPackages.mixBuildDirHook' to 'nativeBuildInputs' instead. +" >&2 +exit 1 diff --git a/pkgs/development/beam-modules/mix-release.nix b/pkgs/development/beam-modules/mix-release.nix index 27a318085710..bbbca0625b44 100644 --- a/pkgs/development/beam-modules/mix-release.nix +++ b/pkgs/development/beam-modules/mix-release.nix @@ -16,6 +16,7 @@ gnused, gnugrep, gawk, + mixBuildDirHook, }@inputs: { @@ -108,6 +109,7 @@ stdenv.mkDerivation ( elixir hex git + mixBuildDirHook ] ++ # Mix deps @@ -171,8 +173,6 @@ stdenv.mkDerivation ( attrs.configurePhase or '' runHook preConfigure - ${./mix-configure-hook.sh} - # This is needed for projects that have a specific compile step # the dependency needs to be compiled in order for the task # to be available. @@ -186,13 +186,12 @@ stdenv.mkDerivation ( ${lib.optionalString (mixNixDeps != { }) '' mkdir -p deps - ${lib.concatMapStringsSep "\n" (dep: '' - dep_name=$(basename ${dep} | cut -d '-' -f2) - dep_path="deps/$dep_name" + ${lib.concatMapAttrsStringSep "\n" (name: dep: '' + dep_path="deps/${name}" if [ -d "${dep}/src" ]; then - ln -s ${dep}/src $dep_path + ln -sv ${dep}/src $dep_path fi - '') (builtins.attrValues mixNixDeps)} + '') mixNixDeps} ''} # Symlink deps to build root. Similar to above, but allows for mixFodDeps diff --git a/pkgs/servers/mobilizon/default.nix b/pkgs/servers/mobilizon/default.nix index c6fe27b0bbfc..ecc7b6bcd336 100644 --- a/pkgs/servers/mobilizon/default.nix +++ b/pkgs/servers/mobilizon/default.nix @@ -40,6 +40,7 @@ mixRelease rec { // { fast_html = prev.fast_html.override { nativeBuildInputs = [ cmake ]; + dontUseCmakeConfigure = true; }; ex_cldr = prev.ex_cldr.overrideAttrs (old: { # We have to use the GitHub sources, as it otherwise tries to download @@ -146,6 +147,7 @@ mixRelease rec { ${lib.getExe nixfmt} pkgs/servers/mobilizon/mix.nix ''; elixirPackage = beamPackages.elixir; + inherit mixNixDeps; }; meta = {