Skip to content

Commit 094534d

Browse files
authored
fix(cli): dev command stderr text overflow on Windows, closes #3995 (#4000)
1 parent cf22f4c commit 094534d

File tree

6 files changed

+109
-16
lines changed

6 files changed

+109
-16
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cli.rs": patch
3+
"cli.js": patch
4+
---
5+
6+
Fixes text overflow on `tauri dev` on Windows.

examples/api/src-tauri/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/api/src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ tauri-build = { path = "../../../core/tauri-build", features = ["isolation"] }
1212
[dependencies]
1313
serde_json = "1.0"
1414
serde = { version = "1.0", features = [ "derive" ] }
15-
tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "http-multipart", "icon-ico", "icon-png", "isolation", "macos-private-api", "reqwest-client", "system-tray", "updater", "global-shortcut"] }
15+
tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "global-shortcut", "http-multipart", "icon-ico", "icon-png", "isolation", "macos-private-api", "reqwest-client", "system-tray", "updater"] }
1616
tiny_http = "0.11"
1717

1818
[features]

tooling/cli/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tooling/cli/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ os_info = "3.2"
5252
semver = "1.0"
5353
regex = "1.5.5"
5454
lazy_static = "1"
55-
libc = "0.2"
5655
terminal_size = "0.1"
5756
unicode-width = "0.1"
5857
tempfile = "3"
@@ -67,10 +66,14 @@ ctrlc = "3.2"
6766

6867
[target."cfg(windows)".dependencies]
6968
encode_unicode = "0.3"
69+
winapi = { version = "0.3", features = ["handleapi", "processenv", "winbase", "wincon", "winnt"] }
7070

7171
[target."cfg(target_os = \"linux\")".build-dependencies]
7272
heck = "0.4"
7373

74+
[target."cfg(unix)".dependencies]
75+
libc = "0.2"
76+
7477
[build-dependencies]
7578
tauri-utils = { version = "1.0.0-rc.0", features = [ "schema", "isolation" ], path = "../../core/tauri-utils" }
7679
schemars = { version = "0.8", features = [ "url" ] }

