purcell / envrc

Emacs support for direnv which operates buffer-locally
378 stars 35 forks source link

Envrc mode enabled, but Racket application relying on SDL2 only works when Emacs is started from within project dir #22

Closed kenranunderscore closed 3 years ago

kenranunderscore commented 3 years ago

Sorry for the long title. I might be doing something wrong here, but I'm trying out a small project using SDL2 for graphics, with my .envrc containing only use nix, and the shell.nix being the following:

with import <nixpkgs> { };

with pkgs;
mkShell {
  buildInputs = [ SDL2 ];
  LD_LIBRARY_PATH = lib.makeLibraryPath [ SDL2 libGL ];
}

The project itself is using Racket (if you need a minimal example, I can of course provide one on demand; I just wanted to make sure I'm not expecting something unreasonable from envrc). Running emacs from within the project directory, I can start the application just fine via Emacs. If my Emacs has been started outside, however, it complains about not being able to load libSDL2.so. The *envrc* buffer prints out the successful block of message, and I get Direnv succeeded in ... in the message buffer. What's more, the LD_LIBRARY_PATH variable (which is crucial for Racket's sdl2 package) is set to the correct value when I M-x getenv LD_LIBRARY_PATH.

Is this an issue or expected behavior? If it's an issue (not necessarily with envrc), I'll glady try and find the reason, but probably need some hints :)

kenranunderscore commented 3 years ago

FWIW here's some sample code that I can reproduce the issue with. It needs racket-mode' as well as a Racket installation and doing raco pkg install sdl2 beforehand. I then run the program with C-c C-c.

#lang racket

(require
 (prefix-in sdl2: sdl2/pretty))

(sdl2:set-main-ready!)
(sdl2:init! '(video))
(define window (sdl2:create-window! "Hello, World!" 0 0 600 400 '()))
(define surface (sdl2:get-window-surface window))
(sdl2:fill-rect! surface
                 #f
                 (sdl2:map-rgb
                  (sdl2:surface-format surface)
                  0 128 255))
(sdl2:update-window-surface! window)
(sdl2:delay! 3000)
(sdl2:quit!)
purcell commented 3 years ago

It should be possible to make this work, yes, but I don't know what C-c C-c does for you. There are some ways that emacs libraries launch processes that can prevent them from picking up the locally-set environment, but I'd need to look at the corresponding emacs code to advise.

kenranunderscore commented 3 years ago

Sorry, C-c C-c executes racket-run-module-at-point (see here). If I'm not mistaken this comes down to calling make-process here.

Can I inspect the environment as soon as that is called somehow?

purcell commented 3 years ago

Yeah, I think that make-process there is problematic because the :buffer arg creates a fresh raw buffer which will not inherit the calling buffer's environment. Ideally that would be fixed in racket-mode (hi @greghendershott), perhaps by using (or copying the technique used by) my inheritenv package. You can also likely use inheritenv-add-advice to patch the racket--cmd-open function with advice unilaterally in your own config.

kenranunderscore commented 3 years ago

Thanks for the explanation! I'll leave this open for a while (if that's OK with you) in case Greg might want to respond here. In the meantime I'll try to learn about defadvice and how these things work.

purcell commented 3 years ago

Sure. If Greg's wondering what we're going on about, I've tried to explain the general issue in the inheritenv readme.

kenranunderscore commented 3 years ago

I've tried doing the following:

(use-package! racket-mode
  :defer t
  :hook ((racket-mode . racket-xp-mode)
         (racket-mode . racket-unicode-input-method-enable)
         (racket-repl-mode . racket-unicode-input-method-enable))
  :config
  (use-package! inheritenv)
  (inheritenv-add-advice #'racket--cmd-open))

I also tried evaluating the last line of the above manually before starting a Racket REPL, but I'm probably misunderstanding something. I'll try around a bit myself and see if I can get somewhere. Thanks again :)

greghendershott commented 3 years ago

This buffer is used to talk to Racket Mode's "command server" -- a single Racket process.

It can be created before you C-c C-c to run your Racket program; for example it also supports racket-xp-mode doing check-syntax when you're merely editing a racket-mode buffer. There is only one command server process/buffer, ever, no matter how many times you C-c C-c, or how many distinct racket-repl-mode REPL buffers you may have.

So in terms of the envrc README: I think this isn't really like a temporary buffer; its lifetime is more like the *Help* buffer example?

purcell commented 3 years ago

That makes sense, but when a command causes a program to be run, that should generally be in the context of the calling buffer's process-environment: users can reasonably expect processes to launch with the currently-effective environment, however that process is later used. It's true that the environment inside that process buffer doesn't matter in its own right, but I believe that associating the process with that buffer causes it to be launched with that buffer's environment, which is unlikely to be what the user expected.

greghendershott commented 3 years ago

Oh, sorry, my previous comment was partly "thinking out loud", and, partly worrying that this can't work the way @kenranunderscore might hope. That is, I'll make the change, I just don't know how much it will help. Also I don't use direnv so I don't know folks' expectations.

If the hope is, "a.rkt and b.rkt are in different directories each with their own .envrc, and C-c C-c will be able to run each with its own environment settings", I don't see how that will be able to work. Due to the back end lifetime I described above.

Instead it's going to be, whatever .envrc was associated with the first file you ran -- or even the first file you opened for editing with racket-xp-mode enabled. Whatever caused the back end to start automatically.

:disappointed:


Having said that, there is a racket-start-back-end command that will (re)start the back end server.

I suppose @kenranunderscore could use that in some buffer, to force the back end to (re)start using the env associated with the buffer. That seems kind of kludgy to me. But I guess it's better than nothing?


So: I will make the change to use/preserve process-environment for the back end's process buffer. I'm just not sure the result will be as good as hoped.

greghendershott commented 3 years ago

I opened an issue and pushed a commit to a topic branch, on the racket-mode repo -- auto-link above.

I'll wait awhile to merge it, in case either of you have any feedback/corrections/comments?

purcell commented 3 years ago

If the hope is, "a.rkt and b.rkt are in different directories each with their own .envrc, and C-c C-c will be able to run each with its own environment settings", I don't see how that will be able to work. Due to the back end lifetime I described above.

Instead it's going to be, whatever .envrc was associated with the first file you ran -- or even the first file you opened for editing with racket-xp-mode enabled. Whatever caused the back end to start automatically.

Yeah, that sounds good. In an ideal world, it'd be possible to have different racket versions in different projects, but that's not going to be a major concern for most people.

I think applying the calling buffer's process-environment whenever the backend process is started/restarted is a very reasonable way to go.

greghendershott commented 3 years ago

@purcell Thanks for the feedback!

@kenranunderscore I just merged this. If you get Racket Mode from MELPA, it should update there within a few hours. If you have further questions, feel free to ask here: https://github.com/greghendershott/racket-mode/issues/539

@purcell As always, thanks for MELPA! :smiley:

purcell commented 3 years ago

Nice. I just triggered an immediate rebuild of that package. :+1:

kenranunderscore commented 3 years ago

I will try to test it out soon, sorry for missing the (really quick) progress yesterday. Thank you very much to both of you (@greghendershott), for handling this and for your work in general! This was actually an amazing conversation and experience for me.