Unverified Commit ef3cbae5 authored by Adam C. Stephens's avatar Adam C. Stephens Committed by GitHub
Browse files

elixir-ls: fix elixir stdlib code detection, elixir: fix deterministic builds...

elixir-ls: fix elixir stdlib code detection, elixir: fix deterministic builds and 1.17 max OTP (#423588)
parents 5f80e843 ffd70542
Loading
Loading
Loading
Loading
+33 −36
Original line number Diff line number Diff line
{
  lib,
  elixir,
  fetchpatch,
  fetchFromGitHub,
  fetchMixDeps,
  makeWrapper,
  mixRelease,
  nix-update-script,
}:
# Based on the work of Hauleth
# None of this would have happened without him

let
mixRelease rec {
  pname = "elixir-ls";
  version = "0.28.1";

  src = fetchFromGitHub {
    owner = "elixir-lsp";
    repo = "elixir-ls";
    rev = "v${version}";
    hash = "sha256-r4P+3MPniDNdF3SG2jfBbzHsoxn826eYd2tsv6bJBoI=";
  };
in
mixRelease {
  inherit
    pname
    version
    src
    elixir
    ;

  inherit elixir;

  stripDebug = true;

@@ -35,43 +30,45 @@ mixRelease {
    hash = "sha256-8zs+99jwf+YX5SwD65FCPmfrYhTCx4AQGCGsDeCKxKc=";
  };

  # elixir-ls is an umbrella app
  # override configurePhase to not skip umbrella children
  configurePhase = ''
    runHook preConfigure
    mix deps.compile --no-deps-check
    runHook postConfigure
  '';
  patches = [
    # fix elixir deterministic support https://github.com/elixir-lsp/elixir-ls/pull/1216
    # remove > 0.28.1
    (fetchpatch {
      url = "https://github.com/elixir-lsp/elixir-ls/pull/1216.patch";
      hash = "sha256-J1Q7XQXWYuCMq48e09deQU71DOElZ2zMTzrceZMky+0=";
    })

    # patch wrapper script to remove elixir detection and inject necessary paths
    ./launch.sh.patch
  ];

  nativeBuildInputs = [
    makeWrapper
  ];

  # elixir-ls require a special step for release
  # compile and release need to be performed together because
  # of the no-deps-check requirement
  buildPhase = ''
    runHook preBuild

    mix do compile --no-deps-check, elixir_ls.release${lib.optionalString (lib.versionAtLeast elixir.version "1.16.0") "2"}

    runHook postBuild
  '';

  installPhase = ''
    runHook preInstall
    mkdir -p $out/bin
    cp -Rv release $out/lib
    # Prepare the wrapper script
    substitute release/language_server.sh $out/bin/elixir-ls \
      --replace 'exec "''${dir}/launch.sh"' "exec $out/lib/launch.sh"
    chmod +x $out/bin/elixir-ls

    substitute release/debug_adapter.sh $out/bin/elixir-debug-adapter \
      --replace 'exec "''${dir}/launch.sh"' "exec $out/lib/launch.sh"
    chmod +x $out/bin/elixir-debug-adapter
    # prepare the launchers
    substituteInPlace $out/lib/launch.sh \
      --replace "ERL_LIBS=\"\$SCRIPTPATH:\$ERL_LIBS\"" \
                "ERL_LIBS=$out/lib:\$ERL_LIBS" \
      --replace "exec elixir" "exec ${elixir}/bin/elixir" \
      --replace 'echo "" | elixir' "echo \"\" | ${elixir}/bin/elixir"
    substituteInPlace $out/lib/exec.zsh \
      --replace "exec elixir" "exec ${elixir}/bin/elixir"
    cp -Rv release $out/libexec

    substituteAllInPlace $out/libexec/launch.sh

    makeWrapper $out/libexec/language_server.sh $out/bin/elixir-ls \
      --set ELS_INSTALL_PREFIX "$out/libexec"

    makeWrapper $out/libexec/debug_adapter.sh $out/bin/elixir-debug-adapter \
      --set ELS_INSTALL_PREFIX "$out/libexec"

    runHook postInstall
  '';

+169 −0
Original line number Diff line number Diff line
diff --git i/scripts/launch.sh w/scripts/launch.sh
index 21afbb1e..975cbdf0 100755
--- i/scripts/launch.sh
+++ w/scripts/launch.sh
@@ -1,125 +1,4 @@
-#!/bin/sh
-# Actual launcher. This does the hard work of figuring out the best way
-# to launch the language server or the debug adapter.
-
-# Running this script is a one-time action per project launch, so we opt for
-# code simplicity instead of performance. Hence some potentially redundant
-# moves here.
-
-
-did_relaunch=$1
-
-# Get the user's preferred shell
-preferred_shell=$(basename "$SHELL")
-
-# Get current dirname
-dirname=$(dirname "$0")
-
-case "${did_relaunch}" in
-  "")
-    if [ "$preferred_shell" = "bash" ]; then
-      >&2 echo "Preferred shell is bash, relaunching"
-      exec "$(which bash)" "$0" relaunch
-    elif [ "$preferred_shell" = "zsh" ]; then
-      >&2 echo "Preferred shell is zsh, relaunching"
-      exec "$(which zsh)" "$0" relaunch
-    elif [ "$preferred_shell" = "fish" ]; then
-      >&2 echo "Preferred shell is fish, launching launch.fish"
-      exec "$(which fish)" "$dirname/launch.fish"
-    else
-      >&2 echo "Preferred shell $preferred_shell is not supported, continuing in POSIX shell"
-    fi
-    ;;
-  *)
-    # We have an arg2, so we got relaunched
-    ;;
-esac
-
-# First order of business, see whether we can setup asdf
-echo "Looking for asdf install" >&2
-
-readlink_f () {
-  cd "$(dirname "$1")" > /dev/null || exit 1
-  filename="$(basename "$1")"
-  if [ -h "$filename" ]; then
-    readlink_f "$(readlink "$filename")"
-  else
-    echo "$(pwd -P)/$filename"
-  fi
-}
-
-export_stdlib_path () {
-  which_elixir_expr=$1
-  stdlib_path=$(eval "$which_elixir_expr")
-  stdlib_real_path=$(readlink_f "$stdlib_path")
-  ELX_STDLIB_PATH=$(echo "$stdlib_real_path" | sed "s/\(.*\)\/bin\/elixir/\1/")
-  export ELX_STDLIB_PATH
-}
-
-# Check if we have the asdf binary for version >= 0.16.0
-if command -v asdf >/dev/null 2>&1; then
-    asdf_version=$(asdf --version 2>/dev/null)
-    version=$(echo "$asdf_version" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
-    major=$(echo "$version" | cut -d. -f1)
-    minor=$(echo "$version" | cut -d. -f2)
-    # If the version is less than 0.16.0 (i.e. major = 0 and minor < 16), use legacy method.
-    if [ "$major" -eq 0 ] && [ "$minor" -lt 16 ]; then
-        ASDF_DIR=${ASDF_DIR:-"${HOME}/.asdf"}
-        ASDF_SH="${ASDF_DIR}/asdf.sh"
-        if test -f "$ASDF_SH"; then
-            >&2 echo "Legacy pre v0.16.0 asdf install found at $ASDF_SH, sourcing"
-            # Source the old asdf.sh script for versions <= 0.15.0
-            . "$ASDF_SH"
-        else
-            >&2 echo "Legacy asdf not found at $ASDF_SH"
-        fi
-    else
-        >&2 echo "asdf executable found at $(command -v asdf). Using ASDF_DIR=${ASDF_DIR}, ASDF_DATA_DIR=${ASDF_DATA_DIR}."
-    fi
-    export_stdlib_path "asdf which elixir"
-else
-    # Fallback to old method for version <= 0.15.x
-    ASDF_DIR=${ASDF_DIR:-"${HOME}/.asdf"}
-    ASDF_SH="${ASDF_DIR}/asdf.sh"
-    if test -f "$ASDF_SH"; then
-        >&2 echo "Legacy pre v0.16.0 asdf install found at $ASDF_SH, sourcing"
-        # Source the old asdf.sh script for versions <= 0.15.0
-        . "$ASDF_SH"
-        export_stdlib_path "asdf which elixir"
-    else
-        >&2 echo "asdf not found"
-        >&2 echo "Looking for mise executable"
-
-        # Look for mise executable
-        if command -v mise >/dev/null 2>&1; then
-            >&2 echo "mise executable found at $(command -v mise), activating"
-            eval "$($(command -v mise) env -s "$preferred_shell")"
-            export_stdlib_path "mise which elixir"
-        else
-            >&2 echo "mise not found"
-            >&2 echo "Looking for rtx executable"
-
-            # Look for rtx executable
-            if command -v rtx >/dev/null 2>&1; then
-                >&2 echo "rtx executable found at $(command -v rtx), activating"
-                eval "$($(command -v rtx) env -s "$preferred_shell")"
-                export_stdlib_path "rtx which elixir"
-            else
-                >&2 echo "rtx not found"
-                >&2 echo "Looking for vfox executable"
-
-                # Look for vfox executable
-                if command -v vfox >/dev/null 2>&1; then
-                    >&2 echo "vfox executable found at $(command -v vfox), activating"
-                    eval "$($(command -v vfox) activate "$preferred_shell")"
-                else
-                    >&2 echo "vfox not found"
-                    export_stdlib_path "which elixir"
-                fi
-            fi
-        fi
-    fi
-fi
+#!/usr/bin/env bash
 
 # In case that people want to tweak the path, which Elixir to use, or
 # whatever prior to launching the language server or the debug adapter, we
@@ -138,29 +17,18 @@ fi
 # script so we can correctly configure the Erlang library path to
 # include the local .ez files, and then do what we were asked to do.
 
-if [ -z "${ELS_INSTALL_PREFIX}" ]; then
-  SCRIPT=$(readlink_f "$0")
-  SCRIPTPATH=$(dirname "$SCRIPT")
-else
-  SCRIPTPATH=${ELS_INSTALL_PREFIX}
-fi
+SCRIPT=$(readlink -f "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")/../libexec
 
 export MIX_ENV=prod
 # Mix.install prints to stdout and reads from stdin
 # we need to make sure it doesn't interfere with LSP/DAP
-echo "" | elixir "$SCRIPTPATH/quiet_install.exs" >/dev/null || exit 1
+echo "" | @elixir@/bin/elixir "$SCRIPTPATH/quiet_install.exs" >/dev/null || exit 1
 
 default_erl_opts="-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none"
 
-if [ "$preferred_shell" = "bash" ]; then
-  source "$dirname/exec.bash"
-elif [ "$preferred_shell" = "zsh" ]; then
-  source "$dirname/exec.zsh"
-else
-  if [ -z "$ELS_ELIXIR_OPTS" ]
-  then
-    # in posix shell does not support arrays
-    >&2 echo "ELS_ELIXIR_OPTS is not supported in current shell"
-  fi
-  exec elixir --erl "$default_erl_opts $ELS_ERL_OPTS" "$SCRIPTPATH/launch.exs"
-fi
+# ensure elixir stdlib can be found
+ELX_STDLIB_PATH=${ELX_STDLIB_PATH:-@elixir@/lib/elixir}
+export ELX_STDLIB_PATH
+
+source "$SCRIPTPATH/exec.bash"
+1 −0
Original line number Diff line number Diff line
@@ -4,5 +4,6 @@ mkDerivation {
  sha256 = "sha256-7Qo6y0KAQ9lwD4oH+7wQ4W5i6INHIBDN9IQAAsYzNJw=";
  # https://hexdocs.pm/elixir/1.17.3/compatibility-and-deprecations.html#compatibility-between-elixir-and-erlang-otp
  minimumOTPVersion = "25";
  maximumOTPVersion = "27";
  escriptPath = "lib/elixir/scripts/generate_app.escript";
}
+18 −14
Original line number Diff line number Diff line
{
  pkgs,
  lib,
  stdenv,
  fetchFromGitHub,
@@ -33,7 +32,7 @@ let
    assertMsg
    concatStringsSep
    getVersion
    optional
    optionals
    optionalString
    toInt
    versions
@@ -68,11 +67,13 @@ let
      "${coreutils}/bin/env $out/bin/elixir"
    else
      "$out/bin/elixir";

  erlc_opts = [ "deterministic" ] ++ optionals debugInfo [ "debug_info" ];
in
assert assertMsg (versionAtLeast (getVersion erlang) minimumOTPVersion) compatibilityMsg;
assert assertMsg maxAssert compatibilityMsg;

stdenv.mkDerivation ({
stdenv.mkDerivation {
  pname = "${baseName}";

  inherit src version debugInfo;
@@ -80,20 +81,23 @@ stdenv.mkDerivation ({
  nativeBuildInputs = [ makeWrapper ];
  buildInputs = [ erlang ];

  env = {
    LANG = "C.UTF-8";
    LC_TYPE = "C.UTF-8";

  ERLC_OPTS =
    let
      erlc_opts = [ "deterministic" ] ++ optional debugInfo "debug_info";
    in
    "[${concatStringsSep "," erlc_opts}]";
    DESTDIR = placeholder "out";
    PREFIX = "/";
    ERL_COMPILER_OPTIONS = "[${concatStringsSep "," erlc_opts}]";
  };

  preBuild = ''
    patchShebangs ${escriptPath} || true
  '';

    substituteInPlace Makefile \
      --replace "/usr/local" $out
  # copy stdlib source files for LSP access
  postInstall = ''
    for d in lib/*; do
      cp -R "$d/lib" "$out/lib/elixir/$d"
    done
  '';

  postFixup = ''
@@ -144,4 +148,4 @@ stdenv.mkDerivation ({
    platforms = platforms.unix;
    teams = [ teams.beam ];
  };
})
}