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/manifest.cj
CHANGED
|
@@ -1,558 +1,632 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cj-tauri.toml 清单(RFC-002 §3):解析 + 校验 + 桌面骨架派生
|
|
3
|
-
*
|
|
4
|
-
* 批次 2 用受控 TOML 子集并手写解析(stdx 无 toml 模块,实测);子集约定:
|
|
5
|
-
* - 段:[app] / [[windows]] / [assets] / [capabilities],无嵌套表;
|
|
6
|
-
* - 值:带引号字符串、整数、true/false;引号值后不写行内注释(整行 # 注释不受限);
|
|
7
|
-
* - 未知键直接报错:清单由本 CLI 生态生成与消费,拼错键要在 check 期暴露。
|
|
8
|
-
*/
|
|
9
|
-
package cli
|
|
10
|
-
|
|
11
|
-
import std.collection.*
|
|
12
|
-
import std.convert.*
|
|
13
|
-
|
|
14
|
-
/** [[windows]] 的一条声明(RFC §3) */
|
|
15
|
-
public class ManifestWindow {
|
|
16
|
-
public var label: String = "main"
|
|
17
|
-
public var title: String = ""
|
|
18
|
-
public var width: Int64 = 1100
|
|
19
|
-
public var height: Int64 = 720
|
|
20
|
-
public var resizable: Bool = true
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** 清单内存模型;字段默认值即「最小清单」的缺省(RFC §3 约定值) */
|
|
24
|
-
public class Manifest {
|
|
25
|
-
public var appId: String = ""
|
|
26
|
-
public var name: String = ""
|
|
27
|
-
public var version: String = ""
|
|
28
|
-
public var icon: String = ""
|
|
29
|
-
public var entry: String = "src/main.cj"
|
|
30
|
-
public var pkg: String = "" // 业务 cjpm 包名:sync 生成 launcher 的 import 用
|
|
31
|
-
public var windows: ArrayList<ManifestWindow> = ArrayList<ManifestWindow>()
|
|
32
|
-
public var assetsRoot: String = "assets"
|
|
33
|
-
public var page: String = "ui/index.html"
|
|
34
|
-
public var capabilitiesDir: String = "assets/capabilities"
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*/
|
|
53
|
-
public
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
case
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
case
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
"
|
|
335
|
-
"
|
|
336
|
-
"
|
|
337
|
-
"
|
|
338
|
-
"
|
|
339
|
-
"
|
|
340
|
-
"
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
let
|
|
362
|
-
return "
|
|
363
|
-
"
|
|
364
|
-
"
|
|
365
|
-
"
|
|
366
|
-
"
|
|
367
|
-
"
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
"
|
|
371
|
-
"
|
|
372
|
-
"\
|
|
373
|
-
"
|
|
374
|
-
"
|
|
375
|
-
"
|
|
376
|
-
"
|
|
377
|
-
"
|
|
378
|
-
"\n
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
"
|
|
394
|
-
"\
|
|
395
|
-
"
|
|
396
|
-
"
|
|
397
|
-
"
|
|
398
|
-
"
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return "
|
|
427
|
-
"
|
|
428
|
-
"
|
|
429
|
-
"\n" +
|
|
430
|
-
"
|
|
431
|
-
"
|
|
432
|
-
"
|
|
433
|
-
"
|
|
434
|
-
"
|
|
435
|
-
"\n" +
|
|
436
|
-
"
|
|
437
|
-
"
|
|
438
|
-
"
|
|
439
|
-
"
|
|
440
|
-
"
|
|
441
|
-
"
|
|
442
|
-
"\n" +
|
|
443
|
-
"
|
|
444
|
-
"
|
|
445
|
-
"
|
|
446
|
-
"\n" +
|
|
447
|
-
"
|
|
448
|
-
"
|
|
449
|
-
"
|
|
450
|
-
"
|
|
451
|
-
"
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
"
|
|
461
|
-
"
|
|
462
|
-
"
|
|
463
|
-
"
|
|
464
|
-
"
|
|
465
|
-
"
|
|
466
|
-
"
|
|
467
|
-
"
|
|
468
|
-
"
|
|
469
|
-
"\n" +
|
|
470
|
-
"
|
|
471
|
-
"
|
|
472
|
-
"
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
" Hilog.info(1, \"CjTauri\", \"
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
"
|
|
502
|
-
"
|
|
503
|
-
"
|
|
504
|
-
"import
|
|
505
|
-
"
|
|
506
|
-
"
|
|
507
|
-
"
|
|
508
|
-
"
|
|
509
|
-
"
|
|
510
|
-
"
|
|
511
|
-
"
|
|
512
|
-
"
|
|
513
|
-
"
|
|
514
|
-
"
|
|
515
|
-
"
|
|
516
|
-
"
|
|
517
|
-
"
|
|
518
|
-
"
|
|
519
|
-
" }\n" +
|
|
520
|
-
"\n" +
|
|
521
|
-
" public
|
|
522
|
-
"
|
|
523
|
-
"
|
|
524
|
-
"
|
|
525
|
-
"
|
|
526
|
-
"
|
|
527
|
-
" }\n" +
|
|
528
|
-
"\n" +
|
|
529
|
-
"
|
|
530
|
-
"
|
|
531
|
-
"
|
|
532
|
-
"
|
|
533
|
-
"
|
|
534
|
-
"
|
|
535
|
-
"
|
|
536
|
-
"
|
|
537
|
-
"
|
|
538
|
-
"
|
|
539
|
-
"
|
|
540
|
-
"
|
|
541
|
-
"
|
|
542
|
-
"
|
|
543
|
-
"
|
|
544
|
-
"
|
|
545
|
-
"
|
|
546
|
-
"
|
|
547
|
-
"
|
|
548
|
-
"
|
|
549
|
-
"
|
|
550
|
-
"
|
|
551
|
-
"
|
|
552
|
-
"
|
|
553
|
-
"
|
|
554
|
-
"
|
|
555
|
-
"
|
|
556
|
-
"
|
|
557
|
-
|
|
558
|
-
|
|
1
|
+
/**
|
|
2
|
+
* cj-tauri.toml 清单(RFC-002 §3):解析 + 校验 + 桌面骨架派生
|
|
3
|
+
*
|
|
4
|
+
* 批次 2 用受控 TOML 子集并手写解析(stdx 无 toml 模块,实测);子集约定:
|
|
5
|
+
* - 段:[app] / [[windows]] / [assets] / [capabilities] / [links],无嵌套表;
|
|
6
|
+
* - 值:带引号字符串、整数、true/false;引号值后不写行内注释(整行 # 注释不受限);
|
|
7
|
+
* - 未知键直接报错:清单由本 CLI 生态生成与消费,拼错键要在 check 期暴露。
|
|
8
|
+
*/
|
|
9
|
+
package cli
|
|
10
|
+
|
|
11
|
+
import std.collection.*
|
|
12
|
+
import std.convert.*
|
|
13
|
+
|
|
14
|
+
/** [[windows]] 的一条声明(RFC §3) */
|
|
15
|
+
public class ManifestWindow {
|
|
16
|
+
public var label: String = "main"
|
|
17
|
+
public var title: String = ""
|
|
18
|
+
public var width: Int64 = 1100
|
|
19
|
+
public var height: Int64 = 720
|
|
20
|
+
public var resizable: Bool = true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 清单内存模型;字段默认值即「最小清单」的缺省(RFC §3 约定值) */
|
|
24
|
+
public class Manifest {
|
|
25
|
+
public var appId: String = ""
|
|
26
|
+
public var name: String = ""
|
|
27
|
+
public var version: String = ""
|
|
28
|
+
public var icon: String = ""
|
|
29
|
+
public var entry: String = "src/main.cj"
|
|
30
|
+
public var pkg: String = "" // 业务 cjpm 包名:sync 生成 launcher 的 import 用
|
|
31
|
+
public var windows: ArrayList<ManifestWindow> = ArrayList<ManifestWindow>()
|
|
32
|
+
public var assetsRoot: String = "assets"
|
|
33
|
+
public var page: String = "ui/index.html"
|
|
34
|
+
public var capabilitiesDir: String = "assets/capabilities"
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 各目标端追加的链接选项(清单 `[links]` 段,键见 apply 里那三条)
|
|
38
|
+
*
|
|
39
|
+
* **为什么需要这个口子**:SDK 没有仓颉绑定的原生能力只能 FFI 直连系统 `.so`,
|
|
40
|
+
* 而符号必须出现在**最终链接**那一侧——桌面是 `targets/desktop`(output-type =
|
|
41
|
+
* executable),鸿蒙是 `targets/ohos/entry`(dynamic,框架自己的 cjpm.toml 写了不生效)。
|
|
42
|
+
* 这两份 toml 都是生成物(「不要手改」),所以在清单留口子;否则用户新增一个原生库
|
|
43
|
+
* 就得改框架生成器,等于 fork。
|
|
44
|
+
*
|
|
45
|
+
* **为什么不写成一条通用 link-option**:同一个库在三个目标端上名字通常不同
|
|
46
|
+
* (Linux `-lfoo` / Windows `foo.lib` / 鸿蒙 `-lfoo_ndk.z`),一条会把三端绑死;
|
|
47
|
+
* 分端各一,写错一端不影响其它端。
|
|
48
|
+
*
|
|
49
|
+
* **语义是「追加」而不是「覆盖」**:框架默认值(`-lcjtbridge`、`-lpasteboard`
|
|
50
|
+
* 等)不能被用户清单顶掉;空串 = 不追加,生成物逐字节与加入本字段之前一致
|
|
51
|
+
* (老工程 check 不会因升级 CLI 判漂移)。
|
|
52
|
+
*/
|
|
53
|
+
public var linkDesktopLinux: String = ""
|
|
54
|
+
public var linkDesktopWindows: String = ""
|
|
55
|
+
public var linkOhos: String = ""
|
|
56
|
+
|
|
57
|
+
/** 可执行名:appId 最后一段 */
|
|
58
|
+
public func exeName(): String {
|
|
59
|
+
let parts = appId.split(".")
|
|
60
|
+
if (parts.size == 0) {
|
|
61
|
+
return "app"
|
|
62
|
+
}
|
|
63
|
+
return parts[parts.size - 1]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 骨架模块名(cjpm [package] name):默认 exeName(appId 最后一段);与业务包名
|
|
68
|
+
* (清单 package = 仓颉包名)撞名时加 _app 后缀。cjpm 强制两条约束(批次 3-PR2 实测):
|
|
69
|
+
* ① 每个工程的 toml 模块名 == 其源文件包名(music:toml `music_app` ↔ `package music_app`);
|
|
70
|
+
* ② 依赖树内模块名唯一。业务包名被 ① 钉死为 package,最常见的命名
|
|
71
|
+
* com.example.<pkg> + package = <pkg> 必然撞 ②,撞时骨架模块名为 <pkg>_app。
|
|
72
|
+
* 二进制名与模块名无关:cjpm 可执行工程恒输出 target/release/bin/main。
|
|
73
|
+
*/
|
|
74
|
+
public func skeletonName(): String {
|
|
75
|
+
if (this.exeName() == this.pkg) {
|
|
76
|
+
return this.exeName() + "_app"
|
|
77
|
+
}
|
|
78
|
+
return this.exeName()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 主窗口:无 [[windows]] 时按 name 给默认窗 */
|
|
82
|
+
public func mainWindow(): ManifestWindow {
|
|
83
|
+
if (windows.size == 0) {
|
|
84
|
+
let w = ManifestWindow()
|
|
85
|
+
w.title = name
|
|
86
|
+
return w
|
|
87
|
+
}
|
|
88
|
+
return windows.get(0).getOrThrow()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------- 解析 ----------
|
|
92
|
+
|
|
93
|
+
public static func fromToml(text: String, source: String): Manifest {
|
|
94
|
+
let m = Manifest()
|
|
95
|
+
var section = ""
|
|
96
|
+
var lineNo: Int64 = 0
|
|
97
|
+
for (raw in text.split("\n")) {
|
|
98
|
+
lineNo += 1
|
|
99
|
+
let trimmed = raw.trimAscii()
|
|
100
|
+
if (trimmed.size == 0 || trimmed.startsWith("#")) {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
if (trimmed.startsWith("[")) {
|
|
104
|
+
section = trimmed.replace("[", "").replace("]", "").trimAscii()
|
|
105
|
+
if (section == "windows") {
|
|
106
|
+
m.windows.add(ManifestWindow())
|
|
107
|
+
}
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
let kv = trimmed.split("=")
|
|
111
|
+
if (kv.size != 2) {
|
|
112
|
+
throw IllegalArgumentException("${source}:${lineNo} 无法解析的行(应为 key = value):${trimmed}")
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
m.apply(section, kv[0].trimAscii(), kv[1].trimAscii(), "${source}:${lineNo}")
|
|
116
|
+
} catch (e: Exception) {
|
|
117
|
+
throw IllegalArgumentException("${e.message}")
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return m
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private func apply(section: String, key: String, value: String, at: String): Unit {
|
|
124
|
+
if (section.size == 0) {
|
|
125
|
+
throw IllegalArgumentException("${at}: 键出现在任何段之前:${key}")
|
|
126
|
+
}
|
|
127
|
+
if (section == "app") {
|
|
128
|
+
match (key) {
|
|
129
|
+
case "id" => this.appId = strValue(value, at)
|
|
130
|
+
case "name" => this.name = strValue(value, at)
|
|
131
|
+
case "version" => this.version = strValue(value, at)
|
|
132
|
+
case "icon" => this.icon = strValue(value, at)
|
|
133
|
+
case "entry" => this.entry = strValue(value, at)
|
|
134
|
+
case "package" => this.pkg = strValue(value, at)
|
|
135
|
+
case _ => throw IllegalArgumentException("${at}: 未知的 [app] 键:${key}")
|
|
136
|
+
}
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
if (section == "windows") {
|
|
140
|
+
if (this.windows.size == 0) {
|
|
141
|
+
this.windows.add(ManifestWindow())
|
|
142
|
+
}
|
|
143
|
+
let w = this.windows.get(this.windows.size - 1).getOrThrow()
|
|
144
|
+
match (key) {
|
|
145
|
+
case "label" => w.label = strValue(value, at)
|
|
146
|
+
case "title" => w.title = strValue(value, at)
|
|
147
|
+
case "width" => w.width = intValue(value, at)
|
|
148
|
+
case "height" => w.height = intValue(value, at)
|
|
149
|
+
case "resizable" => w.resizable = boolValue(value, at)
|
|
150
|
+
case _ => throw IllegalArgumentException("${at}: 未知的 [[windows]] 键:${key}")
|
|
151
|
+
}
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
if (section == "assets") {
|
|
155
|
+
match (key) {
|
|
156
|
+
case "root" => this.assetsRoot = strValue(value, at)
|
|
157
|
+
case "page" => this.page = strValue(value, at)
|
|
158
|
+
case _ => throw IllegalArgumentException("${at}: 未知的 [assets] 键:${key}")
|
|
159
|
+
}
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
if (section == "capabilities") {
|
|
163
|
+
match (key) {
|
|
164
|
+
case "dir" => this.capabilitiesDir = strValue(value, at)
|
|
165
|
+
case _ => throw IllegalArgumentException("${at}: 未知的 [capabilities] 键:${key}")
|
|
166
|
+
}
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
if (section == "links") {
|
|
170
|
+
match (key) {
|
|
171
|
+
case "desktop-linux" => this.linkDesktopLinux = strValue(value, at)
|
|
172
|
+
case "desktop-windows" => this.linkDesktopWindows = strValue(value, at)
|
|
173
|
+
case "ohos" => this.linkOhos = strValue(value, at)
|
|
174
|
+
case _ => throw IllegalArgumentException("${at}: 未知的 [links] 键:${key}")
|
|
175
|
+
}
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
throw IllegalArgumentException("${at}: 未知段:[${section}]")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** 引号值:取首个引号内内容(引号后的行内注释一并忽略);裸值原样 */
|
|
182
|
+
private static func strValue(raw: String, at: String): String {
|
|
183
|
+
let v = raw.trimAscii()
|
|
184
|
+
if (v.startsWith("\"")) {
|
|
185
|
+
let parts = v.split("\"")
|
|
186
|
+
if (parts.size < 3) {
|
|
187
|
+
throw IllegalArgumentException("${at}: 引号值缺右引号:${v}")
|
|
188
|
+
}
|
|
189
|
+
return parts[1]
|
|
190
|
+
}
|
|
191
|
+
return v
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private static func intValue(raw: String, at: String): Int64 {
|
|
195
|
+
let v = raw.trimAscii()
|
|
196
|
+
if (v.startsWith("\"")) {
|
|
197
|
+
throw IllegalArgumentException("${at}: 应为整数,却是字符串:${v}")
|
|
198
|
+
}
|
|
199
|
+
let body = v.split("#")[0].trimAscii()
|
|
200
|
+
try {
|
|
201
|
+
return Int64.parse(body)
|
|
202
|
+
} catch (e: Exception) {
|
|
203
|
+
throw IllegalArgumentException("${at}: 不是整数:${body}")
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private static func boolValue(raw: String, at: String): Bool {
|
|
208
|
+
let v = raw.trimAscii().split("#")[0].trimAscii()
|
|
209
|
+
if (v == "true") {
|
|
210
|
+
return true
|
|
211
|
+
}
|
|
212
|
+
if (v == "false") {
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
throw IllegalArgumentException("${at}: 应为 true/false:${v}")
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------- 校验 ----------
|
|
219
|
+
|
|
220
|
+
/** 纯静态校验(不碰文件系统);返回错误列表,空 = 通过。文件系统检查在 check 命令里 */
|
|
221
|
+
public func validate(): ArrayList<String> {
|
|
222
|
+
let errs = ArrayList<String>()
|
|
223
|
+
if (this.appId.size == 0) {
|
|
224
|
+
errs.add("[app] id 不能为空")
|
|
225
|
+
} else {
|
|
226
|
+
let segs = this.appId.split(".")
|
|
227
|
+
if (segs.size < 2) {
|
|
228
|
+
errs.add("[app] id 至少两段反向域名(如 com.example.music):${this.appId}")
|
|
229
|
+
}
|
|
230
|
+
for (seg in segs) {
|
|
231
|
+
if (seg.size == 0) {
|
|
232
|
+
errs.add("[app] id 的段不能为空:${this.appId}")
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
// 段须字母开头、只含小写字母/数字/下划线——同时满足 Tauri identifier 习惯与
|
|
236
|
+
// 鸿蒙 bundleName 规范(bundleNameFrom 会给 my-app 这类名字生成 pr4_app 段,
|
|
237
|
+
// 下划线是鸿蒙侧的合法字符,不能禁)
|
|
238
|
+
var bad = false
|
|
239
|
+
for (i in 0..seg.size) {
|
|
240
|
+
let c = Int64(seg.toArray()[i])
|
|
241
|
+
if (i == 0) {
|
|
242
|
+
if (c < 97 || c > 122) {
|
|
243
|
+
bad = true
|
|
244
|
+
break
|
|
245
|
+
}
|
|
246
|
+
} else if (!((c >= 48 && c <= 57) || (c >= 97 && c <= 122) || c == 95)) {
|
|
247
|
+
bad = true
|
|
248
|
+
break
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (bad) {
|
|
252
|
+
errs.add("[app] id 的段须字母开头、只含小写字母/数字/下划线(段=${seg})")
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (this.name.size == 0) {
|
|
257
|
+
errs.add("[app] name 不能为空")
|
|
258
|
+
}
|
|
259
|
+
if (this.version.size == 0) {
|
|
260
|
+
errs.add("[app] version 不能为空")
|
|
261
|
+
}
|
|
262
|
+
if (this.pkg.size == 0) {
|
|
263
|
+
errs.add("[app] package 不能为空(sync 生成 launcher 的 import 需要)")
|
|
264
|
+
}
|
|
265
|
+
let labels = ArrayList<String>()
|
|
266
|
+
for (w in this.windows) {
|
|
267
|
+
if (w.label.size == 0) {
|
|
268
|
+
errs.add("[[windows]] label 不能为空")
|
|
269
|
+
continue
|
|
270
|
+
}
|
|
271
|
+
var dup = false
|
|
272
|
+
for (seen in labels) {
|
|
273
|
+
if (seen == w.label) {
|
|
274
|
+
dup = true
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (dup) {
|
|
279
|
+
errs.add("[[windows]] label 重复:${w.label}")
|
|
280
|
+
} else {
|
|
281
|
+
labels.add(w.label)
|
|
282
|
+
}
|
|
283
|
+
if (w.width <= 0 || w.height <= 0) {
|
|
284
|
+
errs.add("[[windows]] ${w.label} 尺寸必须为正")
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (this.page.size == 0) {
|
|
288
|
+
errs.add("[assets] page 不能为空")
|
|
289
|
+
}
|
|
290
|
+
this.checkLink(errs, "[links] desktop-linux", this.linkDesktopLinux)
|
|
291
|
+
this.checkLink(errs, "[links] desktop-windows", this.linkDesktopWindows)
|
|
292
|
+
this.checkLink(errs, "[links] ohos", this.linkOhos)
|
|
293
|
+
return errs
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 追加链接选项的字面量校验
|
|
298
|
+
*
|
|
299
|
+
* `[links]` 的值会被原样拼进生成 toml 的双引号里,所以双引号与换行必须当场拒:
|
|
300
|
+
* 拼坏了的话报错点会漂到 cjpm 的解析里(那里只会说「某行语法错」),看不出是清单写错。
|
|
301
|
+
* 注意 `strValue` 对**带引号**的值取的是首个引号内的内容,所以带引号写法里的内层引号
|
|
302
|
+
* 已被截断;这条守的是裸值写法与后续拼接。
|
|
303
|
+
*/
|
|
304
|
+
private func checkLink(errs: ArrayList<String>, label: String, v: String): Unit {
|
|
305
|
+
if (v.contains("\"")) {
|
|
306
|
+
errs.add(label + " 不能含双引号(会破坏生成 toml 的字符串)")
|
|
307
|
+
}
|
|
308
|
+
if (v.contains("\n") || v.contains("\r")) {
|
|
309
|
+
errs.add(label + " 不能含换行")
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ---------- 桌面骨架派生(RFC §7.1/§7.2;批次 2 仅 desktop) ----------
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 把清单 `[links]` 里的用户追加项拼到框架默认链接选项之后
|
|
317
|
+
*
|
|
318
|
+
* 空串原样返回:未写 `[links]` 的老工程,生成物必须与加入本能力之前**逐字节一致**,
|
|
319
|
+
* 否则升级 CLI 后 `cj-tauri check` 会把所有既有工程判成漂移。
|
|
320
|
+
*/
|
|
321
|
+
private static func appendLink(base: String, extra: String): String {
|
|
322
|
+
let e = extra.trimAscii()
|
|
323
|
+
if (e.size == 0) {
|
|
324
|
+
return base
|
|
325
|
+
}
|
|
326
|
+
return base + " " + e
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** targets/desktop/cjpm.toml;链接段照抄 examples/music 的既有配置(AGENTS:产物口径不变) */
|
|
330
|
+
public func renderDesktopToml(fwRoot: String, cliVer: String): String {
|
|
331
|
+
let native = toTomlPath(fwRoot) + "/native"
|
|
332
|
+
// TOML 注释只认 '#'('//' 是仓颉/C 风格,cjpm 的解析器会当场拒绝),指纹头与 .cj 生成物同款但换字符
|
|
333
|
+
return "# generated by cj-tauri sync (v" + cliVer + ") — 不要手改;改动会被 check 判为漂移\n" +
|
|
334
|
+
"# 派生自 cj-tauri.toml:" + this.appId + "@" + this.version + "\n" +
|
|
335
|
+
"[package]\n" +
|
|
336
|
+
" cjc-version = \"1.0.5\"\n" +
|
|
337
|
+
" name = \"" + this.skeletonName() + "\"\n" +
|
|
338
|
+
" description = \"generated desktop skeleton for " + this.appId + "\"\n" +
|
|
339
|
+
" version = \"" + this.version + "\"\n" +
|
|
340
|
+
" target-dir = \"\"\n" +
|
|
341
|
+
" output-type = \"executable\"\n" +
|
|
342
|
+
" compile-option = \"\"\n" +
|
|
343
|
+
" override-compile-option = \"\"\n" +
|
|
344
|
+
" link-option = \"\"\n" +
|
|
345
|
+
" package-configuration = {}\n" +
|
|
346
|
+
"\n[dependencies]\n" +
|
|
347
|
+
" cjTauri = { path = \"" + toTomlPath(fwRoot) + "\" }\n" +
|
|
348
|
+
" " + this.pkg + " = { path = \"../..\" }\n" +
|
|
349
|
+
"\n[target.x86_64-unknown-linux-gnu]\n" +
|
|
350
|
+
" link-option = \"" + appendLink("-L" + native + " -lcjtbridge -lwebkit2gtk-4.1 -lgtk-3 -lgdk-3 -lglib-2.0 -lgio-2.0 -lsoup-3.0 -ljavascriptcoregtk-4.1 -lgobject-2.0", this.linkDesktopLinux) + "\"\n" +
|
|
351
|
+
"\n[target.x86_64-unknown-linux-gnu.bin-dependencies]\n" +
|
|
352
|
+
" path-option = [\"" + "$" + "{CANGJIE_STDX}\"]\n" +
|
|
353
|
+
"\n[target.x86_64-w64-mingw32]\n" +
|
|
354
|
+
" link-option = \"" + appendLink("-L" + native + " -lcjtbridge --subsystem=windows", this.linkDesktopWindows) + "\"\n" +
|
|
355
|
+
"\n[target.x86_64-w64-mingw32.bin-dependencies]\n" +
|
|
356
|
+
" path-option = [\"" + "$" + "{CANGJIE_STDX}\"]\n"
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** targets/desktop/src/main.cj:launcher 调业务装配点 build()(批次 3,RFC §4.1/§4.2) */
|
|
360
|
+
public func renderDesktopMain(cliVer: String): String {
|
|
361
|
+
let w = this.mainWindow()
|
|
362
|
+
return "// generated by cj-tauri sync (v" + cliVer + ") — 不要手改;改动会被 check 判为漂移\n" +
|
|
363
|
+
"// 桌面 launcher:页面/窗口/资产/能力目录由清单派生;业务装配点 " + this.pkg + ".build()(RFC §4.1/§4.2)\n" +
|
|
364
|
+
"package " + this.skeletonName() + "\n" +
|
|
365
|
+
"\n" +
|
|
366
|
+
"import cjTauri.*\n" +
|
|
367
|
+
"import cjTauri.platform.*\n" +
|
|
368
|
+
"import " + this.pkg + ".*\n" +
|
|
369
|
+
"\n" +
|
|
370
|
+
"main(): Int64 {\n" +
|
|
371
|
+
" let spec = build()\n" +
|
|
372
|
+
" .page(PageSource.Asset(\"" + this.page + "\"))\n" +
|
|
373
|
+
" .window(WindowConfig(\"${w.title}\", ${w.width}, ${w.height}))\n" +
|
|
374
|
+
" .assets(DirectoryAssetResolver(\"" + this.assetsRoot + "\"))\n" +
|
|
375
|
+
" .capabilities(\"" + this.capabilitiesDir + "\")\n" +
|
|
376
|
+
" launch(spec)\n" +
|
|
377
|
+
" return 0\n" +
|
|
378
|
+
"}\n"
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ---------- ohos 骨架派生(RFC §7.1/§7.3;批次 3-PR3) ----------
|
|
382
|
+
// 生成策略:ohos-skeleton 模板 copyTree 整体拷渲染(静态物 + 图标),4 个清单派生文件用下列
|
|
383
|
+
// 渲染器覆盖——check 的漂移检测复用同一批渲染器,生成与校验天然一致。
|
|
384
|
+
|
|
385
|
+
/** 生成物里要写字面 `${X}`:仓颉源码中 ${} 是插值,只能拼接出来 */
|
|
386
|
+
private static func interp(name: String): String {
|
|
387
|
+
return "$" + "{" + name + "}"
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** targets/ohos/AppScope/app.json5:bundleName=id、vendor=name、versionName=version 全部清单派生 */
|
|
391
|
+
public func renderOhosAppJson(cliVer: String): String {
|
|
392
|
+
return "// generated by cj-tauri sync (v" + cliVer + ") — 不要手改;改动会被 check 判为漂移\n" +
|
|
393
|
+
"{\n" +
|
|
394
|
+
" \"app\": {\n" +
|
|
395
|
+
" /* bundleName 是应用在设备上的唯一 ID:两个应用写同一个 bundleName 会互相覆盖安装。\n" +
|
|
396
|
+
" 改它必须重新签名——DevEco 的「自动签名」会按新包名重新申请 profile。\n" +
|
|
397
|
+
" 取值来自清单 [app] id(sync 保证与清单一致)。 */\n" +
|
|
398
|
+
" \"bundleName\": \"" + this.appId + "\",\n" +
|
|
399
|
+
" \"vendor\": \"" + this.name + "\",\n" +
|
|
400
|
+
" \"versionCode\": 1000000,\n" +
|
|
401
|
+
" \"versionName\": \"" + this.version + "\",\n" +
|
|
402
|
+
" \"buildVersion\": \"1\",\n" +
|
|
403
|
+
" \"icon\": \"$media:layered_image\",\n" +
|
|
404
|
+
" \"label\": \"$string:app_name\"\n" +
|
|
405
|
+
" }\n" +
|
|
406
|
+
"}\n"
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* targets/ohos/entry/cjpm.toml:dynamic 入口模块
|
|
411
|
+
*
|
|
412
|
+
* 与模板的差异:① 业务包路径依赖(../../.. = 工程根,业务代码只有一份——RFC §7.3);
|
|
413
|
+
* ② version 随清单;③ 框架路径绝对化(本机口径,与 create 同规矩)。
|
|
414
|
+
* 模块名恒为 ohos_app_cangjie_entry(module.json5 的 srcEntry 钉死,不参与清单命名)。
|
|
415
|
+
*/
|
|
416
|
+
public func renderOhosEntryToml(fwRoot: String, cliVer: String): String {
|
|
417
|
+
let dc = Manifest.interp("DEVECO_CANGJIE_HOME")
|
|
418
|
+
let dn = Manifest.interp("DEVECO_OH_NATIVE_HOME")
|
|
419
|
+
let ce = Manifest.interp("COMPILE_CONDITION_ENTRY")
|
|
420
|
+
let a1 = Manifest.interp("AARCH64_LIBS")
|
|
421
|
+
let a2 = Manifest.interp("AARCH64_MACRO_LIBS")
|
|
422
|
+
let a3 = Manifest.interp("AARCH64_KIT_LIBS")
|
|
423
|
+
let x1 = Manifest.interp("X86_64_OHOS_LIBS")
|
|
424
|
+
let x2 = Manifest.interp("X86_64_OHOS_MACRO_LIBS")
|
|
425
|
+
let x3 = Manifest.interp("X86_64_OHOS_KIT_LIBS")
|
|
426
|
+
return "# generated by cj-tauri sync (v" + cliVer + ") — 不要手改;改动会被 check 判为漂移\n" +
|
|
427
|
+
"[package]\n" +
|
|
428
|
+
" cjc-version = \"1.1.3\"\n" +
|
|
429
|
+
" compile-option = \"--dy-std --cfg=\\\"" + ce + "\\\"\"\n" +
|
|
430
|
+
" description = \"" + this.name + " - cj-tauri 鸿蒙入口 module\"\n" +
|
|
431
|
+
" name = \"ohos_app_cangjie_entry\"\n" +
|
|
432
|
+
" output-type = \"dynamic\"\n" +
|
|
433
|
+
" src-dir = \"./src/main/cangjie\"\n" +
|
|
434
|
+
" target-dir = \"\"\n" +
|
|
435
|
+
" version = \"" + this.version + "\"\n" +
|
|
436
|
+
"\n[dependencies]\n" +
|
|
437
|
+
" # 源码依赖:hvigor 的 CompileCangjie 会把框架各包按本模块(dynamic)需要的形态编进来。\n" +
|
|
438
|
+
" # 路径是本机绝对路径;换机器 / 挪仓库后重跑 cj-tauri sync ohos(与 create 同一条规矩)。\n" +
|
|
439
|
+
" cjTauri = { path = \"" + toTomlPath(fwRoot) + "\" }\n" +
|
|
440
|
+
" # 业务包:业务代码的唯一真相在工程根(RFC §7.3——骨架永不复制业务代码、不改包名)\n" +
|
|
441
|
+
" " + this.pkg + " = { path = \"../../..\" }\n" +
|
|
442
|
+
"\n[profile]\n" +
|
|
443
|
+
" [profile.build]\n" +
|
|
444
|
+
" incremental = true\n" +
|
|
445
|
+
" lto = \"\"\n" +
|
|
446
|
+
" [profile.customized-option]\n" +
|
|
447
|
+
" debug = \"-g -Woff all -Won apilevel-check\"\n" +
|
|
448
|
+
" release = \"--fast-math -O2 -s -Woff all -Won apilevel-check\"\n" +
|
|
449
|
+
" [profile.test]\n" +
|
|
450
|
+
"\n[target.aarch64-linux-ohos]\n" +
|
|
451
|
+
" compile-option = \"-B \\\"" + dc + "/build-tools/third_party/llvm/bin\\\" -B \\\"" + dn + "/sysroot/usr/lib/aarch64-linux-ohos\\\" -L \\\"" + dn + "/sysroot/usr/lib/aarch64-linux-ohos\\\" -L \\\"" + dn + "/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\\\" -L \\\"" + dn + "/llvm/lib/aarch64-linux-ohos\\\" --sysroot \\\"" + dn + "/sysroot\\\"\"\n" +
|
|
452
|
+
// 剪贴板宿主能力(src/clipboard_bridge.cj 的鸿蒙段)用系统粘贴板原生 C API。
|
|
453
|
+
// 最终链接发生在**本模块**(output-type = dynamic),所以开关必须写在这儿——
|
|
454
|
+
// 框架自己的 cjpm.toml 写了也不生效,缺了就是模块里留一堆 `U OH_Pasteboard_*`,
|
|
455
|
+
// 表现是应用启动即 SIGSEGV(模块加载期解析不到符号)。
|
|
456
|
+
// 两个库都要:`OH_Pasteboard_*` 在 libpasteboard、`OH_Udmf*` / `OH_Uds*` 在 libudmf。
|
|
457
|
+
// 2026-10-25 起 http 插件的鸿蒙支(http_ohos.cj)走系统网络栈 ohos.net.http,
|
|
458
|
+
// 模块符号(ohos.net.http:*_ti)在 libohos.net.http.so——缺了应用启动即
|
|
459
|
+
// 「relocating failed: symbol not found」(真机实测),所以进默认链接。
|
|
460
|
+
" link-option = \"" + appendLink("-lpasteboard -ludmf -lohos.net.http", this.linkOhos) + "\"\n" +
|
|
461
|
+
"[target.aarch64-linux-ohos.bin-dependencies]\n" +
|
|
462
|
+
" # 鸿蒙版 stdx:相对路径指向骨架根上的 `stdx` 联接(build-hap.sh 用 CANGJIE_STDX_OHOS\n" +
|
|
463
|
+
" # 建,不入库)。hvigor 只替换它自己的模板变量,写别的 " + "$" + "{...}" + " 会变空串、整条\n" +
|
|
464
|
+
" # path-option 失效(模板里的实测教训,原样保留)。\n" +
|
|
465
|
+
" path-option = [\"" + a1 + "\", \"" + a2 + "\", \"" + a3 + "\", \"../stdx\"]\n" +
|
|
466
|
+
" package-option = {}\n" +
|
|
467
|
+
"\n[target.x86_64-linux-ohos]\n" +
|
|
468
|
+
" compile-option = \"-B \\\"" + dc + "/build-tools/third_party/llvm/bin\\\" -B \\\"" + dn + "/sysroot/usr/lib/x86_64-linux-ohos\\\" -L \\\"" + dn + "/sysroot/usr/lib/x86_64-linux-ohos\\\" -L \\\"" + dn + "/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\\\" -L \\\"" + dn + "/llvm/lib/x86_64-linux-ohos\\\" --sysroot \\\"" + dn + "/sysroot\\\"\"\n" +
|
|
469
|
+
" link-option = \"" + appendLink("-lpasteboard -ludmf -lohos.net.http", this.linkOhos) + "\"\n" +
|
|
470
|
+
"[target.x86_64-linux-ohos.bin-dependencies]\n" +
|
|
471
|
+
" path-option = [\"" + x1 + "\", \"" + x2 + "\", \"" + x3 + "\", \"../stdx\"]\n" +
|
|
472
|
+
" package-option = {}\n"
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* targets/ohos/entry/src/main/cangjie/app_host.cj:鸿蒙侧框架装配(L0 胶水)
|
|
477
|
+
*
|
|
478
|
+
* 业务装配点 build() 三端共用(零平台知识,RFC §4.1);胶水层职责:
|
|
479
|
+
* ① 沙箱目录(ArkWebRuntime.configure)② rawfile 资产解析器 ③ 能力清单挂载 ④ launchWith(mobile)。
|
|
480
|
+
* 能力文件名单来自清单 [capabilities] 目录(sync 枚举后逐个渲染)。
|
|
481
|
+
*/
|
|
482
|
+
public func renderOhosAppHost(cliVer: String, capsFiles: ArrayList<String>): String {
|
|
483
|
+
var capsBlock = ""
|
|
484
|
+
if (capsFiles.size == 0) {
|
|
485
|
+
capsBlock = " Hilog.info(1, \"CjTauri\", \"清单没有 [capabilities] 目录:未挂载能力清单(命令会如实被拒)\")\n"
|
|
486
|
+
} else {
|
|
487
|
+
var k: Int64 = 0
|
|
488
|
+
for (f in capsFiles) {
|
|
489
|
+
let varName = "caps" + k.toString()
|
|
490
|
+
capsBlock = capsBlock +
|
|
491
|
+
" let " + varName + " = readRawText(rm, \"capabilities/" + f + "\")\n" +
|
|
492
|
+
" if (" + varName + ".size > 0) {\n" +
|
|
493
|
+
" spec.capabilityJson(" + varName + ")\n" +
|
|
494
|
+
" } else {\n" +
|
|
495
|
+
" Hilog.info(1, \"CjTauri\", \"rawfile/capabilities/" + f + " 读不到(构建时打进 hap 了吗?):命令会被全部拒绝\")\n" +
|
|
496
|
+
" }\n"
|
|
497
|
+
k += 1
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return "// generated by cj-tauri sync (v" + cliVer + ") — 不要手改;改动会被 check 判为漂移\n" +
|
|
501
|
+
"// 鸿蒙侧框架装配(批次 3-PR3,RFC §4.2/§4.6.5):业务装配点 " + this.pkg + ".build() 三端共用\n" +
|
|
502
|
+
"package ohos_app_cangjie_entry\n" +
|
|
503
|
+
"\n" +
|
|
504
|
+
"import kit.PerformanceAnalysisKit.Hilog\n" +
|
|
505
|
+
"import cjTauri.*\n" +
|
|
506
|
+
"import cjTauri.platform.*\n" +
|
|
507
|
+
"import " + this.pkg + ".*\n" +
|
|
508
|
+
"import ohos.resource_manager.*\n" +
|
|
509
|
+
"\n" +
|
|
510
|
+
"/**\n" +
|
|
511
|
+
" * rawfile 资产解析器(L0,RFC §6.2):hap 里没有工程目录,Asset 只能从 rawfile 解析。\n" +
|
|
512
|
+
" * 与桌面 DirectoryAssetResolver 同形:以「能否读到」为准,失败 = None(host_arkweb 在其上如实报错)。\n" +
|
|
513
|
+
" */\n" +
|
|
514
|
+
"class RawfileAssetResolver <: AssetResolver {\n" +
|
|
515
|
+
" private let rm: ResourceManager\n" +
|
|
516
|
+
"\n" +
|
|
517
|
+
" public init(rm: ResourceManager) {\n" +
|
|
518
|
+
" this.rm = rm\n" +
|
|
519
|
+
" }\n" +
|
|
520
|
+
"\n" +
|
|
521
|
+
" public func readText(name: String): Option<String> {\n" +
|
|
522
|
+
" try {\n" +
|
|
523
|
+
" return Some(String.fromUtf8(this.rm.getRawFileContent(name)))\n" +
|
|
524
|
+
" } catch (e: Exception) {\n" +
|
|
525
|
+
" return None\n" +
|
|
526
|
+
" }\n" +
|
|
527
|
+
" }\n" +
|
|
528
|
+
"\n" +
|
|
529
|
+
" public func exists(name: String): Bool {\n" +
|
|
530
|
+
" return readText(name).isSome()\n" +
|
|
531
|
+
" }\n" +
|
|
532
|
+
"}\n" +
|
|
533
|
+
"\n" +
|
|
534
|
+
"/** 读 hap 里的 rawfile(失败返回空串并打一行 hilog;调用方按「空 = 没有」降级,不让异常冒到生命周期) */\n" +
|
|
535
|
+
"func readRawText(rm: ResourceManager, path: String): String {\n" +
|
|
536
|
+
" try {\n" +
|
|
537
|
+
" return String.fromUtf8(rm.getRawFileContent(path))\n" +
|
|
538
|
+
" } catch (e: Exception) {\n" +
|
|
539
|
+
" Hilog.info(1, \"CjTauri\", \"rawfile " + "$" + "{path} 读取失败: " + "$" + "{e}\")\n" +
|
|
540
|
+
" return \"\"\n" +
|
|
541
|
+
" }\n" +
|
|
542
|
+
"}\n" +
|
|
543
|
+
"\n" +
|
|
544
|
+
"/**\n" +
|
|
545
|
+
" * 装配并运行框架应用(非 UI 线程调用;run() 阻塞到 quit())\n" +
|
|
546
|
+
" *\n" +
|
|
547
|
+
" * 页面取清单 [assets] page(sync 生成时已校验存在);能力清单是构建期拷进 hap 的\n" +
|
|
548
|
+
" * rawfile/capabilities 下的各份 json——文件名单是清单侧的真相,由生成器逐个渲染。\n" +
|
|
549
|
+
" */\n" +
|
|
550
|
+
"public func startApp(filesDir: String, rm: ResourceManager): Unit {\n" +
|
|
551
|
+
" Hilog.info(1, \"CjTauri\", \"startApp begin (ohos skeleton, business = " + this.pkg + ".build())\")\n" +
|
|
552
|
+
" // 沙箱目录要在 load 之前给定:宿主把页面落沙箱再以 file:// 加载(与桌面 load(source) 同一契约)\n" +
|
|
553
|
+
" ArkWebRuntime.configure(filesDir)\n" +
|
|
554
|
+
" let spec = build()\n" +
|
|
555
|
+
" .page(PageSource.Asset(\"" + this.page + "\"))\n" +
|
|
556
|
+
" .assets(RawfileAssetResolver(rm))\n" +
|
|
557
|
+
capsBlock +
|
|
558
|
+
" launchWith(spec, Platform.mobile(filesDir, \"unknown\"))\n" +
|
|
559
|
+
" Hilog.info(1, \"CjTauri\", \"after run\")\n" +
|
|
560
|
+
"}\n"
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* targets/ohos/entry/src/main/cangjie/main_ability.cj:UIAbility(只管生命周期,装配在 app_host.cj)
|
|
565
|
+
*
|
|
566
|
+
* registerSelf / waitAndLogHost / 不碰窗口尺寸 这些段落原样取自 ohos-skeleton 模板——模板已真机验证。
|
|
567
|
+
*/
|
|
568
|
+
public func renderOhosMainAbility(cliVer: String): String {
|
|
569
|
+
return "// generated by cj-tauri sync (v" + cliVer + ") — 不要手改;改动会被 check 判为漂移\n" +
|
|
570
|
+
"// 鸿蒙 UIAbility(批次 3-PR3,RFC §4.2):本类只管生命周期;装配在非 UI 线程的 startApp(见 app_host.cj)\n" +
|
|
571
|
+
"package ohos_app_cangjie_entry\n" +
|
|
572
|
+
"\n" +
|
|
573
|
+
"import kit.PerformanceAnalysisKit.Hilog\n" +
|
|
574
|
+
"import kit.AbilityKit.Want\n" +
|
|
575
|
+
"import kit.AbilityKit.UIAbility\n" +
|
|
576
|
+
"import kit.AbilityKit.LaunchParam\n" +
|
|
577
|
+
"import kit.ArkUI.WindowStage\n" +
|
|
578
|
+
"import cjTauri.*\n" +
|
|
579
|
+
"\n" +
|
|
580
|
+
"class MainAbility <: UIAbility {\n" +
|
|
581
|
+
" public init() {\n" +
|
|
582
|
+
" super()\n" +
|
|
583
|
+
" registerSelf()\n" +
|
|
584
|
+
" }\n" +
|
|
585
|
+
"\n" +
|
|
586
|
+
" public override func onCreate(want: Want, launchParam: LaunchParam): Unit {\n" +
|
|
587
|
+
" Hilog.info(1, \"CjTauri\", \"MainAbility OnCreated." + "$" + "{want.abilityName}\")\n" +
|
|
588
|
+
" // run() 会阻塞到 quit(),只能在非 UI 线程上跑——UI 线程要留给 ArkUI 建树。\n" +
|
|
589
|
+
" // 沙箱准备、rawfile 材化、框架装配都在 startApp 里完成(L0 胶水,见 app_host.cj)。\n" +
|
|
590
|
+
" let filesDir = \"" + "$" + "{this.context.filesDir}\"\n" +
|
|
591
|
+
" let rm = this.context.resourceManager\n" +
|
|
592
|
+
" let _ = spawn { startApp(filesDir, rm) }\n" +
|
|
593
|
+
" }\n" +
|
|
594
|
+
"\n" +
|
|
595
|
+
" public override func onWindowStageCreate(windowStage: WindowStage): Unit {\n" +
|
|
596
|
+
" Hilog.info(1, \"CjTauri\", \"MainAbility onWindowStageCreate.\")\n" +
|
|
597
|
+
" // 等框架把宿主登记上再建树:组件的 build() 只有在该 label 有宿主、页面地址非空时\n" +
|
|
598
|
+
" // 才渲染 `Web`。等不到也照常 loadContent——页面会自己报桥缺失,比静默白屏好排查。\n" +
|
|
599
|
+
" this.waitAndLogHost(DEFAULT_WINDOW_LABEL)\n" +
|
|
600
|
+
" windowStage.loadContent(\"EntryView\")\n" +
|
|
601
|
+
" }\n" +
|
|
602
|
+
"\n" +
|
|
603
|
+
" /**\n" +
|
|
604
|
+
" * 有界等待(3s)默认窗口的宿主登记完成,并把页面地址 / 预执行脚本条数打进 hilog\n" +
|
|
605
|
+
" *\n" +
|
|
606
|
+
" * 白屏定位用:宿主把页面落盘后的 file:// 地址必须非空,否则组件拿不到 src。\n" +
|
|
607
|
+
" */\n" +
|
|
608
|
+
" private func waitAndLogHost(label: String): Unit {\n" +
|
|
609
|
+
" var polls = 0\n" +
|
|
610
|
+
" while (polls < 300) {\n" +
|
|
611
|
+
" match (ArkWebRuntime.hostFor(label)) {\n" +
|
|
612
|
+
" case Some(_) => break\n" +
|
|
613
|
+
" case None => ()\n" +
|
|
614
|
+
" }\n" +
|
|
615
|
+
" sleep(Duration.millisecond * 10)\n" +
|
|
616
|
+
" polls += 1\n" +
|
|
617
|
+
" }\n" +
|
|
618
|
+
" if (polls >= 300) {\n" +
|
|
619
|
+
" Hilog.info(1, \"CjTauri\", \"host not registered in 3s: label=" + "$" + "{label}\")\n" +
|
|
620
|
+
" return\n" +
|
|
621
|
+
" }\n" +
|
|
622
|
+
" Hilog.info(1, \"CjTauri\", \"host registered after " + "$" + "{polls} polls label=" + "$" + "{label}\")\n" +
|
|
623
|
+
" match (ArkWebRuntime.hostFor(label)) {\n" +
|
|
624
|
+
" case Some(h) =>\n" +
|
|
625
|
+
" Hilog.info(1, \"CjTauri\", \"host pageUrl label=" + "$" + "{label} url=\\\"" + "$" + "{h.currentUrl()}\\\"\")\n" +
|
|
626
|
+
" Hilog.info(1, \"CjTauri\", \"host initScripts label=" + "$" + "{label} count=" + "$" + "{h.initScriptsSnapshot().size}\")\n" +
|
|
627
|
+
" case None => Hilog.info(1, \"CjTauri\", \"host missing at loadContent label=" + "$" + "{label}\")\n" +
|
|
628
|
+
" }\n" +
|
|
629
|
+
" }\n" +
|
|
630
|
+
"}\n"
|
|
631
|
+
}
|
|
632
|
+
}
|