@tishlang/tish-format 1.0.12 → 2.0.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.
Files changed (189) hide show
  1. package/Cargo.toml +51 -0
  2. package/LICENSE +13 -0
  3. package/bin/tish-format +0 -0
  4. package/crates/js_to_tish/Cargo.toml +11 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +55 -0
  7. package/crates/js_to_tish/src/lib.rs +11 -0
  8. package/crates/js_to_tish/src/span_util.rs +35 -0
  9. package/crates/js_to_tish/src/transform/expr.rs +611 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +503 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +62 -0
  13. package/crates/tish/build.rs +21 -0
  14. package/crates/tish/src/cargo_native_registry.rs +32 -0
  15. package/crates/tish/src/cli_help.rs +576 -0
  16. package/crates/tish/src/main.rs +853 -0
  17. package/crates/tish/src/repl_completion.rs +199 -0
  18. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  19. package/crates/tish/tests/error_source_location.rs +36 -0
  20. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  21. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  22. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  23. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  24. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  25. package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
  26. package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
  27. package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
  28. package/crates/tish/tests/integration_test.rs +1406 -0
  29. package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
  30. package/crates/tish/tests/shortcircuit.rs +65 -0
  31. package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
  32. package/crates/tish/tests/tty_capability.rs +43 -0
  33. package/crates/tish_ast/Cargo.toml +9 -0
  34. package/crates/tish_ast/src/ast.rs +649 -0
  35. package/crates/tish_ast/src/lib.rs +5 -0
  36. package/crates/tish_build_utils/Cargo.toml +11 -0
  37. package/crates/tish_build_utils/src/lib.rs +577 -0
  38. package/crates/tish_builtins/Cargo.toml +22 -0
  39. package/crates/tish_builtins/src/array.rs +803 -0
  40. package/crates/tish_builtins/src/collections.rs +481 -0
  41. package/crates/tish_builtins/src/construct.rs +199 -0
  42. package/crates/tish_builtins/src/date.rs +538 -0
  43. package/crates/tish_builtins/src/globals.rs +293 -0
  44. package/crates/tish_builtins/src/helpers.rs +35 -0
  45. package/crates/tish_builtins/src/iterator.rs +129 -0
  46. package/crates/tish_builtins/src/lib.rs +21 -0
  47. package/crates/tish_builtins/src/math.rs +89 -0
  48. package/crates/tish_builtins/src/number.rs +96 -0
  49. package/crates/tish_builtins/src/object.rs +36 -0
  50. package/crates/tish_builtins/src/string.rs +646 -0
  51. package/crates/tish_builtins/src/symbol.rs +83 -0
  52. package/crates/tish_builtins/src/typedarrays.rs +298 -0
  53. package/crates/tish_bytecode/Cargo.toml +17 -0
  54. package/crates/tish_bytecode/src/chunk.rs +164 -0
  55. package/crates/tish_bytecode/src/compiler.rs +2604 -0
  56. package/crates/tish_bytecode/src/encoding.rs +102 -0
  57. package/crates/tish_bytecode/src/lib.rs +20 -0
  58. package/crates/tish_bytecode/src/opcode.rs +185 -0
  59. package/crates/tish_bytecode/src/peephole.rs +189 -0
  60. package/crates/tish_bytecode/src/serialize.rs +193 -0
  61. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  62. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  63. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  64. package/crates/tish_compile/Cargo.toml +27 -0
  65. package/crates/tish_compile/src/check.rs +774 -0
  66. package/crates/tish_compile/src/codegen.rs +7317 -0
  67. package/crates/tish_compile/src/infer.rs +1681 -0
  68. package/crates/tish_compile/src/lib.rs +206 -0
  69. package/crates/tish_compile/src/resolve.rs +1951 -0
  70. package/crates/tish_compile/src/types.rs +605 -0
  71. package/crates/tish_compile_js/Cargo.toml +18 -0
  72. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  73. package/crates/tish_compile_js/src/codegen.rs +938 -0
  74. package/crates/tish_compile_js/src/error.rs +20 -0
  75. package/crates/tish_compile_js/src/lib.rs +26 -0
  76. package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
  77. package/crates/tish_compiler_wasm/Cargo.toml +21 -0
  78. package/crates/tish_compiler_wasm/src/lib.rs +57 -0
  79. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
  80. package/crates/tish_core/Cargo.toml +32 -0
  81. package/crates/tish_core/src/console_style.rs +170 -0
  82. package/crates/tish_core/src/json.rs +430 -0
  83. package/crates/tish_core/src/lib.rs +20 -0
  84. package/crates/tish_core/src/macros.rs +36 -0
  85. package/crates/tish_core/src/shape.rs +85 -0
  86. package/crates/tish_core/src/uri.rs +118 -0
  87. package/crates/tish_core/src/value.rs +1350 -0
  88. package/crates/tish_core/src/vmref.rs +183 -0
  89. package/crates/tish_cranelift/Cargo.toml +19 -0
  90. package/crates/tish_cranelift/src/lib.rs +43 -0
  91. package/crates/tish_cranelift/src/link.rs +130 -0
  92. package/crates/tish_cranelift/src/lower.rs +85 -0
  93. package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
  94. package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
  95. package/crates/tish_eval/Cargo.toml +51 -0
  96. package/crates/tish_eval/src/eval.rs +4265 -0
  97. package/crates/tish_eval/src/http.rs +191 -0
  98. package/crates/tish_eval/src/lib.rs +99 -0
  99. package/crates/tish_eval/src/natives.rs +551 -0
  100. package/crates/tish_eval/src/promise.rs +179 -0
  101. package/crates/tish_eval/src/regex.rs +299 -0
  102. package/crates/tish_eval/src/timers.rs +120 -0
  103. package/crates/tish_eval/src/value.rs +336 -0
  104. package/crates/tish_eval/src/value_convert.rs +117 -0
  105. package/crates/tish_ffi/Cargo.toml +26 -0
  106. package/crates/tish_ffi/src/lib.rs +518 -0
  107. package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
  108. package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
  109. package/crates/tish_ffi/tests/loader.rs +65 -0
  110. package/crates/tish_fmt/Cargo.toml +16 -0
  111. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  112. package/crates/tish_fmt/src/lib.rs +2157 -0
  113. package/crates/tish_jsx_web/Cargo.toml +9 -0
  114. package/crates/tish_jsx_web/README.md +5 -0
  115. package/crates/tish_jsx_web/src/lib.rs +2 -0
  116. package/crates/tish_lexer/Cargo.toml +9 -0
  117. package/crates/tish_lexer/src/lib.rs +1104 -0
  118. package/crates/tish_lexer/src/token.rs +170 -0
  119. package/crates/tish_lint/Cargo.toml +18 -0
  120. package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
  121. package/crates/tish_lint/src/lib.rs +281 -0
  122. package/crates/tish_llvm/Cargo.toml +13 -0
  123. package/crates/tish_llvm/src/lib.rs +115 -0
  124. package/crates/tish_lsp/Cargo.toml +25 -0
  125. package/crates/tish_lsp/README.md +26 -0
  126. package/crates/tish_lsp/src/builtin_goto.rs +362 -0
  127. package/crates/tish_lsp/src/import_goto.rs +564 -0
  128. package/crates/tish_lsp/src/main.rs +1459 -0
  129. package/crates/tish_native/Cargo.toml +16 -0
  130. package/crates/tish_native/src/build.rs +481 -0
  131. package/crates/tish_native/src/config.rs +48 -0
  132. package/crates/tish_native/src/lib.rs +416 -0
  133. package/crates/tish_opt/Cargo.toml +13 -0
  134. package/crates/tish_opt/src/lib.rs +1046 -0
  135. package/crates/tish_parser/Cargo.toml +11 -0
  136. package/crates/tish_parser/src/lib.rs +386 -0
  137. package/crates/tish_parser/src/parser.rs +2726 -0
  138. package/crates/tish_pg/Cargo.toml +34 -0
  139. package/crates/tish_pg/README.md +38 -0
  140. package/crates/tish_pg/src/error.rs +52 -0
  141. package/crates/tish_pg/src/lib.rs +955 -0
  142. package/crates/tish_resolve/Cargo.toml +13 -0
  143. package/crates/tish_resolve/src/lib.rs +3601 -0
  144. package/crates/tish_resolve/src/pos.rs +141 -0
  145. package/crates/tish_runtime/Cargo.toml +100 -0
  146. package/crates/tish_runtime/src/http.rs +1347 -0
  147. package/crates/tish_runtime/src/http_fetch.rs +492 -0
  148. package/crates/tish_runtime/src/http_hyper.rs +441 -0
  149. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  150. package/crates/tish_runtime/src/lib.rs +1447 -0
  151. package/crates/tish_runtime/src/native_promise.rs +15 -0
  152. package/crates/tish_runtime/src/promise.rs +558 -0
  153. package/crates/tish_runtime/src/promise_io.rs +38 -0
  154. package/crates/tish_runtime/src/timers.rs +172 -0
  155. package/crates/tish_runtime/src/tty.rs +226 -0
  156. package/crates/tish_runtime/src/ws.rs +778 -0
  157. package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
  158. package/crates/tish_ui/Cargo.toml +17 -0
  159. package/crates/tish_ui/src/jsx.rs +692 -0
  160. package/crates/tish_ui/src/lib.rs +20 -0
  161. package/crates/tish_ui/src/runtime/hooks.rs +573 -0
  162. package/crates/tish_ui/src/runtime/mod.rs +183 -0
  163. package/crates/tish_vm/Cargo.toml +60 -0
  164. package/crates/tish_vm/src/jit.rs +1050 -0
  165. package/crates/tish_vm/src/lib.rs +41 -0
  166. package/crates/tish_vm/src/vm.rs +3536 -0
  167. package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
  168. package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
  169. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  170. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
  171. package/crates/tish_wasm/Cargo.toml +15 -0
  172. package/crates/tish_wasm/src/lib.rs +428 -0
  173. package/crates/tish_wasm_runtime/Cargo.toml +37 -0
  174. package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
  175. package/crates/tish_wasm_runtime/src/lib.rs +42 -0
  176. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  177. package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
  178. package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
  179. package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
  180. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  181. package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
  182. package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
  183. package/justfile +276 -0
  184. package/package.json +2 -2
  185. package/platform/darwin-arm64/tish-fmt +0 -0
  186. package/platform/darwin-x64/tish-fmt +0 -0
  187. package/platform/linux-arm64/tish-fmt +0 -0
  188. package/platform/linux-x64/tish-fmt +0 -0
  189. package/platform/win32-x64/tish-fmt.exe +0 -0
