mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
fix(app): Stop console tools from opening cmd windows on Windows (#20)
Release builds use the GUI subsystem, so a console child spawned without CREATE_NO_WINDOW gets its own console window. git, gh, docker, builds and run configurations already set the flag; language servers, SDK and tool probes, debug adapters, tool downloads, coverage export and perf did not. Add ide_core::process::no_window and use it at every remaining spawn. Fixes #16
This commit is contained in:
@@ -10,6 +10,7 @@ pub mod event;
|
||||
pub mod ids;
|
||||
pub mod ignored;
|
||||
pub mod ipc_path;
|
||||
pub mod process;
|
||||
pub mod project_dir;
|
||||
pub mod settings;
|
||||
pub mod text;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//! Child-process helpers shared by every crate that spawns tools.
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
/// Keeps a console child (java, node, curl, a language server, ...) from
|
||||
/// opening its own console window on Windows. Release builds use the GUI
|
||||
/// subsystem, so without this every spawn flashes a `cmd` window. A no-op
|
||||
/// elsewhere. For a `tokio::process::Command`, pass `cmd.as_std_mut()`.
|
||||
pub fn no_window(cmd: &mut Command) -> &mut Command {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
cmd
|
||||
}
|
||||
@@ -204,7 +204,7 @@ pub fn collect(spec: &CoverageReportSpec) -> IdeResult<CoverageSuite> {
|
||||
CoverageReportSpec::Exported { runner, argv, cwd, env, lcov } => {
|
||||
let (program, args) =
|
||||
argv.split_first().ok_or_else(|| IdeError::invalid("empty coverage export command"))?;
|
||||
let out = Command::new(program)
|
||||
let out = ide_core::process::no_window(&mut Command::new(program))
|
||||
.args(args)
|
||||
.current_dir(cwd)
|
||||
.envs(env)
|
||||
|
||||
@@ -233,7 +233,9 @@ pub async fn prepare_debugpy(spec: &mut AdapterSpec) -> IdeResult<()> {
|
||||
let Some(python) = tokio::task::spawn_blocking(move || resolve_binary(&command, &cwd)).await.ok().flatten() else {
|
||||
return Ok(()); // Reported as a missing adapter when it is started.
|
||||
};
|
||||
let probe = Command::new(&python)
|
||||
let mut probe = Command::new(&python);
|
||||
ide_core::process::no_window(probe.as_std_mut());
|
||||
let probe = probe
|
||||
.args(["-c", "import debugpy.adapter"])
|
||||
.current_dir(&spec.cwd)
|
||||
.stdin(Stdio::null())
|
||||
@@ -335,6 +337,7 @@ impl AdapterLauncher for ProcessLauncher {
|
||||
None => spec.args.clone(),
|
||||
};
|
||||
let mut cmd = Command::new(&binary);
|
||||
ide_core::process::no_window(cmd.as_std_mut());
|
||||
cmd.args(&args)
|
||||
.envs(spec.env.iter().map(|(k, v)| (k, v)))
|
||||
.current_dir(&spec.cwd)
|
||||
|
||||
@@ -55,6 +55,7 @@ pub async fn build(
|
||||
) -> IdeResult<PathBuf> {
|
||||
let cargo = find_binary("cargo", cwd).ok_or_else(|| IdeError::unavailable("`cargo` not found on PATH"))?;
|
||||
let mut cmd = tokio::process::Command::new(cargo);
|
||||
ide_core::process::no_window(cmd.as_std_mut());
|
||||
cmd.args(args)
|
||||
.arg("--message-format=json-render-diagnostics")
|
||||
.current_dir(cwd)
|
||||
|
||||
@@ -390,6 +390,7 @@ pub(crate) fn run_in_terminal(s: &Arc<Session>, args: &Value) -> Result<Value, S
|
||||
return Err("runInTerminal without args".into());
|
||||
};
|
||||
let mut cmd = tokio::process::Command::new(program);
|
||||
ide_core::process::no_window(cmd.as_std_mut());
|
||||
cmd.args(rest).stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).kill_on_drop(true);
|
||||
if let Some(cwd) = args.get("cwd").and_then(Value::as_str).filter(|c| !c.is_empty()) {
|
||||
cmd.current_dir(cwd);
|
||||
|
||||
@@ -241,6 +241,7 @@ impl Peer {
|
||||
) -> std::io::Result<Self> {
|
||||
use std::process::Stdio;
|
||||
let mut cmd = tokio::process::Command::new(command);
|
||||
ide_core::process::no_window(cmd.as_std_mut());
|
||||
cmd.args(args)
|
||||
.current_dir(cwd)
|
||||
.envs(env.iter().map(|(k, v)| (k.as_str(), v.as_str())))
|
||||
|
||||
@@ -72,7 +72,7 @@ fn resolve_rustup_proxy(command: &str, binary: PathBuf) -> PathBuf {
|
||||
if !is_proxy || command.contains('/') {
|
||||
return binary;
|
||||
}
|
||||
let works = std::process::Command::new(&binary)
|
||||
let works = ide_core::process::no_window(&mut std::process::Command::new(&binary))
|
||||
.arg("--version")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
|
||||
@@ -161,7 +161,7 @@ pub fn collect(spec: &ProfileSpec) -> IdeResult<Profile> {
|
||||
if !data.is_file() {
|
||||
return Err(nothing_recorded("perf wrote no data"));
|
||||
}
|
||||
let out = Command::new(perf)
|
||||
let out = ide_core::process::no_window(&mut Command::new(perf))
|
||||
.args(["script", "-i"])
|
||||
.arg(data.as_ref() as &Path)
|
||||
.current_dir(cwd)
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn find_tool(name: &str, config: &RunConfig, dir: &Path, root: &Path) -> Opt
|
||||
pub fn probe(argv: &[String], dir: &Path, config: &RunConfig) -> bool {
|
||||
let Some((program, args)) = argv.split_first() else { return false };
|
||||
let program = resolve(program, config);
|
||||
Command::new(program)
|
||||
ide_core::process::no_window(&mut Command::new(program))
|
||||
.args(args)
|
||||
.current_dir(dir)
|
||||
.envs(&config.env)
|
||||
|
||||
@@ -44,7 +44,7 @@ pub(crate) fn which(name: &str) -> Option<PathBuf> {
|
||||
/// Runs `program args` and returns trimmed stdout (stderr when stdout is empty)
|
||||
/// on success; `None` on failure or after `timeout`.
|
||||
pub(crate) fn query(program: &Path, args: &[&str], timeout: Duration) -> Option<String> {
|
||||
let mut child = Command::new(program)
|
||||
let mut child = ide_core::process::no_window(&mut Command::new(program))
|
||||
.args(args)
|
||||
.env("PATH", search_path())
|
||||
.stdin(Stdio::null())
|
||||
@@ -96,6 +96,7 @@ pub(crate) fn stream(
|
||||
on_line: &dyn Fn(String),
|
||||
) -> IdeResult<String> {
|
||||
let mut cmd = Command::new(program);
|
||||
ide_core::process::no_window(&mut cmd);
|
||||
cmd.args(args).env("PATH", search_path()).stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
cmd.envs(env.iter().map(|(k, v)| (k, v)));
|
||||
if let Some(cwd) = cwd {
|
||||
|
||||
@@ -113,7 +113,7 @@ fn extract_zip(archive: &Path, dest: &Path) -> IdeResult<()> {
|
||||
/// `.tar.xz` goes through the system `tar` (bsdtar on macOS/Windows, GNU tar + xz on Linux).
|
||||
fn extract_tar_xz(archive: &Path, dest: &Path) -> IdeResult<()> {
|
||||
let tar = which::which("tar").map_err(|_| IdeError::unavailable("`tar` is required to unpack .tar.xz archives"))?;
|
||||
let out = std::process::Command::new(tar)
|
||||
let out = ide_core::process::no_window(&mut std::process::Command::new(tar))
|
||||
.arg("-xJf")
|
||||
.arg(archive)
|
||||
.arg("-C")
|
||||
|
||||
@@ -311,7 +311,7 @@ impl Tools {
|
||||
|
||||
fn command(&self, program: impl AsRef<std::ffi::OsStr>) -> Command {
|
||||
let mut cmd = Command::new(program);
|
||||
cmd.env("PATH", &self.child_path);
|
||||
ide_core::process::no_window(&mut cmd).env("PATH", &self.child_path);
|
||||
cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn run_streamed(cmd: Command, cancel: &CancellationToken, emit: Emit) -> Ide
|
||||
/// Like [`run_streamed`] but returns `(success, output, status text)` instead of failing on a non-zero exit.
|
||||
pub fn run_collect(mut cmd: Command, cancel: &CancellationToken, emit: Emit) -> IdeResult<(bool, String, String)> {
|
||||
emit(InstallEvent::Line { text: format!("$ {}", describe(&cmd)) });
|
||||
cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
ide_core::process::no_window(&mut cmd).stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
let mut child = cmd.spawn().map_err(|e| IdeError::unavailable(format!("cannot run {}: {e}", describe(&cmd))))?;
|
||||
let (tx, rx) = mpsc::channel::<String>();
|
||||
let pipes: [Option<Box<dyn Read + Send>>; 2] = [
|
||||
@@ -94,7 +94,7 @@ pub fn run_collect(mut cmd: Command, cancel: &CancellationToken, emit: Emit) ->
|
||||
|
||||
/// Runs `cmd` to completion within `timeout`; returns (success, stdout + stderr).
|
||||
pub fn run_quiet(mut cmd: Command, timeout: Duration) -> Option<(bool, String)> {
|
||||
cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
ide_core::process::no_window(&mut cmd).stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
let mut child = cmd.spawn().ok()?;
|
||||
let mut stdout = child.stdout.take()?;
|
||||
let mut stderr = child.stderr.take()?;
|
||||
@@ -161,6 +161,7 @@ impl Http {
|
||||
|
||||
fn base(&self, url: &str) -> Command {
|
||||
let mut cmd = Command::new(&self.curl);
|
||||
ide_core::process::no_window(&mut cmd);
|
||||
cmd.args(["-fsSL", "--retry", "2", "--connect-timeout", "20", "-A", "openidea"]);
|
||||
if url.starts_with("https://api.github.com/")
|
||||
&& let Ok(token) = std::env::var("GITHUB_TOKEN").or_else(|_| std::env::var("GH_TOKEN"))
|
||||
|
||||
Reference in New Issue
Block a user