diff --git a/pkgs/development/beam-modules/elixir-ls/default.nix b/pkgs/development/beam-modules/elixir-ls/default.nix index a683c6b126cd..237838bc337b 100644 --- a/pkgs/development/beam-modules/elixir-ls/default.nix +++ b/pkgs/development/beam-modules/elixir-ls/default.nix @@ -1,31 +1,26 @@ { 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 + 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" - 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" runHook postInstall ''; diff --git a/pkgs/development/beam-modules/elixir-ls/launch.sh.patch b/pkgs/development/beam-modules/elixir-ls/launch.sh.patch new file mode 100644 index 000000000000..aabfd82a3d58 --- /dev/null +++ b/pkgs/development/beam-modules/elixir-ls/launch.sh.patch @@ -0,0 +1,169 @@ +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" diff --git a/pkgs/development/interpreters/elixir/1.17.nix b/pkgs/development/interpreters/elixir/1.17.nix index 41d72d2ff907..32d6654e11d5 100644 --- a/pkgs/development/interpreters/elixir/1.17.nix +++ b/pkgs/development/interpreters/elixir/1.17.nix @@ -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"; } diff --git a/pkgs/development/interpreters/elixir/generic-builder.nix b/pkgs/development/interpreters/elixir/generic-builder.nix index 638c83d8ce5a..f8aff7f01c19 100644 --- a/pkgs/development/interpreters/elixir/generic-builder.nix +++ b/pkgs/development/interpreters/elixir/generic-builder.nix @@ -1,5 +1,4 @@ { - 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 ]; - LANG = "C.UTF-8"; - LC_TYPE = "C.UTF-8"; - - ERLC_OPTS = - let - erlc_opts = [ "deterministic" ] ++ optional debugInfo "debug_info"; - in - "[${concatStringsSep "," erlc_opts}]"; + env = { + LANG = "C.UTF-8"; + LC_TYPE = "C.UTF-8"; + 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 ]; }; -}) +}