`cjc` Compilation Options
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
27 modified sections5 code block delta18 anchor delta
modified`--package`, `-p` <sup>[frontend]</sup>text+3 lines
v1.0.5
Section Text
1
编译包,使用此选项时需要指定一个目录作为输入,目录中的源码文件需要属于同一个包。2
3
假设有文件 `log/printer.cj`:4
5
6
与文件 `main.cj`:7
8
9
可以使用10
11
12
来编译 `log` 包,`cjc` 会在当前目录下生成一个 `liblog.a` 文件。13
14
可以使用 `liblog.a` 文件来编译 `main.cj` ,编译命令如下:15
16
17
`cjc` 会将 `main.cj` 与 `liblog.a` 一同编译成一个可执行文件 `main` 。Code 1 · cangjie
1
package log2
3
public func printLog(message: String) {4
println("[Log]: ${message}")5
}Code 2 · cangjie
1
import log.*2
3
main() {4
printLog("Everything is great")5
}Code 3 · shell
1
$ cjc -p log --output-type=staticlibCode 4 · shell
1
$ cjc main.cj liblog.av1.1.0
Section Text
1
编译包,使用此选项时需要指定一个目录作为输入,目录中的源码文件需要属于同一个包。2
3
假设有文件 `log/printer.cj`:4
5
<!-- compile -p -->6
<!-- cfg="-p log --output-type=staticlib" -->7
8
9
与文件 `main.cj`:10
11
<!-- compile -p -->12
<!-- cfg="liblog.a" -->13
14
15
可以使用16
17
18
来编译 `log` 包,`cjc` 会在当前目录下生成一个 `liblog.a` 文件。19
20
可以使用 `liblog.a` 文件来编译 `main.cj` ,编译命令如下:21
22
23
`cjc` 会将 `main.cj` 与 `liblog.a` 一同编译成一个可执行文件 `main` 。Code 1 · cangjie
1
package log2
3
public func printLog(message: String) {4
println("[Log]: ${message}")5
}Code 2 · cangjie
1
import log.*2
3
main() {4
printLog("Everything is great")5
}Code 3 · shell
1
$ cjc -p log --output-type=staticlibCode 4 · shell
1
$ cjc main.cj liblog.amodified`--module-name <value>` <sup>[frontend]</sup>textcode+3 lines, -7 lines
v1.0.5
Section Text
1
指定要编译的模块的名称。2
3
假设有文件 `my_module/src/log/printer.cj`:4
5
6
与文件 `main.cj`:7
8
9
可以使用10
11
12
来编译 `log` 包并指定其模块名为 `my_module`,`cjc` 会在 `my_module` 目录下生成一个 `my_module/liblog.a` 文件。13
14
然后可以使用 `liblog.a` 文件来编译导入了 `log` 包的 `main.cj` ,编译命令如下:15
16
17
`cjc` 会将 `main.cj` 与 `liblog.a` 一同编译成一个可执行文件 `main` 。Code 1 · cangjie
1
package log2
3
public func printLog(message: String) {4
println("[Log]: ${message}")5
}Code 2 · cangjie
1
import my_module.log.*2
3
main() {4
printLog("Everything is great")5
}Code 3 · shell
1
$ cjc -p my_module/src/log --module-name my_module --output-type=staticlib -o my_module/liblog.aCode 4 · shell
1
$ cjc main.cj my_module/liblog.av1.1.0
Section Text
1
> **说明:**2
>3
> 该选项已废弃,并会在未来版本被移除。当前版本使用该选项没有功能性作用。Code 1 · cangjie
1
Code 2 · cangjie
1
Code 3 · shell
1
Code 4 · shell
1
modified`--library-path <value>`, `-L <value>`, `-L<value>`text+1 line
v1.0.5
Section Text
1
指定要链接的库文件所在的目录。2
3
使用 `--library <value>` 选项时,通常也需要使用此选项来指定要链接的库文件所在的目录。4
5
`--library-path <value>` 指定的路径会被加入链接器的库文件搜索路径。此外,环境变量 `LIBRARY_PATH` 中指定的路径也会被加入链接器的库文件搜索路径中,通过 `--library-path` 指定的路径会比 `LIBRARY_PATH` 中的路径拥有更高的优先级。6
7
假设有从以下 C 语言源文件通过 C 语言编译器编译得到的动态库文件 `libcProg.so`,8
9
10
仓颉文件 `main.cj`:11
12
13
可以使用14
15
16
来编译 `main.cj` 并指定要链接的 `cProg` 库,这里 `cjc` 会输出一个可执行文件 `main`。17
18
执行 `main` 会有如下输出:19
20
21
**值得注意的是**,由于使用了动态库文件,这里需要将库文件所在目录加入 `$LD_LIBRARY_PATH` 以保证 `main` 能够在执行时进行动态链接。Code 1 · c
1
#include <stdio.h>2
3
void printHello() {4
printf("Hello World\n");5
}Code 2 · cangjie
1
foreign func printHello(): Unit2
3
main(): Int64 {4
unsafe {5
printHello()6
}7
return 08
}Code 3 · shell
1
cjc main.cj -L . -l cProgCode 4 · shell
1
$ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main2
Hello Worldv1.1.0
Section Text
1
指定要链接的库文件所在的目录。2
3
使用 `--library <value>` 选项时,通常也需要使用此选项来指定要链接的库文件所在的目录。4
5
`--library-path <value>` 指定的路径会被加入链接器的库文件搜索路径。此外,环境变量 `LIBRARY_PATH` 中指定的路径也会被加入链接器的库文件搜索路径中,通过 `--library-path` 指定的路径会比 `LIBRARY_PATH` 中的路径拥有更高的优先级。6
7
假设有从以下 C 语言源文件通过 C 语言编译器编译得到的动态库文件 `libcProg.so`,8
9
10
仓颉文件 `main.cj`:11
12
<!-- code_check_manual -->13
14
15
可以使用16
17
18
来编译 `main.cj` 并指定要链接的 `cProg` 库,这里 `cjc` 会输出一个可执行文件 `main`。19
20
执行 `main` 会有如下输出:21
22
23
**值得注意的是**,由于使用了动态库文件,这里需要将库文件所在目录加入 `$LD_LIBRARY_PATH` 以保证 `main` 能够在执行时进行动态链接。Code 1 · c
1
#include <stdio.h>2
3
void printHello() {4
printf("Hello World\n");5
}Code 2 · cangjie
1
foreign func printHello(): Unit2
3
main(): Int64 {4
unsafe {5
printHello()6
}7
return 08
}Code 3 · shell
1
cjc main.cj -L . -l cProgCode 4 · shell
1
$ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main2
Hello Worldmodified`--import-path <value>` <sup>[frontend]</sup>textcode+2 lines, -1 line
v1.0.5
Section Text
1
指定导入模块的 AST 文件的搜索路径。2
3
假设已有以下目录结构,`libs/myModule` 目录中包含 `myModule` 模块的库文件和 `log` 包的 AST 导出文件:4
5
6
且有如下 `main.cj` 文件:7
8
9
可以通过使用 `--import-path ./libs` 来将 `./libs` 加入导入模块的 AST 文件搜索路径,`cjc` 会使用 `./libs/myModule/log.cjo` 文件来对 `main.cj` 文件进行语义检查与编译。10
11
`--import-path` 提供与 `CANGJIE_PATH` 环境变量相同的功能,但通过 `--import-path` 设置的路径拥有更高的优先级。Code 1 · text
1
.2
├── libs3
| └── myModule4
| ├── log.cjo5
| └── libmyModule.a6
└── main.cjCode 2 · cangjie
1
import myModule.log.printLog2
3
main() {4
printLog("Everything is great")5
}v1.1.0
Section Text
1
指定导入模块的 AST 文件的搜索路径。2
3
假设已有以下目录结构,`libs/myModule` 目录中包含 `myModule` 模块的库文件和 `log` 包的 AST 导出文件:4
5
6
且有如下 `main.cj` 文件:7
8
<!-- code_check_manual -->9
10
11
可以通过使用 `--import-path ./libs` 来将 `./libs` 加入导入模块的 AST 文件搜索路径,`cjc` 会使用 `./libs/myModule/myModule.log.cjo` 文件来对 `main.cj` 文件进行语义检查与编译。12
13
`--import-path` 提供与 `CANGJIE_PATH` 环境变量相同的功能,但通过 `--import-path` 设置的路径拥有更高的优先级。Code 1 · text
1
.2
├── libs3
| └── myModule4
| ├── myModule.log.cjo5
| └── libmyModule.log.a6
└── main.cjCode 2 · cangjie
1
import myModule.log.printLog2
3
main() {4
printLog("Everything is great")5
}modified`--scan-dependency` <sup>[frontend]</sup>text+1 line
v1.0.5
Section Text
1
通过 `--scan-dependency` 指令可以获得指定包源码或者一个包的 `cjo` 文件对于其他包的直接依赖以及其他信息,以 `json` 格式输出。2
3
4
5
或Code 1 · cangjie
1
// this file is placed under directory pkgA2
macro package pkgA3
import pkgB.*4
import std.io.*5
import pkgB.subB.*Code 2 · shell
1
cjc --scan-dependency --package pkgACode 3 · shell
1
cjc --scan-dependency pkgA.cjoCode 4 · json
1
{2
"package": "pkgA",3
"isMacro": true,4
"dependencies": [5
{6
"package": "pkgB",7
"isStd": false,8
"imports": [9
{10
"file": "pkgA/pkgA.cj",11
"begin": {12
"line": 2,13
"column": 114
},15
"end": {16
"line": 2,17
"column": 1418
}19
}20
]21
},22
{23
"package": "pkgB.subB",24
"isStd": false,25
"imports": [26
{27
"file": "pkgA/pkgA.cj",28
"begin": {29
"line": 4,30
"column": 131
},32
"end": {33
"line": 4,34
"column": 1935
}36
}37
]38
},39
{40
"package": "std.io",41
"isStd": true,42
"imports": [43
{44
"file": "pkgA/pkgA.cj",45
"begin": {46
"line": 3,47
"column": 148
},49
"end": {50
"line": 3,51
"column": 1652
}53
}54
]55
}56
]57
}v1.1.0
Section Text
1
通过 `--scan-dependency` 指令可以获得指定包源码或者一个包的 `cjo` 文件对于其他包的直接依赖以及其他信息,以 `json` 格式输出。2
3
<!-- code_check_manual -->4
5
6
7
或Code 1 · cangjie
1
// this file is placed under directory pkgA2
macro package pkgA3
import pkgB.*4
import std.io.*5
import pkgB.subB.*Code 2 · shell
1
cjc --scan-dependency --package pkgACode 3 · shell
1
cjc --scan-dependency pkgA.cjoCode 4 · json
1
{2
"package": "pkgA",3
"isMacro": true,4
"dependencies": [5
{6
"package": "pkgB",7
"isStd": false,8
"imports": [9
{10
"file": "pkgA/pkgA.cj",11
"begin": {12
"line": 2,13
"column": 114
},15
"end": {16
"line": 2,17
"column": 1418
}19
}20
]21
},22
{23
"package": "pkgB.subB",24
"isStd": false,25
"imports": [26
{27
"file": "pkgA/pkgA.cj",28
"begin": {29
"line": 4,30
"column": 131
},32
"end": {33
"line": 4,34
"column": 1935
}36
}37
]38
},39
{40
"package": "std.io",41
"isStd": true,42
"imports": [43
{44
"file": "pkgA/pkgA.cj",45
"begin": {46
"line": 3,47
"column": 148
},49
"end": {50
"line": 3,51
"column": 1652
}53
}54
]55
}56
]57
}modified`--static`text+1 line, -1 line
v1.0.5
Section Text
1
静态链接仓颉库。2
3
此选项仅在编译可执行文件时生效。4
5
**值得注意的是:**6
7
`--static` 选项仅适用于 Linux 平台,在其他平台不生效。v1.1.0
Section Text
1
静态链接仓颉库。2
3
此选项仅在编译可执行文件时生效。4
5
**值得注意的是:**6
7
`--static` 选项适用于 Linux、Windows 和 macOS 平台。modified`--static-libs`text+3 lines, -2 lines
v1.0.5
Section Text
1
静态链接仓颉库中除 std 及运行时模块外的其他模块。2
3
此选项仅在编译动态链接库或可执行文件时生效。`cjc` 默认静态链接仓颉库中除 std 及运行时模块外的其他模块。v1.1.0
Section Text
1
> **说明:**2
>3
> 该选项已废弃,并会在未来版本被移除。当前版本使用该选项没有功能性作用。modified`--dy-libs`text+3 lines, -7 lines
v1.0.5
Section Text
1
动态链接仓颉库非 std 的其他模块。2
3
此选项仅在编译动态链接库或可执行文件时生效。4
5
**值得注意的是:**6
7
1. `--static-libs` 和 `--dy-libs` 选项一起使用时,仅最后一个选项生效;8
2. `--static-std` 与 `--dy-libs` 选项不可一起使用,否则会报错;9
3. `--dy-std` 单独使用时,会默认生效 `--dy-libs` 选项,并有相关告警信息提示;10
4. `--dy-libs` 单独使用时,会默认生效 `--dy-std` 选项,并有相关告警信息提示。v1.1.0
Section Text
1
> **说明:**2
>3
> 该选项已废弃,并会在未来版本被移除。当前版本使用该选项没有功能性作用。modified`--lto=[full|thin]`text+1 line, -1 line
v1.0.5
Section Text
1
使能且指定 `LTO` (`Link Time Optimization` 链接时优化)优化编译模式。2
3
**值得注意的是:**4
5
1. `Windows` 以及 `macOS` 平台不支持该功能;6
2. 当使能且指定 `LTO` (`Link Time Optimization` 链接时优化)优化编译模式时,不允许同时使用如下优化编译选项:`-Os`、`-Oz`。7
8
`LTO` 优化支持两种编译模式:9
10
- `--lto=full`:`full LTO` 将所有编译模块合并到一起,在全局上进行优化,这种方式可以获得最大的优化潜力,同时也需要更长的编译时间。11
- `--lto=thin`:相比于 `full LTO`,`thin LTO` 在多模块上使用并行优化,同时默认支持链接时增量编译,编译时间比 `full LTO` 短,因为失去了更多的全局信息,所以优化效果不如 `full LTO`。12
13
- 通常情况下优化效果对比:`full LTO` **>** `thin LTO` **>** 常规静态链接编译。14
- 通常情况下编译时间对比:`full LTO` **>** `thin LTO` **>** 常规静态链接编译。15
16
`LTO` 优化使用场景:17
18
1. 使用以下命令编译可执行文件。19
20
```shell21
$ cjc test.cj --lto=full22
or23
$ cjc test.cj --lto=thin24
```25
26
2. 使用以下命令编译 `LTO` 模式下需要的静态库(`.bc` 文件),并且使用该库文件参与可执行文件编译。27
28
```shell29
# 生成的静态库为 .bc 文件30
$ cjc pkg.cj --lto=full --output-type=staticlib -o libpkg.bc31
# .bc 文件和源文件一起输入给仓颉编译器编译可执行文件32
$ cjc test.cj libpkg.bc --lto=full33
```34
35
> **注意:**36
>37
> `LTO` 模式下的静态库(`.bc` 文件)输入时需要将该文件的路径输入仓颉编译器。38
39
3. 在 `LTO` 模式下,静态链接标准库(`--static-std` & `--static-libs`)时,标准库的代码也会参与 `LTO` 优化,并静态链接到可执行文件;动态链接标准库(`--dy-std` & `--dy-libs`)时,在 `LTO` 模式下依旧使用标准库中的动态库参与链接。40
41
```shell42
# 静态链接,标准库代码也参与 LTO 优化43
$ cjc test.cj --lto=full --static-std44
# 动态链接,依旧使用动态库参与链接,标准库代码不会参与 LTO 优化45
$ cjc test.cj --lto=full --dy-std46
```v1.1.0
Section Text
1
使能且指定 `LTO` (`Link Time Optimization` 链接时优化)优化编译模式。2
3
**值得注意的是:**4
5
1. `Windows` 以及 `macOS` 平台不支持该功能;6
2. 当使能且指定 `LTO` (`Link Time Optimization` 链接时优化)优化编译模式时,不允许同时使用如下优化编译选项:`-Os`、`-Oz`。7
8
`LTO` 优化支持两种编译模式:9
10
- `--lto=full`:`full LTO` 将所有编译模块合并到一起,在全局上进行优化,这种方式可以获得最大的优化潜力,同时也需要更长的编译时间。11
- `--lto=thin`:相比于 `full LTO`,`thin LTO` 在多模块上使用并行优化,同时默认支持链接时增量编译,编译时间比 `full LTO` 短,因为失去了更多的全局信息,所以优化效果不如 `full LTO`。12
13
- 通常情况下优化效果对比:`full LTO` **>** `thin LTO` **>** 常规静态链接编译。14
- 通常情况下编译时间对比:`full LTO` **>** `thin LTO` **>** 常规静态链接编译。15
16
`LTO` 优化使用场景:17
18
1. 使用以下命令编译可执行文件。19
20
```shell21
$ cjc test.cj --lto=full22
or23
$ cjc test.cj --lto=thin24
```25
26
2. 使用以下命令编译 `LTO` 模式下需要的静态库(`.bc` 文件),并且使用该库文件参与可执行文件编译。27
28
```shell29
# 生成的静态库为 .bc 文件30
$ cjc pkg.cj --lto=full --output-type=staticlib -o libpkg.bc31
# .bc 文件和源文件一起输入给仓颉编译器编译可执行文件32
$ cjc test.cj libpkg.bc --lto=full33
```34
35
> **注意:**36
>37
> `LTO` 模式下的静态库(`.bc` 文件)输入时需要将该文件的路径输入仓颉编译器。38
39
3. 在 `LTO` 模式下,静态链接标准库(`--static-std`)时,标准库的代码也会参与 `LTO` 优化,并静态链接到可执行文件;动态链接标准库(`--dy-std`)时,在 `LTO` 模式下依旧使用标准库中的动态库参与链接。40
41
```shell42
# 静态链接,标准库代码也参与 LTO 优化43
$ cjc test.cj --lto=full --static-std44
# 动态链接,依旧使用动态库参与链接,标准库代码不会参与 LTO 优化45
$ cjc test.cj --lto=full --dy-std46
```modified`--target <value>` <sup>[frontend]</sup>text+10 lines
v1.0.5
Section Text
1
指定编译的目标平台的 triple。2
3
参数 `<value>` 一般为符合以下格式的字符串:`<arch>(-<vendor>)-<os>(-<env>)`。其中:4
5
- `<arch>` 表示目标平台的系统架构,例如 `aarch64`,`x86_64` 等;6
- `<vendor>` 表示开发目标平台的厂商,常见的例如 `apple` 等,在没有明确平台厂商或厂商不重要的情况下也经常写作 `unknown` 或直接省略;7
- `<os>` 表示目标平台的操作系统,例如 `Linux`,`Win32` 等;8
- `<env>` 表示目标平台的 ABI 或标准规范,用于更细粒度地区分同一操作系统的不同运行环境,例如 `gnu`,`musl` 等。在操作系统不需要根据 `<env>` 进行更细地区分的时候,此项也可以省略。9
10
目前,`cjc` 已支持交叉编译的本地平台和目标平台如下表所示:11
12
| 本地平台 (host) | 目标平台 (target) |13
| ------------------ | ------------------ |14
| x86_64-linux-gnu | x86_64-windows-gnu |15
| aarch64-linux-gnu | x86_64-windows-gnu |16
17
在使用 `--target` 指定目标平台进行交叉编译之前,请准备好对应目标平台的交叉编译工具链,以及可以在本地平台上运行的、向该目标平台编译的对应 Cangjie SDK 版本。v1.1.0
Section Text
1
指定编译的目标平台的 triple。2
3
参数 `<value>` 一般为符合以下格式的字符串:`<arch>(-<vendor>)-<os>(-<env>)`。其中:4
5
- `<arch>` 表示目标平台的系统架构,例如 `aarch64`,`x86_64` 等;6
- `<vendor>` 表示开发目标平台的厂商,常见的例如 `apple` 等,在没有明确平台厂商或厂商不重要的情况下也经常写作 `unknown` 或直接省略;7
- `<os>` 表示目标平台的操作系统,例如 `Linux`,`Win32` 等;8
- `<env>` 表示目标平台的 ABI 或标准规范,用于更细粒度地区分同一操作系统的不同运行环境,例如 `gnu`,`musl` 等。在操作系统不需要根据 `<env>` 进行更细地区分的时候,此项也可以省略。9
10
目前,`cjc` 已支持交叉编译的本地平台和目标平台如下表所示:11
12
| 本地平台 (host) | 目标平台 (target) |13
| ------------------ | ------------------ |14
| x86_64-linux-gnu | x86_64-windows-gnu |15
| aarch64-linux-gnu | x86_64-windows-gnu |16
| <!--DelRow-->x86_64-windows-gnu | aarch64-linux-ohos |17
| <!--DelRow-->x86_64-windows-gnu | x86_64-linux-ohos |18
| <!--DelRow-->x86_64-apple-darwin | aarch64-linux-ohos |19
| <!--DelRow-->x86_64-apple-darwin | x86_64-linux-ohos |20
| <!--DelRow-->aarch64-apple-darwin | aarch64-linux-ohos |21
| <!--DelRow-->aarch64-apple-darwin| aarch64-apple-ios-simulator |22
| <!--DelRow-->aarch64-apple-darwin| x86_64-apple-ios-simulator |23
| <!--DelRow-->x86_64-linux-gnu| aarch64-linux-android26 |24
| <!--DelRow-->x86_64-apple-darwin | aarch64-linux-android26 |25
| <!--DelRow-->x86_64-windows-gnu | aarch64-linux-android26 |26
27
在使用 `--target` 指定目标平台进行交叉编译之前,请准备好对应目标平台的交叉编译工具链,以及可以在本地平台上运行的、向该目标平台编译的对应 Cangjie SDK 版本。modified`--link-options <value>`<sup>1</sup>text+1 line, -1 line
v1.0.5
Section Text
1
指定链接器选项。2
3
`cjc` 会将该选项的多个参数透传给链接器, 参数之间用空格分隔。可用的参数会因(系统或指定的)链接器的不同而不同。可以多次使用 `--link-options` 指定多个链接器选项。4
5
<sup>1</sup> 上标表示链接器透传选项可能会因为链接器的不同而不同,具体支持的选项请查阅链接器文档。v1.1.0
Section Text
1
指定链接器选项。2
3
`cjc` 会将该选项的多个参数透传给链接器,参数之间用空格分隔。可用的参数会因(系统或指定的)链接器的不同而不同。可以多次使用 `--link-options` 指定多个链接器选项。4
5
<sup>1</sup> 上标表示链接器透传选项可能会因为链接器的不同而不同,具体支持的选项请查阅链接器文档。modified`--profile-compile-time` <sup>[frontend]</sup>text+1 line, -1 line
v1.0.5
Section Text
1
打印各编译阶段的时间消耗数据。v1.1.0
Section Text
1
输出各编译阶段时间消耗数据到一份文件中,该文件以 .time.prof 为后缀, 保存在 `output` 指定的目录中,若`output` 指定的是一个文件,则 .time.prof 与该文件同级。modified`--profile-compile-memory` <sup>[frontend]</sup>text+1 line, -1 line
v1.0.5
Section Text
1
打印各编译阶段的内存消耗数据。v1.1.0
Section Text
1
输出各编译阶段内存消耗数据到一份文件中,该文件以 .mem.prof 为后缀, 保存在 `output` 指定的目录中,若`output` 指定的是一个文件,则 .mem.prof 与该文件同级。modified`--test` <sup>[frontend]</sup>textcode+2 lines
v1.0.5
Section Text
1
`unittest` 测试框架提供的入口,由宏自动生成。当使用 `cjc --test` 选项编译时,程序入口不再是 `main`,而是 `test_entry`。unittest 测试框架的使用方法请参见《仓颉编程语言标准库 API》文档。2
3
对于 `pkgc` 目录下的仓颉文件 `a.cj`:4
<!-- run -->5
6
7
可以在 `pkgc` 目录下使用:8
9
10
来编译 `a.cj` ,执行 `main` 会有如下输出:11
12
> **注意:**13
>14
> 不保证用例每次执行的用时都相同。15
16
17
对于如下目录结构:18
19
20
可以在 `application`目录下使用 `-p` 编译选项配合编译整包:21
22
23
来编译整个 `pkgc` 包下的测试用例 `a1.cj` 和 `a2.cj`。24
25
26
27
执行 `main` 会有如下输出(**输出信息仅供参考**):Code 1 · cangjie
1
import std.unittest.*2
import std.unittest.testmacro.*3
4
@Test5
public class TestA {6
@TestCase7
public func case1(): Unit {8
print("case1\n")9
}10
}Code 2 · shell
1
cjc a.cj --testCode 3 · text
1
case12
--------------------------------------------------------------------------------------------------3
TP: default, time elapsed: 29710 ns, Result:4
TCS: TestA, time elapsed: 26881 ns, RESULT:5
[ PASSED ] CASE: case1 (16747 ns)6
Summary: TOTAL: 17
PASSED: 1, SKIPPED: 0, ERROR: 08
FAILED: 09
--------------------------------------------------------------------------------------------------Code 4 · text
1
application2
├── src3
├── pkgc4
| ├── a1.cj5
| └── a2.cj6
└── a3.cjCode 5 · shell
1
cjc pkgc --test -pCode 6 · cangjie
1
/*a1.cj*/2
package a3
4
import std.unittest.*5
import std.unittest.testmacro.*6
7
@Test8
public class TestA {9
@TestCase10
public func caseA(): Unit {11
print("case1\n")12
}13
}Code 7 · cangjie
1
/*a2.cj*/2
package a3
4
import std.unittest.*5
import std.unittest.testmacro.*6
7
@Test8
public class TestB {9
@TestCase10
public func caseB(): Unit {11
throw IndexOutOfBoundsException()12
}13
}Code 8 · text
1
case12
--------------------------------------------------------------------------------------------------3
TP: a, time elapsed: 367800 ns, Result:4
TCS: TestA, time elapsed: 16802 ns, RESULT:5
[ PASSED ] CASE: caseA (14490 ns)6
TCS: TestB, time elapsed: 347754 ns, RESULT:7
[ ERROR ] CASE: caseB (345453 ns)8
REASON: An exception has occurred:IndexOutOfBoundsException9
at std/core.Exception::init()(std/core/exception.cj:23)10
at std/core.IndexOutOfBoundsException::init()(std/core/index_out_of_bounds_exception.cj:9)11
at a.TestB::caseB()(/home/houle/cjtest/application/pkgc/a2.cj:7)12
at a.lambda.1()(/home/houle/cjtest/application/pkgc/a2.cj:7)13
at std/unittest.TestCases::execute()(std/unittest/test_case.cj:92)14
at std/unittest.UT::run(std/unittest::UTestRunner)(std/unittest/test_runner.cj:194)15
at std/unittest.UTestRunner::doRun()(std/unittest/test_runner.cj:78)16
at std/unittest.UT::run(std/unittest::UTestRunner)(std/unittest/test_runner.cj:200)17
at std/unittest.UTestRunner::doRun()(std/unittest/test_runner.cj:78)18
at std/unittest.UT::run(std/unittest::UTestRunner)(std/unittest/test_runner.cj:200)19
at std/unittest.UTestRunner::doRun()(std/unittest/test_runner.cj:75)20
at std/unittest.entryMain(std/unittest::TestPackage)(std/unittest/entry_main.cj:11)21
Summary: TOTAL: 222
PASSED: 1, SKIPPED: 0, ERROR: 123
FAILED: 024
--------------------------------------------------------------------------------------------------v1.1.0
Section Text
1
`unittest` 测试框架提供的入口,由宏自动生成。当使用 `cjc --test` 选项编译时,程序入口不再是 `main`,而是 `test_entry`。unittest 测试框架的使用方法请参见《仓颉编程语言标准库 API》文档。2
3
对于 `pkgc` 目录下的仓颉文件 `a.cj`:4
5
<!-- run -->6
<!-- cfg="--test" -->7
8
9
可以在 `pkgc` 目录下使用:10
11
12
来编译 `a.cj` ,执行 `main` 会有如下输出:13
14
> **注意:**15
>16
> 不保证用例每次执行的用时都相同。17
18
19
对于如下目录结构:20
21
22
可以在 `application`目录下使用 `-p` 编译选项配合编译整包:23
24
25
来编译整个 `pkgc` 包下的测试用例 `a1.cj` 和 `a2.cj`。26
27
<!-- code_check_manual -->28
29
30
<!-- code_check_manual -->31
32
33
执行 `main` 会有如下输出(**输出信息仅供参考**):Code 1 · cangjie
1
import std.unittest.*2
import std.unittest.testmacro.*3
4
@Test5
public class TestA {6
@TestCase7
public func case1(): Unit {8
print("case1\n")9
}10
}Code 2 · shell
1
cjc a.cj --testCode 3 · text
1
case12
--------------------------------------------------------------------------------------------------3
TP: default, time elapsed: 29710 ns, Result:4
TCS: TestA, time elapsed: 26881 ns, RESULT:5
[ PASSED ] CASE: case1 (16747 ns)6
Summary: TOTAL: 17
PASSED: 1, SKIPPED: 0, ERROR: 08
FAILED: 09
--------------------------------------------------------------------------------------------------Code 4 · text
1
application2
├── src3
├── pkgc4
| ├── a1.cj5
| └── a2.cj6
└── a3.cjCode 5 · shell
1
cjc --test -p pkgcCode 6 · cangjie
1
/*a1.cj*/2
package pkgc3
4
import std.unittest.*5
import std.unittest.testmacro.*6
7
@Test8
public class TestA {9
@TestCase10
public func caseA(): Unit {11
print("case1\n")12
}13
}Code 7 · cangjie
1
/*a2.cj*/2
package pkgc3
4
import std.unittest.*5
import std.unittest.testmacro.*6
7
@Test8
public class TestB {9
@TestCase10
public func caseB(): Unit {11
throw IndexOutOfBoundsException()12
}13
}Code 8 · text
1
case12
--------------------------------------------------------------------------------------------------3
TP: a, time elapsed: 367800 ns, Result:4
TCS: TestA, time elapsed: 16802 ns, RESULT:5
[ PASSED ] CASE: caseA (14490 ns)6
TCS: TestB, time elapsed: 347754 ns, RESULT:7
[ ERROR ] CASE: caseB (345453 ns)8
REASON: An exception has occurred:IndexOutOfBoundsException9
at std/core.Exception::init()(std/core/exception.cj:23)10
at std/core.IndexOutOfBoundsException::init()(std/core/index_out_of_bounds_exception.cj:9)11
at a.TestB::caseB()(/home/houle/cjtest/application/pkgc/a2.cj:7)12
at a.lambda.1()(/home/houle/cjtest/application/pkgc/a2.cj:7)13
at std/unittest.TestCases::execute()(std/unittest/test_case.cj:92)14
at std/unittest.UT::run(std/unittest::UTestRunner)(std/unittest/test_runner.cj:194)15
at std/unittest.UTestRunner::doRun()(std/unittest/test_runner.cj:78)16
at std/unittest.UT::run(std/unittest::UTestRunner)(std/unittest/test_runner.cj:200)17
at std/unittest.UTestRunner::doRun()(std/unittest/test_runner.cj:78)18
at std/unittest.UT::run(std/unittest::UTestRunner)(std/unittest/test_runner.cj:200)19
at std/unittest.UTestRunner::doRun()(std/unittest/test_runner.cj:75)20
at std/unittest.entryMain(std/unittest::TestPackage)(std/unittest/entry_main.cj:11)21
Summary: TOTAL: 222
PASSED: 1, SKIPPED: 0, ERROR: 123
FAILED: 024
--------------------------------------------------------------------------------------------------modified`--test-only` <sup>[frontend]</sup>textcode+5 lines, -1 line
v1.0.5
Section Text
1
`--test-only` 选项用于单独编译包的测试部分。2
3
如果启用此选项,编译器将仅编译包中的测试文件(以 `_test.cj` 结尾)。4
5
> **注意:**6
>7
> 使用此选项时,应单独以常规模式编译相同的包,然后通过 `-L`/`-l` 链接选项添加依赖,或在使用 `LTO` 选项时添加依赖的 `.bc` 文件。否则,编译器将报缺少依赖的符号的错误。8
9
示例:10
11
12
13
使用编译器编译的命令如下:Code 1 · cangjie
1
/*main.cj*/2
package my_pkg3
4
func concatM(s1: String, s2: String): String {5
return s1 + s26
}7
8
main() {9
println(concatM("a", "b"))10
011
}Code 2 · cangjie
1
/*main_test.cj*/2
package my_pkg3
4
@Test5
class Tests {6
@TestCase7
public func case1(): Unit {8
@Expect("ac", concatM("a", "c"))9
}10
}Code 3 · shell
1
# Compile the production part of the package first, only `main.cj` file would be compiled here2
cjc -p my_pkg --output-type=static -o=output/libmain.a3
# Compile the test part of the package, Only `main_test.cj` file would be compiled here4
cjc -p my_pkg --test-only -L output -lmainv1.1.0
Section Text
1
`--test-only` 选项用于单独编译包的测试部分。2
3
如果启用此选项,编译器将仅编译包中的测试文件(以 `_test.cj` 结尾)。4
5
> **注意:**6
>7
> 使用此选项时,应单独以常规模式编译相同的包,然后通过 `-L`/`-l` 链接选项添加依赖,或在使用 `LTO` 选项时添加依赖的 `.bc` 文件。否则,编译器将报缺少依赖的符号的错误。8
9
示例:10
<!-- run -my_pkg -->11
<!-- cfg="-p my_pkg --output-type=staticlib -o=output/libmain.a" -->12
13
<!-- run -my_pkg-->14
<!-- cfg="-p my_pkg --test-only -L output -lmain --import-path output" -->15
16
17
使用编译器编译的命令如下:Code 1 · cangjie
1
/*main.cj*/2
package my_pkg3
4
func concatM(s1: String, s2: String): String {5
return s1 + s26
}7
8
main(): Int64 {9
println(concatM("a", "b"))10
011
}Code 2 · cangjie
1
/*main_test.cj*/2
package my_pkg3
4
@Test5
class Tests {6
@TestCase7
public func case1(): Unit {8
@Expect("ac", concatM("a", "c"))9
}10
}Code 3 · shell
1
# Compile the production part of the package first, only `main.cj` file would be compiled here2
cjc -p my_pkg --output-type=staticlib -o=output/libmain.a3
# Compile the test part of the package, Only `main_test.cj` file would be compiled here4
cjc -p my_pkg --test-only -L output -lmain --import-path outputmodified宏选项text+1 line, -1 line
v1.0.5
Section Text
1
`cjc` 支持以下宏选项,关于宏的更多内容请参见[“宏的简介”](../Macro/macro_introduction.md)章节。v1.1.0
Section Text
1
`cjc` 支持以下宏选项,关于宏的更多内容请参见<!--RP1-->[宏](../Macro/macro_introduction.md)<!--RP1End-->章节。modified条件编译选项text+1 line, -1 line
v1.0.5
Section Text
1
`cjc` 支持以下条件编译选项,关于条件编译的更多内容请参见[“条件编译”](../compile_and_build/conditional_compilation.md)。v1.1.0
Section Text
1
`cjc` 支持以下条件编译选项,关于条件编译的更多内容请参见<!--RP2-->[条件编译](../compile_and_build/conditional_compilation.md)<!--RP2End-->。modified`--aggressive-parallel-compile`, `--apc`, `--aggressive-parallel-compile=<value>`, `--apc=<value>` <sup>[frontend]</sup>text+2 lines, -2 lines
v1.0.5
Section Text
1
开启此选项后,编译器会采用更加激进的策略(可能会对优化造成影响,从而导致程序运行性能下降)执行激进并行编译,以便获得更高的编译效率。其中 `value` 是一个可选参数,表示激进并行编译部分允许的最大并行数:2
3
- 如果使用 `value`,则 `value` 必须是一个合理的非负整数,当 `value` 大于硬件支持的最大并行能力时,编译器会基于硬件能力自动计算最大并行数。建议将 `value` 设置为小于硬件的物理核数的非负整数。4
- 如果不使用 `value`,则激进并行编译默认开启,且激进并行编译部分的并行数与 `--jobs` 一致。5
6
此外,如果两次编译同一份代码时此选项的 `value` 值不同,或此选项的开关状态不同,编译器不保证这两次编译的产物的二进制一致性。7
8
激进并行编译的开启或关闭规则如下:9
10
- 在以下场景中,激进并行编译将由编译器强制关闭,无法启用:11
12
- `--fobf-string`13
- `--fobf-const`14
- `--fobf-layout`15
- `--fobf-cf-flatten`16
- `--fobf-cf-bogus`17
- `--lto`18
- `--coverage`19
- 编译 Windows 目标20
- 编译 macOS 目标21
22
- 若使用 `--aggressive-parallel-compile=<value>` 或 `--apc=<value>`,则激进并行编译的开关由 `value` 控制:23
24
- `value <= 1`:关闭激进并行编译。25
- `value > 1`:开启激进并行编译,且激进并行编译的并行数取决于 `value`。26
27
- 若使用 `--aggressive-parallel-compile` 或 `--apc`,则激进并行编译默认开启,且激进并行编译的并行数与 `--jobs` 一致。28
29
- 若该编译选项未设置,编译器将根据场景默认开启或关闭激进并行编译:30
31
- `-O0` 或 `-g`:激进并行编译将由编译器默认开启,且激进并行编译的并行数与 `--jobs` 一致;可以通过 `--aggressive-parallel-compile=<value>` 或 `--apc=<value>` 且 `value <= 1` 关闭激进并行编译。32
- 非 `-O0` 且非 `-g`:激进并行编译将由编译器默认关闭;可以通过 `--aggressive-parallel-compile=<value>` 或 `--apc=<value>` 且 `value > 1` 开启激进并行编译。v1.1.0
Section Text
1
开启此选项后,编译器会采用更加激进的策略(可能会对优化造成影响,从而导致程序运行性能下降)执行激进并行编译,以便获得更高的编译效率。其中 `value` 是一个可选参数,表示激进并行编译部分允许的最大并行数:2
3
- 如果使用 `value`,则 `value` 必须是一个合理的非负整数,当 `value` 大于硬件支持的最大并行能力时,编译器会基于硬件能力自动计算最大并行数。建议将 `value` 设置为小于硬件的物理核数的非负整数。4
- 如果不使用 `value`,则激进并行编译默认开启,且激进并行编译部分的并行数与 `--jobs` 一致。5
6
此外,如果两次编译同一份代码时此选项的 `value` 值不同,或此选项的开关状态不同,编译器不保证这两次编译的产物的二进制一致性。7
8
激进并行编译的开启或关闭规则如下:9
10
- 在以下场景中,激进并行编译将由编译器强制关闭,无法启用:11
12
- `--fobf-string`13
- `--fobf-const`14
- `--fobf-layout`15
- `--fobf-cf-flatten`16
- `--fobf-cf-bogus`17
- `--lto`18
- `--coverage`19
- 编译 Windows 目标20
- 编译 macOS 目标21
22
- 若使用 `--aggressive-parallel-compile=<value>` 或 `--apc=<value>`,则激进并行编译的开关由 `value` 控制:23
24
- `value <= 1`:关闭激进并行编译。25
- `value > 1`:开启激进并行编译,且激进并行编译的并行数取决于 `value`。26
27
- 若使用 `--aggressive-parallel-compile` 或 `--apc`,则激进并行编译默认开启,且激进并行编译的并行数与 `--jobs` 一致。28
29
- 若该编译选项未设置,编译器将根据场景默认开启或关闭激进并行编译:30
31
- `-O0` :激进并行编译将由编译器默认开启,且激进并行编译的并行数与 `--jobs` 一致;可以通过 `--aggressive-parallel-compile=<value>` 或 `--apc=<value>` 且 `value <= 1` 关闭激进并行编译。32
- 非 `-O0` :激进并行编译将由编译器默认关闭;可以通过 `--aggressive-parallel-compile=<value>` 或 `--apc=<value>` 且 `value > 1` 开启激进并行编译。modified代码混淆选项text+1 line, -1 line
v1.0.5
Section Text
1
`cjc` 支持代码混淆功能,以提供对代码的额外安全保护,默认不开启。2
3
`cjc` 支持以下代码混淆选项:v1.1.0
Section Text
1
`cjc` 支持代码混淆功能,以提供额外的安全保护。代码混淆功能默认不开启。2
3
`cjc` 支持以下代码混淆选项:modified`--fobf-const`text+1 line, -1 line
v1.0.5
Section Text
1
开启常量混淆。2
3
混淆代码中使用的数值常量,将数值运算指令替换成等效的、更复杂的数值运算指令序列。v1.1.0
Section Text
1
开启常量混淆。2
3
混淆代码中使用的数值常量,将其数值运算指令替换成等效的、更复杂的数值运算指令序列。modified`--obf-config <file>`text+2 lines, -1 line
v1.0.5
Section Text
1
指定代码混淆配置文件路径。2
3
在配置文件中可以禁止混淆工具对某些函数或者符号进行混淆。4
5
配置文件的具体格式如下:6
7
8
第一个参数 `obf_func` 是具体的混淆功能:9
10
- `obf-cf-bogus`:虚假控制流混淆11
- `obf-cf-flatten`:控制流平坦化混淆12
- `obf-const`:常数混淆13
- `obf-layout`:外形混淆14
15
第二个参数 `name` 是需要被保留的对象,由多个 `field` 组成。`field` 表示字段名,可以是包名、类名、结构体名、枚举名、函数名或变量名。16
17
`field` 之间用分隔符 `'.'` 分隔。如果 `field` 是函数名,则需要将函数的参数类型用括号 `'()'` 修饰并附加在函数名后面。对于无参函数括号内的内容为空。18
19
例如,假设在包 `packA` 中有以下代码:20
21
22
如果要禁止控制流平坦化功能混淆 `funcA`,用户可以编写如下规则:23
24
25
用户也可以使用通配符编写更加灵活的规则,达到一条规则保留多个对象的效果。目前支持的通配符包含以下 3 类:26
27
混淆功能通配符:28
29
| 混淆功能通配符 | 说明 |30
| :-------------- | :----------------------- |31
| `?` | 匹配名称中的单个字符 |32
| `*` | 匹配名称中的任意数量字符 |33
34
字段名通配符:35
36
| 字段名通配符 | 说明 |37
| :------------ | :------------------------------------------------------------ |38
| `?` | 匹配字段名中单个非分隔符 `'.'` 的字符 |39
| `*` | 匹配字段名中的不包含分隔符 `'.'` 和参数的任意数量字符 |40
| `**` | 匹配字段名中的任意数量字符,包括字段之间的分隔符 `'.'` 和参数。`'**'` 只有在单独作为一个 `field` 时才生效,否则会被当作 `'*'` 处理 |41
42
函数的参数类型通配符:43
44
| 参数类型通配符 | 说明 |45
| :-------------- | :---------------------- |46
| `...` | 匹配任意数量的参数 |47
| `***` | 匹配一个任意类型的参数 |48
49
> **说明:**50
>51
> 参数类型也由字段名组成,因此也可以使用字段名通配符对单个参数类型进行匹配。52
53
以下是通配符使用示例:54
55
例子 1:56
57
58
该规则表示禁止 `obf-cf-flatten` 功能混淆函数 `pro?.myfunc()`,`pro?.myfunc()` 可以匹配 `pro0.myfunc()`,但不能匹配 `pro00.myfunc()`。59
60
例子 2:61
62
63
该规则表示禁止任何混淆功能混淆包 `pro0` 下的任何函数和变量。64
65
例子 3:66
67
68
该规则表示禁止任何混淆功能混淆函数 `pro*.myfunc(...)`,`pro*.myfunc(...)` 可以匹配以 `pro` 开头的任意单层包内的 `myfunc` 函数,且可以为任意参数。69
70
如果需要匹配多层包名,比如 `pro0.mypack.myfunc()`,请使用 `pro*.**.myfunc(...)`。请注意 `'**'` 只有单独作为字段名时才生效,因此 `pro**.myfunc(...)` 和 `pro*.myfunc(...)` 等价,无法匹配多层包名。如果要匹配以 `pro` 开头的所有包下的所有 `myfunc` 函数(包括类中名为 `myfunc` 的函数),请使用 `pro*.**.myfunc(...)`。71
72
例子 4:73
74
75
该规则表示禁止 `obf-cf-*` 功能混淆函数 `pro0.MyClassA.myfunc(**.MyClassB, ***, ...)`,其中 `obf-cf-*` 会匹配 `obf-cf-bogus` 和 `obf-cf-flatten` 两种混淆功能,`pro0.MyClassA.myfunc(**.MyClassB, ***, ...)` 会匹配函数 `pro0.MyClassA.myfunc`,且函数的第一个参数可以是任意包下的 `MyClassB` 类型,第二个参数可以是任意类型,后面可以接零至多个任意参数。Code 1 · text
1
obf_func1 name12
obf_func2 name23
...Code 2 · cangjie
1
package packA2
class MyClassA {3
func funcA(a: String, b: Int64): String {4
return a5
}6
}Code 3 · text
1
obf-cf-flatten packA.MyClassA.funcA(std.core.String, Int64)Code 4 · text
1
obf-cf-flatten pro?.myfunc()Code 5 · text
1
* pro0.**Code 6 · text
1
* pro*.myfunc(...)Code 7 · text
1
obf-cf-* pro0.MyClassA.myfunc(**.MyClassB, ***, ...)v1.1.0
Section Text
1
指定代码混淆配置文件路径。2
3
在配置文件中可以禁止混淆工具对某些函数或者符号进行混淆。4
5
配置文件的具体格式如下:6
7
8
第一个参数 `obf_func` 是具体的混淆功能:9
10
- `obf-cf-bogus`:虚假控制流混淆11
- `obf-cf-flatten`:控制流平坦化混淆12
- `obf-const`:常数混淆13
- `obf-layout`:外形混淆14
15
第二个参数 `name` 是需要被保留的对象,由多个 `field` 组成。`field` 表示字段名,可以是包名、类名、结构体名、枚举名、函数名或变量名。16
17
`field` 之间用分隔符 `'.'` 分隔。如果 `field` 是函数名,则需要将函数的参数类型用括号 `'()'` 修饰并附加在函数名后面。对于无参函数括号内的内容为空。18
19
例如,假设在包 `packA` 中有以下代码:20
21
<!-- compile -->22
23
24
如果要禁止控制流平坦化功能混淆 `funcA`,用户可以编写如下规则:25
26
27
用户也可以使用通配符编写更加灵活的规则,达到一条规则保留多个对象的目的。目前支持的通配符包含以下 3 类:28
29
混淆功能通配符:30
31
| 混淆功能通配符 | 说明 |32
| :-------------- | :----------------------- |33
| `?` | 匹配名称中的单个字符 |34
| `*` | 匹配名称中的任意数量字符 |35
36
字段名通配符:37
38
| 字段名通配符 | 说明 |39
| :------------ | :------------------------------------------------------------ |40
| `?` | 匹配字段名中单个非分隔符 `'.'` 的字符 |41
| `*` | 匹配字段名中的不包含分隔符 `'.'` 和参数的任意数量字符 |42
| `**` | 匹配字段名中的任意数量字符,包括字段之间的分隔符 `'.'` 和参数。`'**'` 只有在单独作为一个 `field` 时才生效,否则会被当作 `'*'` 处理 |43
44
函数的参数类型通配符:45
46
| 参数类型通配符 | 说明 |47
| :-------------- | :---------------------- |48
| `...` | 匹配任意数量的参数 |49
| `***` | 匹配一个任意类型的参数 |50
51
> **说明:**52
>53
> 参数类型也由字段名组成,因此也可以使用字段名通配符对单个参数类型进行匹配。54
55
以下是通配符使用示例:56
57
例子 1:58
59
60
该规则表示禁止 `obf-cf-flatten` 功能混淆函数 `pro?.myfunc()`,`pro?.myfunc()` 可以匹配 `pro0.myfunc()`,但不能匹配 `pro00.myfunc()`。61
62
例子 2:63
64
65
该规则表示禁止任何混淆功能混淆包 `pro0` 下的任何函数和变量。66
67
例子 3:68
69
70
该规则表示禁止任何混淆功能混淆函数 `pro*.myfunc(...)`,`pro*.myfunc(...)` 可以匹配以 `pro` 开头的任意单层包内的 `myfunc` 函数,且可以为任意参数。71
72
如果需要匹配多层包名,比如 `pro0.mypack.myfunc()`,请使用 `pro*.**.myfunc(...)`。请注意 `'**'` 只有单独作为字段名时才生效,因此 `pro**.myfunc(...)` 和 `pro*.myfunc(...)` 等价,无法匹配多层包名。如果要匹配以 `pro` 开头的所有包下的所有 `myfunc` 函数(包括类中名为 `myfunc` 的函数),请使用 `pro*.**.myfunc(...)`。73
74
例子 4:75
76
77
该规则表示禁止 `obf-cf-*` 功能混淆函数 `pro0.MyClassA.myfunc(**.MyClassB, ***, ...)`,其中 `obf-cf-*` 会匹配 `obf-cf-bogus` 和 `obf-cf-flatten` 两种混淆功能,`pro0.MyClassA.myfunc(**.MyClassB, ***, ...)` 会匹配函数 `pro0.MyClassA.myfunc`,且函数的第一个参数可以是任意包下的 `MyClassB` 类型,第二个参数可以是任意类型,后面可以接零至多个任意参数。Code 1 · text
1
obf_func1 name12
obf_func2 name23
...Code 2 · cangjie
1
package packA2
class MyClassA {3
func funcA(a: String, b: Int64): String {4
return a5
}6
}Code 3 · text
1
obf-cf-flatten packA.MyClassA.funcA(std.core.String, Int64)Code 4 · text
1
obf-cf-flatten pro?.myfunc()Code 5 · text
1
* pro0.**Code 6 · text
1
* pro*.myfunc(...)Code 7 · text
1
obf-cf-* pro0.MyClassA.myfunc(**.MyClassB, ***, ...)modified`--enable-eh` <sup>[frontend]</sup>textcode+3 lines, -1 line
v1.0.5
Section Text
1
启用该选项后,仓颉将支持效应处理器(Effect Handlers),Effect Handlers 是一种先进的控制流机制,用于实现模块化、可恢复的副作用处理。2
3
Effect Handler 允许程序员将副作用操作与其处理逻辑解耦,从而编写出更清晰、更具可组合性的代码。这种机制有助于提高抽象层次,尤其适用于处理日志记录、输入输出、状态变更等操作,从而避免主流程被副作用逻辑污染。4
5
效应的工作机制类似异常处理,但不使用 `throw` 和 `catch`,而是通过 `perform` 执行效应,使用 `handle` 进行捕获与处理。每个效应需通过继承 `stdx.effect.Command` 类来定义。6
7
与传统异常机制不同,Effect Handler 在处理效应后可以选择 恢复执行(`resume`),即向原始调用点注入一个值并继续运行。这种“恢复”能力使得程序员可以对控制流进行更精细的操作,特别适合用于构建模拟器、解释器或协作式多任务系统等需要高度控制的场景。8
9
示例:10
11
12
在上述示例中,定义了一个新的 `Command` 的子类 `GetNumber`。13
14
- 在 `main` 函数中,使用 `try-handle` 结构来处理该效应。15
- 在 `try` 块中,首先打印一行提示信息(`"About to perform"`),然后使用 `perform GetNumber()` 执行效应,`perform` 表达式的返回值会被赋给变量 `a`,执行一个效应会将执行流跳转到捕获这个效应的 `handle` 块。16
- 在 `handle` 块中,捕获并处理 `GetNumber` 效应,先打印一条信息(`"It is performed"`),然后使用 `resume with 9` 将常数 `9` 注入回原始调用点,然后恢复 `perform` 之后的执行流,打印(`"It is resumed, a = 9"`)。17
18
输出结果如下:19
20
21
> **注意:**22
>23
> - Effect Handler 当前仍属于实验性特性,该选项可能在未来版本中发生变化,请谨慎使用。24
> - 使用 Effect Handler 需引入 `stdx.effect` 库。Code 1 · cangjie
1
import stdx.effect.Command2
3
// 定义一个名为 GetNumber 的 Command4
class GetNumber <: Command<Int64> {}5
6
main() {7
try {8
println("About to perform")9
10
// 执行 GetNumber 效应11
let a = perform GetNumber()12
13
// handler 恢复后将从此处继续执行14
println("It is resumed, a = ${a}")15
} handle(e: GetNumber) {16
// 处理 GetNumber 效应17
println("It is performed")18
19
// 恢复执行,并注入值 920
resume with 921
}22
023
}Code 2 · text
1
About to perform2
It is performed3
It is resumed, a = 9v1.1.0
Section Text
1
启用该选项后,仓颉将支持效应处理器(Effect Handlers),Effect Handlers 是一种先进的控制流机制,用于实现模块化、可恢复的副作用处理。2
3
Effect Handler 允许程序员将副作用操作与其处理逻辑解耦,从而编写出更清晰、更具可组合性的代码。这种机制有助于提高抽象层次,尤其适用于处理日志记录、输入输出、状态变更等操作,从而避免主流程被副作用逻辑污染。4
5
效应的工作机制类似异常处理,但不使用 `throw` 和 `catch`,而是通过 `perform` 执行效应,使用 `handle` 进行捕获与处理。每个效应需通过继承 `stdx.effect.Command` 类来定义。6
7
与传统异常机制不同,Effect Handler 在处理效应后可以选择 恢复执行(`resume`),即向原始调用点注入一个值并继续运行。这种“恢复”能力使得程序员可以对控制流进行更精细的操作,特别适合用于构建模拟器、解释器或协作式多任务系统等需要高度控制的场景。8
9
示例:10
11
<!-- verify -->12
<!-- cfg="--enable-eh --experimental" -->13
14
15
在上述示例中,定义了一个新的 `Command` 的子类 `GetNumber`。16
17
- 在 `main` 函数中,使用 `try-handle` 结构来处理该效应。18
- 在 `try` 块中,首先打印一行提示信息(`"About to perform"`),然后使用 `perform GetNumber()` 执行效应,`perform` 表达式的返回值会被赋给变量 `a`,执行一个效应会将执行流跳转到捕获这个效应的 `handle` 块。19
- 在 `handle` 块中,捕获并处理 `GetNumber` 效应,先打印一条信息(`"It is performed"`),然后使用 `resume with 9` 将常数 `9` 注入回原始调用点,然后恢复 `perform` 之后的执行流,打印(`"It is resumed, a = 9"`)。20
21
输出结果如下:22
23
24
> **注意:**25
>26
> - Effect Handler 当前仍属于实验性特性,该选项可能在未来版本中发生变化,请谨慎使用。27
> - 使用 Effect Handler 需引入 `stdx.effect` 库。<!--DelEnd-->Code 1 · cangjie
1
import stdx.effect.Command2
3
// 定义一个名为 GetNumber 的 Command4
class GetNumber <: Command<Int64> {}5
6
main() {7
try {8
println("About to perform")9
10
// 执行 GetNumber 效应11
let a = perform GetNumber()12
13
// handler 恢复后将从此处继续执行14
println("It is resumed, a = ${a}")15
} handle(e: GetNumber) {16
// 处理 GetNumber 效应17
println("It is performed")18
19
// 恢复执行,并注入值 920
resume with 921
}22
023
}Code 2 · text
1
About to perform2
It is performed3
It is resumed, a = 9modified增量编译text+1 line, -1 line
v1.0.5
Section Text
1
通过 `--incremental-compile`<sup>[frontend]</sup>开启增量编译。开启后,`cjc`会在编译时根据前次编译的缓存文件加快此次编译的速度。2
3
> **注意:**4
>5
> 该选项为实验性功能,使用该功能生成的二进制有可能会存在潜在的运行时问题,请注意使用该选项的风险。此选项必须配合 `--experimental` 选项一同使用。6
> 指定此选项时会保存增量编译缓存及日志到输出文件路径下的 `.cached`目录。v1.1.0
Section Text
1
通过 `--incremental-compile` <sup>[frontend]</sup> 开启增量编译。开启后,`cjc`会在编译时根据前次编译的缓存文件加快此次编译的速度。2
3
> **注意:**4
>5
> 该选项为实验性功能,使用该功能生成的二进制有可能会存在潜在的运行时问题,请注意使用该选项的风险。此选项必须配合 `--experimental` 选项一同使用。6
> 指定此选项时会保存增量编译缓存及日志到输出文件路径下的 `.cached`目录。modified输出 CHIRtext+1 line, -1 line
v1.0.5
Section Text
1
通过 `--emit-chir=[raw|opt]`<sup>[frontend]</sup>指定输出 CHIR 编译阶段的序列化产物,`raw` 输出编译器优化前的 CHIR,`opt` 输出编译器优化后的 CHIR。使用 `--emit-chir` 则默认输出编译器优化后的 CHIR。v1.1.0
Section Text
1
通过 `--emit-chir=[raw|opt]` <sup>[frontend]</sup> 指定输出 CHIR 编译阶段的序列化产物,`raw` 输出编译器优化前的 CHIR,`opt` 输出编译器优化后的 CHIR。使用 `--emit-chir` 则默认输出编译器优化后的 CHIR。modified`--output-type=[exe|staticlib|dylib|obj]` <sup>[frontend]</sup>text+9 lines, -2 lines
modified`--dy-std`text+10 lines, -3 lines
modified`--pgo-instr-gen`, `--pgo-instr-gen=<.profraw>`textcode+2 lines
removed实验性功能选项
v1.0.5
1
实验性功能选项added`--compile-target==[exe|staticlib|dylib]` <sup>[frontend]</sup>
v1.1.0
1
`--compile-target==[exe|staticlib|dylib]` <sup>[frontend]</sup>2
3
该选项专用于 `--output-type=obj` 模式,默认为 `exe`。因生成的 `.obj/.o` 文件属于编译中间产物,通过指定 `--compile-target` 可明确编译器采用对应编译策略,生成适配不同最终产物类型的中间文件;后续编译器可直接将该 `.obj/.o` 文件作为输入进行链接。4
5
> **注意:**6
>7
> 该选项为实验性功能,使用存在潜在风险,且必须配合 `--experimental` 选项使用。8
9
例如以下命令分步编译链接生成可执行文件:10
11
<!--compile-->12
13
14
在步骤 2 中,`-lcangjie-std-core` 用于指定编译过程中的标准库依赖。手动链接场景下,依赖项名称必须严格遵循上述格式(例如 `-lcangjie-std-math`, `-lcangjie-std-collection.concurrent` 等形式),否则会导致符号未定义错误。15
16
**规格说明:**17
18
1. 不支持以 `.o` 文件作为输入并再次指定 `--output-type=obj` 的场景。 无效用法示例:`cjc main.o --output-type=obj --compile-target=exe` 。19
2. 当 `--output-type` 指定为其他非 `obj` 类型时,`--compile-target` 选项不会生效(将被忽略)。无效用法示例:`cjc main.cj --output-type=exe --compile-target=dylib` 。20
3. 若输入文件仅为 `.o` 中间目标文件,当前步骤配置的 `--output-type`(默认值为 `exe`)必须与生成该 `.o` 文件时指定的 `--compile-target` 保持逻辑一致。例如,若 `.o` 文件是通过 `--compile-target=dylib` 编译生成(用于构建动态库),则链接该文件时也需将 `--output-type` 指定为 `dylib` ,否则可能导致链接失败或产物类型不匹配。21
22
// main.cj23
main(){24
println("hello cangjie")25
}26
27
# 1.指定 --output-type 为 obj,并明确 --compile-target 为 exe28
cjc main.cj --output-type=obj --experimental -o main.o --compile-target=exe 29
30
# 2.将中间产物链接为可执行文件31
cjc main.o -lcangjie-std-core -o main --experimentaladded`--compile-as-exe`
v1.1.0
1
`--compile-as-exe`2
3
该选项启用后,将屏蔽 LTO 模式下加载 BC 文件的符号可见性,仅保留 package init 符号的可见性。基于此,LLVM 原生优化会执行激进的无效符号剔除操作。该选项仅在开启 `--lto` 编译参数时生效。4
5
# 编译通过6
$ cjc test.cj --lto=[full|thin] --compile-as-exe7
# 编译报错8
$ cjc test.cj --compile-as-exeadded`--link-option <value>`<sup>1</sup>
v1.1.0
1
`--link-option <value>`<sup>1</sup>2
3
指定链接器选项。4
5
`cjc` 会将该选项的值作为一个参数透传给链接器。可用的参数会因(系统或指定的)链接器的不同而不同。可以多次使用 `--link-option` 指定多个链接器选项。added`--sanitize==[address|thread|hwsaddress]` <sup>[frontend]</sup>
v1.1.0
1
`--sanitize==[address|thread|hwsaddress]` <sup>[frontend]</sup>2
3
用于开启 sanitizer 编译期间插桩功能,运行时检测程序中的各种错误。并链接对应 sanitizer 的库。使用该选项前需下载支持 sanitizer 功能的专属 SDK 包(如 cangjie-sdk-linux-aarch64-sanitizer.tar.gz),并确保 SDK 已正确部署。4
5
- `--sanitize=address` 用于检测内存错误,对应 sdk 中的 cangjie/runtime/lib/linux_aarch64_cjnative/asan 目录。6
- `--sanitize=thread` 用于检测数据竞争,对应 sdk 中的 cangjie/runtime/lib/linux_aarch64_cjnative/tsan 目录。7
- `--sanitize=hwaddress` 用于检测内存访问错误行为,对应 sdk 中的 cangjie/runtime/lib/linux_aarch64_cjnative/hwasan 目录。8
9
使用示例:10
11
12
> **注意:**13
>14
> `--sanitize` 选项不能与 `--compile-macro` 同时使用,否则会报错。15
16
# 编译 cangjie 文件 17
cjc --sanitize=address main.cj -o main18
19
# 手动指定 sanitizer 运行时库路径(必填,否则运行时找不到库)20
export LD_LIBRARY_PATH=${CANGJIE_HOME}/runtime/lib/arch/Asan:$LD_LIBRARY_PATH 21
22
# 运行23
./mainadded--sanitize-set-rpath <sup>[frontend]</sup>
v1.1.0
1
--sanitize-set-rpath <sup>[frontend]</sup>2
3
该选项用于自动将对应 sanitizer 运行时库的路径写入程序的 RPATH 字段,无需用户额外执行 `export LD_LIBRARY_PATH=${CANGJIE_HOME}/runtime/lib/arch/[asan|tsan|hwasan]:$LD_LIBRARY_PATH` 指定库路径,可简化命令。4
5
使用示例:6
7
cjc --sanitize=address main.cj -o main --sanitize-set-rpath8
9
./mainadded实验性功能选项<!--Del-->
v1.1.0
1
实验性功能选项<!--Del-->added打印 AST
v1.1.0
1
打印 AST2
3
可通过 `--dump-ast` <sup>[frontend]</sup> 打印 AST。默认输出到文件,产物目录会创建以包名(或使用 `-o` 指定的产物名)命名的 *_AST 目录,文件命名为 `编号_阶段名称_ast.txt`。加上 `--dump-to-screen` <sup>[frontend]</sup> 可输出到屏幕。added打印 CHIR
v1.1.0
1
打印 CHIR2
3
可通过 `--dump-chir` <sup>[frontend]</sup> 打印 CHIR。默认输出到文件,产物目录会创建以包名(或使用 `-o` 指定的产物名)命名的 *_CHIR 目录,文件命名为 `编号_阶段名称.chirtxt`。加上 `--dump-to-screen` <sup>[frontend]</sup> 可输出到屏幕。added打印 LLVM IR
v1.1.0
1
打印 LLVM IR2
3
可通过 `--dump-ir` <sup>[frontend]</sup> 打印 LLVM IR。默认输出到文件,产物目录会创建以包名(或使用 `-o` 指定的产物名)命名的 *_IR 目录并在 *_IR 目录下创建 `编号_阶段名称` 子文件夹,文件名为 `子模块编号-包名.ll`,子模块的编号和数量与编译并发度相关。加上 `--dump-to-screen` <sup>[frontend]</sup> 可输出到屏幕。added打印 AST, CHIR, LLVM IR
v1.1.0
1
打印 AST, CHIR, LLVM IR2
3
可通过 `--dump-all` <sup>[frontend]</sup> 打印 AST, CHIR, LLVM IR。默认输出到文件,产物目录会创建以包名(或使用 `-o` 指定的产物名)命名的 *_AST, *_CHIR, *_IR 目录。加上 `--dump-to-screen` <sup>[frontend]</sup> 可输出到屏幕。added将 dump 内容打印到屏幕上
v1.1.0
1
将 dump 内容打印到屏幕上2
3
可通过 `--dump-to-screen` <sup>[frontend]</sup> 配合前端相关的转储选项(如 `--dump-ast` <sup>[frontend]</sup>, `--dump-chir` <sup>[frontend]</sup>, `--dump-ir` <sup>[frontend]</sup> 和 `--dump-all` <sup>[frontend]</sup>)将相应的中间表示文本内容打印到屏幕上。4
5
> **注意:**6
>7
> 输出到屏幕时仅显示最终结果;输出到文件时会在产物目录下创建以 `_AST`, `_CHIR`, `_IR` 为后缀的目录,用以保存中间过程的详细信息。