elixir-ls: fix elixir stdlib code detection, elixir: fix deterministic builds and 1.17 max OTP (#423588)

authored by

Adam C. Stephens and committed by
GitHub
ef3cbae5 5f80e843

+220 -49
+32 -35
pkgs/development/beam-modules/elixir-ls/default.nix
··· 1 { 2 lib, 3 elixir, 4 fetchFromGitHub, 5 fetchMixDeps, 6 mixRelease, 7 nix-update-script, 8 }: 9 - # Based on the work of Hauleth 10 - # None of this would have happened without him 11 12 - let 13 pname = "elixir-ls"; 14 version = "0.28.1"; 15 src = fetchFromGitHub { 16 owner = "elixir-lsp"; 17 repo = "elixir-ls"; 18 rev = "v${version}"; 19 hash = "sha256-r4P+3MPniDNdF3SG2jfBbzHsoxn826eYd2tsv6bJBoI="; 20 }; 21 - in 22 - mixRelease { 23 - inherit 24 - pname 25 - version 26 - src 27 - elixir 28 - ; 29 30 stripDebug = true; 31 ··· 35 hash = "sha256-8zs+99jwf+YX5SwD65FCPmfrYhTCx4AQGCGsDeCKxKc="; 36 }; 37 38 - # elixir-ls is an umbrella app 39 - # override configurePhase to not skip umbrella children 40 - configurePhase = '' 41 - runHook preConfigure 42 - mix deps.compile --no-deps-check 43 - runHook postConfigure 44 - ''; 45 46 # elixir-ls require a special step for release 47 # compile and release need to be performed together because 48 # of the no-deps-check requirement 49 buildPhase = '' 50 runHook preBuild 51 mix do compile --no-deps-check, elixir_ls.release${lib.optionalString (lib.versionAtLeast elixir.version "1.16.0") "2"} 52 runHook postBuild 53 ''; 54 55 installPhase = '' 56 - runHook preInstall 57 mkdir -p $out/bin 58 - cp -Rv release $out/lib 59 - # Prepare the wrapper script 60 - substitute release/language_server.sh $out/bin/elixir-ls \ 61 - --replace 'exec "''${dir}/launch.sh"' "exec $out/lib/launch.sh" 62 - chmod +x $out/bin/elixir-ls 63 64 - substitute release/debug_adapter.sh $out/bin/elixir-debug-adapter \ 65 - --replace 'exec "''${dir}/launch.sh"' "exec $out/lib/launch.sh" 66 - chmod +x $out/bin/elixir-debug-adapter 67 - # prepare the launchers 68 - substituteInPlace $out/lib/launch.sh \ 69 - --replace "ERL_LIBS=\"\$SCRIPTPATH:\$ERL_LIBS\"" \ 70 - "ERL_LIBS=$out/lib:\$ERL_LIBS" \ 71 - --replace "exec elixir" "exec ${elixir}/bin/elixir" \ 72 - --replace 'echo "" | elixir' "echo \"\" | ${elixir}/bin/elixir" 73 - substituteInPlace $out/lib/exec.zsh \ 74 - --replace "exec elixir" "exec ${elixir}/bin/elixir" 75 runHook postInstall 76 ''; 77
··· 1 { 2 lib, 3 elixir, 4 + fetchpatch, 5 fetchFromGitHub, 6 fetchMixDeps, 7 + makeWrapper, 8 mixRelease, 9 nix-update-script, 10 }: 11 12 + mixRelease rec { 13 pname = "elixir-ls"; 14 version = "0.28.1"; 15 + 16 src = fetchFromGitHub { 17 owner = "elixir-lsp"; 18 repo = "elixir-ls"; 19 rev = "v${version}"; 20 hash = "sha256-r4P+3MPniDNdF3SG2jfBbzHsoxn826eYd2tsv6bJBoI="; 21 }; 22 + 23 + inherit elixir; 24 25 stripDebug = true; 26 ··· 30 hash = "sha256-8zs+99jwf+YX5SwD65FCPmfrYhTCx4AQGCGsDeCKxKc="; 31 }; 32 33 + patches = [ 34 + # fix elixir deterministic support https://github.com/elixir-lsp/elixir-ls/pull/1216 35 + # remove > 0.28.1 36 + (fetchpatch { 37 + url = "https://github.com/elixir-lsp/elixir-ls/pull/1216.patch"; 38 + hash = "sha256-J1Q7XQXWYuCMq48e09deQU71DOElZ2zMTzrceZMky+0="; 39 + }) 40 + 41 + # patch wrapper script to remove elixir detection and inject necessary paths 42 + ./launch.sh.patch 43 + ]; 44 + 45 + nativeBuildInputs = [ 46 + makeWrapper 47 + ]; 48 49 # elixir-ls require a special step for release 50 # compile and release need to be performed together because 51 # of the no-deps-check requirement 52 buildPhase = '' 53 runHook preBuild 54 + 55 mix do compile --no-deps-check, elixir_ls.release${lib.optionalString (lib.versionAtLeast elixir.version "1.16.0") "2"} 56 + 57 runHook postBuild 58 ''; 59 60 installPhase = '' 61 mkdir -p $out/bin 62 + cp -Rv release $out/libexec 63 + 64 + substituteAllInPlace $out/libexec/launch.sh 65 + 66 + makeWrapper $out/libexec/language_server.sh $out/bin/elixir-ls \ 67 + --set ELS_INSTALL_PREFIX "$out/libexec" 68 + 69 + makeWrapper $out/libexec/debug_adapter.sh $out/bin/elixir-debug-adapter \ 70 + --set ELS_INSTALL_PREFIX "$out/libexec" 71 72 runHook postInstall 73 ''; 74
+169
pkgs/development/beam-modules/elixir-ls/launch.sh.patch
···
··· 1 + diff --git i/scripts/launch.sh w/scripts/launch.sh 2 + index 21afbb1e..975cbdf0 100755 3 + --- i/scripts/launch.sh 4 + +++ w/scripts/launch.sh 5 + @@ -1,125 +1,4 @@ 6 + -#!/bin/sh 7 + -# Actual launcher. This does the hard work of figuring out the best way 8 + -# to launch the language server or the debug adapter. 9 + - 10 + -# Running this script is a one-time action per project launch, so we opt for 11 + -# code simplicity instead of performance. Hence some potentially redundant 12 + -# moves here. 13 + - 14 + - 15 + -did_relaunch=$1 16 + - 17 + -# Get the user's preferred shell 18 + -preferred_shell=$(basename "$SHELL") 19 + - 20 + -# Get current dirname 21 + -dirname=$(dirname "$0") 22 + - 23 + -case "${did_relaunch}" in 24 + - "") 25 + - if [ "$preferred_shell" = "bash" ]; then 26 + - >&2 echo "Preferred shell is bash, relaunching" 27 + - exec "$(which bash)" "$0" relaunch 28 + - elif [ "$preferred_shell" = "zsh" ]; then 29 + - >&2 echo "Preferred shell is zsh, relaunching" 30 + - exec "$(which zsh)" "$0" relaunch 31 + - elif [ "$preferred_shell" = "fish" ]; then 32 + - >&2 echo "Preferred shell is fish, launching launch.fish" 33 + - exec "$(which fish)" "$dirname/launch.fish" 34 + - else 35 + - >&2 echo "Preferred shell $preferred_shell is not supported, continuing in POSIX shell" 36 + - fi 37 + - ;; 38 + - *) 39 + - # We have an arg2, so we got relaunched 40 + - ;; 41 + -esac 42 + - 43 + -# First order of business, see whether we can setup asdf 44 + -echo "Looking for asdf install" >&2 45 + - 46 + -readlink_f () { 47 + - cd "$(dirname "$1")" > /dev/null || exit 1 48 + - filename="$(basename "$1")" 49 + - if [ -h "$filename" ]; then 50 + - readlink_f "$(readlink "$filename")" 51 + - else 52 + - echo "$(pwd -P)/$filename" 53 + - fi 54 + -} 55 + - 56 + -export_stdlib_path () { 57 + - which_elixir_expr=$1 58 + - stdlib_path=$(eval "$which_elixir_expr") 59 + - stdlib_real_path=$(readlink_f "$stdlib_path") 60 + - ELX_STDLIB_PATH=$(echo "$stdlib_real_path" | sed "s/\(.*\)\/bin\/elixir/\1/") 61 + - export ELX_STDLIB_PATH 62 + -} 63 + - 64 + -# Check if we have the asdf binary for version >= 0.16.0 65 + -if command -v asdf >/dev/null 2>&1; then 66 + - asdf_version=$(asdf --version 2>/dev/null) 67 + - version=$(echo "$asdf_version" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') 68 + - major=$(echo "$version" | cut -d. -f1) 69 + - minor=$(echo "$version" | cut -d. -f2) 70 + - # If the version is less than 0.16.0 (i.e. major = 0 and minor < 16), use legacy method. 71 + - if [ "$major" -eq 0 ] && [ "$minor" -lt 16 ]; then 72 + - ASDF_DIR=${ASDF_DIR:-"${HOME}/.asdf"} 73 + - ASDF_SH="${ASDF_DIR}/asdf.sh" 74 + - if test -f "$ASDF_SH"; then 75 + - >&2 echo "Legacy pre v0.16.0 asdf install found at $ASDF_SH, sourcing" 76 + - # Source the old asdf.sh script for versions <= 0.15.0 77 + - . "$ASDF_SH" 78 + - else 79 + - >&2 echo "Legacy asdf not found at $ASDF_SH" 80 + - fi 81 + - else 82 + - >&2 echo "asdf executable found at $(command -v asdf). Using ASDF_DIR=${ASDF_DIR}, ASDF_DATA_DIR=${ASDF_DATA_DIR}." 83 + - fi 84 + - export_stdlib_path "asdf which elixir" 85 + -else 86 + - # Fallback to old method for version <= 0.15.x 87 + - ASDF_DIR=${ASDF_DIR:-"${HOME}/.asdf"} 88 + - ASDF_SH="${ASDF_DIR}/asdf.sh" 89 + - if test -f "$ASDF_SH"; then 90 + - >&2 echo "Legacy pre v0.16.0 asdf install found at $ASDF_SH, sourcing" 91 + - # Source the old asdf.sh script for versions <= 0.15.0 92 + - . "$ASDF_SH" 93 + - export_stdlib_path "asdf which elixir" 94 + - else 95 + - >&2 echo "asdf not found" 96 + - >&2 echo "Looking for mise executable" 97 + - 98 + - # Look for mise executable 99 + - if command -v mise >/dev/null 2>&1; then 100 + - >&2 echo "mise executable found at $(command -v mise), activating" 101 + - eval "$($(command -v mise) env -s "$preferred_shell")" 102 + - export_stdlib_path "mise which elixir" 103 + - else 104 + - >&2 echo "mise not found" 105 + - >&2 echo "Looking for rtx executable" 106 + - 107 + - # Look for rtx executable 108 + - if command -v rtx >/dev/null 2>&1; then 109 + - >&2 echo "rtx executable found at $(command -v rtx), activating" 110 + - eval "$($(command -v rtx) env -s "$preferred_shell")" 111 + - export_stdlib_path "rtx which elixir" 112 + - else 113 + - >&2 echo "rtx not found" 114 + - >&2 echo "Looking for vfox executable" 115 + - 116 + - # Look for vfox executable 117 + - if command -v vfox >/dev/null 2>&1; then 118 + - >&2 echo "vfox executable found at $(command -v vfox), activating" 119 + - eval "$($(command -v vfox) activate "$preferred_shell")" 120 + - else 121 + - >&2 echo "vfox not found" 122 + - export_stdlib_path "which elixir" 123 + - fi 124 + - fi 125 + - fi 126 + - fi 127 + -fi 128 + +#!/usr/bin/env bash 129 + 130 + # In case that people want to tweak the path, which Elixir to use, or 131 + # whatever prior to launching the language server or the debug adapter, we 132 + @@ -138,29 +17,18 @@ fi 133 + # script so we can correctly configure the Erlang library path to 134 + # include the local .ez files, and then do what we were asked to do. 135 + 136 + -if [ -z "${ELS_INSTALL_PREFIX}" ]; then 137 + - SCRIPT=$(readlink_f "$0") 138 + - SCRIPTPATH=$(dirname "$SCRIPT") 139 + -else 140 + - SCRIPTPATH=${ELS_INSTALL_PREFIX} 141 + -fi 142 + +SCRIPT=$(readlink -f "$0") 143 + +SCRIPTPATH=$(dirname "$SCRIPT")/../libexec 144 + 145 + export MIX_ENV=prod 146 + # Mix.install prints to stdout and reads from stdin 147 + # we need to make sure it doesn't interfere with LSP/DAP 148 + -echo "" | elixir "$SCRIPTPATH/quiet_install.exs" >/dev/null || exit 1 149 + +echo "" | @elixir@/bin/elixir "$SCRIPTPATH/quiet_install.exs" >/dev/null || exit 1 150 + 151 + default_erl_opts="-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none" 152 + 153 + -if [ "$preferred_shell" = "bash" ]; then 154 + - source "$dirname/exec.bash" 155 + -elif [ "$preferred_shell" = "zsh" ]; then 156 + - source "$dirname/exec.zsh" 157 + -else 158 + - if [ -z "$ELS_ELIXIR_OPTS" ] 159 + - then 160 + - # in posix shell does not support arrays 161 + - >&2 echo "ELS_ELIXIR_OPTS is not supported in current shell" 162 + - fi 163 + - exec elixir --erl "$default_erl_opts $ELS_ERL_OPTS" "$SCRIPTPATH/launch.exs" 164 + -fi 165 + +# ensure elixir stdlib can be found 166 + +ELX_STDLIB_PATH=${ELX_STDLIB_PATH:-@elixir@/lib/elixir} 167 + +export ELX_STDLIB_PATH 168 + + 169 + +source "$SCRIPTPATH/exec.bash"
+1
pkgs/development/interpreters/elixir/1.17.nix
··· 4 sha256 = "sha256-7Qo6y0KAQ9lwD4oH+7wQ4W5i6INHIBDN9IQAAsYzNJw="; 5 # https://hexdocs.pm/elixir/1.17.3/compatibility-and-deprecations.html#compatibility-between-elixir-and-erlang-otp 6 minimumOTPVersion = "25"; 7 escriptPath = "lib/elixir/scripts/generate_app.escript"; 8 }
··· 4 sha256 = "sha256-7Qo6y0KAQ9lwD4oH+7wQ4W5i6INHIBDN9IQAAsYzNJw="; 5 # https://hexdocs.pm/elixir/1.17.3/compatibility-and-deprecations.html#compatibility-between-elixir-and-erlang-otp 6 minimumOTPVersion = "25"; 7 + maximumOTPVersion = "27"; 8 escriptPath = "lib/elixir/scripts/generate_app.escript"; 9 }
+18 -14
pkgs/development/interpreters/elixir/generic-builder.nix
··· 1 { 2 - pkgs, 3 lib, 4 stdenv, 5 fetchFromGitHub, ··· 33 assertMsg 34 concatStringsSep 35 getVersion 36 - optional 37 optionalString 38 toInt 39 versions ··· 68 "${coreutils}/bin/env $out/bin/elixir" 69 else 70 "$out/bin/elixir"; 71 in 72 assert assertMsg (versionAtLeast (getVersion erlang) minimumOTPVersion) compatibilityMsg; 73 assert assertMsg maxAssert compatibilityMsg; 74 75 - stdenv.mkDerivation ({ 76 pname = "${baseName}"; 77 78 inherit src version debugInfo; ··· 80 nativeBuildInputs = [ makeWrapper ]; 81 buildInputs = [ erlang ]; 82 83 - LANG = "C.UTF-8"; 84 - LC_TYPE = "C.UTF-8"; 85 - 86 - ERLC_OPTS = 87 - let 88 - erlc_opts = [ "deterministic" ] ++ optional debugInfo "debug_info"; 89 - in 90 - "[${concatStringsSep "," erlc_opts}]"; 91 92 preBuild = '' 93 patchShebangs ${escriptPath} || true 94 95 - substituteInPlace Makefile \ 96 - --replace "/usr/local" $out 97 ''; 98 99 postFixup = '' ··· 144 platforms = platforms.unix; 145 teams = [ teams.beam ]; 146 }; 147 - })
··· 1 { 2 lib, 3 stdenv, 4 fetchFromGitHub, ··· 32 assertMsg 33 concatStringsSep 34 getVersion 35 + optionals 36 optionalString 37 toInt 38 versions ··· 67 "${coreutils}/bin/env $out/bin/elixir" 68 else 69 "$out/bin/elixir"; 70 + 71 + erlc_opts = [ "deterministic" ] ++ optionals debugInfo [ "debug_info" ]; 72 in 73 assert assertMsg (versionAtLeast (getVersion erlang) minimumOTPVersion) compatibilityMsg; 74 assert assertMsg maxAssert compatibilityMsg; 75 76 + stdenv.mkDerivation { 77 pname = "${baseName}"; 78 79 inherit src version debugInfo; ··· 81 nativeBuildInputs = [ makeWrapper ]; 82 buildInputs = [ erlang ]; 83 84 + env = { 85 + LANG = "C.UTF-8"; 86 + LC_TYPE = "C.UTF-8"; 87 + DESTDIR = placeholder "out"; 88 + PREFIX = "/"; 89 + ERL_COMPILER_OPTIONS = "[${concatStringsSep "," erlc_opts}]"; 90 + }; 91 92 preBuild = '' 93 patchShebangs ${escriptPath} || true 94 + ''; 95 96 + # copy stdlib source files for LSP access 97 + postInstall = '' 98 + for d in lib/*; do 99 + cp -R "$d/lib" "$out/lib/elixir/$d" 100 + done 101 ''; 102 103 postFixup = '' ··· 148 platforms = platforms.unix; 149 teams = [ teams.beam ]; 150 }; 151 + }