cj-tauri 2.0.0 → 2.1.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/README.md +206 -139
- package/bin/cj-tauri.js +287 -287
- package/cjpm.toml +63 -52
- package/cli/cjpm.toml +22 -22
- package/cli/src/doctor.cj +308 -306
- package/cli/src/main.cj +129 -129
- package/cli/src/manifest.cj +643 -558
- package/cli/src/manifest_test.cj +87 -0
- package/cli/src/project.cj +770 -770
- package/cli/src/resolve.cj +348 -348
- package/cli/src/scaffold.cj +369 -369
- package/cli/src/sync.cj +638 -526
- package/cli/src/util.cj +168 -168
- package/cli/templates/app/README.md +51 -48
- package/cli/templates/app/assets/capabilities/default.json +7 -7
- package/cli/templates/app/assets/ui/index.html +120 -118
- package/cli/templates/app/cj-tauri.toml +32 -22
- package/cli/templates/app/cjpm.toml +34 -34
- package/cli/templates/app/src/main.cj +101 -90
- package/cli/templates/app-react/README.md +56 -56
- package/cli/templates/app-react/assets/capabilities/default.json +7 -7
- package/cli/templates/app-react/assets/ui/index.html +25 -25
- package/cli/templates/app-react/assets/ui/package.json +19 -19
- package/cli/templates/app-react/assets/ui/src/App.css +67 -67
- package/cli/templates/app-react/assets/ui/src/App.jsx +124 -124
- package/cli/templates/app-react/assets/ui/src/main.jsx +6 -6
- package/cli/templates/app-react/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-react/cj-tauri.toml +33 -23
- package/cli/templates/app-react/cjpm.toml +34 -34
- package/cli/templates/app-react/src/main.cj +91 -91
- package/cli/templates/app-vue/README.md +56 -56
- package/cli/templates/app-vue/assets/capabilities/default.json +7 -7
- package/cli/templates/app-vue/assets/ui/index.html +25 -25
- package/cli/templates/app-vue/assets/ui/package.json +18 -18
- package/cli/templates/app-vue/assets/ui/src/App.vue +176 -176
- package/cli/templates/app-vue/assets/ui/src/main.js +4 -4
- package/cli/templates/app-vue/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-vue/cj-tauri.toml +33 -23
- package/cli/templates/app-vue/cjpm.toml +34 -34
- package/cli/templates/app-vue/src/main.cj +91 -91
- package/cli/templates/ohos-skeleton/AppScope/app.json5 +13 -13
- package/cli/templates/ohos-skeleton/AppScope/resources/base/element/string.json +8 -8
- package/cli/templates/ohos-skeleton/AppScope/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/README.md +84 -79
- package/cli/templates/ohos-skeleton/build-hap.sh +15 -0
- package/cli/templates/ohos-skeleton/build-profile.json5 +46 -46
- package/cli/templates/ohos-skeleton/entry/build-profile.json5 +26 -26
- package/cli/templates/ohos-skeleton/entry/cjpm.toml +51 -43
- package/cli/templates/ohos-skeleton/entry/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/entry/oh-package.json5 +9 -9
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/ability_stage.cj +17 -17
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/app_host.cj +94 -94
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -167
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/main_ability.cj +139 -139
- package/cli/templates/ohos-skeleton/entry/src/main/module.json5 +57 -49
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/color.json +8 -8
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/string.json +16 -16
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/profile/main_pages.json +2 -2
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/capabilities.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/index.html +92 -92
- package/cli/templates/ohos-skeleton/hvigor/hvigor-config.json5 +5 -5
- package/cli/templates/ohos-skeleton/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/oh-package.json5 +10 -10
- package/native/build_win.bat +62 -62
- package/native/core/bridge_core.c +1681 -1626
- package/native/core/bridge_core.h +675 -618
- package/native/core/bridge_js.h +108 -108
- package/native/platforms/harmonyos/README.md +115 -6
- package/native/platforms/linux/bridge_linux.c +2787 -2485
- package/native/platforms/windows/bridge_win.c +3745 -3248
- package/native/tests/gtk_threading_probe.c +143 -143
- package/native/tests/test_bridge_core.c +1339 -1228
- package/package.json +53 -53
- package/src/api_system.cj +67 -67
- package/src/app.cj +335 -969
- package/src/app_events.cj +166 -0
- package/src/app_host_ops.cj +287 -0
- package/src/app_run.cj +283 -0
- package/src/app_spec.cj +76 -76
- package/src/arkweb_menu.cj +216 -0
- package/src/arkweb_script.cj +142 -142
- package/src/capability.cj +409 -409
- package/src/clipboard_bridge.cj +178 -0
- package/src/dialog_spec.cj +79 -0
- package/src/dragdrop.cj +26 -26
- package/src/embedded_assets.cj +98 -98
- package/src/host.cj +752 -607
- package/src/host_arkweb.cj +924 -778
- package/src/host_webkit.cj +663 -570
- package/src/host_webview2.cj +663 -570
- package/src/hotkey.cj +66 -66
- package/src/ipc_hub.cj +401 -337
- package/src/ipc_message.cj +286 -286
- package/src/launch.cj +62 -62
- package/src/logging.cj +127 -127
- package/src/menu.cj +197 -197
- package/src/platform/platform_app_context.cj +77 -77
- package/src/platform/platform_assets.cj +33 -33
- package/src/platform/platform_discover.cj +176 -176
- package/src/platform/platform_env.cj +29 -29
- package/src/platform/platform_feature.cj +31 -31
- package/src/platform/platform_page_source.cj +87 -87
- package/src/platform/platform_target_info.cj +32 -32
- package/src/platform_host_provider.cj +87 -87
- package/src/plugin.cj +333 -333
- package/src/plugins/clipboard/plugin_clipboard.cj +152 -152
- package/src/plugins/dialog/plugin_dialog.cj +209 -209
- package/src/plugins/fs/plugin_fs.cj +105 -1
- package/src/plugins/http/http_backend.cj +131 -0
- package/src/plugins/http/http_desktop.cj +195 -0
- package/src/plugins/http/http_ohos.cj +204 -0
- package/src/plugins/http/plugin_http.cj +310 -459
- package/src/plugins/log/plugin_log.cj +105 -105
- package/src/plugins/menu/plugin_menu.cj +158 -158
- package/src/plugins/notification/plugin_notification.cj +154 -154
- package/src/plugins/serial/plugin_serial.cj +458 -454
- package/src/plugins/serial/serial_bridge.cj +194 -194
- package/src/plugins/serial/serial_manager_ohos.cj +92 -0
- package/src/plugins/serial/serial_ohos.cj +529 -0
- package/src/plugins/shell/plugin_shell.cj +258 -258
- package/src/plugins/sqlite/plugin_sqlite.cj +705 -710
- package/src/plugins/sqlite/sqlite_ffi.cj +206 -211
- package/src/plugins/ws/plugin_ws.cj +488 -470
- package/src/process_hidden.cj +174 -174
- package/src/scope.cj +391 -391
- package/src/shell_event.cj +69 -69
- package/src/tests/app_spec_test.cj +130 -127
- package/src/tests/capability_test.cj +243 -243
- package/src/tests/dialog_spec_test.cj +73 -0
- package/src/tests/fake_host.cj +201 -201
- package/src/tests/ipc_hub_test.cj +563 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +264 -251
- package/src/tests/plugin_clipboard_test.cj +222 -222
- package/src/tests/plugin_dialog_test.cj +237 -237
- package/src/tests/plugin_fs_test.cj +207 -0
- package/src/tests/plugin_http_test.cj +124 -124
- package/src/tests/plugin_notification_test.cj +205 -205
- package/src/tests/plugin_serial_test.cj +48 -0
- package/src/tests/plugin_test.cj +605 -585
- package/src/tests/plugin_ws_test.cj +40 -40
- package/src/tests/process_hidden_test.cj +129 -129
- package/src/tests/scope_test.cj +350 -350
- package/src/tests/system_host_test.cj +99 -81
- package/src/tests/urlutil_test.cj +53 -53
- package/src/tests/version_test.cj +75 -75
- package/src/tests/window_registry_test.cj +123 -123
- package/src/tray.cj +90 -90
- package/src/urlutil.cj +159 -159
- package/src/version.cj +44 -44
- package/src/window.cj +51 -51
- package/src/window_registry.cj +116 -116
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/plugins/serial/serial_ohos_stub.cj +0 -61
- package/src/plugins/sqlite/sqlite_ohos_stub.cj +0 -91
package/src/process_hidden.cj
CHANGED
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
package cjTauri
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 后台(静默)子进程:起一个**控制台程序**,但不要那个控制台窗口。
|
|
5
|
-
*
|
|
6
|
-
* 为什么需要它:cj-tauri 的应用产物是 GUI 子系统(Windows 的 `--subsystem=windows`),父进程
|
|
7
|
-
* 没有控制台;此时用 `std.process.launch` 拉起一个**控制台子系统**的程序(cjh、ffmpeg 这类 CLI),
|
|
8
|
-
* Windows 会给子进程单独配一个控制台窗口——用户看到的就是「点了一下,旁边蹦出一个黑窗口」。
|
|
9
|
-
* `std.process.launch` 的签名里没有可以传创建标志的地方,所以这条下沉到 C 桥
|
|
10
|
-
* (平台差异只允许出现在 `@When` 与 `bridge_*.c`,AGENTS §2):
|
|
11
|
-
* Windows = `CreateProcessW` + `DETACHED_PROCESS`(子进程**根本没有** console:`CREATE_NO_WINDOW`
|
|
12
|
-
* 只是不显示窗口,它仍持有一个隐藏 console、conhost 照挂;三个标准句柄显式指到 NUL,否则子进程一
|
|
13
|
-
* println 就可能死在自己的控制台上),Linux = `posix_spawn` + 自成会话(终端 Ctrl-C 不连带打死)。
|
|
14
|
-
*
|
|
15
|
-
* 与 `std.process.launch` 的取舍:这条**拿不到子进程的 stdout / stderr**(Windows 指向 NUL、
|
|
16
|
-
* Linux 指向 /dev/null)。要读它的输出就别用这个——那需要管道,是另一件事。
|
|
17
|
-
*
|
|
18
|
-
* 句柄口径:只留 pid(Windows 的进程句柄当场关掉,不占句柄表;Linux 的子进程是分离的,
|
|
19
|
-
* 父进程退出后它照样活着)。要让它结束就 `stop()`,要判它还在不在就 `isAlive()`。
|
|
20
|
-
*/
|
|
21
|
-
public class BackgroundProcess {
|
|
22
|
-
private let pid: Int64
|
|
23
|
-
|
|
24
|
-
init(pid: Int64) {
|
|
25
|
-
this.pid = pid
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 进程号(恒 > 0:拿到它的路径只有 `launchHidden` 成功那一条) */
|
|
29
|
-
public func processId(): Int64 {
|
|
30
|
-
return pid
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** 还在跑吗(查不到即按「不在」上报——调用方据此决定要不要重新拉起) */
|
|
34
|
-
public func isAlive(): Bool {
|
|
35
|
-
return hiddenPidAlive(pid)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 让它结束:先按正常终止(Windows = TerminateProcess;Linux = SIGTERM,3 秒不走再 SIGKILL)。
|
|
40
|
-
* 幂等——进程已经不在了也算成功。
|
|
41
|
-
*/
|
|
42
|
-
public func stop(): Unit {
|
|
43
|
-
if (!hiddenPidKill(pid)) {
|
|
44
|
-
eprintln("[cj-tauri] 终止子进程失败(pid=${pid})")
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 静默启动一个后台进程:`exePath` 是程序路径,`args` 是参数表(**不要自己加引号**,
|
|
51
|
-
* 空格 / 引号由平台按各自规则转义)。
|
|
52
|
-
*
|
|
53
|
-
* 返回 `None` 的三种情况:路径为空、参数里混进了内部分隔符(0x1F)、平台起不来进程
|
|
54
|
-
* (原因已由 C 桥打进 stderr)。**绝不在没起进程时返回一个 pid**。
|
|
55
|
-
*
|
|
56
|
-
* 典型用法(应用侧 AI 后端:随包的 cjh):
|
|
57
|
-
*
|
|
58
|
-
* ```cangjie
|
|
59
|
-
* match (launchHidden(bin, ["web", "--port", "8765"])) {
|
|
60
|
-
* case Some(p) => backendPid = p.processId()
|
|
61
|
-
* case None => eprintln("[app] 后端没起来")
|
|
62
|
-
* }
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
public func launchHidden(exePath: String, args: Array<String>): Option<BackgroundProcess> {
|
|
66
|
-
if (exePath.isEmpty()) {
|
|
67
|
-
eprintln("[cj-tauri] launchHidden: 程序路径为空")
|
|
68
|
-
return None
|
|
69
|
-
}
|
|
70
|
-
let wire = match (joinHiddenArgs(args)) {
|
|
71
|
-
case Some(w) => w
|
|
72
|
-
case None => return None
|
|
73
|
-
}
|
|
74
|
-
let pid = spawnHiddenPid(exePath, wire)
|
|
75
|
-
if (pid <= 0) {
|
|
76
|
-
return None
|
|
77
|
-
}
|
|
78
|
-
return Some(BackgroundProcess(pid))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 把参数表拼成 C 桥认的「线格式」:按 0x1F(单元分隔符)连接,由平台侧再切成 argv。
|
|
83
|
-
*
|
|
84
|
-
* 这个字节不会出现在正常参数里,所以一旦出现就**当场拒绝**(`None`)——默默拼错比拒绝更糟:
|
|
85
|
-
* 参数被劈成两个、而调用方拿到的是「启动成功」。
|
|
86
|
-
*
|
|
87
|
-
* 抽成纯函数是为了单测:真正难的不是拼接,是「拼出来还能不能原样切开」这条不变量
|
|
88
|
-
* (`src/tests/process_hidden_test.cj`)。
|
|
89
|
-
*/
|
|
90
|
-
public func joinHiddenArgs(args: Array<String>): Option<String> {
|
|
91
|
-
let sep = "\u{1F}"
|
|
92
|
-
var wire = ""
|
|
93
|
-
var first = true
|
|
94
|
-
for (a in args) {
|
|
95
|
-
if (let Some(_) <- a.indexOf(sep)) {
|
|
96
|
-
eprintln("[cj-tauri] launchHidden: 参数里不允许出现 0x1F 分隔符(${a})")
|
|
97
|
-
return None
|
|
98
|
-
}
|
|
99
|
-
if (!first) {
|
|
100
|
-
wire += sep
|
|
101
|
-
}
|
|
102
|
-
wire += a
|
|
103
|
-
first = false
|
|
104
|
-
}
|
|
105
|
-
return Some(wire)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 静默启动的 C 桥出口:成功回 pid(> 0),失败回 0(平台已把原因写进 stderr)。
|
|
110
|
-
*
|
|
111
|
-
* 鸿蒙侧没有 C 桥产物(`native/platforms/harmonyos/` 目前只有契约),照桌面那份写会让模块
|
|
112
|
-
* 加载期就 SIGSEGV(与 `log_bridge.cj` 同一条坑,AGENTS §4)——所以按 `env` 分叉,如实降级。
|
|
113
|
-
* 判据用 `env` 而不是 `arch`:桌面 aarch64 Linux 也有 C 桥。
|
|
114
|
-
*/
|
|
115
|
-
@When[env != "ohos"]
|
|
116
|
-
@C
|
|
117
|
-
foreign func cj_bridge_spawn_hidden(exe: CString, args: CString): Int64
|
|
118
|
-
|
|
119
|
-
/** 终止 pid(幂等);鸿蒙侧无桥,不用声明。 */
|
|
120
|
-
@When[env != "ohos"]
|
|
121
|
-
@C
|
|
122
|
-
foreign func cj_bridge_kill_pid(pid: Int64): Int32
|
|
123
|
-
|
|
124
|
-
/** 探活(1 = 活着);鸿蒙侧无桥,不用声明。 */
|
|
125
|
-
@When[env != "ohos"]
|
|
126
|
-
@C
|
|
127
|
-
foreign func cj_bridge_pid_alive(pid: Int64): Int32
|
|
128
|
-
|
|
129
|
-
@When[env != "ohos"]
|
|
130
|
-
func spawnHiddenPid(exePath: String, argsWire: String): Int64 {
|
|
131
|
-
var pid: Int64 = 0
|
|
132
|
-
unsafe {
|
|
133
|
-
pid = cj_bridge_spawn_hidden(LibC.mallocCString(exePath), LibC.mallocCString(argsWire))
|
|
134
|
-
}
|
|
135
|
-
return pid
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** 鸿蒙侧:没有桥就没有后台静默启动,如实报 0 并说清代价(用 std.process.launch 会带出控制台) */
|
|
139
|
-
@When[env == "ohos"]
|
|
140
|
-
func spawnHiddenPid(exePath: String, argsWire: String): Int64 {
|
|
141
|
-
let _ = argsWire
|
|
142
|
-
eprintln("[cj-tauri] 本平台没有 C 桥,无法后台静默启动:${exePath}")
|
|
143
|
-
return 0
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
@When[env != "ohos"]
|
|
147
|
-
func hiddenPidAlive(pid: Int64): Bool {
|
|
148
|
-
var alive: Int32 = 0
|
|
149
|
-
unsafe {
|
|
150
|
-
alive = cj_bridge_pid_alive(pid)
|
|
151
|
-
}
|
|
152
|
-
return alive == 1
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@When[env == "ohos"]
|
|
156
|
-
func hiddenPidAlive(pid: Int64): Bool {
|
|
157
|
-
let _ = pid
|
|
158
|
-
return false
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
@When[env != "ohos"]
|
|
162
|
-
func hiddenPidKill(pid: Int64): Bool {
|
|
163
|
-
var rc: Int32 = 0
|
|
164
|
-
unsafe {
|
|
165
|
-
rc = cj_bridge_kill_pid(pid)
|
|
166
|
-
}
|
|
167
|
-
return rc == 1
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
@When[env == "ohos"]
|
|
171
|
-
func hiddenPidKill(pid: Int64): Bool {
|
|
172
|
-
let _ = pid
|
|
173
|
-
return false
|
|
174
|
-
}
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 后台(静默)子进程:起一个**控制台程序**,但不要那个控制台窗口。
|
|
5
|
+
*
|
|
6
|
+
* 为什么需要它:cj-tauri 的应用产物是 GUI 子系统(Windows 的 `--subsystem=windows`),父进程
|
|
7
|
+
* 没有控制台;此时用 `std.process.launch` 拉起一个**控制台子系统**的程序(cjh、ffmpeg 这类 CLI),
|
|
8
|
+
* Windows 会给子进程单独配一个控制台窗口——用户看到的就是「点了一下,旁边蹦出一个黑窗口」。
|
|
9
|
+
* `std.process.launch` 的签名里没有可以传创建标志的地方,所以这条下沉到 C 桥
|
|
10
|
+
* (平台差异只允许出现在 `@When` 与 `bridge_*.c`,AGENTS §2):
|
|
11
|
+
* Windows = `CreateProcessW` + `DETACHED_PROCESS`(子进程**根本没有** console:`CREATE_NO_WINDOW`
|
|
12
|
+
* 只是不显示窗口,它仍持有一个隐藏 console、conhost 照挂;三个标准句柄显式指到 NUL,否则子进程一
|
|
13
|
+
* println 就可能死在自己的控制台上),Linux = `posix_spawn` + 自成会话(终端 Ctrl-C 不连带打死)。
|
|
14
|
+
*
|
|
15
|
+
* 与 `std.process.launch` 的取舍:这条**拿不到子进程的 stdout / stderr**(Windows 指向 NUL、
|
|
16
|
+
* Linux 指向 /dev/null)。要读它的输出就别用这个——那需要管道,是另一件事。
|
|
17
|
+
*
|
|
18
|
+
* 句柄口径:只留 pid(Windows 的进程句柄当场关掉,不占句柄表;Linux 的子进程是分离的,
|
|
19
|
+
* 父进程退出后它照样活着)。要让它结束就 `stop()`,要判它还在不在就 `isAlive()`。
|
|
20
|
+
*/
|
|
21
|
+
public class BackgroundProcess {
|
|
22
|
+
private let pid: Int64
|
|
23
|
+
|
|
24
|
+
init(pid: Int64) {
|
|
25
|
+
this.pid = pid
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 进程号(恒 > 0:拿到它的路径只有 `launchHidden` 成功那一条) */
|
|
29
|
+
public func processId(): Int64 {
|
|
30
|
+
return pid
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 还在跑吗(查不到即按「不在」上报——调用方据此决定要不要重新拉起) */
|
|
34
|
+
public func isAlive(): Bool {
|
|
35
|
+
return hiddenPidAlive(pid)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 让它结束:先按正常终止(Windows = TerminateProcess;Linux = SIGTERM,3 秒不走再 SIGKILL)。
|
|
40
|
+
* 幂等——进程已经不在了也算成功。
|
|
41
|
+
*/
|
|
42
|
+
public func stop(): Unit {
|
|
43
|
+
if (!hiddenPidKill(pid)) {
|
|
44
|
+
eprintln("[cj-tauri] 终止子进程失败(pid=${pid})")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 静默启动一个后台进程:`exePath` 是程序路径,`args` 是参数表(**不要自己加引号**,
|
|
51
|
+
* 空格 / 引号由平台按各自规则转义)。
|
|
52
|
+
*
|
|
53
|
+
* 返回 `None` 的三种情况:路径为空、参数里混进了内部分隔符(0x1F)、平台起不来进程
|
|
54
|
+
* (原因已由 C 桥打进 stderr)。**绝不在没起进程时返回一个 pid**。
|
|
55
|
+
*
|
|
56
|
+
* 典型用法(应用侧 AI 后端:随包的 cjh):
|
|
57
|
+
*
|
|
58
|
+
* ```cangjie
|
|
59
|
+
* match (launchHidden(bin, ["web", "--port", "8765"])) {
|
|
60
|
+
* case Some(p) => backendPid = p.processId()
|
|
61
|
+
* case None => eprintln("[app] 后端没起来")
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
public func launchHidden(exePath: String, args: Array<String>): Option<BackgroundProcess> {
|
|
66
|
+
if (exePath.isEmpty()) {
|
|
67
|
+
eprintln("[cj-tauri] launchHidden: 程序路径为空")
|
|
68
|
+
return None
|
|
69
|
+
}
|
|
70
|
+
let wire = match (joinHiddenArgs(args)) {
|
|
71
|
+
case Some(w) => w
|
|
72
|
+
case None => return None
|
|
73
|
+
}
|
|
74
|
+
let pid = spawnHiddenPid(exePath, wire)
|
|
75
|
+
if (pid <= 0) {
|
|
76
|
+
return None
|
|
77
|
+
}
|
|
78
|
+
return Some(BackgroundProcess(pid))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 把参数表拼成 C 桥认的「线格式」:按 0x1F(单元分隔符)连接,由平台侧再切成 argv。
|
|
83
|
+
*
|
|
84
|
+
* 这个字节不会出现在正常参数里,所以一旦出现就**当场拒绝**(`None`)——默默拼错比拒绝更糟:
|
|
85
|
+
* 参数被劈成两个、而调用方拿到的是「启动成功」。
|
|
86
|
+
*
|
|
87
|
+
* 抽成纯函数是为了单测:真正难的不是拼接,是「拼出来还能不能原样切开」这条不变量
|
|
88
|
+
* (`src/tests/process_hidden_test.cj`)。
|
|
89
|
+
*/
|
|
90
|
+
public func joinHiddenArgs(args: Array<String>): Option<String> {
|
|
91
|
+
let sep = "\u{1F}"
|
|
92
|
+
var wire = ""
|
|
93
|
+
var first = true
|
|
94
|
+
for (a in args) {
|
|
95
|
+
if (let Some(_) <- a.indexOf(sep)) {
|
|
96
|
+
eprintln("[cj-tauri] launchHidden: 参数里不允许出现 0x1F 分隔符(${a})")
|
|
97
|
+
return None
|
|
98
|
+
}
|
|
99
|
+
if (!first) {
|
|
100
|
+
wire += sep
|
|
101
|
+
}
|
|
102
|
+
wire += a
|
|
103
|
+
first = false
|
|
104
|
+
}
|
|
105
|
+
return Some(wire)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 静默启动的 C 桥出口:成功回 pid(> 0),失败回 0(平台已把原因写进 stderr)。
|
|
110
|
+
*
|
|
111
|
+
* 鸿蒙侧没有 C 桥产物(`native/platforms/harmonyos/` 目前只有契约),照桌面那份写会让模块
|
|
112
|
+
* 加载期就 SIGSEGV(与 `log_bridge.cj` 同一条坑,AGENTS §4)——所以按 `env` 分叉,如实降级。
|
|
113
|
+
* 判据用 `env` 而不是 `arch`:桌面 aarch64 Linux 也有 C 桥。
|
|
114
|
+
*/
|
|
115
|
+
@When[env != "ohos"]
|
|
116
|
+
@C
|
|
117
|
+
foreign func cj_bridge_spawn_hidden(exe: CString, args: CString): Int64
|
|
118
|
+
|
|
119
|
+
/** 终止 pid(幂等);鸿蒙侧无桥,不用声明。 */
|
|
120
|
+
@When[env != "ohos"]
|
|
121
|
+
@C
|
|
122
|
+
foreign func cj_bridge_kill_pid(pid: Int64): Int32
|
|
123
|
+
|
|
124
|
+
/** 探活(1 = 活着);鸿蒙侧无桥,不用声明。 */
|
|
125
|
+
@When[env != "ohos"]
|
|
126
|
+
@C
|
|
127
|
+
foreign func cj_bridge_pid_alive(pid: Int64): Int32
|
|
128
|
+
|
|
129
|
+
@When[env != "ohos"]
|
|
130
|
+
func spawnHiddenPid(exePath: String, argsWire: String): Int64 {
|
|
131
|
+
var pid: Int64 = 0
|
|
132
|
+
unsafe {
|
|
133
|
+
pid = cj_bridge_spawn_hidden(LibC.mallocCString(exePath), LibC.mallocCString(argsWire))
|
|
134
|
+
}
|
|
135
|
+
return pid
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** 鸿蒙侧:没有桥就没有后台静默启动,如实报 0 并说清代价(用 std.process.launch 会带出控制台) */
|
|
139
|
+
@When[env == "ohos"]
|
|
140
|
+
func spawnHiddenPid(exePath: String, argsWire: String): Int64 {
|
|
141
|
+
let _ = argsWire
|
|
142
|
+
eprintln("[cj-tauri] 本平台没有 C 桥,无法后台静默启动:${exePath}")
|
|
143
|
+
return 0
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@When[env != "ohos"]
|
|
147
|
+
func hiddenPidAlive(pid: Int64): Bool {
|
|
148
|
+
var alive: Int32 = 0
|
|
149
|
+
unsafe {
|
|
150
|
+
alive = cj_bridge_pid_alive(pid)
|
|
151
|
+
}
|
|
152
|
+
return alive == 1
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@When[env == "ohos"]
|
|
156
|
+
func hiddenPidAlive(pid: Int64): Bool {
|
|
157
|
+
let _ = pid
|
|
158
|
+
return false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@When[env != "ohos"]
|
|
162
|
+
func hiddenPidKill(pid: Int64): Bool {
|
|
163
|
+
var rc: Int32 = 0
|
|
164
|
+
unsafe {
|
|
165
|
+
rc = cj_bridge_kill_pid(pid)
|
|
166
|
+
}
|
|
167
|
+
return rc == 1
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@When[env == "ohos"]
|
|
171
|
+
func hiddenPidKill(pid: Int64): Bool {
|
|
172
|
+
let _ = pid
|
|
173
|
+
return false
|
|
174
|
+
}
|