Subject: bug#77430: closed (Re: bug#77430: compilation-start should remember shell-file-name)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 19 Apr 2025 13:50:04 +0000

Your bug report

#77430: compilation-start should remember shell-file-name

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
77430: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=77430
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#77430: compilation-start should remember shell-file-name
From: Eli Zaretskii <[email-masked]>
To: Siyuan Chen <[email-masked]>
Cc: [email-masked]
Date: Sat, 19 Apr 2025 16:48:57 +0300
List-Id: <debbugs-submit.debbugs.gnu.org>

> From: Siyuan Chen <[email-masked]>
> Date: Fri, 4 Apr 2025 16:15:06 +0800
> Cc: [email-masked]
> 
> > Sorry, I don't understand: are you saying that you cannot arrange for
> the buffer-local value of shell-file-name to point to Bash and stay
> that way for the subsequent commands?  If you need to use
> with-current-buffer or something similar, it should be easy no?
> 
> The original problem is not about `compile' but `recompile', i.e. press 'g' in the *compilation* buffer. The
> *compilation* buffer doesn't know anything about the last shell-file-name, so it uses cmdproxy by default. 
> 
> Anyway, this is no longer a problem now, see below.
> 
> > I think this is the cleanest solution for problems like this on
> Windows, since you don't really want to override shell-file-name, you
> just want to invoke the commands via Bash.
> 
> Your method really inspired me. Thank you so much! 
> 
> Based on it, I've redesigned my compilation workflow. Basically, whenever users invoke
> `sc-haskell-projectile-stack-compile' (a tailored compile command integrated with projectile), it firstly
> generates two files in the Haskell project folder: 
> 
> .emacs_stack_launch.bat
> ```
> set MSYSTEM=MINGW32
> "c:/Users/Chansey/AppData/Local/Programs/stack/x86_64-windows/msys2-20230526/usr/bin/bash" --login
> "e:/my-haskell-project/.emacs_stack_launch_script.sh" %*
> ```
> 
> .emacs_stack_launch_script.sh
> ```
> export STACK_ROOT=/c/sr
> export
> PATH=/c/env/haskell/stack/v2.15.1:/c/Users/Chansey/AppData/Roaming/local/bin:/c/PROGRA~1/Git/cmd:$PATH
> 
> $*
> ```
> 
> then it calls (compile ".emacs_stack_launch.bat stack build").
> 
> This method is working great so far. Plus, the file generation brings several benefits:
> 
> 1. We can set local variables in .dir-locals (e.g. msys2-path, environment-variables) per project for
> fine-grained control over compilation.
> 
> 2. No need to tailored `recompile'. It just uses the last generated files. So it works pretty well when users
> press 'g' in the *compilation* buffer.
> 
> 3. Support all the compile commands and opts, like stack build, stack run, stack build --test, etc.
> 
> 4. Easy debugging with .emacs_stack_launch.bat and .emacs_stack_launch_script.sh. 
> 
> The code is something like this:
> 
> ```
> (defun sc-haskell-projectile-stack--run-project-cmd (command)
>   (let* ((default-directory (projectile-compilation-dir))
>          (command (projectile-read-command
>                    "Compile command: "
>                    (or projectile-project-compilation-cmd
>                        command))))
>     (sc-haskell-projectile-stack--gen-launch-file)
>     (sc-haskell-projectile-stack--gen-launch-script-file)
>     (compile (format "%s %s"
>                      ".emacs_stack_launch.bat"
>                      command)
>              projectile-compile-use-comint-mode)))
> 
> (defun sc-haskell-projectile-stack-compile ()
>   (sc-haskell-projectile-stack--run-project-cmd "stack build"))
> 
> (defun sc-haskell-projectile-stack-test ()
>   (sc-haskell-projectile-stack--run-project-cmd "stack build --test"))
> 
> (defun sc-haskell-projectile-stack-run ()
>   (sc-haskell-projectile-stack--run-project-cmd "stack run"))
> ```
> 
> Additionally, I also created a projectile-based shell command, which has to be different from projectile-based
> compile command.
> 
> ```
> (defun sc-haskell-projectile-stack-shell ()
>   (interactive)
>   (let ((current-prefix-arg 4)
>         (explicit-shell-file-name
>          (expand-file-name "usr/bin/bash" sc-haskell-projectile-stack-msys2-path))
>         (explicit-bash-args
>          '("--login" "--noediting" "-i")))
>     (sc-haskell-projectile-stack--gen-msys2-bashrc-init-file)
>     (with-environment-variables (("MSYSTEM" "MINGW32"))
>       (call-interactively 'projectile-run-shell))))
> ```
> 
> Unlike the previous method, this one still rebinds explicit-shell-file-name, otherwise we have to call "bash -
> -login --noediting -i" in the .emacs_stack_launch_script.sh, which would invoke bash twice — something I'd
> rather avoid (it still can work though).
> 
> My solution is to add a hook in .bashrc
> 
> ```
> if [ -f $HOME/.bashrc_init.sh ]; then
>     . $HOME/.bashrc_init.sh
>     rm $HOME/.bashrc_init.sh
> fi
> ```
> 
> Basically, whenever users invoke `sc-haskell-projectile-stack-shell, it generates .bashrc_init.sh in the msys2
> home USER folder. The commands in .bashrc_init.sh are similar to those in emacs_stack_launch_script and
> can, of course, be configured via .dir-locals.el. It might seem a bit unusual, but it works well so far. 
> 
> Just like to share my current design here — any suggestions would be greatly appreciated.

Thanks.

I guess we can close this bug now.

Subject: compilation-start should remember shell-file-name
From: Siyuan Chen <[email-masked]>
To: [email-masked]
Date: Tue, 1 Apr 2025 21:42:21 +0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Background:

Recently, I've been setting up a Haskell compilation environment with Emacs
on Windows. The project depends on diagrams-cairo, which depends on GTK+,
so I have to build it via msys64 with MINGW. It works well in a non Emacs
environment, so I'd like to adapt it to the Emacs environment.

Everything is OK, except when I press 'g' in the *compilation* buffer.

To simplify the problem, below only the minimal test is provided.

Reproduce steps:

1. Create a file test.el in E:/tmp

2. Open that file and paste the following code
```
(defun my/compile ()
  (interactive)
  (let ((shell-file-name "C:/msys64/usr/bin/bash")
        (compilation-environment
"PATH=/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl")
        )
    (compile "ls")
    ))
