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/resolve.cj
CHANGED
|
@@ -1,348 +1,348 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cj-tauri CLI:宿主平台 / 框架根目录 / stdx / 仓颉工具链 解析
|
|
3
|
-
*/
|
|
4
|
-
package cli
|
|
5
|
-
|
|
6
|
-
import std.env.*
|
|
7
|
-
import std.fs.*
|
|
8
|
-
import std.collection.*
|
|
9
|
-
|
|
10
|
-
// ===== 宿主平台 =====
|
|
11
|
-
|
|
12
|
-
func hostIsWindows(): Bool {
|
|
13
|
-
if (let Some(os) <- getVariable("OS")) {
|
|
14
|
-
if (os.contains("Windows")) {
|
|
15
|
-
return true
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
if (getVariable("WINDIR").isSome()) {
|
|
19
|
-
return true
|
|
20
|
-
}
|
|
21
|
-
return false
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func hostOsName(): String {
|
|
25
|
-
if (hostIsWindows()) {
|
|
26
|
-
return "Windows"
|
|
27
|
-
}
|
|
28
|
-
return "Linux"
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func hostTriple(): String {
|
|
32
|
-
if (hostIsWindows()) {
|
|
33
|
-
return "x86_64-w64-mingw32"
|
|
34
|
-
}
|
|
35
|
-
return "x86_64-unknown-linux-gnu"
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
func bridgeLibName(): String {
|
|
39
|
-
if (hostIsWindows()) {
|
|
40
|
-
return "libcjtbridge.dll"
|
|
41
|
-
}
|
|
42
|
-
return "libcjtbridge.so"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
func pathListSep(): String {
|
|
46
|
-
if (hostIsWindows()) {
|
|
47
|
-
return ";"
|
|
48
|
-
}
|
|
49
|
-
return ":"
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
func exeSuffix(): String {
|
|
53
|
-
if (hostIsWindows()) {
|
|
54
|
-
return ".exe"
|
|
55
|
-
}
|
|
56
|
-
return ""
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** 生成项目 cjpm.toml 里的 cjc-version(最低要求,非精确版本) */
|
|
60
|
-
func minCjcVersion(): String {
|
|
61
|
-
return "1.0.5"
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ===== 环境 / 工具链 =====
|
|
65
|
-
|
|
66
|
-
func envOr(key: String, def: String): String {
|
|
67
|
-
if (let Some(v) <- getVariable(key)) {
|
|
68
|
-
return v
|
|
69
|
-
}
|
|
70
|
-
return def
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
func currentDir(): String {
|
|
74
|
-
try {
|
|
75
|
-
return stripTrailingSep(getWorkingDirectory().toString())
|
|
76
|
-
} catch (e: Exception) {
|
|
77
|
-
return "."
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** 当前可执行文件路径(Windows 支持 getCommandLine,其它平台可能抛异常) */
|
|
82
|
-
func currentExePath(): Option<String> {
|
|
83
|
-
try {
|
|
84
|
-
let cmd = getCommandLine()
|
|
85
|
-
if (cmd.size > 0) {
|
|
86
|
-
return Some(cmd[0])
|
|
87
|
-
}
|
|
88
|
-
} catch (e: Exception) {
|
|
89
|
-
return None
|
|
90
|
-
}
|
|
91
|
-
return None
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** 在 PATH 中查找可执行文件(Windows 自动补 .exe) */
|
|
95
|
-
func findOnPath(name: String): Option<String> {
|
|
96
|
-
let path = envOr("PATH", "")
|
|
97
|
-
for (dir in path.split(pathListSep())) {
|
|
98
|
-
if (dir.size == 0) {
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
let cand = joinPath(stripTrailingSep(dir), name)
|
|
102
|
-
if (exists(cand)) {
|
|
103
|
-
return Some(cand)
|
|
104
|
-
}
|
|
105
|
-
if (hostIsWindows()) {
|
|
106
|
-
let candExe = cand + ".exe"
|
|
107
|
-
if (exists(candExe)) {
|
|
108
|
-
return Some(candExe)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return None
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** 仓颉 SDK 根目录:CANGJIE_HOME → PATH 中的 cjc */
|
|
116
|
-
func resolveCangjieHome(): Option<String> {
|
|
117
|
-
if (let Some(h0) <- getVariable("CANGJIE_HOME")) {
|
|
118
|
-
let h = stripTrailingSep(h0)
|
|
119
|
-
if (exists(joinPath(h, "bin"))) {
|
|
120
|
-
return Some(h)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (let Some(cjc) <- findOnPath("cjc")) {
|
|
124
|
-
if (let Some(binDir) <- parentDir(cjc)) {
|
|
125
|
-
if (let Some(home) <- parentDir(binDir)) {
|
|
126
|
-
return Some(home)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return None
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** cjpm:PATH → <SDK>/tools/bin/cjpm[.exe] */
|
|
134
|
-
func resolveCjpm(): Option<String> {
|
|
135
|
-
if (let Some(p) <- findOnPath("cjpm")) {
|
|
136
|
-
return Some(p)
|
|
137
|
-
}
|
|
138
|
-
if (let Some(home) <- resolveCangjieHome()) {
|
|
139
|
-
let cand = joinPath(joinPath(joinPath(home, "tools"), "bin"), "cjpm" + exeSuffix())
|
|
140
|
-
if (exists(cand)) {
|
|
141
|
-
return Some(cand)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return None
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** 仓颉运行时库目录(含 libcangjie-runtime) */
|
|
148
|
-
func findRuntimeLibDir(home: String): Option<String> {
|
|
149
|
-
let libRoot = joinPath(joinPath(home, "runtime"), "lib")
|
|
150
|
-
if (!exists(libRoot)) {
|
|
151
|
-
return None
|
|
152
|
-
}
|
|
153
|
-
var prefix = "linux"
|
|
154
|
-
if (hostIsWindows()) {
|
|
155
|
-
prefix = "windows"
|
|
156
|
-
}
|
|
157
|
-
for (info in listDir(libRoot)) {
|
|
158
|
-
if (!info.isDirectory()) {
|
|
159
|
-
continue
|
|
160
|
-
}
|
|
161
|
-
let nm = baseName(info.path.toString())
|
|
162
|
-
if (nm.startsWith(prefix)) {
|
|
163
|
-
return Some(joinPath(libRoot, nm))
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return None
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// ===== 框架根目录 =====
|
|
170
|
-
|
|
171
|
-
func looksLikeFrameworkRoot(dir: String): Bool {
|
|
172
|
-
if (!exists(joinPath(dir, "cjpm.toml"))) {
|
|
173
|
-
return false
|
|
174
|
-
}
|
|
175
|
-
return exists(joinPath(joinPath(joinPath(dir, "cli"), "templates"), "app"))
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
func findFrameworkRootFrom(start: String): Option<String> {
|
|
179
|
-
var cur: Option<String> = Some(stripTrailingSep(start))
|
|
180
|
-
var guard = 0
|
|
181
|
-
while (guard < 64) {
|
|
182
|
-
guard++
|
|
183
|
-
if (let Some(d) <- cur) {
|
|
184
|
-
if (looksLikeFrameworkRoot(d)) {
|
|
185
|
-
return Some(d)
|
|
186
|
-
}
|
|
187
|
-
cur = parentDir(d)
|
|
188
|
-
} else {
|
|
189
|
-
return None
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return None
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** 框架根目录:CJ_TAURI_ROOT → 工作目录向上 → 可执行文件位置向上 */
|
|
196
|
-
func resolveFrameworkRoot(): Option<String> {
|
|
197
|
-
if (let Some(env) <- getVariable("CJ_TAURI_ROOT")) {
|
|
198
|
-
let dir = stripTrailingSep(env)
|
|
199
|
-
if (looksLikeFrameworkRoot(dir)) {
|
|
200
|
-
return Some(dir)
|
|
201
|
-
}
|
|
202
|
-
eprintln("[cj-tauri] 告警: CJ_TAURI_ROOT=${env} 不是有效的 cj-tauri 框架根目录")
|
|
203
|
-
}
|
|
204
|
-
if (let Some(found) <- findFrameworkRootFrom(currentDir())) {
|
|
205
|
-
return Some(found)
|
|
206
|
-
}
|
|
207
|
-
if (let Some(exe) <- currentExePath()) {
|
|
208
|
-
if (let Some(dir) <- parentDir(exe)) {
|
|
209
|
-
if (let Some(found) <- findFrameworkRootFrom(dir)) {
|
|
210
|
-
return Some(found)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return None
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// ===== TOML 取值 =====
|
|
218
|
-
|
|
219
|
-
/** 取字符串中第一个 TOML 双引号串(并反转义 \\ 与 \") */
|
|
220
|
-
func firstQuoted(s: String): Option<String> {
|
|
221
|
-
let b = s.toArray()
|
|
222
|
-
var i = 0
|
|
223
|
-
while (i < b.size && b[i] != 0x22) {
|
|
224
|
-
i++
|
|
225
|
-
}
|
|
226
|
-
if (i >= b.size) {
|
|
227
|
-
return None
|
|
228
|
-
}
|
|
229
|
-
let out = ArrayList<UInt8>()
|
|
230
|
-
var j = i + 1
|
|
231
|
-
while (j < b.size) {
|
|
232
|
-
if (b[j] == 0x5C && j + 1 < b.size) {
|
|
233
|
-
out.add(b[j + 1])
|
|
234
|
-
j += 2
|
|
235
|
-
continue
|
|
236
|
-
}
|
|
237
|
-
if (b[j] == 0x22) {
|
|
238
|
-
break
|
|
239
|
-
}
|
|
240
|
-
out.add(b[j])
|
|
241
|
-
j++
|
|
242
|
-
}
|
|
243
|
-
return Some(String.fromUtf8(out.toArray()))
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/** 从 cjpm.toml 文本取指定 target 段的 path-option 首项 */
|
|
247
|
-
func pathOptionFor(content: String, triple: String): Option<String> {
|
|
248
|
-
var inSection = false
|
|
249
|
-
for (rawLine in content.split("\n")) {
|
|
250
|
-
let line = trimAscii(rawLine)
|
|
251
|
-
if (line.startsWith("[")) {
|
|
252
|
-
inSection = line.contains(triple)
|
|
253
|
-
continue
|
|
254
|
-
}
|
|
255
|
-
if (inSection && line.startsWith("path-option")) {
|
|
256
|
-
if (let Some(v) <- firstQuoted(line)) {
|
|
257
|
-
return Some(expandTomlEnv(v))
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return None
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/** cjpm.toml 里的 `${NAME}` 环境变量引用:与 cjpm 手册《环境变量配置》同款语义
|
|
265
|
-
* (bin-dependencies 的 path-option 支持)。源码仓 24 个 toml 的 stdx 路径已统一改成
|
|
266
|
-
* `${CANGJIE_STDX}`(issue #3),CLI 从 toml 读回 path-option 必须同款展开,
|
|
267
|
-
* 否则拿到的是字面量 `…` 尾巴。逐字节扫描单遍展开(可含多个变量,不递归展开变量值——
|
|
268
|
-
* 与 cjpm 行为一致)。String 本机 SDK 无 subString 成员,故按字节拼(镜像 firstQuoted)。 */
|
|
269
|
-
func expandTomlEnv(s: String): String {
|
|
270
|
-
let bytes = s.toArray()
|
|
271
|
-
let n = bytes.size
|
|
272
|
-
let out = ArrayList<UInt8>()
|
|
273
|
-
var i = 0
|
|
274
|
-
let DOLLAR: UInt8 = 0x24 // '$'
|
|
275
|
-
let LBRACE: UInt8 = 0x7B // '{'
|
|
276
|
-
let RBRACE: UInt8 = 0x7D // '}'
|
|
277
|
-
while (i < n) {
|
|
278
|
-
if (bytes[i] == DOLLAR && i + 1 < n && bytes[i + 1] == LBRACE) {
|
|
279
|
-
var j = i + 2
|
|
280
|
-
while (j < n && bytes[j] != RBRACE) {
|
|
281
|
-
j++
|
|
282
|
-
}
|
|
283
|
-
if (j < n) {
|
|
284
|
-
let nameBytes = ArrayList<UInt8>()
|
|
285
|
-
var k = i + 2
|
|
286
|
-
while (k < j) {
|
|
287
|
-
nameBytes.add(bytes[k])
|
|
288
|
-
k++
|
|
289
|
-
}
|
|
290
|
-
let name = String.fromUtf8(nameBytes.toArray())
|
|
291
|
-
let value = envOr(name, "")
|
|
292
|
-
let vbytes = value.toArray()
|
|
293
|
-
var m = 0
|
|
294
|
-
while (m < vbytes.size) {
|
|
295
|
-
out.add(vbytes[m])
|
|
296
|
-
m++
|
|
297
|
-
}
|
|
298
|
-
i = j + 1
|
|
299
|
-
continue
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
out.add(bytes[i])
|
|
303
|
-
i++
|
|
304
|
-
}
|
|
305
|
-
return String.fromUtf8(out.toArray())
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/** 项目 cjpm.toml 中 cjTauri 依赖路径 */
|
|
309
|
-
func frameworkFromProject(projectRoot: String): Option<String> {
|
|
310
|
-
let toml = joinPath(projectRoot, "cjpm.toml")
|
|
311
|
-
if (!exists(toml)) {
|
|
312
|
-
return None
|
|
313
|
-
}
|
|
314
|
-
for (rawLine in readText(toml).split("\n")) {
|
|
315
|
-
let line = trimAscii(rawLine)
|
|
316
|
-
if (line.startsWith("cjTauri")) {
|
|
317
|
-
if (let Some(v) <- firstQuoted(line)) {
|
|
318
|
-
return Some(stripTrailingSep(v))
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return None
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/** stdx 目录:CANGJIE_STDX → 项目 cjpm.toml → 框架 cjpm.toml */
|
|
326
|
-
func resolveStdx(fwRoot: String, projectRoot: Option<String>): Option<String> {
|
|
327
|
-
if (let Some(v0) <- getVariable("CANGJIE_STDX")) {
|
|
328
|
-
let v = stripTrailingSep(v0)
|
|
329
|
-
if (v.size > 0 && exists(v)) {
|
|
330
|
-
return Some(v)
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if (let Some(proj) <- projectRoot) {
|
|
334
|
-
let toml = joinPath(proj, "cjpm.toml")
|
|
335
|
-
if (exists(toml)) {
|
|
336
|
-
if (let Some(v) <- pathOptionFor(readText(toml), hostTriple())) {
|
|
337
|
-
return Some(stripTrailingSep(v))
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
let fwToml = joinPath(fwRoot, "cjpm.toml")
|
|
342
|
-
if (exists(fwToml)) {
|
|
343
|
-
if (let Some(v) <- pathOptionFor(readText(fwToml), hostTriple())) {
|
|
344
|
-
return Some(stripTrailingSep(v))
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return None
|
|
348
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* cj-tauri CLI:宿主平台 / 框架根目录 / stdx / 仓颉工具链 解析
|
|
3
|
+
*/
|
|
4
|
+
package cli
|
|
5
|
+
|
|
6
|
+
import std.env.*
|
|
7
|
+
import std.fs.*
|
|
8
|
+
import std.collection.*
|
|
9
|
+
|
|
10
|
+
// ===== 宿主平台 =====
|
|
11
|
+
|
|
12
|
+
func hostIsWindows(): Bool {
|
|
13
|
+
if (let Some(os) <- getVariable("OS")) {
|
|
14
|
+
if (os.contains("Windows")) {
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (getVariable("WINDIR").isSome()) {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func hostOsName(): String {
|
|
25
|
+
if (hostIsWindows()) {
|
|
26
|
+
return "Windows"
|
|
27
|
+
}
|
|
28
|
+
return "Linux"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func hostTriple(): String {
|
|
32
|
+
if (hostIsWindows()) {
|
|
33
|
+
return "x86_64-w64-mingw32"
|
|
34
|
+
}
|
|
35
|
+
return "x86_64-unknown-linux-gnu"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func bridgeLibName(): String {
|
|
39
|
+
if (hostIsWindows()) {
|
|
40
|
+
return "libcjtbridge.dll"
|
|
41
|
+
}
|
|
42
|
+
return "libcjtbridge.so"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func pathListSep(): String {
|
|
46
|
+
if (hostIsWindows()) {
|
|
47
|
+
return ";"
|
|
48
|
+
}
|
|
49
|
+
return ":"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func exeSuffix(): String {
|
|
53
|
+
if (hostIsWindows()) {
|
|
54
|
+
return ".exe"
|
|
55
|
+
}
|
|
56
|
+
return ""
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 生成项目 cjpm.toml 里的 cjc-version(最低要求,非精确版本) */
|
|
60
|
+
func minCjcVersion(): String {
|
|
61
|
+
return "1.0.5"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ===== 环境 / 工具链 =====
|
|
65
|
+
|
|
66
|
+
func envOr(key: String, def: String): String {
|
|
67
|
+
if (let Some(v) <- getVariable(key)) {
|
|
68
|
+
return v
|
|
69
|
+
}
|
|
70
|
+
return def
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func currentDir(): String {
|
|
74
|
+
try {
|
|
75
|
+
return stripTrailingSep(getWorkingDirectory().toString())
|
|
76
|
+
} catch (e: Exception) {
|
|
77
|
+
return "."
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 当前可执行文件路径(Windows 支持 getCommandLine,其它平台可能抛异常) */
|
|
82
|
+
func currentExePath(): Option<String> {
|
|
83
|
+
try {
|
|
84
|
+
let cmd = getCommandLine()
|
|
85
|
+
if (cmd.size > 0) {
|
|
86
|
+
return Some(cmd[0])
|
|
87
|
+
}
|
|
88
|
+
} catch (e: Exception) {
|
|
89
|
+
return None
|
|
90
|
+
}
|
|
91
|
+
return None
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 在 PATH 中查找可执行文件(Windows 自动补 .exe) */
|
|
95
|
+
func findOnPath(name: String): Option<String> {
|
|
96
|
+
let path = envOr("PATH", "")
|
|
97
|
+
for (dir in path.split(pathListSep())) {
|
|
98
|
+
if (dir.size == 0) {
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
let cand = joinPath(stripTrailingSep(dir), name)
|
|
102
|
+
if (exists(cand)) {
|
|
103
|
+
return Some(cand)
|
|
104
|
+
}
|
|
105
|
+
if (hostIsWindows()) {
|
|
106
|
+
let candExe = cand + ".exe"
|
|
107
|
+
if (exists(candExe)) {
|
|
108
|
+
return Some(candExe)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return None
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** 仓颉 SDK 根目录:CANGJIE_HOME → PATH 中的 cjc */
|
|
116
|
+
func resolveCangjieHome(): Option<String> {
|
|
117
|
+
if (let Some(h0) <- getVariable("CANGJIE_HOME")) {
|
|
118
|
+
let h = stripTrailingSep(h0)
|
|
119
|
+
if (exists(joinPath(h, "bin"))) {
|
|
120
|
+
return Some(h)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (let Some(cjc) <- findOnPath("cjc")) {
|
|
124
|
+
if (let Some(binDir) <- parentDir(cjc)) {
|
|
125
|
+
if (let Some(home) <- parentDir(binDir)) {
|
|
126
|
+
return Some(home)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return None
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** cjpm:PATH → <SDK>/tools/bin/cjpm[.exe] */
|
|
134
|
+
func resolveCjpm(): Option<String> {
|
|
135
|
+
if (let Some(p) <- findOnPath("cjpm")) {
|
|
136
|
+
return Some(p)
|
|
137
|
+
}
|
|
138
|
+
if (let Some(home) <- resolveCangjieHome()) {
|
|
139
|
+
let cand = joinPath(joinPath(joinPath(home, "tools"), "bin"), "cjpm" + exeSuffix())
|
|
140
|
+
if (exists(cand)) {
|
|
141
|
+
return Some(cand)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return None
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** 仓颉运行时库目录(含 libcangjie-runtime) */
|
|
148
|
+
func findRuntimeLibDir(home: String): Option<String> {
|
|
149
|
+
let libRoot = joinPath(joinPath(home, "runtime"), "lib")
|
|
150
|
+
if (!exists(libRoot)) {
|
|
151
|
+
return None
|
|
152
|
+
}
|
|
153
|
+
var prefix = "linux"
|
|
154
|
+
if (hostIsWindows()) {
|
|
155
|
+
prefix = "windows"
|
|
156
|
+
}
|
|
157
|
+
for (info in listDir(libRoot)) {
|
|
158
|
+
if (!info.isDirectory()) {
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
let nm = baseName(info.path.toString())
|
|
162
|
+
if (nm.startsWith(prefix)) {
|
|
163
|
+
return Some(joinPath(libRoot, nm))
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return None
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ===== 框架根目录 =====
|
|
170
|
+
|
|
171
|
+
func looksLikeFrameworkRoot(dir: String): Bool {
|
|
172
|
+
if (!exists(joinPath(dir, "cjpm.toml"))) {
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
return exists(joinPath(joinPath(joinPath(dir, "cli"), "templates"), "app"))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func findFrameworkRootFrom(start: String): Option<String> {
|
|
179
|
+
var cur: Option<String> = Some(stripTrailingSep(start))
|
|
180
|
+
var guard = 0
|
|
181
|
+
while (guard < 64) {
|
|
182
|
+
guard++
|
|
183
|
+
if (let Some(d) <- cur) {
|
|
184
|
+
if (looksLikeFrameworkRoot(d)) {
|
|
185
|
+
return Some(d)
|
|
186
|
+
}
|
|
187
|
+
cur = parentDir(d)
|
|
188
|
+
} else {
|
|
189
|
+
return None
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return None
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** 框架根目录:CJ_TAURI_ROOT → 工作目录向上 → 可执行文件位置向上 */
|
|
196
|
+
func resolveFrameworkRoot(): Option<String> {
|
|
197
|
+
if (let Some(env) <- getVariable("CJ_TAURI_ROOT")) {
|
|
198
|
+
let dir = stripTrailingSep(env)
|
|
199
|
+
if (looksLikeFrameworkRoot(dir)) {
|
|
200
|
+
return Some(dir)
|
|
201
|
+
}
|
|
202
|
+
eprintln("[cj-tauri] 告警: CJ_TAURI_ROOT=${env} 不是有效的 cj-tauri 框架根目录")
|
|
203
|
+
}
|
|
204
|
+
if (let Some(found) <- findFrameworkRootFrom(currentDir())) {
|
|
205
|
+
return Some(found)
|
|
206
|
+
}
|
|
207
|
+
if (let Some(exe) <- currentExePath()) {
|
|
208
|
+
if (let Some(dir) <- parentDir(exe)) {
|
|
209
|
+
if (let Some(found) <- findFrameworkRootFrom(dir)) {
|
|
210
|
+
return Some(found)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return None
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ===== TOML 取值 =====
|
|
218
|
+
|
|
219
|
+
/** 取字符串中第一个 TOML 双引号串(并反转义 \\ 与 \") */
|
|
220
|
+
func firstQuoted(s: String): Option<String> {
|
|
221
|
+
let b = s.toArray()
|
|
222
|
+
var i = 0
|
|
223
|
+
while (i < b.size && b[i] != 0x22) {
|
|
224
|
+
i++
|
|
225
|
+
}
|
|
226
|
+
if (i >= b.size) {
|
|
227
|
+
return None
|
|
228
|
+
}
|
|
229
|
+
let out = ArrayList<UInt8>()
|
|
230
|
+
var j = i + 1
|
|
231
|
+
while (j < b.size) {
|
|
232
|
+
if (b[j] == 0x5C && j + 1 < b.size) {
|
|
233
|
+
out.add(b[j + 1])
|
|
234
|
+
j += 2
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
if (b[j] == 0x22) {
|
|
238
|
+
break
|
|
239
|
+
}
|
|
240
|
+
out.add(b[j])
|
|
241
|
+
j++
|
|
242
|
+
}
|
|
243
|
+
return Some(String.fromUtf8(out.toArray()))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** 从 cjpm.toml 文本取指定 target 段的 path-option 首项 */
|
|
247
|
+
func pathOptionFor(content: String, triple: String): Option<String> {
|
|
248
|
+
var inSection = false
|
|
249
|
+
for (rawLine in content.split("\n")) {
|
|
250
|
+
let line = trimAscii(rawLine)
|
|
251
|
+
if (line.startsWith("[")) {
|
|
252
|
+
inSection = line.contains(triple)
|
|
253
|
+
continue
|
|
254
|
+
}
|
|
255
|
+
if (inSection && line.startsWith("path-option")) {
|
|
256
|
+
if (let Some(v) <- firstQuoted(line)) {
|
|
257
|
+
return Some(expandTomlEnv(v))
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return None
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** cjpm.toml 里的 `${NAME}` 环境变量引用:与 cjpm 手册《环境变量配置》同款语义
|
|
265
|
+
* (bin-dependencies 的 path-option 支持)。源码仓 24 个 toml 的 stdx 路径已统一改成
|
|
266
|
+
* `${CANGJIE_STDX}`(issue #3),CLI 从 toml 读回 path-option 必须同款展开,
|
|
267
|
+
* 否则拿到的是字面量 `…` 尾巴。逐字节扫描单遍展开(可含多个变量,不递归展开变量值——
|
|
268
|
+
* 与 cjpm 行为一致)。String 本机 SDK 无 subString 成员,故按字节拼(镜像 firstQuoted)。 */
|
|
269
|
+
func expandTomlEnv(s: String): String {
|
|
270
|
+
let bytes = s.toArray()
|
|
271
|
+
let n = bytes.size
|
|
272
|
+
let out = ArrayList<UInt8>()
|
|
273
|
+
var i = 0
|
|
274
|
+
let DOLLAR: UInt8 = 0x24 // '$'
|
|
275
|
+
let LBRACE: UInt8 = 0x7B // '{'
|
|
276
|
+
let RBRACE: UInt8 = 0x7D // '}'
|
|
277
|
+
while (i < n) {
|
|
278
|
+
if (bytes[i] == DOLLAR && i + 1 < n && bytes[i + 1] == LBRACE) {
|
|
279
|
+
var j = i + 2
|
|
280
|
+
while (j < n && bytes[j] != RBRACE) {
|
|
281
|
+
j++
|
|
282
|
+
}
|
|
283
|
+
if (j < n) {
|
|
284
|
+
let nameBytes = ArrayList<UInt8>()
|
|
285
|
+
var k = i + 2
|
|
286
|
+
while (k < j) {
|
|
287
|
+
nameBytes.add(bytes[k])
|
|
288
|
+
k++
|
|
289
|
+
}
|
|
290
|
+
let name = String.fromUtf8(nameBytes.toArray())
|
|
291
|
+
let value = envOr(name, "")
|
|
292
|
+
let vbytes = value.toArray()
|
|
293
|
+
var m = 0
|
|
294
|
+
while (m < vbytes.size) {
|
|
295
|
+
out.add(vbytes[m])
|
|
296
|
+
m++
|
|
297
|
+
}
|
|
298
|
+
i = j + 1
|
|
299
|
+
continue
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
out.add(bytes[i])
|
|
303
|
+
i++
|
|
304
|
+
}
|
|
305
|
+
return String.fromUtf8(out.toArray())
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** 项目 cjpm.toml 中 cjTauri 依赖路径 */
|
|
309
|
+
func frameworkFromProject(projectRoot: String): Option<String> {
|
|
310
|
+
let toml = joinPath(projectRoot, "cjpm.toml")
|
|
311
|
+
if (!exists(toml)) {
|
|
312
|
+
return None
|
|
313
|
+
}
|
|
314
|
+
for (rawLine in readText(toml).split("\n")) {
|
|
315
|
+
let line = trimAscii(rawLine)
|
|
316
|
+
if (line.startsWith("cjTauri")) {
|
|
317
|
+
if (let Some(v) <- firstQuoted(line)) {
|
|
318
|
+
return Some(stripTrailingSep(v))
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return None
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** stdx 目录:CANGJIE_STDX → 项目 cjpm.toml → 框架 cjpm.toml */
|
|
326
|
+
func resolveStdx(fwRoot: String, projectRoot: Option<String>): Option<String> {
|
|
327
|
+
if (let Some(v0) <- getVariable("CANGJIE_STDX")) {
|
|
328
|
+
let v = stripTrailingSep(v0)
|
|
329
|
+
if (v.size > 0 && exists(v)) {
|
|
330
|
+
return Some(v)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (let Some(proj) <- projectRoot) {
|
|
334
|
+
let toml = joinPath(proj, "cjpm.toml")
|
|
335
|
+
if (exists(toml)) {
|
|
336
|
+
if (let Some(v) <- pathOptionFor(readText(toml), hostTriple())) {
|
|
337
|
+
return Some(stripTrailingSep(v))
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
let fwToml = joinPath(fwRoot, "cjpm.toml")
|
|
342
|
+
if (exists(fwToml)) {
|
|
343
|
+
if (let Some(v) <- pathOptionFor(readText(fwToml), hostTriple())) {
|
|
344
|
+
return Some(stripTrailingSep(v))
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return None
|
|
348
|
+
}
|