bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


From: Siyuan Chen
Subject: bug#77430: compilation-start should remember shell-file-name
Date: Fri, 4 Apr 2025 16:15:06 +0800

> 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.

Thank you!

On Tue, Apr 1, 2025 at 11:57 PM Eli Zaretskii <eliz@gnu.org> wrote:
> Cc: 77430@debbugs.gnu.org
> Date: Tue, 01 Apr 2025 18:50:36 +0300
> From: Eli Zaretskii <eliz@gnu.org>
>
> > From: Siyuan Chen <chansey97@gmail.com>
> > Date: Tue, 1 Apr 2025 23:13:46 +0800
> > Cc: 77430@debbugs.gnu.org
> >
> > >  Why cannot your my/compile function do this:
> > > 
> > >  (setq-local shell-file-name "C:/msys64/usr/bin/bash")
> > > 
> > > instead of let-binding it?
> >
> > This is only for the current file buffer.
> >
> > For example, if you focus on a project file buffer (e.g. test.hs) and M-x my/compile, it only sets
> > `shell-file-name' for that file buffer. However, the `compile` actually using the `shell-file-name' belongs to the
> > *compilation* buffer.
> >
> > ```
> > (defun compilation-start (command &optional mode name-function highlight-regexp
> >                                   continue)
> >   ...
> >   (with-current-buffer outbuf ; <--- The `outbuf' is the *compilation* buffer
> >     ...
> >     (comint-exec
> >      outbuf (compilation--downcase-mode-name mode-name)
> >      shell-file-name ; <--- use the buffer local variable shell-file-name in the *compilation* buffer
> >      nil `(,shell-command-switch ,command))
> >     ...
> >     )
> > ...
> > )
> > ```
> >
> > So the following code doesn't work:
> >
> > ```
> > (defun my/compile ()
> >   (interactive)
> >   (let (
> >         (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")
> >
> >         )
> >     (setq-local shell-file-name "C:/msys64/usr/bin/bash")
> >     (compile "ls")
> >     ))
> > ```
> >
> > M-x my/compile
> >
> > Compilation exited abnormally.
>
> 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?

Alternatively, how about making a Windows batch file, which would
invoke the compilation command via Bash.  So you compile command would
look like this:

  M-x compile RET mycomp ls RET

where mycomp.bat is a batch file which does

  @echo off
  C:\msys64\usr\bin\bash --login -c %*

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.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]