```
NOTE: You can think of "ls" as a compilation command such as "stack build"
in Haskell.

3. Evaluate Last S-expression.

4. M-x my/compile

The *compilation* buffer lists the files normally.

5. Press 'g' in the *compilation* buffer, i.e. `recompile'.

The expected behavior: List the files again.

The actual behavior: Compilation exited abnormally.

The problem is that `recompile' does not realize that `shell-file-name' has
been updated to bash. It still uses the default cmdproxy.

I have created a patch to workaround the issue. It remembers the original
`shell-file-name' in `compilation-start', so we can use it when we
`recompile'.

Thanks.

Best regards,
Siyuan Chen
From 027e2f57af984478ef2cc09bf09628b1f65dc5dc Mon Sep 17 00:00:00 2001
From: Siyuan Chen <[email-masked]>
Date: Tue, 1 Apr 2025 21:06:01 +0800
Subject: [PATCH] Remember the original shell-file-name, so we can use it when
 we recompile.

---
 lisp/progmodes/compile.el | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index 1ca58b3ac7d..13f20311a83 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -1973,6 +1973,7 @@ Returns the compilation buffer created."
 	    (replace-regexp-in-string "-mode\\'" "" (symbol-name mode))))
 	 (thisdir default-directory)
 	 (thisenv compilation-environment)
+         (thishell shell-file-name)
          (buffer-path (and (local-variable-p 'exec-path) exec-path))
          (buffer-env (and (local-variable-p 'process-environment)
                           process-environment))
@@ -2048,6 +2049,7 @@ Returns the compilation buffer created."
         ;; NB: must be done after (funcall mode) as that resets local variables
         (setq-local compilation-directory thisdir)
         (setq-local compilation-environment thisenv)
+        (setq-local shell-file-name thishell)
         (if buffer-path
             (setq-local exec-path buffer-path)
           (kill-local-variable 'exec-path))
-- 
2.32.0.windows.2