@@ -0,0 +1,16 @@
1
+ [package]
2
+ name = "tishlang_native"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Native code generation backend for Tish (Cranelift / direct compile)"
6
+
7
+ license-file = { workspace = true }
8
+ repository = { workspace = true }
9
+ [dependencies]
10
+ tishlang_build_utils = { path = "../tish_build_utils", version = ">=0.1" }
11
+ tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
12
+ tishlang_bytecode = { path = "../tish_bytecode", version = ">=0.1" }
13
+ tishlang_compile = { path = "../tish_compile", version = ">=0.1" }
14
+ tishlang_cranelift = { path = "../tish_cranelift", version = ">=0.1" }
15
+ tishlang_llvm = { path = "../tish_llvm", version = ">=0.1" }
16
+ tishlang_opt = { path = "../tish_opt", version = ">=0.1" }
@@ -0,0 +1,481 @@
1
+ //! Build native binary via cargo (interim path until Cranelift backend is ready).
2
+
3
+ use std::fs;
4
+ use std::path::Path;
5
+
6
+ use tishlang_compile::ResolvedNativeModule;
7
+
8
+ use crate::config::{NativeArtifact, NativeBuildConfig};
9
+
10
+ /// `tishlang_runtime` Cargo feature names (subset of CLI / compile feature names).
11
+ const RUNTIME_CARGO_FEATURES: &[&str] = &[
12
+ "http",
13
+ "http-hyper",
14
+ "http-io-uring",
15
+ "fs",
16
+ "process",
17
+ "regex",
18
+ "ws",
19
+ "tty",
20
+ ];
21
+
22
+ /// Map CLI/compile features to flags passed to `tishlang_runtime` in the temp crate's Cargo.toml.
23
+ /// `full` enables every optional runtime capability (matches `tish build --feature full` / LANGUAGE.md).
24
+ fn runtime_features_for_cargo(features: &[String]) -> Vec<String> {
25
+ let mut out = Vec::new();
26
+ for f in features {
27
+ if f == "full" {
28
+ for name in RUNTIME_CARGO_FEATURES {
29
+ if !out.iter().any(|x: &String| x == *name) {
30
+ out.push((*name).to_string());
31
+ }
32
+ }
33
+ continue;
34
+ }
35
+ if RUNTIME_CARGO_FEATURES.contains(&f.as_str()) && !out.contains(f) {
36
+ out.push(f.clone());
37
+ }
38
+ }
39
+ out
40
+ }
41
+
42
+ /// `[profile.release]` for nested `cargo build` of generated crates.
43
+ fn nested_release_profile_toml() -> &'static str {
44
+ if std::env::var("TISH_FAST_NATIVE_BUILD").as_deref() == Ok("1") {
45
+ r#"[profile.release]
46
+ opt-level = 1
47
+ lto = false
48
+ codegen-units = 16
49
+ incremental = true
50
+ strip = false
51
+ debug = 0
52
+ panic = "abort"
53
+ "#
54
+ } else {
55
+ r#"[profile.release]
56
+ # Reduce binary size: strip symbols, abort on panic (no unwinding), single codegen unit
57
+ strip = true
58
+ panic = "abort"
59
+ codegen-units = 1
60
+ lto = "fat"
61
+ "#
62
+ }
63
+ }
64
+
65
+ /// Inject `mod generated_native;` after the crate attribute so the binary crate can call `crate::generated_native::…`.
66
+ fn inject_generated_native_mod(rust_code: &str) -> String {
67
+ if let Some(pos) = rust_code.find("\n\n") {
68
+ let (a, b) = rust_code.split_at(pos + 2);
69
+ format!("{}mod generated_native;\n{}", a, b)
70
+ } else {
71
+ format!("{}\n\nmod generated_native;\n", rust_code)
72
+ }
73
+ }
74
+
75
+ /// Whether to embed mimalloc as the `#[global_allocator]` of rust-AOT BINARY output. tish workloads
76
+ /// are allocation-bound (a sampling profile of object/array code spends most time in malloc/free — see
77
+ /// `docs/perf.md`); mimalloc gives ~20% on object/array/bundle code, the same lever as the `tish` CLI's
78
+ /// own `fast-alloc` and the reason JSC ships bmalloc. Default ON; `TISH_NATIVE_FAST_ALLOC=0` opts out
79
+ /// (e.g. a target whose C toolchain can't build mimalloc). Callers also skip it for staticlib output (a
80
+ /// library does not own the final program's allocator) and cross builds (avoid cross-compiling C).
81
+ fn fast_alloc_enabled() -> bool {
82
+ std::env::var("TISH_NATIVE_FAST_ALLOC")
83
+ .map(|v| v != "0")
84
+ .unwrap_or(true)
85
+ }
86
+
87
+ /// Insert a mimalloc `#[global_allocator]` into the generated crate root, after the leading
88
+ /// `#![allow(...)]` inner attribute (mirrors [`inject_generated_native_mod`]; an inner attribute must
89
+ /// precede any item, and the codegen emits exactly one — `#![allow(unused, non_snake_case)]`).
90
+ fn inject_global_allocator(rust_code: &str) -> String {
91
+ const STMT: &str =
92
+ "#[global_allocator]\nstatic TISH_GLOBAL_ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;\n\n";
93
+ if let Some(pos) = rust_code.find("\n\n") {
94
+ let (a, b) = rust_code.split_at(pos + 2);
95
+ format!("{a}{STMT}{b}")
96
+ } else {
97
+ format!("{rust_code}\n\n{STMT}")
98
+ }
99
+ }
100
+
101
+ pub(crate) fn rust_code_needs_tokio(rust_code: &str) -> bool {
102
+ rust_code.contains("#[tokio::main]") || rust_code.contains("tokio::runtime::Runtime")
103
+ }
104
+
105
+ pub fn build_via_cargo(
106
+ rust_code: &str,
107
+ native_modules: Vec<ResolvedNativeModule>,
108
+ output_path: &Path,
109
+ features: &[String],
110
+ extra_dependencies_toml: &str,
111
+ generated_native_rs: Option<&str>,
112
+ project_root: Option<&Path>,
113
+ ) -> Result<(), String> {
114
+ build_via_cargo_with_config(
115
+ rust_code,
116
+ native_modules,
117
+ output_path,
118
+ features,
119
+ extra_dependencies_toml,
120
+ generated_native_rs,
121
+ project_root,
122
+ &NativeBuildConfig::desktop(),
123
+ )
124
+ }
125
+
126
+ #[allow(clippy::too_many_arguments)] // orthogonal cargo build inputs; bundling would just relocate the same fields
127
+ pub fn build_via_cargo_with_config(
128
+ rust_code: &str,
129
+ native_modules: Vec<ResolvedNativeModule>,
130
+ output_path: &Path,
131
+ features: &[String],
132
+ extra_dependencies_toml: &str,
133
+ generated_native_rs: Option<&str>,
134
+ project_root: Option<&Path>,
135
+ build_config: &NativeBuildConfig,
136
+ ) -> Result<(), String> {
137
+ let out_stem = output_path
138
+ .file_stem()
139
+ .and_then(|s| s.to_str())
140
+ .unwrap_or("tish_out");
141
+ let cargo_name = tishlang_build_utils::cargo_target_name(out_stem);
142
+ let build_dir = tishlang_build_utils::create_build_dir("tish_build", out_stem)?;
143
+
144
+ let runtime_path = tishlang_build_utils::find_runtime_path_for_project(project_root)?;
145
+
146
+ let runtime_features = runtime_features_for_cargo(features);
147
+ let runtime_refs: Vec<&str> = runtime_features.iter().map(String::as_str).collect();
148
+ let features_str = if runtime_refs.is_empty() {
149
+ String::new()
150
+ } else {
151
+ format!(", features = {:?}", runtime_refs)
152
+ };
153
+
154
+ let needs_tokio = rust_code_needs_tokio(rust_code);
155
+ let tokio_dep = if needs_tokio {
156
+ "\ntokio = { version = \"1\", features = [\"rt-multi-thread\", \"macros\"] }\n"
157
+ } else {
158
+ ""
159
+ };
160
+
161
+ let native_deps: String = native_modules
162
+ .iter()
163
+ .filter(|m| m.use_path_dependency)
164
+ .map(|m| {
165
+ let path = m.crate_path.display().to_string().replace('\\', "/");
166
+ format!("{} = {{ path = {:?} }}\n", m.package_name, path)
167
+ })
168
+ .collect();
169
+
170
+ let mut more_deps = String::new();
171
+ more_deps.push_str(tokio_dep);
172
+ if !native_deps.is_empty() {
173
+ more_deps.push_str(&format!("\n{}", native_deps));
174
+ }
175
+ if !extra_dependencies_toml.trim().is_empty() {
176
+ more_deps.push_str(&format!("\n{}", extra_dependencies_toml));
177
+ }
178
+
179
+ let rust_main = if generated_native_rs.is_some() {
180
+ inject_generated_native_mod(rust_code)
181
+ } else {
182
+ rust_code.to_string()
183
+ };
184
+
185
+ // mimalloc as the program's global allocator — binary output only (a staticlib does not own the
186
+ // allocator), native only (don't cross-compile mimalloc's C). Adds one cached dep + a global_alloc
187
+ // statement; semantically transparent. `TISH_NATIVE_FAST_ALLOC=0` opts out.
188
+ let use_fast_alloc = fast_alloc_enabled()
189
+ && build_config.artifact != NativeArtifact::StaticLib
190
+ && build_config.cargo_target.is_none();
191
+ if use_fast_alloc {
192
+ more_deps.push_str("\nmimalloc = \"0.1\"\n");
193
+ }
194
+ let rust_main = if use_fast_alloc {
195
+ inject_global_allocator(&rust_main)
196
+ } else {
197
+ rust_main
198
+ };
199
+
200
+ let tish_ui_path = std::path::Path::new(&runtime_path)
201
+ .parent()
202
+ .ok_or_else(|| "invalid tishlang_runtime path (no parent)".to_string())?
203
+ .join("tish_ui");
204
+ let ui_dep = if rust_code.contains("tishlang_ui") {
205
+ format!(
206
+ "\ntishlang_ui = {{ path = {:?}, default-features = false, features = [\"runtime\"] }}\n",
207
+ tish_ui_path.display().to_string().replace('\\', "/")
208
+ )
209
+ } else {
210
+ String::new()
211
+ };
212
+
213
+ let profile = nested_release_profile_toml();
214
+ let src_file = if build_config.artifact == NativeArtifact::StaticLib {
215
+ "lib.rs"
216
+ } else {
217
+ "main.rs"
218
+ };
219
+ let crate_section = if build_config.artifact == NativeArtifact::StaticLib {
220
+ format!(
221
+ r#"[lib]
222
+ name = "{}"
223
+ crate-type = ["staticlib"]
224
+ path = "src/lib.rs"
225
+
226
+ "#,
227
+ cargo_name
228
+ )
229
+ } else {
230
+ format!(
231
+ r#"[[bin]]
232
+ name = "{}"
233
+ path = "src/main.rs"
234
+
235
+ "#,
236
+ cargo_name
237
+ )
238
+ };
239
+ let cargo_toml = format!(
240
+ r#"[package]
241
+ name = "tish_output"
242
+ version = "0.1.0"
243
+ edition = "2021"
244
+
245
+ {}{}
246
+ [dependencies]
247
+ tishlang_runtime = {{ path = {:?}{} }}
248
+ {}{}"#,
249
+ crate_section, profile, runtime_path, features_str, more_deps, ui_dep
250
+ );
251
+
252
+ fs::write(build_dir.join("Cargo.toml"), cargo_toml)
253
+ .map_err(|e| format!("Cannot write Cargo.toml: {}", e))?;
254
+ if let Some(gen) = generated_native_rs {
255
+ fs::write(build_dir.join("src/generated_native.rs"), gen)
256
+ .map_err(|e| format!("Cannot write generated_native.rs: {}", e))?;
257
+ }
258
+ fs::write(build_dir.join("src").join(src_file), rust_main)
259
+ .map_err(|e| format!("Cannot write {}: {}", src_file, e))?;
260
+
261
+ let workspace_target = Path::new(&runtime_path)
262
+ .parent()
263
+ .and_then(|p| p.parent())
264
+ .map(|ws| ws.join("target"));
265
+ let target_dir = workspace_target.filter(|p| p.exists());
266
+ let cross = build_config.cargo_target.as_deref();
267
+ let release_sub = if let Some(triple) = cross {
268
+ format!("{triple}/release")
269
+ } else {
270
+ "release".to_string()
271
+ };
272
+ let binary_dir = target_dir
273
+ .as_ref()
274
+ .map(|t| t.join(&release_sub))
275
+ .unwrap_or_else(|| build_dir.join("target").join(&release_sub));
276
+
277
+ tishlang_build_utils::run_cargo_build(&build_dir, target_dir.as_deref(), cross)?;
278
+
279
+ let artifact = if build_config.artifact == NativeArtifact::StaticLib {
280
+ tishlang_build_utils::find_release_staticlib(&binary_dir, &cargo_name)?
281
+ } else {
282
+ tishlang_build_utils::find_release_binary(&binary_dir, &cargo_name)?
283
+ };
284
+ let target = if build_config.artifact == NativeArtifact::StaticLib {
285
+ if output_path.extension().is_some_and(|e| e == "a") {
286
+ output_path.to_path_buf()
287
+ } else if output_path.to_string_lossy().ends_with('/') || output_path.is_dir() {
288
+ output_path.join(format!("lib{out_stem}.a"))
289
+ } else {
290
+ output_path.with_extension("a")
291
+ }
292
+ } else {
293
+ tishlang_build_utils::resolve_output_path(output_path, out_stem)
294
+ };
295
+ tishlang_build_utils::copy_binary_to_output(&artifact, &target)?;
296
+
297
+ Ok(())
298
+ }
299
+
300
+ /// Build several native binaries in **one** nested Cargo project (shared `tishlang_runtime` compile).
301
+ ///
302
+ /// `bins` order must match `outputs`: each `(stem, rust_code, generated_native_rs)` pairs with
303
+ /// `outputs[i].0` (entry path — used only for validation) and `outputs[i].1` (final binary path).
304
+ #[allow(clippy::too_many_arguments)] // orthogonal batch-build inputs (bins/outputs/modules/flags)
305
+ pub(crate) fn build_many_via_cargo(
306
+ bins: Vec<(String, String, Option<String>)>,
307
+ native_modules: Vec<ResolvedNativeModule>,
308
+ features: &[String],
309
+ extra_dependencies_toml: &str,
310
+ needs_tokio: bool,
311
+ needs_ui: bool,
312
+ outputs: &[(&Path, &Path)],
313
+ project_root: Option<&Path>,
314
+ ) -> Result<(), String> {
315
+ if bins.len() != outputs.len() {
316
+ return Err(format!(
317
+ "build_many_via_cargo: bins ({}) != outputs ({})",
318
+ bins.len(),
319
+ outputs.len()
320
+ ));
321
+ }
322
+ for (i, (stem, _, _)) in bins.iter().enumerate() {
323
+ let entry = outputs[i].0;
324
+ let expect = entry.file_stem().and_then(|s| s.to_str()).unwrap_or("");
325
+ if expect != stem {
326
+ return Err(format!(
327
+ "build_many_via_cargo: stem mismatch at {}: {} vs {}",
328
+ i, stem, expect
329
+ ));
330
+ }
331
+ }
332
+
333
+ let batch_id = format!("many_{}", std::process::id());
334
+ let build_dir = tishlang_build_utils::create_build_dir("tish_build_many", &batch_id)?;
335
+
336
+ let runtime_path = tishlang_build_utils::find_runtime_path_for_project(project_root)?;
337
+
338
+ let runtime_features = runtime_features_for_cargo(features);
339
+ let runtime_refs: Vec<&str> = runtime_features.iter().map(String::as_str).collect();
340
+ let features_str = if runtime_refs.is_empty() {
341
+ String::new()
342
+ } else {
343
+ format!(", features = {:?}", runtime_refs)
344
+ };
345
+
346
+ let tokio_dep = if needs_tokio {
347
+ "\ntokio = { version = \"1\", features = [\"rt-multi-thread\", \"macros\"] }\n"
348
+ } else {
349
+ ""
350
+ };
351
+
352
+ let native_deps: String = native_modules
353
+ .iter()
354
+ .filter(|m| m.use_path_dependency)
355
+ .map(|m| {
356
+ let path = m.crate_path.display().to_string().replace('\\', "/");
357
+ format!("{} = {{ path = {:?} }}\n", m.package_name, path)
358
+ })
359
+ .collect();
360
+
361
+ let mut more_deps = String::new();
362
+ more_deps.push_str(tokio_dep);
363
+ if !native_deps.is_empty() {
364
+ more_deps.push_str(&format!("\n{}", native_deps));
365
+ }
366
+ if !extra_dependencies_toml.trim().is_empty() {
367
+ more_deps.push_str(&format!("\n{}", extra_dependencies_toml));
368
+ }
369
+ // mimalloc global allocator for every binary in the batch (all are executables, always native here).
370
+ let use_fast_alloc = fast_alloc_enabled();
371
+ if use_fast_alloc {
372
+ more_deps.push_str("\nmimalloc = \"0.1\"\n");
373
+ }
374
+
375
+ let tish_ui_path = std::path::Path::new(&runtime_path)
376
+ .parent()
377
+ .ok_or_else(|| "invalid tishlang_runtime path (no parent)".to_string())?
378
+ .join("tish_ui");
379
+ let ui_dep = if needs_ui {
380
+ format!(
381
+ "\ntishlang_ui = {{ path = {:?}, default-features = false, features = [\"runtime\"] }}\n",
382
+ tish_ui_path.display().to_string().replace('\\', "/")
383
+ )
384
+ } else {
385
+ String::new()
386
+ };
387
+
388
+ let mut bin_tables = String::new();
389
+ for (stem, rust_code, generated_native_rs) in &bins {
390
+ let bin_dir = build_dir.join("src/bin").join(stem);
391
+ fs::create_dir_all(&bin_dir).map_err(|e| format!("create bin dir: {}", e))?;
392
+
393
+ let rust_main = if generated_native_rs.is_some() {
394
+ inject_generated_native_mod(rust_code)
395
+ } else {
396
+ rust_code.clone()
397
+ };
398
+ let rust_main = if use_fast_alloc {
399
+ inject_global_allocator(&rust_main)
400
+ } else {
401
+ rust_main
402
+ };
403
+
404
+ fs::write(bin_dir.join("main.rs"), rust_main)
405
+ .map_err(|e| format!("write main.rs for {}: {}", stem, e))?;
406
+ if let Some(gen) = generated_native_rs {
407
+ fs::write(bin_dir.join("generated_native.rs"), gen)
408
+ .map_err(|e| format!("write generated_native.rs for {}: {}", stem, e))?;
409
+ }
410
+
411
+ bin_tables.push_str(&format!(
412
+ r#"[[bin]]
413
+ name = "{stem}"
414
+ path = "src/bin/{stem}/main.rs"
415
+
416
+ "#
417
+ ));
418
+ }
419
+
420
+ let profile = nested_release_profile_toml();
421
+ let cargo_toml = format!(
422
+ r#"[package]
423
+ name = "tish_output_many"
424
+ version = "0.1.0"
425
+ edition = "2021"
426
+
427
+ {}{}
428
+ [dependencies]
429
+ tishlang_runtime = {{ path = {:?}{} }}
430
+ {}{}"#,
431
+ bin_tables, profile, runtime_path, features_str, more_deps, ui_dep
432
+ );
433
+
434
+ fs::write(build_dir.join("Cargo.toml"), cargo_toml)
435
+ .map_err(|e| format!("Cannot write Cargo.toml: {}", e))?;
436
+
437
+ let workspace_target = Path::new(&runtime_path)
438
+ .parent()
439
+ .and_then(|p| p.parent())
440
+ .map(|ws| ws.join("target"));
441
+ let target_dir = workspace_target.filter(|p| p.exists());
442
+ let binary_dir = target_dir
443
+ .as_ref()
444
+ .map(|t| t.join("release"))
445
+ .unwrap_or_else(|| build_dir.join("target").join("release"));
446
+
447
+ tishlang_build_utils::run_cargo_build(&build_dir, target_dir.as_deref(), None)?;
448
+
449
+ for i in 0..bins.len() {
450
+ let stem = bins[i].0.as_str();
451
+ let output_path = outputs[i].1;
452
+ let binary = tishlang_build_utils::find_release_binary(binary_dir.as_path(), stem)?;
453
+ let target = tishlang_build_utils::resolve_output_path(output_path, stem);
454
+ tishlang_build_utils::copy_binary_to_output(&binary, &target)?;
455
+ }
456
+
457
+ Ok(())
458
+ }
459
+
460
+ #[cfg(test)]
461
+ mod tests {
462
+ use super::runtime_features_for_cargo;
463
+
464
+ #[test]
465
+ fn runtime_features_full_expands() {
466
+ let f = runtime_features_for_cargo(&["full".to_string()]);
467
+ assert!(f.contains(&"http".to_string()));
468
+ assert!(f.contains(&"fs".to_string()));
469
+ assert!(f.contains(&"process".to_string()));
470
+ assert!(f.contains(&"regex".to_string()));
471
+ assert!(f.contains(&"ws".to_string()));
472
+ }
473
+
474
+ #[test]
475
+ fn runtime_features_merges_full_and_specific() {
476
+ let f = runtime_features_for_cargo(&["full".to_string(), "http".to_string()]);
477
+ // `full` expands to every RUNTIME_CARGO_FEATURES entry; redundant `http` must not duplicate.
478
+ assert_eq!(f.len(), super::RUNTIME_CARGO_FEATURES.len());
479
+ assert_eq!(f.iter().filter(|x| *x == "http").count(), 1);
480
+ }
481
+ }
@@ -0,0 +1,48 @@
1
+ //! Native build configuration (desktop binary vs iOS staticlib, cross-target).
2
+
3
+ use tishlang_compile::NativeEmitMode;
4
+
5
+ /// Output artifact kind for `tish build --target native`.
6
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7
+ pub enum NativeArtifact {
8
+ #[default]
9
+ Bin,
10
+ StaticLib,
11
+ }
12
+
13
+ /// Options passed from the CLI into nested `cargo build`.
14
+ #[derive(Debug, Clone, Default)]
15
+ pub struct NativeBuildConfig {
16
+ pub artifact: NativeArtifact,
17
+ /// When set, run `cargo build --target <triple>` and skip `-C target-cpu=native`.
18
+ pub cargo_target: Option<String>,
19
+ pub emit_mode: NativeEmitMode,
20
+ }
21
+
22
+ impl NativeBuildConfig {
23
+ pub fn desktop() -> Self {
24
+ Self::default()
25
+ }
26
+
27
+ pub fn ios_staticlib(triple: &str) -> Self {
28
+ Self {
29
+ artifact: NativeArtifact::StaticLib,
30
+ cargo_target: Some(triple.to_string()),
31
+ emit_mode: NativeEmitMode::EmbeddedLib,
32
+ }
33
+ }
34
+
35
+ pub fn is_cross_compile(&self) -> bool {
36
+ self.cargo_target.is_some()
37
+ }
38
+ }
39
+
40
+ /// Filter runtime features for iOS sandbox builds.
41
+ pub fn ios_runtime_features(features: &[String]) -> Vec<String> {
42
+ const ALLOW: &[&str] = &["http", "http-hyper", "regex", "timers"];
43
+ features
44
+ .iter()
45
+ .filter(|f| ALLOW.contains(&f.as_str()))
46
+ .cloned()
47
+ .collect()
48
+ }