beamPackages.buildMix: move to fixed point builder and hooks (#475224)
This commit is contained in:
@@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
+20
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user