tooling/cli/src/dev.rs

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::{
2323
env::set_current_dir,
2424
ffi::OsStr,
2525
fs::FileType,
26-
io::BufReader,
26+
io::{BufReader, Write},
2727
path::{Path, PathBuf},
2828
process::{exit, Command},
2929
sync::{
@@ -320,7 +320,6 @@ fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
320320
default_gitignore.push(".gitignore");
321321
if !default_gitignore.exists() {
322322
if let Ok(mut file) = std::fs::File::create(default_gitignore.clone()) {
323-
use std::io::Write;
324323
let _ = file.write_all(TAURI_DEV_WATCHER_GITIGNORE);
325324
}
326325
}
@@ -422,7 +421,7 @@ fn kill_before_dev_process() {
422421

423422
if !kill_children_script_path.exists() {
424423
if let Ok(mut file) = std::fs::File::create(&kill_children_script_path) {
425-
use std::{io::Write, os::unix::fs::PermissionsExt};
424+
use std::os::unix::fs::PermissionsExt;
426425
let _ = file.write_all(KILL_CHILDREN_SCRIPT);
427426
let mut permissions = file.metadata().unwrap().permissions();
428427
permissions.set_mode(0o770);
@@ -448,9 +447,15 @@ fn start_app(
448447
command
449448
.env(
450449
"CARGO_TERM_PROGRESS_WIDTH",
451-
terminal_size::terminal_size()
452-
.map(|(w, _)| w.0)
453-
.unwrap_or(80)
450+
terminal::stderr_width()
451+
.map(|width| {
452+
if cfg!(windows) {
453+
std::cmp::min(60, width)
454+
} else {
455+
width
456+
}
457+
})
458+
.unwrap_or(if cfg!(windows) { 60 } else { 80 })
454459
.to_string(),
455460
)
456461
.env("CARGO_TERM_PROGRESS_WHEN", "always");
@@ -506,19 +511,18 @@ fn start_app(
506511
std::thread::spawn(move || {
507512
let mut buf = Vec::new();
508513
let mut lines = stderr_lines_.lock().unwrap();
514+
let mut io_stderr = std::io::stderr();
509515
loop {
510516
buf.clear();
511517
match tauri_utils::io::read_line(&mut stderr, &mut buf) {
512518
Ok(s) if s == 0 => break,
513519
_ => (),
514520
}
515-
let line = String::from_utf8_lossy(&buf).into_owned();
516-
if line.ends_with('\r') {
517-
eprint!("{}", line);
518-
} else {
519-
eprintln!("{}", line);
521+
let _ = io_stderr.write_all(&buf);
522+
if !buf.ends_with(&[b'\r']) {
523+
let _ = io_stderr.write_all(b"\n");
520524
}
521-
lines.push(line);
525+
lines.push(String::from_utf8_lossy(&buf).into_owned());
522526
}
523527
});
524528

@@ -554,3 +558,82 @@ fn start_app(
554558

555559
Ok(child_arc)
556560
}
561+
562+
// taken from https://github.com/rust-lang/cargo/blob/78b10d4e611ab0721fc3aeaf0edd5dd8f4fdc372/src/cargo/core/shell.rs#L514
563+
#[cfg(unix)]
564+
mod terminal {
565+
use std::mem;
566+
567+
pub fn stderr_width() -> Option<usize> {
568+
unsafe {
569+
let mut winsize: libc::winsize = mem::zeroed();
570+
// The .into() here is needed for FreeBSD which defines TIOCGWINSZ
571+
// as c_uint but ioctl wants c_ulong.
572+
#[allow(clippy::useless_conversion)]
573+
if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 {
574+
return None;
575+
}
576+
if winsize.ws_col > 0 {
577+
Some(winsize.ws_col as usize)
578+
} else {
579+
None
580+
}
581+
}
582+
}
583+
}
584+
585+
// taken from https://github.com/rust-lang/cargo/blob/78b10d4e611ab0721fc3aeaf0edd5dd8f4fdc372/src/cargo/core/shell.rs#L543
586+
#[cfg(windows)]
587+
mod terminal {
588+
use std::{cmp, mem, ptr};
589+
use winapi::um::fileapi::*;
590+
use winapi::um::handleapi::*;
591+
use winapi::um::processenv::*;
592+
use winapi::um::winbase::*;
593+
use winapi::um::wincon::*;
594+
use winapi::um::winnt::*;
595+
596+
pub fn stderr_width() -> Option<usize> {
597+
unsafe {
598+
let stdout = GetStdHandle(STD_ERROR_HANDLE);
599+
let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
600+
if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 {
601+
return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
602+
}
603+
604+
// On mintty/msys/cygwin based terminals, the above fails with
605+
// INVALID_HANDLE_VALUE. Use an alternate method which works
606+
// in that case as well.
607+
let h = CreateFileA(
608+
"CONOUT$\0".as_ptr() as *const CHAR,
609+
GENERIC_READ | GENERIC_WRITE,
610+
FILE_SHARE_READ | FILE_SHARE_WRITE,
611+
ptr::null_mut(),
612+
OPEN_EXISTING,
613+
0,
614+
ptr::null_mut(),
615+
);
616+
if h == INVALID_HANDLE_VALUE {
617+
return None;
618+
}
619+
620+
let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
621+
let rc = GetConsoleScreenBufferInfo(h, &mut csbi);
622+
CloseHandle(h);
623+
if rc != 0 {
624+
let width = (csbi.srWindow.Right - csbi.srWindow.Left) as usize;
625+
// Unfortunately cygwin/mintty does not set the size of the
626+
// backing console to match the actual window size. This
627+
// always reports a size of 80 or 120 (not sure what
628+
// determines that). Use a conservative max of 60 which should
629+
// work in most circumstances. ConEmu does some magic to
630+
// resize the console correctly, but there's no reasonable way
631+
// to detect which kind of terminal we are running in, or if
632+
// GetConsoleScreenBufferInfo returns accurate information.
633+
return Some(cmp::min(60, width));
634+
}
635+
636+
None
637+
}
638+
}
639+
}

0 commit comments

Comments
 (0)