Merge pull request #329828 from mattpolzin/mix-release-named

beamPackages.mixRelease: support named releases
This commit is contained in:
Yt 2024-07-25 22:43:43 +00:00 committed by GitHub
commit 88733d6c42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,56 +1,61 @@
{ stdenv {
, lib stdenv,
, elixir lib,
, erlang elixir,
, hex erlang,
, git hex,
, rebar git,
, rebar3 rebar,
, fetchMixDeps rebar3,
, findutils fetchMixDeps,
, ripgrep findutils,
, bbe ripgrep,
, makeWrapper bbe,
, coreutils makeWrapper,
, gnused coreutils,
, gnugrep gnused,
, gawk gnugrep,
gawk,
}@inputs: }@inputs:
{ pname {
, version pname,
, src version,
, nativeBuildInputs ? [ ] src,
, buildInputs ? [ ] nativeBuildInputs ? [ ],
, meta ? { } buildInputs ? [ ],
, enableDebugInfo ? false meta ? { },
, mixEnv ? "prod" enableDebugInfo ? false,
, compileFlags ? [ ] mixEnv ? "prod",
compileFlags ? [ ],
# Build a particular named release.
# see https://hexdocs.pm/mix/1.12/Mix.Tasks.Release.html#content
mixReleaseName ? "",
# Options to be passed to the Erlang compiler. As documented in the reference # Options to be passed to the Erlang compiler. As documented in the reference
# manual, these must be valid Erlang terms. They will be turned into an # manual, these must be valid Erlang terms. They will be turned into an
# erlang list and set as the ERL_COMPILER_OPTIONS environment variable. # erlang list and set as the ERL_COMPILER_OPTIONS environment variable.
# See https://www.erlang.org/doc/man/compile # See https://www.erlang.org/doc/man/compile
, erlangCompilerOptions ? [ ] erlangCompilerOptions ? [ ],
# Deterministic Erlang builds remove full system paths from debug information # Deterministic Erlang builds remove full system paths from debug information
# among other things to keep builds more reproducible. See their docs for more: # among other things to keep builds more reproducible. See their docs for more:
# https://www.erlang.org/doc/man/compile # https://www.erlang.org/doc/man/compile
, erlangDeterministicBuilds ? true erlangDeterministicBuilds ? true,
# Mix dependencies provided as a fixed output derivation # Mix dependencies provided as a fixed output derivation
, mixFodDeps ? null mixFodDeps ? null,
# Mix dependencies generated by mix2nix # Mix dependencies generated by mix2nix
# #
# This assumes each dependency is built by buildMix or buildRebar3. Each # This assumes each dependency is built by buildMix or buildRebar3. Each
# dependency needs to have a setup hook to add the lib path to $ERL_LIBS. # dependency needs to have a setup hook to add the lib path to $ERL_LIBS.
# This is how Mix finds dependencies. # This is how Mix finds dependencies.
, mixNixDeps ? { } mixNixDeps ? { },
, elixir ? inputs.elixir elixir ? inputs.elixir,
, erlang ? inputs.erlang erlang ? inputs.erlang,
, hex ? inputs.hex.override { inherit elixir; } hex ? inputs.hex.override { inherit elixir; },
# Remove releases/COOKIE # Remove releases/COOKIE
# #
@ -65,172 +70,204 @@
# #
# You can always specify a custom cookie by using RELEASE_COOKIE environment # You can always specify a custom cookie by using RELEASE_COOKIE environment
# variable, regardless of the value of this attr. # variable, regardless of the value of this attr.
, removeCookie ? true removeCookie ? true,
# This reduces closure size, but can lead to some hard to understand runtime # This reduces closure size, but can lead to some hard to understand runtime
# errors, so use with caution. See e.g. # errors, so use with caution. See e.g.
# https://github.com/whitfin/cachex/issues/205 # https://github.com/whitfin/cachex/issues/205
# https://framagit.org/framasoft/mobilizon/-/issues/1169 # https://framagit.org/framasoft/mobilizon/-/issues/1169
, stripDebug ? false stripDebug ? false,
, ... ...
}@attrs: }@attrs:
let let
# Remove non standard attributes that cannot be coerced to strings # Remove non standard attributes that cannot be coerced to strings
overridable = builtins.removeAttrs attrs [ "compileFlags" "erlangCompilerOptions" "mixNixDeps" ]; overridable = builtins.removeAttrs attrs [
"compileFlags"
"erlangCompilerOptions"
"mixNixDeps"
];
in in
assert mixNixDeps != { } -> mixFodDeps == null; assert mixNixDeps != { } -> mixFodDeps == null;
assert stripDebug -> !enableDebugInfo; assert stripDebug -> !enableDebugInfo;
stdenv.mkDerivation (overridable // { stdenv.mkDerivation (
nativeBuildInputs = nativeBuildInputs ++ overridable
# Erlang/Elixir deps // {
[ erlang elixir hex git ] ++ nativeBuildInputs =
# Mix deps nativeBuildInputs
(builtins.attrValues mixNixDeps) ++ ++
# other compile-time deps # Erlang/Elixir deps
[ findutils ripgrep bbe makeWrapper ]; [
erlang
elixir
hex
git
]
++
# Mix deps
(builtins.attrValues mixNixDeps)
++
# other compile-time deps
[
findutils
ripgrep
bbe
makeWrapper
];
buildInputs = buildInputs; buildInputs = buildInputs;
MIX_ENV = mixEnv; MIX_ENV = mixEnv;
MIX_DEBUG = if enableDebugInfo then 1 else 0; MIX_DEBUG = if enableDebugInfo then 1 else 0;
HEX_OFFLINE = 1; HEX_OFFLINE = 1;
DEBUG = if enableDebugInfo then 1 else 0; # for Rebar3 compilation DEBUG = if enableDebugInfo then 1 else 0; # for Rebar3 compilation
# The API with `mix local.rebar rebar path` makes a copy of the binary # The API with `mix local.rebar rebar path` makes a copy of the binary
# some older dependencies still use rebar. # some older dependencies still use rebar.
MIX_REBAR = "${rebar}/bin/rebar"; MIX_REBAR = "${rebar}/bin/rebar";
MIX_REBAR3 = "${rebar3}/bin/rebar3"; MIX_REBAR3 = "${rebar3}/bin/rebar3";
ERL_COMPILER_OPTIONS = ERL_COMPILER_OPTIONS =
let let
options = erlangCompilerOptions ++ lib.optionals erlangDeterministicBuilds [ "deterministic" ]; options = erlangCompilerOptions ++ lib.optionals erlangDeterministicBuilds [ "deterministic" ];
in in
"[${lib.concatStringsSep "," options}]"; "[${lib.concatStringsSep "," options}]";
LC_ALL = "C.UTF-8"; LC_ALL = "C.UTF-8";
postUnpack = '' postUnpack =
# Mix and Hex ''
export MIX_HOME="$TEMPDIR/mix" # Mix and Hex
export HEX_HOME="$TEMPDIR/hex" export MIX_HOME="$TEMPDIR/mix"
export HEX_HOME="$TEMPDIR/hex"
# Rebar # Rebar
export REBAR_GLOBAL_CONFIG_DIR="$TEMPDIR/rebar3" export REBAR_GLOBAL_CONFIG_DIR="$TEMPDIR/rebar3"
export REBAR_CACHE_DIR="$TEMPDIR/rebar3.cache" export REBAR_CACHE_DIR="$TEMPDIR/rebar3.cache"
${lib.optionalString (mixFodDeps != null) '' ${lib.optionalString (mixFodDeps != null) ''
# Compilation of the dependencies will require that the dependency path is # Compilation of the dependencies will require that the dependency path is
# writable, thus a copy to the $TEMPDIR is inevitable here. # writable, thus a copy to the $TEMPDIR is inevitable here.
export MIX_DEPS_PATH="$TEMPDIR/deps" export MIX_DEPS_PATH="$TEMPDIR/deps"
cp --no-preserve=mode -R "${mixFodDeps}" "$MIX_DEPS_PATH" cp --no-preserve=mode -R "${mixFodDeps}" "$MIX_DEPS_PATH"
''} ''}
'' + (attrs.postUnpack or ""); ''
+ (attrs.postUnpack or "");
configurePhase = attrs.configurePhase or '' configurePhase =
runHook preConfigure attrs.configurePhase or ''
runHook preConfigure
${./mix-configure-hook.sh} ${./mix-configure-hook.sh}
# This is needed for projects that have a specific compile step # This is needed for projects that have a specific compile step
# the dependency needs to be compiled in order for the task # the dependency needs to be compiled in order for the task
# to be available. # to be available.
# #
# Phoenix projects for example will need compile.phoenix. # Phoenix projects for example will need compile.phoenix.
mix deps.compile --no-deps-check --skip-umbrella-children mix deps.compile --no-deps-check --skip-umbrella-children
# Symlink dependency sources. This is needed for projects that require # Symlink dependency sources. This is needed for projects that require
# access to the source of their dependencies. For example, Phoenix # access to the source of their dependencies. For example, Phoenix
# projects need javascript assets to build asset bundles. # projects need javascript assets to build asset bundles.
${lib.optionalString (mixNixDeps != { }) '' ${lib.optionalString (mixNixDeps != { }) ''
mkdir -p deps mkdir -p deps
${lib.concatMapStringsSep "\n" (dep: '' ${lib.concatMapStringsSep "\n" (dep: ''
dep_name=$(basename ${dep} | cut -d '-' -f2) dep_name=$(basename ${dep} | cut -d '-' -f2)
dep_path="deps/$dep_name" dep_path="deps/$dep_name"
if [ -d "${dep}/src" ]; then if [ -d "${dep}/src" ]; then
ln -s ${dep}/src $dep_path ln -s ${dep}/src $dep_path
fi
'') (builtins.attrValues mixNixDeps)}
''}
# Symlink deps to build root. Similar to above, but allows for mixFodDeps
# Phoenix projects to find javascript assets.
${lib.optionalString (mixFodDeps != null) ''
ln -s ../deps ./
''}
runHook postConfigure
'';
buildPhase =
attrs.buildPhase or ''
runHook preBuild
mix compile --no-deps-check ${lib.concatStringsSep " " compileFlags}
runHook postBuild
'';
installPhase =
attrs.installPhase or ''
runHook preInstall
mix release ${mixReleaseName} --no-deps-check --path "$out"
runHook postInstall
'';
postFixup =
''
echo "removing files for Microsoft Windows"
rm -f "$out"/bin/*.bat
echo "wrapping programs in $out/bin with their runtime deps"
for f in $(find $out/bin/ -type f -executable); do
wrapProgram "$f" \
--prefix PATH : ${
lib.makeBinPath [
coreutils
gnused
gnugrep
gawk
]
}
done
''
+ lib.optionalString removeCookie ''
if [ -e $out/releases/COOKIE ]; then
echo "removing $out/releases/COOKIE"
rm $out/releases/COOKIE
fi fi
'') (builtins.attrValues mixNixDeps)} ''
''} + ''
if [ -e $out/erts-* ]; then
# ERTS is included in the release, then erlang is not required as a runtime dependency.
# But, erlang is still referenced in some places. To removed references to erlang,
# following steps are required.
# Symlink deps to build root. Similar to above, but allows for mixFodDeps # 1. remove references to erlang from plain text files
# Phoenix projects to find javascript assets. for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches); do
${lib.optionalString (mixFodDeps != null) '' echo "removing references to erlang in $file"
ln -s ../deps ./ substituteInPlace "$file" --replace "${erlang}/lib/erlang" "$out"
''} done
runHook postConfigure # 2. remove references to erlang from .beam files
''; #
# No need to do anything, because it has been handled by "deterministic" option specified
# by ERL_COMPILER_OPTIONS.
buildPhase = attrs.buildPhase or '' # 3. remove references to erlang from normal binary files
runHook preBuild for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches --binary --iglob '!*.beam'); do
echo "removing references to erlang in $file"
# use bbe to substitute strings in binary files, because using substituteInPlace
# on binaries will raise errors
bbe -e "s|${erlang}/lib/erlang|$out|" -o "$file".tmp "$file"
rm -f "$file"
mv "$file".tmp "$file"
done
mix compile --no-deps-check ${lib.concatStringsSep " " compileFlags} # References to erlang should be removed from output after above processing.
fi
runHook postBuild ''
''; + lib.optionalString stripDebug ''
# Strip debug symbols to avoid hardreferences to "foreign" closures actually
installPhase = attrs.installPhase or '' # not needed at runtime, while at the same time reduce size of BEAM files.
runHook preInstall erl -noinput -eval 'lists:foreach(fun(F) -> io:format("Stripping ~p.~n", [F]), beam_lib:strip(F) end, filelib:wildcard("'"$out"'/**/*.beam"))' -s init stop
'';
mix release --no-deps-check --path "$out" }
)
runHook postInstall
'';
postFixup = ''
echo "removing files for Microsoft Windows"
rm -f "$out"/bin/*.bat
echo "wrapping programs in $out/bin with their runtime deps"
for f in $(find $out/bin/ -type f -executable); do
wrapProgram "$f" \
--prefix PATH : ${lib.makeBinPath [
coreutils
gnused
gnugrep
gawk
]}
done
'' + lib.optionalString removeCookie ''
if [ -e $out/releases/COOKIE ]; then
echo "removing $out/releases/COOKIE"
rm $out/releases/COOKIE
fi
'' + ''
if [ -e $out/erts-* ]; then
# ERTS is included in the release, then erlang is not required as a runtime dependency.
# But, erlang is still referenced in some places. To removed references to erlang,
# following steps are required.
# 1. remove references to erlang from plain text files
for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches); do
echo "removing references to erlang in $file"
substituteInPlace "$file" --replace "${erlang}/lib/erlang" "$out"
done
# 2. remove references to erlang from .beam files
#
# No need to do anything, because it has been handled by "deterministic" option specified
# by ERL_COMPILER_OPTIONS.
# 3. remove references to erlang from normal binary files
for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches --binary --iglob '!*.beam'); do
echo "removing references to erlang in $file"
# use bbe to substitute strings in binary files, because using substituteInPlace
# on binaries will raise errors
bbe -e "s|${erlang}/lib/erlang|$out|" -o "$file".tmp "$file"
rm -f "$file"
mv "$file".tmp "$file"
done
# References to erlang should be removed from output after above processing.
fi
'' + lib.optionalString stripDebug ''
# Strip debug symbols to avoid hardreferences to "foreign" closures actually
# not needed at runtime, while at the same time reduce size of BEAM files.
erl -noinput -eval 'lists:foreach(fun(F) -> io:format("Stripping ~p.~n", [F]), beam_lib:strip(F) end, filelib:wildcard("'"$out"'/**/*.beam"))' -s init stop
'';
})