@tishlang/tish-format 3.11.0 → 3.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tish-format +0 -0
- package/crates/tish_fmt/Cargo.toml +1 -1
- package/crates/tish_native/src/build.rs +36 -0
- package/crates/tish_runtime/src/fs_ext.rs +30 -1
- package/crates/tish_runtime/src/http_prefork.rs +1 -0
- package/crates/tish_runtime/src/lib.rs +18 -4
- package/crates/tish_runtime/src/process_spawn.rs +1 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
package/bin/tish-format
CHANGED
|
Binary file
|
|
@@ -45,6 +45,13 @@ fn runtime_features_for_cargo(features: &[String]) -> Vec<String> {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/// `[profile.release]` for nested `cargo build` of generated crates.
|
|
48
|
+
///
|
|
49
|
+
/// - default: fat LTO, one codegen unit — smallest and fastest binary, slowest to produce (the
|
|
50
|
+
/// final link is single-threaded and dominates a warm build of a large app).
|
|
51
|
+
/// - `TISH_FAST_NATIVE_BUILD=1`: iteration profile, no LTO, opt-level 1.
|
|
52
|
+
/// - `TISH_NATIVE_THIN_LTO=1`: thin LTO over 8 codegen units — keeps most of fat's size/speed win
|
|
53
|
+
/// while letting rustc parallelise the link. Same trade the GBA path offers via
|
|
54
|
+
/// `TISH_GBA_THIN_LTO`; for desktop apps this is the CI-friendly shipping profile.
|
|
48
55
|
fn nested_release_profile_toml() -> &'static str {
|
|
49
56
|
if std::env::var("TISH_FAST_NATIVE_BUILD").as_deref() == Ok("1") {
|
|
50
57
|
r#"[profile.release]
|
|
@@ -55,6 +62,13 @@ incremental = true
|
|
|
55
62
|
strip = false
|
|
56
63
|
debug = 0
|
|
57
64
|
panic = "abort"
|
|
65
|
+
"#
|
|
66
|
+
} else if std::env::var("TISH_NATIVE_THIN_LTO").as_deref() == Ok("1") {
|
|
67
|
+
r#"[profile.release]
|
|
68
|
+
strip = true
|
|
69
|
+
panic = "abort"
|
|
70
|
+
codegen-units = 8
|
|
71
|
+
lto = "thin"
|
|
58
72
|
"#
|
|
59
73
|
} else {
|
|
60
74
|
r#"[profile.release]
|
|
@@ -1179,6 +1193,7 @@ edition = "2021"
|
|
|
1179
1193
|
|
|
1180
1194
|
#[cfg(test)]
|
|
1181
1195
|
mod tests {
|
|
1196
|
+
use super::nested_release_profile_toml;
|
|
1182
1197
|
use super::runtime_features_for_cargo;
|
|
1183
1198
|
|
|
1184
1199
|
/// The generated ROM crate must inherit the project's pin, or GBA builds float against whatever
|
|
@@ -1219,6 +1234,27 @@ mod tests {
|
|
|
1219
1234
|
let _ = fs::remove_dir_all(&base);
|
|
1220
1235
|
}
|
|
1221
1236
|
|
|
1237
|
+
/// `TISH_NATIVE_THIN_LTO=1` selects thin LTO for native release builds; fast wins over it.
|
|
1238
|
+
#[test]
|
|
1239
|
+
fn native_release_profile_follows_env() {
|
|
1240
|
+
use std::sync::Mutex;
|
|
1241
|
+
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
|
1242
|
+
let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
|
1243
|
+
std::env::remove_var("TISH_FAST_NATIVE_BUILD");
|
|
1244
|
+
std::env::remove_var("TISH_NATIVE_THIN_LTO");
|
|
1245
|
+
assert!(nested_release_profile_toml().contains("lto = \"fat\""));
|
|
1246
|
+
std::env::set_var("TISH_NATIVE_THIN_LTO", "1");
|
|
1247
|
+
let thin = nested_release_profile_toml();
|
|
1248
|
+
assert!(thin.contains("lto = \"thin\"") && thin.contains("codegen-units = 8"));
|
|
1249
|
+
std::env::set_var("TISH_FAST_NATIVE_BUILD", "1");
|
|
1250
|
+
assert!(
|
|
1251
|
+
nested_release_profile_toml().contains("lto = false"),
|
|
1252
|
+
"fast wins over thin"
|
|
1253
|
+
);
|
|
1254
|
+
std::env::remove_var("TISH_FAST_NATIVE_BUILD");
|
|
1255
|
+
std::env::remove_var("TISH_NATIVE_THIN_LTO");
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1222
1258
|
/// `TISH_NATIVE_TARGET_DIR`: unset/empty → None (temp-dir build); set → that dir, created.
|
|
1223
1259
|
#[test]
|
|
1224
1260
|
fn shared_native_target_dir_follows_env() {
|
|
@@ -218,9 +218,25 @@ pub fn copy_file_core(args: &[Value]) -> Result<Value, Value> {
|
|
|
218
218
|
}
|
|
219
219
|
pub fn realpath_core(args: &[Value]) -> Result<Value, Value> {
|
|
220
220
|
std::fs::canonicalize(path_arg(args, 0))
|
|
221
|
-
.map(|p| Value::String(p.to_string_lossy().into()))
|
|
221
|
+
.map(|p| Value::String(strip_verbatim_prefix(&p.to_string_lossy()).into()))
|
|
222
222
|
.map_err(io_err)
|
|
223
223
|
}
|
|
224
|
+
|
|
225
|
+
/// `std::fs::canonicalize` on Windows returns the verbatim form — `\\?\C:\Users\…` for a drive
|
|
226
|
+
/// path, `\\?\UNC\server\share\…` for a network one. Node's `fs.realpath` never does: it hands
|
|
227
|
+
/// back `C:\Users\…` / `\\server\share\…`, and programs treat the result as a display / URL /
|
|
228
|
+
/// comparison string, not just an OS handle (Dune showed `\\?\C:\…` as its workspace title, and a
|
|
229
|
+
/// `?` inside a URL path starts the query string). Match Node. No-op on POSIX and for paths
|
|
230
|
+
/// that carry no prefix.
|
|
231
|
+
pub(crate) fn strip_verbatim_prefix(p: &str) -> String {
|
|
232
|
+
if let Some(rest) = p.strip_prefix("\\\\?\\UNC\\") {
|
|
233
|
+
format!("\\\\{rest}")
|
|
234
|
+
} else if let Some(rest) = p.strip_prefix("\\\\?\\") {
|
|
235
|
+
rest.to_string()
|
|
236
|
+
} else {
|
|
237
|
+
p.to_string()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
224
240
|
pub fn readlink_core(args: &[Value]) -> Result<Value, Value> {
|
|
225
241
|
std::fs::read_link(path_arg(args, 0))
|
|
226
242
|
.map(|p| Value::String(p.to_string_lossy().into()))
|
|
@@ -393,3 +409,16 @@ pub fn constants() -> Value {
|
|
|
393
409
|
m.insert("X_OK".into(), Value::Number(1.0));
|
|
394
410
|
Value::object(m)
|
|
395
411
|
}
|
|
412
|
+
|
|
413
|
+
#[cfg(test)]
|
|
414
|
+
mod verbatim_prefix_tests {
|
|
415
|
+
use super::strip_verbatim_prefix;
|
|
416
|
+
|
|
417
|
+
#[test]
|
|
418
|
+
fn strips_windows_verbatim_prefixes_like_node() {
|
|
419
|
+
assert_eq!(strip_verbatim_prefix("\\\\?\\C:\\Users\\x\\ui"), "C:\\Users\\x\\ui");
|
|
420
|
+
assert_eq!(strip_verbatim_prefix("\\\\?\\UNC\\srv\\share\\d"), "\\\\srv\\share\\d");
|
|
421
|
+
assert_eq!(strip_verbatim_prefix("C:\\Users\\x"), "C:\\Users\\x");
|
|
422
|
+
assert_eq!(strip_verbatim_prefix("/Users/x/ui"), "/Users/x/ui");
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -108,6 +108,7 @@ pub fn spawn_children(n: usize) -> io::Result<Vec<Child>> {
|
|
|
108
108
|
let mut out = Vec::with_capacity(n - 1);
|
|
109
109
|
for i in 1..n {
|
|
110
110
|
let mut cmd = Command::new(&exe);
|
|
111
|
+
crate::no_console_window(&mut cmd);
|
|
111
112
|
cmd.args(&args);
|
|
112
113
|
cmd.env("TISH_WORKER_ID", i.to_string());
|
|
113
114
|
cmd.env("TISH_HTTP_PREFORK", "child");
|
|
@@ -1013,6 +1013,20 @@ pub fn process_cwd(_args: &[Value]) -> Value {
|
|
|
1013
1013
|
}
|
|
1014
1014
|
|
|
1015
1015
|
#[cfg(feature = "process")]
|
|
1016
|
+
/// Windows: a child spawned from a GUI-subsystem parent (a desktop app) gets its own console
|
|
1017
|
+
/// window unless created with CREATE_NO_WINDOW — a packaged app spawning git / node / shell
|
|
1018
|
+
/// helpers at boot flashed a terminal window per child and stole focus. No-op elsewhere.
|
|
1019
|
+
#[cfg(windows)]
|
|
1020
|
+
pub fn no_console_window(cmd: &mut std::process::Command) -> &mut std::process::Command {
|
|
1021
|
+
use std::os::windows::process::CommandExt;
|
|
1022
|
+
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
|
1023
|
+
cmd.creation_flags(CREATE_NO_WINDOW)
|
|
1024
|
+
}
|
|
1025
|
+
#[cfg(not(windows))]
|
|
1026
|
+
pub fn no_console_window(cmd: &mut std::process::Command) -> &mut std::process::Command {
|
|
1027
|
+
cmd
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1016
1030
|
pub fn process_exec(args: &[Value]) -> Value {
|
|
1017
1031
|
use std::process::Command;
|
|
1018
1032
|
let cmd = args
|
|
@@ -1022,7 +1036,7 @@ pub fn process_exec(args: &[Value]) -> Value {
|
|
|
1022
1036
|
if cmd.is_empty() {
|
|
1023
1037
|
return Value::Number(0.0);
|
|
1024
1038
|
}
|
|
1025
|
-
match Command::new("sh").arg("-c").arg(&cmd).status() {
|
|
1039
|
+
match no_console_window(Command::new("sh").arg("-c").arg(&cmd)).status() {
|
|
1026
1040
|
Ok(status) => Value::Number(status.code().unwrap_or(1) as f64),
|
|
1027
1041
|
Err(_) => Value::Number(1.0),
|
|
1028
1042
|
}
|
|
@@ -1046,7 +1060,7 @@ pub fn process_exec_file(args: &[Value]) -> Value {
|
|
|
1046
1060
|
Some(Value::Array(a)) => a.borrow().iter().map(|v| v.to_display_string()).collect(),
|
|
1047
1061
|
_ => Vec::new(),
|
|
1048
1062
|
};
|
|
1049
|
-
match Command::new(&program).args(&argv).status() {
|
|
1063
|
+
match no_console_window(Command::new(&program).args(&argv)).status() {
|
|
1050
1064
|
Ok(status) => Value::Number(status.code().unwrap_or(1) as f64),
|
|
1051
1065
|
Err(_) => Value::Number(1.0),
|
|
1052
1066
|
}
|
|
@@ -1093,7 +1107,7 @@ pub fn process_exec_capture(args: &[Value]) -> Value {
|
|
|
1093
1107
|
if cmd.is_empty() {
|
|
1094
1108
|
return process_capture_obj(0, String::new(), String::new());
|
|
1095
1109
|
}
|
|
1096
|
-
process_capture_result(Command::new("sh").arg("-c").arg(&cmd).output())
|
|
1110
|
+
process_capture_result(no_console_window(Command::new("sh").arg("-c").arg(&cmd)).output())
|
|
1097
1111
|
}
|
|
1098
1112
|
|
|
1099
1113
|
/// `process.execFileCapture(program, [args])` — run a program directly, WITHOUT a shell, capturing
|
|
@@ -1115,7 +1129,7 @@ pub fn process_exec_file_capture(args: &[Value]) -> Value {
|
|
|
1115
1129
|
Some(Value::Array(a)) => a.borrow().iter().map(|v| v.to_display_string()).collect(),
|
|
1116
1130
|
_ => Vec::new(),
|
|
1117
1131
|
};
|
|
1118
|
-
process_capture_result(Command::new(&program).args(&argv).output())
|
|
1132
|
+
process_capture_result(no_console_window(Command::new(&program).args(&argv)).output())
|
|
1119
1133
|
}
|
|
1120
1134
|
|
|
1121
1135
|
#[cfg(all(test, feature = "process", unix))]
|
|
@@ -198,6 +198,7 @@ pub fn process_spawn(args: &[Value]) -> Value {
|
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
let mut cmd = Command::new(program);
|
|
201
|
+
crate::no_console_window(&mut cmd);
|
|
201
202
|
if let Some(Value::Array(a)) = obj_field(opts, "args") {
|
|
202
203
|
for v in a.borrow().iter() {
|
|
203
204
|
cmd.arg(v.to_display_string());
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|