Unverified Commit 8bdef3ae authored by Adam C. Stephens's avatar Adam C. Stephens
Browse files

beamPackages.buildMix: update to use fixed point

parent 29dd3361
Loading
Loading
Loading
Loading
+105 −135
Original line number Diff line number Diff line
{
  stdenv,
  writeText,
  elixir,
  erlang,
  hex,
  lib,
  mixConfigureHook,
  stdenv,
  writeText,
}:

lib.extendMkDerivation {
  constructDrv = stdenv.mkDerivation;
  excludeDrvArgNames = [
    "mixEnv"
  ];
  extendDrvArgs =
    finalAttrs:
    {
  name,
  version,
  src,
  buildInputs ? [ ],
      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 ? [ ],
  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;
    }@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" ''
      # This is used by package builders such as mixRelease
      setupHook = writeText "setupHook.sh" ''
        addToSearchPath ERL_LIBS "$1/lib/erlang/lib"
      '';

        buildInputs = buildInputs ++ [ ];
      nativeBuildInputs = nativeBuildInputs ++ [
        elixir
        hex
        mixConfigureHook
      ];
      propagatedBuildInputs = propagatedBuildInputs ++ beamDeps;

        configurePhase =
          attrs.configurePhase or ''
            runHook preConfigure

            ${./mix-configure-hook.sh}
            ${lib.optionalString (removeConfig && isNull appConfigPath)
              # By default, we don't want to include whatever config a dependency brings; per
      # 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
                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
          + lib.optionalString (!isNull appConfigPath) ''
            cp -r ${appConfigPath} config
          ''
            }

            runHook postConfigure
          '';
          + ''
            runHook postPatch
          ''
        );

      buildPhase =
          attrs.buildPhase or ''
        args.buildPhase or ''
          runHook preBuild

          export HEX_HOME="$TEMPDIR/hex"
          export MIX_HOME="$TEMPDIR/mix"
          mix compile --no-deps-check

          runHook postBuild
        '';

      installPhase =
          attrs.installPhase or ''
        args.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}"
          mkdir -p "$out/lib/erlang/lib/${finalAttrs.name}-${finalAttrs.version}-${args.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
          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/${name}-${version}" "$reldir"
              cp  -Hrt "$out/lib/erlang/lib/${finalAttrs.name}-${finalAttrs.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;
      dontStrip = args.dontStrip or false;

      passthru = {
          packageName = name;
          env = shell self;
        inherit beamDeps;
      }
      // passthru;
    };
}
    );
in
lib.fix pkg
+8 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
{
  makeSetupHook,
}:
{
  mixConfigureHook = makeSetupHook {
    name = "mix-configure-hook.sh";
  } ./mix-configure-hook.sh;
}
+34 −0
Original line number Diff line number Diff line
# 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
+0 −18
Original line number Diff line number Diff line
# 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:"