Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Haskell Server Wrapper Function for Stack specific $PATH #111

Open
Pitometsu opened this issue Feb 14, 2021 · 4 comments
Open

Haskell Server Wrapper Function for Stack specific $PATH #111

Pitometsu opened this issue Feb 14, 2021 · 4 comments

Comments

@Pitometsu
Copy link

Pitometsu commented Feb 14, 2021

I have errors like

Severity: DsError
Message:  haskell-language-server: could not execute: hspec-discover

when run haskell-language-server. I guess the reason is because hspec-discover in package.yaml:tests:build-tools:, and so installed by stack (not by nix-shell as rest, not-build-specific, tools).

But I have no such problems when run it like env PATH=$PATH:`bash -c 'source <(stack config env); echo $PATH'` haskell-language-server.

I can't just run stack exec haskell-language-server to just simply have all the stack's environments available, because stack using nix --pure for more robust and reproducible builds, and so that cause nix shells conflicts (not sure -- is it because of shells recursivity, or stack can't decide, which nix shell is build one, according to STACK_IN_NIX_SHELL environment variable, I guess).


So the question is: how construction like env PATH=$PATH:`bash -c 'source <(stack config env); echo $PATH'` haskell-language-server could be passed to lsp-haskell-server-wrapper-function properly?

Possible workaround would be: just add each tool from stack's package.yaml:tests:build-tools: (like hspec-discover here) into top-level shell.nix, but that would be fragile and require continuous project build tools consistency manual maintaining.

@Pitometsu
Copy link
Author

Pitometsu commented Feb 14, 2021

Would it be something lke:

(lambda (argv)
  (let
    ((original-path (getenv-internal "PATH"))
     (stack-env-path (shell-command-to-string "stack config env")) ;; TODO: handle case when stack is not present
     (args ((mapconcat ’identity argv " "))))
    (list (format "env PATH=%s:%s %s"  ;; Should it handle empty environment variable case here?
     original-path stack-env-path args))))

???


Result (in *Messages*):

LSP :: The following servers support current file but do not have automatic installation configuration: lsp-haskell
You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
(If you have already installed the server check *lsp-log*).

And no *lsp-log* now. Looks like, lsp-haskell is not able to determine presence of haskell-language-server with that wrapper function anymore.

@Pitometsu
Copy link
Author

Pitometsu commented Feb 14, 2021

Also here possible lags with direnv-mode and lorri tools, and refreshing their caches (like M-x direnv-update-environment RET and M-& lorri shell RET M-x eshell-command RET lorri shell RET.

@Anton-Latukha
Copy link
Contributor

Anton-Latukha commented May 31, 2021

Maybe the people have difficulty responding, because the case & semantics are not ordered. It is hard to parse the semantics, even weak interpretation does not infer into the concrete case.

It would be easier to understand if you would describe the environment of the project, what is used for what, and then nail the case.

Overall, from my experience, and from referring to the current official Stack docs, Stack just as 3 years ago, has very weak Nix integration: link.

Cabal has much better integration with Nix, running in Nix environment. Cabal would detect that it is running in Nix environment & would use Nix to load dependencies & work as Nix client, download the Haskell binary caches from Nixpkgs Hydra CI, and store the build artifacts in Nix.

Also, important to note that stack config env & stack config --any-key stack path & stack path --any-key are all effectful:

$ stack path --project-root

Preparing to install GHC (tinfo6) to an isolated location.
This will not interfere with any system-level installation.
ghc-tinfo6-8.10.4:   10.59 MiB / 207.59 MiB (  5.10%) downloaded...^C

Tangent:
To tell a secret - using cabal inside Nix is equivalent to stack but with binary caches. As cabal would infer the package dependency versions from Nixpkgs which in fact inherits Stackage provided package set every midnight (thou I do not remember Nightly or LTS), and so the Stackage package set is a core of Nixpkgs haskellPackages, and all other Hackage set weekly gets handcrafted updates in Nixpkgs to be aligned to Stackage package versions. Thou there is a maintainer change there which probably would result in some infrastructure changes & also need to mention Cabal-Nix setup nevertheless comes with its own configuration & maintenance woes, but if there is strong reliance on Stackage versions - it should be a pretty stable calm experience.

Stack in Nix is also a legitimate combination, if the project uses a lot of FFI & so system package dependencies.


But, overall the solution may be (if I understand the question correctly) as simple as this:
Of course, there is a directive to inherit something from host environment into a pure Nix environment:

🅸 ~/s/h/hnix:2021-05-27-refactors◦> echo "$IN_NIX_SHELL"

(nope)
🅸 ~/s/h/hnix:2021-05-27-refactors◦> bash
> export LALALA=TRALALA
> echo "$LALALA"
TRALALA
-- Entering Nix environment:
> nix-shell -p haskellPackages.stack --pure --keep LALALA
[user@host ~/path]$ echo "$IN_NIX_SHELL"
pure
[user@host ~/path]$ echo "$LALALA"
TRALALA

Note:
NixOS/nix#419 (comment)

⬆️ It is true. It was true when it was written, & it is the same currently. Would spare the details, but can recommend not do shell work for Nix, or if do any - upfront be ready to throw the work out the window to not be disappointed. Nix taught me to not care to contribute when doing contribution, as caring does not depend on me but on one holding the "merge" button.

Also important to note that some "deep" Nix people - use all bashisms possible, which heavily hardcodes Nix to Bash. When operating with Nix in the code - the /usr/bin/env sh (more portable) or /bin/sh (a bit less portable), which preferably to be bash or dash, need to be run as a wrapper, for example, to make nix-shell -p .. --pure --keep PATH inheritance work. The other shells do comply to the environment sharing standards - but Nix hardcode on bash is so strong, fish or zsh or something a bit more exotic - Nix rigidness is not fully compatible with them. Nix promises portability & not follows POSIX or production-grade RFC standards (as Syslog RFC 3164).

I'd wanted to give less of a disclaimer, but the solution involves quirks that demand to be mentioned to implement proper solutions & save people's time upfront.

Sadly there is a lot of such open secret undocumented technical difficulties which when researched are frequently caused by social context reasons, seems like Nix details (at least in the past) borrowed heavilly from the Conway's law & Cognitive dimensions of notations.

@Anton-Latukha
Copy link
Contributor

Anton-Latukha commented Aug 13, 2021

Yuriy @Pitometsu, can the topic be closed?

Having somewhat extensive knowledge on the Haskell tooling & Nix situation both from prior work experience ("having skin in the game" with Stack & Nix situation in the past at work, where I built infrastructure pipeline) & my current experience with them and knowing new things on it.

The only thing I can tell - is that Stack and Nix combination completely disintegrates in the main agenda of them (the dependency/package management, Stack goes its own way, has own package inference, own system of building artifacts, while Nix infra demands 100% declarative descriptiveness & reproducibility & integration from Stack, which Stack does not provide for Nix, which produces a result that accumulates downsides of both - not reproducible builds that are not declarative, caching does not work, no deduplication, therefore the set size of the build artifacts during work is tremendous, builds always start anew, the number of custom hacks required is "the more hacks the better" which is a nice indication that path does not work, the maintenance cost of combination is unreasonable, it is undocumented, unsupported use, so no support from both upstreams...). So far Nix & Stack are not designed for each other. Use of Cabal(or HPack or DHall) with Nix is much more happy path & reasonable choice that should succeed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants