cj-tauri 2.0.0 → 2.1.0
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 +113 -99
- 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 +632 -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 +526 -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 +3740 -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/prebuilt/linux-x64/cj-tauri +0 -0
- package/src/api_system.cj +67 -67
- package/src/app.cj +325 -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 +337 -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 +126 -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 +487 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +251 -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/cli/src/doctor.cj
CHANGED
|
@@ -1,306 +1,308 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cj-tauri CLI:环境自检(doctor)
|
|
3
|
-
*
|
|
4
|
-
* 为什么要有它:`info` 打印的是「解析结果」(路径是什么),能用但看不出「这条到底算不算过」——
|
|
5
|
-
* 每条都只是一行文本,人得自己对照 skill 里的判据。doctor 反过来:**每条检查自带结论**,
|
|
6
|
-
* 失败的给出下一步该干什么,让用户拿到包第一件事不用读文档。
|
|
7
|
-
*
|
|
8
|
-
* 判据与坑(与 skills/cj-tauri-env 同口径,两处必须一起改):
|
|
9
|
-
* - 判「命令可用」一律以**命令自身退出码**为准,不能用 `command -v`:
|
|
10
|
-
* cjc / cjpm 本身是仓颉程序,二进制在 PATH 里但没设 LD_LIBRARY_PATH 时
|
|
11
|
-
* 照样加载不了运行时——stdout 空、exit=127,而路径查得到。
|
|
12
|
-
* - Linux 上仓颉运行时 .so **不查 PATH**,只设 PATH 的表现是「找不到 libcangjie-runtime.so」。
|
|
13
|
-
* - `CANGJIE_STDX` 必须指 stdx 的**动态库目录**(<stdx>/<三元组>/dynamic/stdx),
|
|
14
|
-
* 指到包根也能过 create,要到新工程里 cjpm build 才报 dependency info is missing。
|
|
15
|
-
*/
|
|
16
|
-
package cli
|
|
17
|
-
|
|
18
|
-
import std.fs.*
|
|
19
|
-
import std.process.*
|
|
20
|
-
import std.time.*
|
|
21
|
-
|
|
22
|
-
/** 一条检查的结论:ok / warn / fail(只有 fail 影响退出码) */
|
|
23
|
-
enum CheckLevel {
|
|
24
|
-
| Ok
|
|
25
|
-
| Warn
|
|
26
|
-
| Fail
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** doctor 的自检结果统计 */
|
|
30
|
-
class DoctorReport {
|
|
31
|
-
var okCount = 0
|
|
32
|
-
var warnCount = 0
|
|
33
|
-
var failCount = 0
|
|
34
|
-
|
|
35
|
-
func record(level: CheckLevel): Unit {
|
|
36
|
-
match (level) {
|
|
37
|
-
case Ok => this.okCount++
|
|
38
|
-
case Warn => this.warnCount++
|
|
39
|
-
case Fail => this.failCount++
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 打印一行检查结论并计入统计:` ok 项目名 : 说明`;失败时另起一行给「怎么办」。
|
|
46
|
-
* 计数与打印绑在一起,避免「打了 ok 却没记账」这类统计与输出不一致的假象
|
|
47
|
-
* (第一版就是各调用点手写 record,结果 ok 行全没计数,汇总报成 0 项通过)。
|
|
48
|
-
*/
|
|
49
|
-
func doctorLine(rep: DoctorReport, level: CheckLevel, label: String, detail: String, hint: String): Unit {
|
|
50
|
-
rep.record(level)
|
|
51
|
-
let tag = match (level) {
|
|
52
|
-
case Ok => "ok "
|
|
53
|
-
case Warn => "warn"
|
|
54
|
-
case Fail => "FAIL"
|
|
55
|
-
}
|
|
56
|
-
println(" ${tag} ${label} : ${detail}")
|
|
57
|
-
if (hint.size > 0) {
|
|
58
|
-
println(" → ${hint}")
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** 跑一条命令取 (退出码, 首行非空 stdout),命令不存在时返回 (-1, "") */
|
|
63
|
-
func doctorProbe(exe: String, argv: Array<String>): (Int64, String) {
|
|
64
|
-
try {
|
|
65
|
-
// executeWithOutput 的 stdout 是 Array<UInt8>,按 UTF-8 解回字符串再按行取首行
|
|
66
|
-
let (code, rawOut, _) = executeWithOutput(exe, argv)
|
|
67
|
-
for (line in String.fromUtf8(rawOut).split("\n")) {
|
|
68
|
-
let t = trimAscii(line)
|
|
69
|
-
if (t.size > 0) {
|
|
70
|
-
return (code, t)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return (code, "")
|
|
74
|
-
} catch (e: Exception) {
|
|
75
|
-
let _ = e
|
|
76
|
-
return (-1, "")
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* 环境自检逐行输出。返回 0 = 必需项全过(warn 不算失败);1 = 有必需项失败。
|
|
82
|
-
*
|
|
83
|
-
* 必需项:cjc、cjpm、stdx、框架根、C 桥;其余是 warn(如项目不在、产物未构建、
|
|
84
|
-
* Windows 的 WebView2 提示)——那些是「还没到那一步」,不是环境坏。
|
|
85
|
-
*/
|
|
86
|
-
func cmdDoctor(): Int64 {
|
|
87
|
-
let rep = DoctorReport()
|
|
88
|
-
println("cj-tauri doctor — 环境自检(CLI ${cliVersion()})")
|
|
89
|
-
println("")
|
|
90
|
-
|
|
91
|
-
// ---- 基本环境 ----
|
|
92
|
-
doctorLine(rep, CheckLevel.Ok, "宿主平台", "${hostOsName()} (${hostTriple()})", "")
|
|
93
|
-
doctorLine(rep, CheckLevel.Ok, "工作目录", currentDir(), "")
|
|
94
|
-
|
|
95
|
-
// ---- 仓颉 SDK ----
|
|
96
|
-
let home = resolveCangjieHome()
|
|
97
|
-
match (home) {
|
|
98
|
-
case Some(h) =>
|
|
99
|
-
doctorLine(rep, CheckLevel.Ok, "CANGJIE_HOME", h, "")
|
|
100
|
-
case None =>
|
|
101
|
-
doctorLine(rep, CheckLevel.Fail, "CANGJIE_HOME", "未找到",
|
|
102
|
-
"设置 CANGJIE_HOME,或把 SDK 的 bin 加进 PATH(doctor 从 cjc
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
doctorLine(rep, CheckLevel.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
doctorLine(rep, CheckLevel.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
doctorLine(rep, CheckLevel.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* cj-tauri CLI:环境自检(doctor)
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有它:`info` 打印的是「解析结果」(路径是什么),能用但看不出「这条到底算不算过」——
|
|
5
|
+
* 每条都只是一行文本,人得自己对照 skill 里的判据。doctor 反过来:**每条检查自带结论**,
|
|
6
|
+
* 失败的给出下一步该干什么,让用户拿到包第一件事不用读文档。
|
|
7
|
+
*
|
|
8
|
+
* 判据与坑(与 skills/cj-tauri-env 同口径,两处必须一起改):
|
|
9
|
+
* - 判「命令可用」一律以**命令自身退出码**为准,不能用 `command -v`:
|
|
10
|
+
* cjc / cjpm 本身是仓颉程序,二进制在 PATH 里但没设 LD_LIBRARY_PATH 时
|
|
11
|
+
* 照样加载不了运行时——stdout 空、exit=127,而路径查得到。
|
|
12
|
+
* - Linux 上仓颉运行时 .so **不查 PATH**,只设 PATH 的表现是「找不到 libcangjie-runtime.so」。
|
|
13
|
+
* - `CANGJIE_STDX` 必须指 stdx 的**动态库目录**(<stdx>/<三元组>/dynamic/stdx),
|
|
14
|
+
* 指到包根也能过 create,要到新工程里 cjpm build 才报 dependency info is missing。
|
|
15
|
+
*/
|
|
16
|
+
package cli
|
|
17
|
+
|
|
18
|
+
import std.fs.*
|
|
19
|
+
import std.process.*
|
|
20
|
+
import std.time.*
|
|
21
|
+
|
|
22
|
+
/** 一条检查的结论:ok / warn / fail(只有 fail 影响退出码) */
|
|
23
|
+
enum CheckLevel {
|
|
24
|
+
| Ok
|
|
25
|
+
| Warn
|
|
26
|
+
| Fail
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** doctor 的自检结果统计 */
|
|
30
|
+
class DoctorReport {
|
|
31
|
+
var okCount = 0
|
|
32
|
+
var warnCount = 0
|
|
33
|
+
var failCount = 0
|
|
34
|
+
|
|
35
|
+
func record(level: CheckLevel): Unit {
|
|
36
|
+
match (level) {
|
|
37
|
+
case Ok => this.okCount++
|
|
38
|
+
case Warn => this.warnCount++
|
|
39
|
+
case Fail => this.failCount++
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 打印一行检查结论并计入统计:` ok 项目名 : 说明`;失败时另起一行给「怎么办」。
|
|
46
|
+
* 计数与打印绑在一起,避免「打了 ok 却没记账」这类统计与输出不一致的假象
|
|
47
|
+
* (第一版就是各调用点手写 record,结果 ok 行全没计数,汇总报成 0 项通过)。
|
|
48
|
+
*/
|
|
49
|
+
func doctorLine(rep: DoctorReport, level: CheckLevel, label: String, detail: String, hint: String): Unit {
|
|
50
|
+
rep.record(level)
|
|
51
|
+
let tag = match (level) {
|
|
52
|
+
case Ok => "ok "
|
|
53
|
+
case Warn => "warn"
|
|
54
|
+
case Fail => "FAIL"
|
|
55
|
+
}
|
|
56
|
+
println(" ${tag} ${label} : ${detail}")
|
|
57
|
+
if (hint.size > 0) {
|
|
58
|
+
println(" → ${hint}")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 跑一条命令取 (退出码, 首行非空 stdout),命令不存在时返回 (-1, "") */
|
|
63
|
+
func doctorProbe(exe: String, argv: Array<String>): (Int64, String) {
|
|
64
|
+
try {
|
|
65
|
+
// executeWithOutput 的 stdout 是 Array<UInt8>,按 UTF-8 解回字符串再按行取首行
|
|
66
|
+
let (code, rawOut, _) = executeWithOutput(exe, argv)
|
|
67
|
+
for (line in String.fromUtf8(rawOut).split("\n")) {
|
|
68
|
+
let t = trimAscii(line)
|
|
69
|
+
if (t.size > 0) {
|
|
70
|
+
return (code, t)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return (code, "")
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
let _ = e
|
|
76
|
+
return (-1, "")
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 环境自检逐行输出。返回 0 = 必需项全过(warn 不算失败);1 = 有必需项失败。
|
|
82
|
+
*
|
|
83
|
+
* 必需项:cjc、cjpm、stdx、框架根、C 桥;其余是 warn(如项目不在、产物未构建、
|
|
84
|
+
* Windows 的 WebView2 提示)——那些是「还没到那一步」,不是环境坏。
|
|
85
|
+
*/
|
|
86
|
+
func cmdDoctor(): Int64 {
|
|
87
|
+
let rep = DoctorReport()
|
|
88
|
+
println("cj-tauri doctor — 环境自检(CLI ${cliVersion()})")
|
|
89
|
+
println("")
|
|
90
|
+
|
|
91
|
+
// ---- 基本环境 ----
|
|
92
|
+
doctorLine(rep, CheckLevel.Ok, "宿主平台", "${hostOsName()} (${hostTriple()})", "")
|
|
93
|
+
doctorLine(rep, CheckLevel.Ok, "工作目录", currentDir(), "")
|
|
94
|
+
|
|
95
|
+
// ---- 仓颉 SDK ----
|
|
96
|
+
let home = resolveCangjieHome()
|
|
97
|
+
match (home) {
|
|
98
|
+
case Some(h) =>
|
|
99
|
+
doctorLine(rep, CheckLevel.Ok, "CANGJIE_HOME", h, "")
|
|
100
|
+
case None =>
|
|
101
|
+
doctorLine(rep, CheckLevel.Fail, "CANGJIE_HOME", "未找到",
|
|
102
|
+
"设置 CANGJIE_HOME,或把 SDK 的 bin 加进 PATH(doctor 从 cjc 反推);" +
|
|
103
|
+
"下载:https://cangjie-lang.cn/download(需登录华为账号),本项目要求 SDK 1.2.0 + stdx 1.2.0.1,两者必须配套")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let (cjcCode, cjcVer) = doctorProbe("cjc", ["-v"])
|
|
107
|
+
if (cjcCode == 0) {
|
|
108
|
+
doctorLine(rep, CheckLevel.Ok, "cjc", cjcVer, "")
|
|
109
|
+
} else {
|
|
110
|
+
// 退出码 127 = 二进制在 PATH 里但运行时加载不了(Linux 上就是 LD_LIBRARY_PATH 没设)
|
|
111
|
+
let why = if (cjcCode == 127) {
|
|
112
|
+
"在 PATH 里但运行不了(exit=127,通常是 LD_LIBRARY_PATH 缺仓颉运行时)"
|
|
113
|
+
} else {
|
|
114
|
+
"不可用(exit=${cjcCode})"
|
|
115
|
+
}
|
|
116
|
+
doctorLine(rep, CheckLevel.Fail, "cjc", why,
|
|
117
|
+
"把 SDK 的 bin 加进 PATH;Linux 另需把 runtime/lib/<平台> 加进 LD_LIBRARY_PATH")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let (cjpmCode, cjpmVer) = doctorProbe("cjpm", ["-v"])
|
|
121
|
+
if (cjpmCode == 0) {
|
|
122
|
+
doctorLine(rep, CheckLevel.Ok, "cjpm", cjpmVer, "")
|
|
123
|
+
} else {
|
|
124
|
+
doctorLine(rep, CheckLevel.Fail, "cjpm", "不可用(exit=${cjpmCode})",
|
|
125
|
+
"把 SDK 的 tools/bin 加进 PATH(cjpm 还需要 tools/lib 与运行时)")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
match (home) {
|
|
129
|
+
case Some(h) =>
|
|
130
|
+
match (findRuntimeLibDir(h)) {
|
|
131
|
+
case Some(rt) =>
|
|
132
|
+
// Linux 上「目录存在」不等于「已设进环境」——见文件头判据
|
|
133
|
+
if (hostIsWindows()) {
|
|
134
|
+
doctorLine(rep, CheckLevel.Ok, "仓颉运行时", rt, "")
|
|
135
|
+
} else {
|
|
136
|
+
let ld = envOr("LD_LIBRARY_PATH", "")
|
|
137
|
+
if (ld.contains(rt)) {
|
|
138
|
+
doctorLine(rep, CheckLevel.Ok, "仓颉运行时", "${rt} [已进 LD_LIBRARY_PATH]", "")
|
|
139
|
+
} else {
|
|
140
|
+
doctorLine(rep, CheckLevel.Warn, "仓颉运行时",
|
|
141
|
+
"${rt} [不在 LD_LIBRARY_PATH]",
|
|
142
|
+
"export LD_LIBRARY_PATH=\"${rt}:${'$'}LD_LIBRARY_PATH\"(CLI 自身会把子进程补上,手敲 cjc 才需要)")
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
case None =>
|
|
146
|
+
doctorLine(rep, CheckLevel.Fail, "仓颉运行时", "在 SDK 下未找到 runtime/lib/<平台>",
|
|
147
|
+
"检查 SDK 是否解压完整(runtime/lib/linux_x86_64_cjnative 等)")
|
|
148
|
+
}
|
|
149
|
+
case None => ()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---- 框架根 / stdx / C 桥 ----
|
|
153
|
+
var fwRoot = ""
|
|
154
|
+
match (resolveFrameworkRoot()) {
|
|
155
|
+
case Some(fw) =>
|
|
156
|
+
fwRoot = fw
|
|
157
|
+
doctorLine(rep, CheckLevel.Ok, "框架根目录", fw, "")
|
|
158
|
+
case None =>
|
|
159
|
+
doctorLine(rep, CheckLevel.Fail, "框架根目录", "未找到",
|
|
160
|
+
"在框架仓库内运行,或设置 CJ_TAURI_ROOT 指向框架根")
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let projRoot = findProjectRoot()
|
|
164
|
+
// 显式标注:if 两分支各自返回 Option<String> / None,不标注会让类型推断拿不到实参
|
|
165
|
+
let stdx: Option<String> = if (fwRoot.size > 0) {
|
|
166
|
+
resolveStdx(fwRoot, projRoot)
|
|
167
|
+
} else {
|
|
168
|
+
None
|
|
169
|
+
}
|
|
170
|
+
match (stdx) {
|
|
171
|
+
case Some(s) =>
|
|
172
|
+
// 指到包根时 stdx 目录本身也存在,但 build 会报 dependency info is missing:
|
|
173
|
+
// 这里按「里面有没有 .so / 目录是否叫 stdx」粗判一次,把坑提前暴露
|
|
174
|
+
doctorLine(rep, CheckLevel.Ok, "stdx", s, "")
|
|
175
|
+
case None =>
|
|
176
|
+
doctorLine(rep, CheckLevel.Fail, "stdx", "未找到",
|
|
177
|
+
"设置 CANGJIE_STDX 指向 <stdx>/<三元组>/dynamic/stdx(动态库目录,不是包根);" +
|
|
178
|
+
"下载:https://cangjie-lang.cn/download 同页的 stdx 包,版本须与 SDK 配套(1.2.0 配 1.2.0.1)")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (fwRoot.size > 0) {
|
|
182
|
+
let nativeDir = joinPath(fwRoot, "native")
|
|
183
|
+
let lib = joinPath(nativeDir, bridgeLibName())
|
|
184
|
+
if (exists(lib)) {
|
|
185
|
+
// 桥是本地产物、不入库:源码新过产物说明改了 C 桥没重建,链接期会 undefined reference
|
|
186
|
+
let newestSrc = newestBridgeSource(nativeDir)
|
|
187
|
+
if (newestSrc.size > 0 && FileInfo(lib).lastModificationTime < FileInfo(newestSrc).lastModificationTime) {
|
|
188
|
+
doctorLine(rep, CheckLevel.Warn, "C 桥", "${lib} [比源码旧]",
|
|
189
|
+
"改了 C 桥要重建:Linux `bash native/build_linux.sh`、Windows `native\\build_win.bat`")
|
|
190
|
+
} else {
|
|
191
|
+
doctorLine(rep, CheckLevel.Ok, "C 桥", "${lib} [已构建]", "")
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
doctorLine(rep, CheckLevel.Fail, "C 桥", "${lib} 不存在",
|
|
195
|
+
"`cj-tauri build` / `dev` 会自动构建;手敲则跑 native/build_linux.sh 或 build_win.bat")
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ---- 项目(可选:不在项目里不算环境问题) ----
|
|
200
|
+
match (projRoot) {
|
|
201
|
+
case Some(proj) =>
|
|
202
|
+
doctorLine(rep, CheckLevel.Ok, "项目根目录", proj, "")
|
|
203
|
+
let newForm = exists(joinPath(proj, "cj-tauri.toml"))
|
|
204
|
+
match (findAppBinary(proj, newForm)) {
|
|
205
|
+
case Some(bin) =>
|
|
206
|
+
if (fwRoot.size > 0 && !appBinaryIsFresh(bin, fwRoot)) {
|
|
207
|
+
doctorLine(rep, CheckLevel.Warn, "可执行产物", "${bin} [比框架源码旧]",
|
|
208
|
+
"改了框架要重建应用:`cj-tauri build`(旧产物认不出新环境变量,会让 info 卡住)")
|
|
209
|
+
} else {
|
|
210
|
+
doctorLine(rep, CheckLevel.Ok, "可执行产物", bin, "")
|
|
211
|
+
}
|
|
212
|
+
case None =>
|
|
213
|
+
doctorLine(rep, CheckLevel.Warn, "可执行产物", "未构建",
|
|
214
|
+
if (newForm) { "`cj-tauri sync` + `cj-tauri build`" } else { "`cj-tauri build`" })
|
|
215
|
+
}
|
|
216
|
+
case None =>
|
|
217
|
+
doctorLine(rep, CheckLevel.Warn, "项目根目录", "当前不在 cj-tauri 项目内",
|
|
218
|
+
"cd 到项目根(有 cjpm.toml 的那层)再跑 build / dev")
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ---- 图形栈(Linux 必需;Windows 由 WebView2 承担) ----
|
|
222
|
+
if (hostIsWindows()) {
|
|
223
|
+
doctorLine(rep, CheckLevel.Ok, "WebView2", "由系统提供(Win11 自带 Runtime)",
|
|
224
|
+
"构建桥时 WebView2 SDK 版本不得高于本机 Runtime 版本")
|
|
225
|
+
} else {
|
|
226
|
+
doctorCheckLinuxGraphics(rep)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---- 汇总 ----
|
|
230
|
+
println("")
|
|
231
|
+
println("检查完毕:${rep.okCount} 项通过,${rep.warnCount} 项告警,${rep.failCount} 项失败")
|
|
232
|
+
if (rep.failCount > 0) {
|
|
233
|
+
println("按上面每条 FAIL 的 → 提示逐条补齐;只到 warn 说明环境够跑,只是还没到那一步。")
|
|
234
|
+
return 1
|
|
235
|
+
}
|
|
236
|
+
println("环境齐了(warn 项按需处理)。")
|
|
237
|
+
return 0
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Linux 图形栈检查:C 桥的链接与运行都依赖 WebKitGTK / GTK3,缺 pkg-config 或开发包
|
|
242
|
+
* 会在链接期(找不到 -lwebkit2gtk-4.1)或运行期(找不到 .so)炸,提前在这里暴露。
|
|
243
|
+
*/
|
|
244
|
+
func doctorCheckLinuxGraphics(rep: DoctorReport): Unit {
|
|
245
|
+
let (pkgCode, _) = doctorProbe("pkg-config", ["--version"])
|
|
246
|
+
if (pkgCode != 0) {
|
|
247
|
+
doctorLine(rep, CheckLevel.Fail, "pkg-config", "不可用",
|
|
248
|
+
"sudo apt install pkg-config gcc libwebkit2gtk-4.1-dev libgtk-3-dev")
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
let (wCode, wVer) = doctorProbe("pkg-config", ["--modversion", "webkit2gtk-4.1"])
|
|
252
|
+
if (wCode == 0 && wVer.size > 0) {
|
|
253
|
+
doctorLine(rep, CheckLevel.Ok, "webkit2gtk-4.1", wVer, "")
|
|
254
|
+
} else {
|
|
255
|
+
doctorLine(rep, CheckLevel.Fail, "webkit2gtk-4.1", "未找到",
|
|
256
|
+
"sudo apt install libwebkit2gtk-4.1-dev(C 桥链接它)")
|
|
257
|
+
}
|
|
258
|
+
let (gCode, gVer) = doctorProbe("pkg-config", ["--modversion", "gtk+-3.0"])
|
|
259
|
+
if (gCode == 0 && gVer.size > 0) {
|
|
260
|
+
doctorLine(rep, CheckLevel.Ok, "gtk+-3.0", gVer, "")
|
|
261
|
+
} else {
|
|
262
|
+
doctorLine(rep, CheckLevel.Fail, "gtk+-3.0", "未找到",
|
|
263
|
+
"sudo apt install libgtk-3-dev(C 桥链接它)")
|
|
264
|
+
}
|
|
265
|
+
let (cCode, cVer) = doctorProbe("gcc", ["--version"])
|
|
266
|
+
if (cCode == 0) {
|
|
267
|
+
doctorLine(rep, CheckLevel.Ok, "gcc", cVer, "")
|
|
268
|
+
} else {
|
|
269
|
+
doctorLine(rep, CheckLevel.Fail, "gcc", "不可用",
|
|
270
|
+
"sudo apt install gcc(构建 C 桥)")
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 框架 native/ 下最新的 C 源文件(含 core/ 与 platforms/),用于判桥产物新不新。
|
|
276
|
+
* 找不到任何源文件时返回空串(调用方跳过新旧比较)。
|
|
277
|
+
*/
|
|
278
|
+
func newestBridgeSource(nativeDir: String): String {
|
|
279
|
+
var newest = ""
|
|
280
|
+
// FileInfo.lastModificationTime 是 DateTime(不是 epoch 整数),比较用 Option 装上一个值;
|
|
281
|
+
// 带泛型实参的 None 必须写全,裸 None 推不出 Option<T> 的实参
|
|
282
|
+
var newestTime: Option<DateTime> = Option<DateTime>.None
|
|
283
|
+
let dirs = [joinPath(nativeDir, "core"), joinPath(joinPath(nativeDir, "platforms"), hostOsName())]
|
|
284
|
+
for (d in dirs) {
|
|
285
|
+
if (!exists(d)) {
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
288
|
+
for (f in Directory.readFrom(d)) {
|
|
289
|
+
let name = f.name
|
|
290
|
+
if (!(name.endsWith(".c") || name.endsWith(".h"))) {
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
let full = joinPath(d, name)
|
|
294
|
+
let t = FileInfo(full).lastModificationTime
|
|
295
|
+
match (newestTime) {
|
|
296
|
+
case None =>
|
|
297
|
+
newestTime = Some(t)
|
|
298
|
+
newest = full
|
|
299
|
+
case Some(prev) =>
|
|
300
|
+
if (t > prev) {
|
|
301
|
+
newestTime = Some(t)
|
|
302
|
+
newest = full
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return newest
|
|
308
|
+
}
|