Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 33 additions & 36 deletions pkgs/development/beam-modules/elixir-ls/default.nix
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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
'';

Expand Down
169 changes: 169 additions & 0 deletions pkgs/development/beam-modules/elixir-ls/launch.sh.patch
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this line is weird.

By default, it will use hex to fetch the source again, and run with that instead of anything from Nixpkgs' package.

With ELS_LOCAL=1 it will run the interpreter in the project in $store/libexec/../, which will fail.

What exactly are we attempting to do here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package needs more changes on top of what is in this PR. This was a first pass at cleanup, but this package is indeed weird in that it's not really a package but a set of wrapper scripts. Ideally we properly package it and set ELS_LOCAL so it uses code from the store instead of downloading. Until then I wouldn't expect setting that env var to work.

I've used these changes without issues though. Do you have a reproducer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used these changes without issues though. Do you have a reproducer?

I'm more curious what impurity made it work for you, since there is no codepath that uses the .beam built here, it always uses the one re-downloaded into the mix home path, without the 1216.patch we need.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the problem now, but it's still not clear to me why it worked on my machine.

Anyway, can you try this PR? #427805


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 change: 1 addition & 0 deletions pkgs/development/interpreters/elixir/1.17.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
32 changes: 18 additions & 14 deletions pkgs/development/interpreters/elixir/generic-builder.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
pkgs,
lib,
stdenv,
fetchFromGitHub,
Expand Down Expand Up @@ -33,7 +32,7 @@ let
assertMsg
concatStringsSep
getVersion
optional
optionals
optionalString
toInt
versions
Expand Down Expand Up @@ -68,32 +67,37 @@ 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;

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 = ''
Expand Down Expand Up @@ -144,4 +148,4 @@ stdenv.mkDerivation ({
platforms = platforms.unix;
teams = [ teams.beam ];
};
})
}
Loading