msys2 / msys2.github.io

The MSYS2 homepage
https://www.msys2.org
Other
2.3k stars 302 forks source link

Document usage as the Terminal within JetBrains IDEs - "Process Terminal local Is Running" error #207

Open jacob-pro opened 2 years ago

jacob-pro commented 2 years ago

So I'm trying to use MSYS as my terminal within IntelliJ, PyCharm and CLion, and thought I would open this issue here in case anyone else has similar difficulties:

I started off copying the command documented here for Windows Terminal: https://www.msys2.org/docs/terminals/, i.e. invoking msys2_shell.cmd.

image

This does work, however it leads to an incorrect warning when closing the terminal, the IDE incorrectly thinks that 'Process Terminal local Is Running' when it is not:

image

I'm 99% certain the reason for this (after doing some testing) is because the IDE is looking to see if the terminal process has any child processes running. So it thinks msys2_shell.cmd is the terminal process, and that bash.exe is an active child process:

image

I was able to work around this by setting the ide to invoke "C:\msys64\usr\bin\bash.exe" --login directly:

image

However this requires manually setting all the environment variables that would have been applied by the msys2_shell.cmd script.

Unfortunately the JetBrains Terminal environment variables are scoped locally to the individual project, there is no way to set them globally - so I have had to set them at the Windows level - which isn't ideal :(

image

I now have MSYS working as my terminal in CLion/IntelliJ, and it correctly detects if there is a process running or not when closing the terminal tab.

lazka commented 2 years ago

What about C:\msys64\usr\bin\env.exe MSYSTEM=MINGW64 CHERE_INVOKING=1 /usr/bin/bash.exe --login ?

jacob-pro commented 2 years ago

Thanks @lazka , but unfortunately I still get incorrect "Process Terminal Local Is Running" prompt in my IDE, I believe this is because it is still a child process:

image

I think this is because env doesn't behave the same way on Windows, I can see the Linux version tries to use execvp which should replace the running process - but clearly the MSYS/Cygwin implementation spawns a child process instead?

jacob-pro commented 2 years ago

I guess the possible solutions to this whole issue could be:

  1. We find a way to do proper process replacement to invoke bash.exe within a single process (unlikely?)
  2. JetBrains adds an option to set Terminal environment variables globally for all projects.
  3. JetBrains adds an option to check if subprocesses are running based on an extra step down the process tree (ugly?)
  4. We patch bash.exe to support setting these environment variables via its CLI interface?

I had a go at #1 myself but haven't been successful...

The following code seems to launch when I open the exe directly, but opening it in within JetBrains causes STATUS_ACCESS_VIOLATION errors - not sure if this is my fault, or I have a feeling that process replacement is really not properly supported on Windows:

use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null;
use windows::Win32::Foundation::GetLastError;

extern "C" {
    fn _wexecve(
        cmdname: *const u16,
        argv: *const *const u16,
        argp: *const *const u16,
    ) -> cty::intptr_t;
}

fn execve(cmdname: &str, parameters: &[String], environment: &[String]) {
    let cmdname = wide_string(cmdname);
    let parameters = parameters.iter().map(wide_string).collect::<Vec<_>>();
    let p_ref = parameters
        .iter()
        .map(|p| p.as_ptr())
        .chain(once(null()))
        .collect::<Vec<_>>();
    let environment = environment.iter().map(wide_string).collect::<Vec<_>>();
    let env_ref = environment
        .iter()
        .map(|p| p.as_ptr())
        .chain(once(null()))
        .collect::<Vec<_>>();
    let retval = unsafe { _wexecve(cmdname.as_ptr(), p_ref.as_ptr(), env_ref.as_ptr()) };
    let last_error = unsafe { GetLastError() };
    println!("retval {}, error: {}", retval, last_error.to_hresult().message());
}

fn wide_string<T: AsRef<str>>(value: T) -> Vec<u16> {
    OsStr::new(value.as_ref())
        .encode_wide()
        .chain(once(0))
        .collect()
}

fn main() {
    execve(
        r"C:\msys64\usr\bin\bash.exe",
        &vec![r"C:\msys64\usr\bin\bash.exe".to_string(), "-l".to_string()],
        &vec!["MSYSTEM=MINGW64".to_string(), "MSYS2_PATH_TYPE=inherit".to_string()],
    );
}