Project Management Tool
Overview
cjpm (Cangjie Project Manager) is the official project management tool for the Cangjie language, designed to manage and maintain the module system of Cangjie projects. It covers operations such as module initialization, dependency checking, and updates. It provides a unified compilation entry point, supporting incremental compilation, parallel compilation, and custom compilation commands.
Usage Instructions
Execute the cjpm -h command to view the usage instructions for the project management tool, as shown below.
Cangjie Project Manager
Usage:
cjpm [subcommand] [option]
Available subcommands:
init Init a new cangjie module
check Check the dependencies
update Update cjpm.lock
tree Display the package dependencies in the source code
build Compile the current module
run Compile and run an executable product
test Unittest a local package or module
bench Run benchmarks in a local package or module
clean Clean up the target directory
install Install a cangjie binary
uninstall Uninstall a cangjie binary
Available options:
-h, --help help for cjpm
-v, --version version for cjpm
Use "cjpm [subcommand] --help" for more information about a command.
Basic usage commands are as follows:
cjpm build --help
cjpm is the name of the main program, build is the currently executed available command, and --help is the available configuration option for the current command (configuration options typically have both long and short forms with the same effect).
Upon successful execution, the following result will be displayed:
Compile a local module and all of its dependencies.
Usage:
cjpm build [option]
Available options:
-h, --help help for build
-i, --incremental enable incremental compilation
-j, --jobs <N> the number of jobs to spawn in parallel during the build process
-V, --verbose enable verbose
-g enable compile debug version target
--coverage enable coverage
--cfg enable the customized option 'cfg'
--enable-features <value> explicitly specify comma-separated list of features to be enabled
--no-feature-deduce disables auto-enabling of features, deduced from other options, machine properties, etc.
-m, --member <value> specify a member module of the workspace
--target <value> generate code for the given target platform
--target-dir <value> specify target directory
-o, --output <value> specify product name when compiling an executable file
-l, --lint enable cjlint code check
--mock enable support of mocking classes in tests
--skip-script disable script 'build.cj'.
Note:
When using
cjpmcentral repository features, external dependencies are required. Please refer to thestdx.net.tlslibrary documentation and follow the instructions to install the necessary external dependencies.
Command Descriptions
init
init is used to initialize a new Cangjie module or workspace. When initializing a module, it creates a configuration file cjpm.toml in the current folder by default and creates a src source code folder. If the module’s output is of the executable type, it generates a default main.cj file under src, which prints hello world after compilation. When initializing a workspace, only the cjpm.toml file is created, and it scans existing Cangjie modules under the path and adds them to the members field by default. If cjpm.toml already exists or main.cj is already present in the source folder, the corresponding file creation steps will be skipped.
init has several configurable options:
--workspacecreates a new workspace configuration file. When this option is specified, other options are automatically ignored.--name <value>specifies therootpackage name of the new module. If not specified, it defaults to the name of the parent folder.--path <value>specifies the path for the new module. If not specified, it defaults to the current folder.--type=<executable|static|dynamic>specifies the output type of the new module. If omitted, it defaults toexecutable.--experimentalinitializes the Cangjie multi-platform project.
Examples:
Input: cjpm init
Output: cjpm init success
Input: cjpm init --name demo --path project
Output: cjpm init success
Input: cjpm init --type=static
Output: cjpm init success
check
The check command is used to check the dependencies required by the project. Upon successful execution, it prints the valid package compilation order.
check has several configurable options:
-m, --member <value>can only be used in a workspace to specify a single module as the check entry point.--no-testswhen configured, test-related dependencies will not be printed.--skip-scriptwhen configured, the build script compilation and execution will be skipped.
Examples:
Input: cjpm check
Output:
The valid serial compilation order is:
b.pkgA -> b
cjpm check success
Input: cjpm check
Output:
Error: cyclic dependency
b.B -> c.C
c.C -> d.D
d.D -> b.B
Note: In the above output, b.B represents a subpackage named b.B in the module with b as the root package.
Input: cjpm check
Output:
Error: can not find the following dependencies
pro1.xoo
pro1.yoo
pro2.zoo
update
update is used to update the contents of cjpm.toml to cjpm.lock. When cjpm.lock does not exist, it will be generated. The cjpm.lock file records metadata such as version numbers of git dependencies for use in the next build.
update has the following configurable option:
--skip-scriptwhen configured, the build script compilation and execution will be skipped.
Input: cjpm update
Output: cjpm update success
tree
The tree command is used to visually display the package dependency relationships in Cangjie source code.
tree has several configurable options:
-V, --verboseadds detailed information to package nodes, including package name, version number, and package path.--depth <N>specifies the maximum depth of the dependency tree. The value must be a non-negative integer. When this option is specified, all packages are treated as root nodes by default. The value of N represents the maximum depth of child nodes for each dependency tree.-p, --package <value>specifies a package as the root node to display its sub-dependencies. The value required is the package name.--invert <value>specifies a package as the root node and inverts the dependency tree to show which packages depend on it. The value required is the package name.--target <value>includes dependencies for the specified target platform in the analysis and displays the dependency relationships.--no-testsexcludes dependencies listed in thetest-dependenciesfield.--skip-scriptwhen configured, the build script compilation and execution will be skipped.
For example, the source code directory structure of module a is as follows:
src
├── main.cj
├── aoo
│ └── a.cj
├── boo
│ └── b.cj
├── coo
│ └── c.cj
├── doo
│ └── d.cj
└── eoo
└── e.cj
The dependency relationships are: package a imports packages a.aoo and a.boo; subpackage aoo imports package a.coo; subpackage boo imports package coo; subpackage doo imports package coo.
Input: cjpm tree
Output:
|-- a
└── a.aoo
└── a.coo
└── a.boo
└── a.coo
|-- a.doo
└── a.coo
|-- a.eoo
cjpm tree success
Input: cjpm tree --depth 2 -p a
Output:
|-- a
└── a.aoo
└── a.coo
└── a.boo
└── a.coo
cjpm tree success
Input: cjpm tree --depth 0
Output:
|-- a
|-- a.eoo
|-- a.aoo
|-- a.boo
|-- a.doo
|-- a.coo
cjpm tree success
Input: cjpm tree --invert a.coo --verbose
Output:
|-- a.coo 1.2.0 (.../src/coo)
└── a.aoo 1.1.0 (.../src/aoo)
└── a 1.0.0 (.../src)
└── a.boo 1.1.0 (.../src/boo)
└── a 1.0.0 (.../src)
└── a.doo 1.3.0 (.../src/doo)
cjpm tree success
build
build is used to build the current Cangjie project. Before executing this command, it checks dependencies. If the check passes, it calls cjc to perform the build.
build has several configurable options:
-i, --incrementalspecifies incremental compilation. By default, full compilation is performed.-j, --jobs <N>specifies the maximum number of parallel compilation jobs. The final maximum concurrency is the minimum ofNand2 times the number of CPU cores.-V, --verbosedisplays compilation logs.-ggeneratesdebugversion output.--coveragegenerates coverage information. By default, coverage is disabled.--cfgwhen specified, customcfgoptions incjpm.tomlcan be passed through. Forcjpm.tomlconfiguration, refer to the profile.customized-option section.--enable-features <value>Explicitly specifies the features to enable, which can be comma-separated.--no-feature-deduceDisables automatic feature enabling, including inference from other options or machine-related properties.-m, --member <value>can only be used in a workspace to specify a single module as the compilation entry point.--target <value>when specified, enables cross-compilation to the target platform. Forcjpm.tomlconfiguration, refer to the target section.--target-dir <value>specifies the output directory for the build artifacts.-o, --output <value>specifies the name of the output executable file. The default name ismain(main.exeon Windows). Note that compiling an executable namedcjcis currently not supported.-l, --lintUsed to invoke the Cangjie language static analysis tool for code inspection during compilation.--mockenablesmocktesting for classes in the build version with this option.--skip-scriptwhen configured, the build script compilation and execution will be skipped.
Note:
- The
-i, --incrementaloption only enables package-level incremental compilation incjpm. Developers can manually pass--incremental-compileand--experimentalcompilation options in the configuration file’scompile-optionfield to enable function-level incremental compilation provided by thecjccompiler.- The
-i, --incrementaloption currently only supports incremental analysis based on source code. If imported library content changes, developers need to rebuild using full compilation.- Source files ending with
_test.cjand test cases within ordinary source files are ignored during thebuildphase. Other compilation-related commands(run/install/bundle)also ignore the aforementioned files during the compilation phase.
Intermediate files generated during compilation are stored in the target folder by default, while executable files are stored in target/release/bin or target/debug/bin folders based on the compilation mode. To run the executable, refer to the run command.
To ensure reproducible builds, this command creates a cjpm.lock file containing the exact versions of all transitive dependencies, which will be used for subsequent builds. To update this file, use the update command. If reproducible builds are required for all project participants, this file should be committed to the version control system.
Examples:
Input: cjpm build -V
Output:
compile package module1.package1: cjc --import-path "target/release" --output-dir "target/release/module1" -p "src/package1" --output-type=staticlib -o libmodule1.package1.a
compile package module1: cjc --import-path "target/release" --output-dir "target/release/bin" -p "src" --output-type=exe -o main
cjpm build success
Input: cjpm build
Output: cjpm build success
Note:
According to the Cangjie package management specifications, only valid source code packages that meet the requirements can be correctly included in the compilation scope. If warnings like
no '.cj' fileappear during compilation, it is likely because the corresponding package does not meet the specifications, causing the source files to be excluded. In such cases, refer to the Cangjie Package Management Specifications to modify the code directory structure.
Before executing cjpm build, cjpm checks the package dependency relationships of the current module or workspace. If mutual imports between packages form a dependency cycle, the build will be aborted, and an error message will be returned, indicating the cyclic dependency path.
For example, the source code directory structure of module demo is as follows:
src
├── main.cj
├── aoo
│ └── a.cj
├── boo
│ └── b.cj
└── coo
└── c.cj
The dependency relationships are: package demo.aoo imports package demo.boo, package demo.boo imports package demo.coo, and package demo.coo imports package demo.aoo. The mutual imports between these three packages form a cycle, resulting in a cyclic dependency:
Input: cjpm build
Output:
cyclic dependency:
demo.boo -> demo.coo
demo.coo -> demo.aoo
demo.aoo -> demo.boo
Error: cjpm build failed
When a cyclic dependency occurs, developers can troubleshoot based on the error message. In the example above, the import chain is demo.aoo -> demo.boo -> demo.coo -> demo.aoo. Developers can start analyzing from each package’s directory to identify and remove unnecessary imports to resolve the cyclic dependency. For example, start with demo.aoo and check which source files import demo.boo. If these files do not functionally depend on demo.boo, the corresponding imports can be removed. Repeat this process for demo.boo and demo.coo to eliminate redundant imports and resolve the cyclic dependency.
If functional cyclic dependencies are confirmed, consider the following solutions:
- Refactor import order: Ensure dependencies are unidirectional to avoid cycles. For example, the part of
demo.coothat depends ondemo.aoocan be independently implemented to break the cycle. - Split modules: Move interdependent code into a separate package. For example, merge the three subpackages into one.
In other commands involving package dependency resolution (e.g., tree), similar errors will appear for cyclic dependencies, and the same solutions can be applied.
run
run is used to execute the binary output of the current project. The run command implicitly executes the build command to generate the final binary for execution.
run has several configurable options:
--name <value>specifies the name of the binary to run. If not specified, it defaults tomain. In a workspace, binaries are stored intarget/release/binby default.--build-args <value>controls the parameters for thebuildcompilation process.--skip-buildskips the compilation process and directly runs the binary.--run-args <value>passes arguments to the binary being executed.--target-dir <value>specifies the output directory for the executable.-gruns thedebugversion of the binary.-V, --verbosedisplays execution logs.--skip-scriptwhen configured, the build script compilation and execution will be skipped.
Examples:
Input: cjpm run
Output: cjpm run success
Input: cjpm run -g // This implicitly executes cjpm build -i -g
Output: cjpm run success
Input: cjpm run --build-args="-s -j16" --run-args="a b c"
Output: cjpm run success
test
test is used to compile and run unit test cases for Cangjie files, printing the test results upon completion. The compiled output is stored in target/release/unittest_bin by default. For writing unit test cases, refer to the std.unittest library documentation in the Cangjie Programming Language Standard Library API.
This command can specify the path of a single package to test (multiple packages can be specified, e.g., cjpm test path1 path2). If no path is specified, module-level unit tests are executed by default. During module-level unit testing, only the current module’s tests are executed; tests in directly or indirectly dependent modules are not run. The test command requires the current project to compile successfully with build.
The unit test code structure for a module is as follows, where xxx.cj contains the package’s source code and xxx_test.cj contains the unit test code:
└── src
│ ├── koo
│ │ ├── koo.cj
│ │ └── koo_test.cj
│ ├── zoo
│ │ ├── zoo.cj
│ │ └── zoo_test.cj
│ ├── main.cj
│ └── main_test.cj
└── cjpm.toml
-
Single-module testing scenario:
Input: cjpm test Progress report: group test.koo 100% [||||||||||||||||||||||||||||] ✓ (00:00:01) group test.zoo 0% [----------------------------] (00:00:00) test TestZ.sayhi (00:00:00) passed: 1, failed: 0 33% [|||||||||-------------------] 1/3 (00:00:01) Output: -------------------------------------------------------------------------------------------------- TP: test, time elapsed: 177921 ns, RESULT: TCS: TestM, time elapsed: 177921 ns, RESULT: [ PASSED ] CASE: sayhi (177921 ns) Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- TP: test.koo, time elapsed: 134904 ns, RESULT: TCS: TestK, time elapsed: 134904 ns, RESULT: [ PASSED ] CASE: sayhi (134904 ns) Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- TP: test.zoo, time elapsed: 132013 ns, RESULT: TCS: TestZ, time elapsed: 132013 ns, RESULT: [ PASSED ] CASE: sayhi (132013 ns) Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- Project tests finished, time elapsed: 444838 ns, RESULT: TP: test.*, time elapsed: 312825 ns, RESULT: PASSED: TP: test.zoo, time elapsed: 132013 ns TP: test.koo, time elapsed: 312825 ns TP: test, time elapsed: 312825 ns Summary: TOTAL: 3 PASSED: 3, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- cjpm test success -
Single Package Test Scenario
Input: cjpm test src/koo Output: -------------------------------------------------------------------------------------------------- TP: test.koo, time elapsed: 160133 ns, RESULT: TCS: TestK, time elapsed: 160133 ns, RESULT: [ PASSED ] CASE: sayhi (160133 ns) Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- Project tests finished, time elapsed: 160133 ns, RESULT: TP: test.*, time elapsed: 160133 ns, RESULT: PASSED: TP: test.koo, time elapsed: 160133 ns Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- cjpm test success -
Multi-Package Test Scenario
Input: cjpm test src/koo src Output: -------------------------------------------------------------------------------------------------- TP: test.koo, time elapsed: 168204 ns, RESULT: TCS: TestK, time elapsed: 168204 ns, RESULT: [ PASSED ] CASE: sayhi (168204 ns) Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- TP: test, time elapsed: 171541 ns, RESULT: TCS: TestM, time elapsed: 171541 ns, RESULT: [ PASSED ] CASE: sayhi (171541 ns) Summary: TOTAL: 1 PASSED: 1, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- Project tests finished, time elapsed: 339745 ns, RESULT: TP: test.*, time elapsed: 339745 ns, RESULT: PASSED: TP: test.koo, time elapsed: 339745 ns TP: test, time elapsed: 339745 ns Summary: TOTAL: 2 PASSED: 2, SKIPPED: 0, ERROR: 0 FAILED: 0 -------------------------------------------------------------------------------------------------- cjpm test success
test has multiple configurable options:
-j, --jobs <N>Specifies the maximum number of parallel compilations. The final maximum concurrency is the minimum ofNand2 times the number of CPU cores.-V, --verboseWhen enabled, outputs unit test logs.-gGenerates adebugversion of the unit test artifacts, which are stored in thetarget/debug/unittest_bindirectory.-i, --incrementalSpecifies incremental compilation of test code. Full compilation is performed by default.--no-runCompiles only the unit test artifacts without execution.--skip-buildExecutes only the pre-built unit test artifacts.--coverageGenerates raw coverage data. When usingcjpm test --coverage, themainfunction in the source code will not be executed as the program entry point and will appear as uncovered. It is recommended to avoid writing redundantmainfunctions after usingcjpm test.--cfgWhen specified, customcfgoptions fromcjpm.tomlare passed through.--module <value>Specifies the target test module, which must be directly or indirectly dependent on the current module (or be the module itself). Multiple modules can be specified using--module "module1 module2". If not specified, only the current module is tested by default.-m, --member <value>Can only be used in a workspace to specify testing a single module.--target <value>When specified, cross-compiles unit test artifacts for the target platform. Refer to the target section forcjpm.tomlconfigurations.--target-dir <value>Specifies the output directory for unit test artifacts.--dry-runWhen enabled, only prints the test cases without execution.--filter <value>Filters a subset of tests. Thevalueformat is as follows:--filter=*Matches all test classes.--filter=*.*Matches all test cases of all test classes (same result as*).--filter=*.*Test,*.*case*Matches all test cases ending withTestin any test class, or all test cases containingcasein their names.--filter=MyTest*.*Test,*.*case*,-*.*myTestMatches all test cases in classes starting withMyTestand ending withTest, or containingcasein their names, or excluding those containingmyTest.
--include-tags <value>Runs a subset of tests marked with the@Tagmacro. Thevalueformat is as follows:--include-tags=UnittestRuns all tests marked with@Tag[Unittest].--include-tags=Unittest,SmokeRuns all tests marked with either@Tag[Unittest]or@Tag[Smoke](or both).--include-tags=Unittest+SmokeRuns all tests marked with both@Tag[Unittest]and@Tag[Smoke].--include-tags=Unittest+Smoke+JiraTask3271,BackendRuns all tests marked with@Tag[Backend]or any of@Tag[Unittest, Smoke, JiraTask3271].
--exclude-tags <value>Excludes a subset of tests marked with the@Tagmacro. Thevalueformat is as follows:--exclude-tags=UnittestRuns all tests not marked with@Tag[Unittest].--exclude-tags=Unittest,SmokeRuns all tests not marked with either@Tag[Unittest]or@Tag[Smoke](or both).--exclude-tags=Unittest+Smoke+JiraTask3271Runs all tests not marked with all of@Tag[Unittest, Smoke, JiraTask3271].--include-tags=Unittest --exclude-tags=SmokeRuns all tests marked with@Tag[Unittest]but not@Tag[Smoke].
--no-colorDisables colored console output.--random-seed <N>Specifies the value of the random seed.--timeout-each <value>The format is%d[millis|s|m|h], specifying the default timeout for each test case.--parallelSpecifies the parallel execution scheme for test cases. Thevalueformat is as follows:<BOOL>Can betrueorfalse. Whentrue, test classes can run in parallel, with the number of parallel processes controlled by the available CPU cores.nCoresSpecifies that the number of parallel test processes equals the available CPU cores.NUMBERSpecifies a positive integer for the number of parallel test processes.NUMBERnCoresSpecifies that the number of parallel test processes is a multiple of the available CPU cores. The value must be positive (supports floating-point or integer values).
--show-tagsDisplays@Taginformation in the test report. In--dry-runmode withxmlformat reports,Taginformation is always included.--show-all-outputEnables output printing for all test cases, including passed ones.--no-capture-outputDisables test output capture, printing output immediately during test execution.--report-path <value>Specifies the path for generating test execution reports.--report-format <value>Specifies the report output format. Currently, onlyxmlandxml-per-packageformat (case-insensitive) is supported for unit test reports. Other values will throw an exception.--skip-scriptSkips the compilation and execution of build scripts.--no-progressDisables progress reporting. Implicitly enabled if--dry-runis specified.--progress-briefDisplays a brief (single-line) progress report instead of a detailed one.--progress-entries-limit <value>Limits the number of entries displayed in the progress report. Default:0. Allowed values:0No limit on the number of entries.nWherenis a positive integer, specifying the maximum number of entries displayed simultaneously in the terminal.
Examples of cjpm test parameter usage:
Input:
cjpm test src --coverage
cjcov --root=./ --html-details -o html_output
Output: cjpm test success
Coverage generation: HTML files are generated in the html_output directory, with the main coverage report file named index.html.
Input: cjpm test --filter=*
Output: cjpm test success
Input: cjpm test src --report-path=reports --report-format=xml
Output: cjpm test success
Note:
cjpm testautomatically builds all packages withmocksupport, allowing developers to performmocktests on custom classes or classes from dependent modules. To enablemockfor classes from binary dependencies, build withmocksupport usingcjpm build --mock. Avoid writing redundantmainfunctions after usingcjpm test.
bench
The bench command is used to execute performance test cases in test files and directly print the test results. The compiled artifacts are stored by default in the target/release/unittest_bin directory. Performance test cases are annotated with the @Bench macro. For more details on how to write performance test code, refer to the description of the std.unittest library in the Cangjie Programming Language Standard Library API.
This command can specify the path of a single package to test (multiple packages can be specified, e.g., cjpm bench path1 path2). If no path is specified, module-level unit tests are executed by default. Similar to test, when executing module-level unit tests, only the current module’s unit tests are performed by default. The bench command requires that the current project can be successfully compiled with build.
Like the test subcommand, if you have an xxx.cj file, xxx_test.cj can also contain performance test cases.
Input: cjpm bench
Output:
TP: bench, time elapsed: 8107939844 ns, RESULT:
TCS: Test_UT, time elapsed: 8107939844 ns, RESULT:
| Case | Median | Err | Err% | Mean |
|:-----------|---------:|------------:|-------:|---------:|
| Benchmark1 | 5.438 ns | ±0.00439 ns | ±0.1% | 5.420 ns |
Summary: TOTAL: 1
PASSED: 1, SKIPPED: 0, ERROR: 0
FAILED: 0
--------------------------------------------------------------------------------------------------
Project tests finished, time elapsed: 8107939844 ns, RESULT:
TP: bench.*, time elapsed: 8107939844 ns, RESULT:
PASSED:
TP: bench, time elapsed: 8107939844 ns, RESULT:
Summary: TOTAL: 1
PASSED: 1, SKIPPED: 0, ERROR: 0
FAILED: 0
bench has several configurable options:
-j, --jobs <N>: Specifies the maximum number of parallel compilation jobs. The final maximum concurrency is the minimum ofNand2 × CPU cores.-V, --verbose: When enabled, outputs unit test logs.-g: Generates adebugversion of the unit test artifacts, which are stored in thetarget/debug/unittest_bindirectory.-i, --incremental: Specifies incremental compilation for test code. Full compilation is performed by default.--no-run: Only compiles unit test artifacts without executing them.--skip-build: Only executes unit test artifacts without compiling them.--cfg: Allows passing customcfgoptions fromcjpm.toml.--module <value>: Specifies the target test module. The specified module must be directly or indirectly dependent on the current module (or be the module itself). Multiple modules can be specified using--module "module1 module2". If not specified, only the current module is tested.-m, --member <value>: Only usable in a workspace, specifies a single module to test.--target <value>: Specifies cross-compilation for the target platform. Refer to thecross-compile-configurationsection incjpm.tomlfor configuration details.--target-dir <value>: Specifies the output directory for unit test artifacts.--dry-run: Prints the test cases without executing them.--filter <value>: Filters a subset of tests. Thevalueformat is as follows:--filter=*: Matches all test classes.--filter=*.*: Matches all test cases in all test classes (same result as*).--filter=*.*Test,*.*case*: Matches all test cases ending withTestor containingcasein their names.--filter=MyTest*.*Test,*.*case*,-*.*myTest: Matches all test cases in classes starting withMyTestand ending withTest, or containingcasein their names, but excludes those containingmyTest.
--include-tags <value>: Runs tests annotated with the specified@Tagmacro subsets. Thevalueformat is as follows:--include-tags=Unittest: Runs all tests marked with@Tag[Unittest].--include-tags=Unittest,Smoke: Runs all tests marked with either@Tag[Unittest]or@Tag[Smoke](or both).--include-tags=Unittest+Smoke: Runs all tests marked with both@Tag[Unittest, Smoke].--include-tags=Unittest+Smoke+JiraTask3271,Backend: Runs all tests marked with@Tag[Backend]or@Tag[Unittest, Smoke, JiraTask3271].
--exclude-tags <value>: Excludes tests annotated with the specified@Tagmacro subsets. Thevalueformat is as follows:--exclude-tags=Unittest: Runs all tests not marked with@Tag[Unittest].--exclude-tags=Unittest,Smoke: Runs all tests not marked with either@Tag[Unittest]or@Tag[Smoke](or both).--exclude-tags=Unittest+Smoke+JiraTask3271: Runs all tests not marked with@Tag[Unittest, Smoke, JiraTask3271].--include-tags=Unittest --exclude-tags=Smoke: Runs all tests marked with@Tag[Unittest]but not@Tag[Smoke].
--no-color: Disables colored console output.--show-tagsis used to display@Taginformation from test cases in the test report. In--dry-runmode with the test report inxmlformat,Taginformation will always be included.--random-seed <N>: Specifies the random seed value.--report-path <value>: Specifies the path for the generated report. Unlike thetestsubcommand, it defaults tobench_report.--report-format <value>: Performance test reports only supportcsvandcsv-rawformats.--baseline-path <value>: Path to an existing report for comparison with current performance results. By default, it uses the--report-pathvalue.--skip-script: Skips the compilation and execution of build scripts.
Example usage of cjpm bench options:
Input: cjpm bench
Output: cjpm bench success
Input: cjpm bench src
Output: cjpm bench success
Input: cjpm bench src --filter=*
Output: cjpm bench success
Input: cjpm bench src --report-format=csv
Output: cjpm bench success
Note:
cjpm benchdoes not fully supportmockto avoid any overhead frommockprocessing in the compiler during benchmarking. When usingcjpm benchoptions, the compiler will not report errors ifmockis used, allowing regular tests and benchmarks to be compiled together. However, avoid running benchmarks that usemock, as this will throw a runtime exception.
clean
The clean command removes temporary build artifacts (the target directory). It supports the short option -g to clean only debug artifacts and the long option --target-dir <value> to specify the directory to clean. Developers must ensure the safety of cleaning the specified directory. If cjpm build --coverage or cjpm test --coverage was used, it also removes the cov_output directory and *.gcno/*.gcda files in the current directory. The --skip-script option skips the compilation and execution of build scripts.
Examples:
Input: cjpm clean
Output: cjpm clean success
Input: cjpm clean --target-dir temp
Output: cjpm clean success
Note:
On Windows, cleaning executable files or parent directories immediately after subprocess execution may fail. If this occurs, retry the
cleancommand after a short delay.
install
The install command installs a Cangjie project. It first compiles the project and then installs the artifacts to the specified path, naming them after the project (with .exe suffix on Windows). The installed project must be of type executable.
install has several configurable options:
-V, --verbose: Shows installation logs.-m, --member <value>: Only usable in a workspace, specifies a single module to install.-g: Generates adebugversion of the installation artifacts.--path <value>: Specifies the local project path to install. Defaults to the current directory.--root <value>: Specifies the installation path for executables. Defaults to$HOME/.cjpmon Linux/macOS and%USERPROFILE%/.cjpmon Windows.--git <value>: Specifies the Git URL of the project to install.--branch <value>: Specifies the Git branch to install.--tag <value>: Specifies the Git tag to install.--commit <value>: Specifies the Git commit ID to install.-j, --jobs <N>: Specifies the maximum number of parallel compilation jobs. The final maximum concurrency is the minimum ofNand2 × CPU cores.--cfg: Allows passing customcfgoptions fromcjpm.toml.--target-dir <value>: Specifies the output directory for compilation artifacts.--name <value>: Specifies the name of the installed artifact.--skip-build: Skips the compilation phase and directly installs artifacts. Requires the project to be already compiled and only works for local installations.--list: Prints the list of installed artifacts.--skip-script: Skips the compilation and execution of build scripts for the module to install.
Notes on install:
- Two installation methods: local project (via
--path) and Git project (via--git). Only one can be configured; otherwise,installwill error. If neither is specified, the current directory’s local project is installed by default. - Incremental compilation is enabled by default.
- Git-related options (
--branch,--tag,--commit) are ignored unless--gitis specified. Priority:--commit>--branch>--tag. - Existing executables with the same name will be replaced.
- Executables are installed to
root/bin, whererootis the specified or default installation path. - Dynamic library dependencies are installed to
root/libs, organized by program name. Add the corresponding directory toLD_LIBRARY_PATH(Linux),PATH(Windows), orDYLD_LIBRARY_PATH(macOS) for usage. - The default installation path is added to
PATHduringenvsetup. - Git project installation removes the compilation artifacts directory afterward.
- If the project has only one executable artifact,
--namerenames it during installation. For multiple artifacts,--nameinstalls only the specified artifact. --listprints installed artifacts, ignoring all options except--root. With--root, it prints artifacts in the specified path; otherwise, it uses the default path.
Examples:
cjpm install --path path/to/project # Installs from a local path
cjpm install --git url # Installs from a Git URL
uninstall
The uninstall command removes a Cangjie project, deleting its executables and dependency files.
uninstall requires the name parameter to specify the artifact to uninstall. Multiple names can be specified for sequential removal. The --root <value> option specifies the installation path to uninstall (defaults to $HOME/.cjpm on Linux/macOS and %USERPROFILE%/.cjpm on Windows). Artifacts in root/bin and dependencies in root/libs are removed.
Note:
cjpmdoes not support Chinese paths onWindows. If issues arise, modify the directory name;cjpmdoes not support paths containing\onLinux/macOS. If issues arise, modify the directory name.
Module Configuration File Description
The cjpm.toml file configures basic information, dependencies, compilation options, etc. cjpm primarily uses this file for parsing and execution. Module names can be renamed in cjpm.toml, but package names cannot.
Example configuration:
[package] # Single-module configuration (cannot coexist with workspace)
cjc-version = "1.0.0" # Minimum required `cjc` version (required)
name = "demo" # Module name and root package name (required)
description = "nothing here" # Description (optional)
version = "1.0.0" # Module version (required)
compile-option = "" # Additional compilation options (optional)
override-compile-option = "" # Additional global compilation options (optional)
link-option = "" # Linker passthrough options (optional)
output-type = "executable" # Output type (required)
src-dir = "" # Source directory (optional)
target-dir = "" # Output directory (optional)
package-configuration = {} # Per-package configuration (optional)
[workspace] # Workspace configuration (cannot coexist with package)
members = [] # Workspace member modules (required)
build-members = [] # Modules to build (subset of members, optional)
test-members = [] # Modules to test (subset of build-members, optional)
compile-option = "" # Workspace-wide compilation options (optional)
override-compile-option = "" # Workspace-wide global compilation options (optional)
link-option = "" # Workspace-wide linker options (optional)
target-dir = "" # Output directory (optional)
[dependencies] # Source dependencies (optional)
coo = { git = "xxx", branch = "dev" } # Git dependency
doo = { path = "./pro1" } # Local source dependency
[test-dependencies] # Test-phase dependencies (same format as dependencies, optional)
[script-dependencies] # Build script dependencies (same format as dependencies, optional)
[replace] # Dependency replacement (same format as dependencies, optional)
[ffi.c] # C library dependencies (optional)
clib1.path = "xxx"
[profile] # Command profile configuration (optional)
build = {} # Build command options
test = {} # Test command options
bench = {} # Bench command options
customized-option = {} # Custom passthrough options
[target.x86_64-unknown-linux-gnu] # Platform-specific configuration (optional)
compile-option = "value1" # Compilation options for specific targets
override-compile-option = "value2" # Global compilation options for specific targets
link-option = "value3" # Linker options for specific targets
[target.x86_64-w64-mingw32.dependencies] # Platform-specific source dependencies (optional)
[target.x86_64-w64-mingw32.test-dependencies] # Platform-specific test dependencies (optional)
[target.x86_64-unknown-linux-gnu.bin-dependencies] # Binary library dependencies for specific targets (optional)
path-option = ["./test/pro0", "./test/pro1"] # Directory-based binary dependencies
[target.x86_64-unknown-linux-gnu.bin-dependencies.package-option] # File-based binary dependencies
"pro0.xoo" = "./test/pro0/pro0.xoo.cjo"
"pro0.yoo" = "./test/pro0/pro0.yoo.cjo"
"pro1.zoo" = "./test/pro1/pro1.zoo.cjo"
For detailed information about CJO files, see CJO Artifacts.
Unused fields default to empty (for paths, the default is the directory containing the configuration file).
“cjc-version”
Minimum required Cangjie compiler version. Must be compatible with the current environment. A valid version number consists of three natural numbers separated by ., with no leading zeros. Examples:
1.0.0: Valid.1.00.0: Invalid (leading zero in00).1.2e.0: Invalid (2eis not a natural number).
“name”
Current Cangjie module name, also the root package name. Must be a valid identifier (letters, numbers, underscores, starting with a letter). Examples: cjDemo, cj_demo_1.
Note:
Unicode characters are not supported. Module names must be ASCII-only identifiers.
“description”
Module description (free-form text).
“version”
Module version number, managed by the module owner. Format is the same as cjc-version.
“compile-option”
Additional compilation options passed to cjc. In multi-module builds, each module’s compile-option applies to all its packages.
Example:
compile-option = "-O1 -V"
Commands are inserted into the compilation command during build. Multiple commands can be separated by spaces. Refer to the Cangjie Programming Language Development Guide for available options.
“override-compile-option”
Additional global compilation options passed to cjc. In multi-module builds, the entry module’s override-compile-option applies to all dependent modules’ packages.
Example:
override-compile-option = "-O1 -V"
Commands are appended after compile-option and take higher precedence. Refer to the Cangjie Programming Language Development Guide for available options.
Note:
override-compile-optionaffects dependent modules. Ensure no conflicts with theircompile-option.- In workspaces, only the
workspace’soverride-compile-optionapplies to all modules.
“link-option”
Linker passthrough options, e.g., for secure compilation:
link-option = "-z noexecstack -z relro -z now --strip-all"
Note:
Only applies to dynamic libraries and executables.
“output-type”
Output artifact type: executable, static (static library), or dynamic (dynamic library). Defaults to executable for cjpm init.
| Input | Description |
|---|---|
| “executable” | Executable program |
| “static” | Static library |
| “dynamic” | Dynamic library |
| Others | Error report |
“src-dir”
This field can specify the source code storage path. If not specified, it defaults to the src directory.
“target-dir”
This field can specify the output directory for compiled artifacts. If not specified, it defaults to the target directory. If this field is not empty, executing cjpm clean will delete the directory specified by this field. Developers must ensure the safety of clearing this directory.
Note:
If the
--target-diroption is specified during compilation, this option will take higher precedence.
target-dir = "temp"
“package-configuration”
Per-package configuration options for individual modules. This option is a map structure, where the package name to be configured serves as the key, and the package-specific configuration serves as the value. Currently configurable options include output type and conditional options (output-type, compile-option). These options can be omitted and configured as needed. As shown below, the output type of the demo.aoo package in the demo module will be specified as a dynamic library, and the -g command will be passed through to the demo.aoo package during compilation.
[package.package-configuration."demo.aoo"]
output-type = "dynamic"
compile-option = "-g"
If mutually compatible compilation options are configured in different fields, the priority of the generated commands is as follows.
[package]
compile-option = "-O1"
[package.package-configuration.demo]
compile-option = "-O2"
# The profile field will be introduced later
[profile.customized-option]
cfg1 = "-O0"
Input: cjpm build --cfg1 -V
Output: cjc --import-path build -O0 -O1 -O2 ...
By configuring this field, multiple binary artifacts can be generated simultaneously (when generating multiple binary artifacts, the -o, --output <value> option will be invalid). Example:
Example of source code structure, with the module named demo:
src
├── aoo
│ └── aoo.cj
├── boo
│ └── boo.cj
├── coo
│ └── coo.cj
└── main.cj
Example of configuration:
[package.package-configuration."demo.aoo"]
output-type = "executable"
[package.package-configuration."demo.boo"]
output-type = "executable"
Example of multiple binary artifacts:
Input: cjpm build
Output: cjpm build success
Input: tree target/release/bin
Output: target/release/bin
|-- demo.aoo
|-- demo.boo
`-- demo
“workspace”
This field can manage multiple modules as a workspace, supporting the following configuration items:
members = ["aoo", "path/to/boo"]: Lists local source code modules included in this workspace, supporting absolute and relative paths. Members of this field must be modules and cannot be another workspace.build-members = []: Modules to be compiled this time. If not specified, all modules in the workspace are compiled by default. Members of this field must be included in themembersfield.test-members = []: Modules to be tested this time. If not specified, unit tests are run on all modules in the workspace by default. Members of this field must be included in thebuild-membersfield.compile-option = "": Public compilation options for the workspace (optional).override-compile-option = "": Public global compilation options for the workspace (optional).link-option = "": Public linking options for the workspace (optional).target-dir = "": Output directory for the workspace (optional, defaults totarget).
Public configuration items in the workspace apply to all member modules. For example: If a source dependency like [dependencies] xoo = { path = "path_xoo" } is configured, all member modules can directly use the xoo module without needing to configure it in each submodule’s cjpm.toml.
Note:
The
packagefield is used to configure general module information and cannot coexist with theworkspacefield in the samecjpm.toml. All other fields exceptpackagecan be used in the workspace.
Example of a workspace directory structure:
root_path
├── aoo
│ ├── src
│ └── cjpm.toml
├── boo
│ ├── src
│ └── cjpm.toml
├── coo
│ ├── src
│ └── cjpm.toml
└── cjpm.toml
Example of workspace configuration file usage:
[workspace]
members = ["aoo", "boo", "coo"]
build-members = ["aoo", "boo"]
test-members = ["aoo"]
compile-option = "-Woff all"
override-compile-option = "-O2"
[dependencies]
xoo = { path = "path_xoo" }
[ffi.c]
abc = { path = "libs" }
“dependencies”
This field imports other Cangjie modules as dependencies via source code, containing information about other modules required for the current build. Currently, it supports both local path dependencies and remote git dependencies.
To specify a local dependency, use the path field, which must contain a valid local path. For example, the code structure of the two submodules pro0 and pro1 and the main module is as follows:
├── pro0
│ ├── cjpm.toml
│ └── src
│ └── zoo
│ └── zoo.cj
├── pro1
│ ├── cjpm.toml
│ └── src
│ ├── xoo
│ │ └── xoo.cj
│ └── yoo
│ └── yoo.cj
├── cjpm.toml
└── src
├── aoo
│ └── aoo.cj
├── boo
│ └── boo.cj
└── main.cj
After configuring the main module’s cjpm.toml as follows, the pro0 and pro1 modules can be used in the source code:
[dependencies]
pro0 = { path = "./pro0" }
pro1 = { path = "./pro1" }
To specify a remote git dependency, use the git field, which must contain a valid url in any format supported by git. To configure a git dependency, at most one branch, tag, or commitId field can be included to select a specific branch, tag, or commit hash, respectively. If multiple such fields are configured, only the highest-priority configuration will take effect, with the priority order being commitId > branch > tag. For example, after configuring as follows, the pro0 and pro1 modules from the specified git repository can be used in the source code:
[dependencies]
pro0 = { git = "https://github.com/example", tag = "v1.0.0"}
pro1 = { git = "https://gitee.com/example", branch = "dev"}
In this case, cjpm will download the latest version of the corresponding repository and save the current commit-hash in the cjpm.lock file. All subsequent cjpm calls will use the saved version until cjpm update is executed.
Authentication is often required to access git repositories. cjpm does not request credentials, so existing git authentication support should be used. If the protocol for git is https, an existing git credential helper must be used. On Windows, the credential helper is installed by default with git. On Linux/macOS, refer to the git-config documentation in the official git documentation for details on setting up a credential helper. If the protocol is ssh or git, key-based authentication should be used. If the key is protected by a passphrase, the developer must ensure that ssh-agent is running and the key is added via ssh-add before using cjpm.
The dependencies field can specify the compilation output type via the output-type attribute. The specified type can differ from the compilation output type of the source dependency itself and can only be static or dynamic, as shown below:
[dependencies]
pro0 = { path = "./pro0", output-type = "static" }
pro1 = { git = "https://gitee.com/example", output-type = "dynamic" }
After the above configuration, the output-type settings in the cjpm.toml files of pro0 and pro1 will be ignored, and the two modules’ outputs will be compiled into static and dynamic types, respectively.
“test-dependencies”
This field has the same format as the dependencies field. It is used to specify dependencies that are only used during testing and not required for building the main project. Module developers should use this field for dependencies that downstream users of this module do not need to be aware of.
Dependencies within test-dependencies can only be used in test files named like xxx_test.cj. During compilation, these dependencies will not be compiled. The configuration format of test-dependencies in cjpm.toml is the same as that of dependencies.
“script-dependencies”
This field has the same format as the dependencies field. It is used to specify dependencies that are only used during build script compilation and not required for building the main project. Build script-related features will be detailed in the Other-Build Scripts section.
“replace”
This field has the same format as the dependencies field. It is used to specify replacements for indirect dependencies with the same name. The configured dependencies will be the final versions used when compiling the module.
For example, the module aaa depends on a local module bbb:
[package]
name = "aaa"
[dependencies]
bbb = { path = "path/to/bbb" }
When the main module demo depends on aaa, bbb becomes an indirect dependency of demo. In this case, if the main module demo wants to replace bbb with another module of the same name (e.g., the bbb module under another path new/path/to/bbb), it can be configured as follows:
[package]
name = "demo"
[dependencies]
aaa = { path = "path/to/aaa" }
[replace]
bbb = { path = "new/path/to/bbb" }
After configuration, the actual indirect dependency bbb used when compiling the demo module will be the bbb module under new/path/to/bbb, and the bbb module under path/to/bbb configured in aaa will not be compiled.
Note:
Only the
replacefield of the entry module takes effect during compilation.
“ffi.c”
This field configures external C library dependencies for the current Cangjie module. It contains the information required to depend on the library, including the library name and path.
Developers need to compile the dynamic or static library themselves and place it under the specified path. Refer to the example below.
Instructions for calling external C dynamic libraries in Cangjie:
- Compile the corresponding
hello.cfile into a.solibrary (executeclang -shared -fPIC hello.c -o libhello.soin the file path). - Modify the project’s
cjpm.tomlfile to configure theffi.cfield, as shown in the example below. Here,./src/is the relative path of the compiledlibhello.soto the current directory, andhellois the library name. - Execute
cjpm buildto compile successfully.
[ffi.c]
hello = { path = "./src/" }
To specify C library configurations for different platforms, refer to target.
Note:
In multi-module scenarios on
Windowssystems, if multiple modules configure libraries with the same namec, due to the unique library loading strategy ofWindows, the system will prioritize loading library files from the runtime directory first. As a result, the actual library file used may differ from those on other systems.
“profile”
profile is a command profile configuration item used to control default settings during command execution. Currently, the following scenarios are supported: build, test, bench, run, and customized-option.
“profile.build”
[profile.build]
lto = "full" # Whether to enable `LTO` (Link Time Optimization) compilation mode. This feature is only supported on target platforms of `Linux/OpenHarmony`.
performance_analysis = true # Enable compilation performance analysis.
incremental = true # Whether to enable incremental compilation by default.
[profile.build.combined]
demo = "dynamic" # Compile the module into a single dynamic library file. The key is the module name.
Compilation process control items. All fields are optional and will not take effect if not configured. Only the profile.build settings of the top-level module take effect.
The lto configuration can be full or thin, corresponding to two compilation modes supported by LTO optimization: full LTO merges all compilation modules for global optimization, offering the highest optimization potential but requiring longer compilation time; thin LTO uses parallel optimization across multiple modules and supports incremental compilation during linking by default, with shorter compilation time than full LTO but less optimization due to reduced global information.
The performance_analysis configuration can be true or false, indicating whether to enable compilation performance analysis. When enabled, cjpm generates .prof and .json files in the performance_analysis directory under the compilation output directory, recording time and memory consumption during compilation. For example, if the default compilation output directory is target and the compilation mode is debug, the directory structure is as follows:
demo
├── cjpm.toml
├── src
| └── demo.cj
└── target
└── debug
└── performance_analysis
├── xxx1.prof
├── ...
├── xxxN.prof
├── xxx1.json
├── ...
└── xxxN.json
The combined configuration is a key-value pair where the key is the module name (package.name) and the value is dynamic. Before configuring this, the module compiles each package into separate dynamic or static library files based on package.output-type. After configuration, the module’s compilation method changes to:
- Subpackages other than
rootare compiled as static libraries. - The
rootpackage is compiled as a dynamic library, linking all subpackage static libraries, regardless of whether the subpackages are dependencies of therootpackage. When other modules depend on this dynamic library as a binary dependency, they can use all symbols from the subpackages.
For example, assume the module demo has the following structure:
demo
├── cjpm.toml
└── src
├── aoo
| └── aoo.cj
├── boo
| └── boo.cj
└── demo.cj
The module configuration file cjpm.toml is configured as follows:
[package]
name = "demo"
[profile.build.combined]
demo = "dynamic"
After compilation, the final output directory target/release/demo will contain the following files (using Linux as an example):
|-- libdemo.so
|-- libdemo.aoo.a
|-- libdemo.boo.a
|-- demo.cjo
|-- demo.aoo.cjo
|-- demo.boo.cjo
Module developers can provide all cjo files and the root package dynamic library libdemo.so to other modules as binary dependencies without providing the subpackage static library files. After other modules depend on this dynamic library, they can depend on all its subpackages in code, such as importing demo.aoo via import demo.aoo.
Note:
- When applying this configuration, compiling the
rootpackage dynamic library requires all subpackage static libraries, so ensure therootpackage is not directly or indirectly imported by its subpackages.- Currently, the
profile.build.combinedconfiguration is experimental and unstable. Developers enabling this configuration should note the following limitations:
- If a module configured with this field directly or indirectly depends on other source modules, those dependent modules must also be configured with this field.
- Source modules depended on by build scripts will not take effect if configured with
profile.build.combined.- The
profile.build.combinedoption is not supported when the compilation target platform ismacOS.
If combined is enabled, cyclic dependencies not identifiable via imports may occur, resulting in cyclic dependency errors. Solutions are as follows:
- If the error message includes
because of combined module 'demo', it means the moduledemois configured as acombinedmodule, and a subpackage ofdemodirectly or indirectly depends on therootpackage. Developers can locate and remove such imports or disable thecombinedconfiguration to resolve the issue. - If the error message includes
between combined modules, it means bothrootpackages in the entry are configured ascombinedmodules, and there are mutual dependencies between them (including subpackages). Developers can locate and remove imports from onecombinedmodule to another or disable bothcombinedconfigurations to resolve the issue.
“profile.test”
[profile.test] # Example usage
parallel=true
filter=*.*
no-color = true
timeout-each = "4m"
random-seed = 10
bench = false
report-path = "reports"
report-format = "xml"
verbose = true
[profile.test.build]
compile-option = ""
lto = "thin"
mock = "on"
[profile.test.env]
MY_ENV = { value = "abc" }
cjHeapSize = { value = "32GB", splice-type = "replace" }
PATH = { value = "/usr/bin", splice-type = "prepend" }
Test configuration supports specifying options during test compilation and execution. All fields are optional and will not take effect if not configured. Only the profile.test settings of the top-level module take effect. The option list aligns with the console execution options provided by cjpm test. If an option is configured in both the configuration file and the console, the console option takes precedence. profile.test supports the following runtime options:
filter: Specifies the test case filter. The value is a string with the same format as the--filtervalue in the test command description.timeout-each <value>: Thevalueformat is%d[millis|s|m|h], specifying the default timeout for each test case.parallel: Specifies the parallel execution scheme for test cases. Thevaluecan be:<BOOL>:trueorfalse. Iftrue, test classes can run in parallel, with the number of parallel processes controlled by the CPU cores available on the system.nCores: The number of parallel test processes equals the available CPU cores.NUMBER: The number of parallel test processes. Must be a positive integer.NUMBERnCores: The number of parallel test processes is a multiple of the available CPU cores. Must be a positive number (supports integers or floats).
option:<value>: Works with@Configureto define runtime options. For example:random-seed: Specifies the random seed value. Must be a positive integer.no-color: Specifies whether to disable colored output in the console. Can betrueorfalse.report-path: Specifies the path for test execution reports (cannot be configured via@Configure).report-format: Specifies the report output format. Currently, unit test reports only supportxmlandxml-per-packageformat (case-insensitive). Other values will throw an exception (cannot be configured via@Configure). Performance test reports only supportcsvandcsv-rawformats.verbose: Specifies whether to display detailed compilation information. Can betrueorfalse.
“profile.test.build”
Specifies supported compilation options, including:
compile-option: A string containing additionalcjccompilation options, supplementing the top-levelcompile-optionfield.lto: Specifies whether to enableLTOoptimization. Can bethinorfull. This feature is only supported on target platforms ofLinux/OpenHarmony.mock: Explicitly sets themockmode. Possible values:on,off,runtime-error. The default value fortest/buildsubcommands ison, and forbenchsubcommands, it isruntime-error.
“profile.test.env”
Configures temporary environment variables when running executables during the test command. The key is the name of the environment variable to configure, with the following options:
value: Specifies the environment variable value.splice-type: Specifies how to splice the environment variable. Optional; defaults toabsent. Possible values:absent: The configuration only takes effect if no environment variable with the same name exists. If one exists, the configuration is ignored.replace: The configuration replaces any existing environment variable with the same name.prepend: The configuration is prepended to any existing environment variable with the same name.append: The configuration is appended to any existing environment variable with the same name.
“profile.bench”
[profile.bench] # Example usage
no-color = true
random-seed = 10
report-path = "bench_report"
baseline-path = ""
report-format = "csv"
verbose = true
Benchmark configuration supports specifying options during benchmark compilation and execution. All fields are optional and will not take effect if not configured. Only the profile.bench settings of the top-level module take effect. The option list aligns with the console execution options provided by cjpm bench. If an option is configured in both the configuration file and the console, the console option takes precedence. profile.bench supports the following runtime options:
filter: Specifies the benchmark case filter. The value is a string with the same format as the--filtervalue in the bench command description.option:<value>: Works with@Configureto define runtime options. For example:random-seed: Specifies the random seed value. Must be a positive integer.no-color: Specifies whether to disable colored output in the console. Can betrueorfalse.report-path: Specifies the path for benchmark execution reports (cannot be configured via@Configure).report-format: Specifies the report output format. Currently, unit test reports only supportxmlandxml-per-packageformat (case-insensitive). Other values will throw an exception (cannot be configured via@Configure). Performance test reports only supportcsvandcsv-rawformats.verbose: Specifies whether to display detailed compilation information. Can betrueorfalse.baseline-path: The path of an existing report to compare with the current performance results. By default, it uses the--report-pathvalue.
“profile.bench.build”
Used to specify additional compilation options when building executables for cjpm bench. Shares the same configuration as profile.test.build.
“profile.bench.env”
Supports configuring environment variables when running executables with the bench command, following the same configuration method as profile.test.env.
“profile.run”
Options for running executables, supporting environment variable configuration env when executing the run command, following the same configuration method as profile.test.env.
“profile.customized-option”
[profile.customized-option]
cfg1 = "--cfg=\"feature1=lion, feature2=cat\""
cfg2 = "--cfg=\"feature1=tiger, feature2=dog\""
cfg3 = "-O2"
Custom options passed through to cjc. Enabled via --cfg1 --cfg3. The customized-option set for each module applies to all packages within that module. For example, when executing cjpm build --cfg1 --cfg3, the command passed to cjc would be --cfg="feature1=lion, feature2=cat" -O2.
Note:
The conditional value here must be a valid identifier.
“target”
Multi-backend, multi-platform isolation options for configuring different settings across various backends and platforms. Taking the Linux system as an example, the target configuration is as follows:
[target.x86_64-unknown-linux-gnu] # Configuration items for Linux systems
compile-option = "value1" # Additional compilation command options
override-compile-option = "value2" # Additional global compilation command options
link-option = "value3" # Linker passthrough options
[target.x86_64-unknown-linux-gnu.dependencies] # Source dependency configuration
[target.x86_64-unknown-linux-gnu.test-dependencies] # Test-phase dependency configuration
[target.x86_64-unknown-linux-gnu.bin-dependencies] # Cangjie binary library dependencies
path-option = ["./test/pro0", "./test/pro1"]
[target.x86_64-unknown-linux-gnu.bin-dependencies.package-option]
"pro0.xoo" = "./test/pro0/pro0.xoo.cjo"
"pro0.yoo" = "./test/pro0/pro0.yoo.cjo"
"pro1.zoo" = "./test/pro1/pro1.zoo.cjo"
[target.x86_64-unknown-linux-gnu.ffi.c] # C language binary library dependencies
"ctest" = "./test/c"
[target.x86_64-unknown-linux-gnu.debug] # Debug configuration for Linux systems
[target.x86_64-unknown-linux-gnu.debug.test-dependencies]
[target.x86_64-unknown-linux-gnu.release] # Release configuration for Linux systems
[target.x86_64-unknown-linux-gnu.release.bin-dependencies]
Developers can add a series of configuration items for a specific target by configuring the target.target-name field. The target name can be obtained in the corresponding Cangjie environment via the command cjc -v, where the Target item in the command output represents the target name for that environment. The above example applies to the Linux system but is also applicable to other platforms, where the target name can similarly be obtained via cjc -v.
Dedicated configuration items for a specific target will apply to the compilation process for that target, as well as cross-compilation processes where other targets specify this target as the target platform. The list of configurable items includes:
compile-option: Additional compilation command optionsoverride-compile-option: Additional global compilation command optionslink-option: Linker passthrough optionsdependencies: Source dependency configuration, structured similarly to thedependenciesfieldtest-dependencies: Test-phase dependency configuration, structured similarly to thetest-dependenciesfieldbin-dependencies: Cangjie binary library dependencies, described belowffi.c: Configuration for external C library dependencies in Cangjie modules, structured similarly to theffi.cfieldcompile-macros-for-target: Macro package control items for cross-compilation, which do not support distinguishing betweendebugandreleasecompilation modes below
Developers can configure target.target-name.debug and target.target-name.release fields to specify additional configurations unique to debug and release compilation modes for that target. The configurable items are the same as above. Configurations under these fields will only apply to the corresponding compilation mode of the target.
“target.target-name[.debug/release].bin-dependencies”
This field is used to import pre-compiled Cangjie library output files suitable for the specified target. The following example demonstrates importing three packages from the pro0 and pro1 modules.
Note:
Unless specifically required, it is not recommended to use this field. Instead, use the
dependenciesfield described earlier to import module source code.
├── test
│ ├── pro0
│ │ ├── libpro0.xoo.so
│ │ ├── pro0.xoo.cjo
│ │ ├── libpro0.yoo.so
│ │ └── pro0.yoo.cjo
│ └── pro1
│ ├── libpro1.zoo.so
│ └── pro1.zoo.cjo
├── src
│ └── main.cj
└── cjpm.toml
Method 1: Import via path-option:
[target.x86_64-unknown-linux-gnu.bin-dependencies]
path-option = ["./test/pro0", "./test/pro1"]
The path-option is a string array structure, where each element represents the path name to be imported. cjpm will automatically import all Cangjie library packages under that path that comply with the naming rules, where the library name format should be full-package-name. For example, the library name corresponding to pro0.xoo.cjo in the above example should be libpro0.xoo.so or libpro0.xoo.a. Packages whose library names do not comply with this rule can only be imported via the package-option.
Method 2: Import via package-option:
[target.x86_64-unknown-linux-gnu.bin-dependencies.package-option]
"pro0.xoo" = "./test/pro0/pro0.xoo.cjo"
"pro0.yoo" = "./test/pro0/pro0.yoo.cjo"
"pro1.zoo" = "./test/pro1/pro1.zoo.cjo"
The package-option is a map structure, where pro0.xoo serves as the key (strings containing . in toml configuration files must be enclosed in ""), so the key value corresponds to libpro0.xoo.so. The path to the frontend file cjo serves as the value, and the corresponding .a or .so file for that cjo must be placed in the same path.
Note:
If the same package is imported via both
package-optionandpath-option, thepackage-optionfield takes higher precedence.
The following code example in main.cj demonstrates calling the pro0.xoo, pro0.yoo, and pro1.zoo packages:
import pro0.xoo.*
import pro0.yoo.*
import pro1.zoo.*
main(): Int64 {
var res = x + y + z // x, y, z are values defined in pro0.xoo, pro0.yoo, and pro1.zoo respectively
println(res)
return 0
}
Note:
The dependent Cangjie dynamic library files may be compilation outputs of the
rootpackage generated by other modules through theprofile.build.combinedconfiguration, containing symbols for all its sub-packages. During dependency checking, if a package’s corresponding Cangjie library is not found, therootpackage corresponding to that package will be used as a dependency, and a warning will be printed. Developers must ensure that therootpackage imported in this way is generated via the corresponding method; otherwise, the library file may not contain symbols for sub-packages, leading to compilation errors. For example, if the source code imports thedemo.aoopackage viaimport demo.aoo, and the binary dependency does not contain the corresponding Cangjie library for that package,cjpmwill attempt to find the dynamic library for therootpackage corresponding to that package, i.e.,libdemo.so. If found, it will use that library as the dependency.
“target.target-name.compile-macros-for-target”
This field configures the cross-compilation method for macro packages, with the following three scenarios:
Method 1: By default, macro packages only compile outputs for the local platform during cross-compilation, not for the target platform. This applies to all macro packages within the module.
[target.target-platform]
compile-macros-for-target = ""
Method 2: During cross-compilation, outputs for both the local and target platforms are compiled. This applies to all macro packages within the module.
[target.target-platform]
compile-macros-for-target = "all" # The configuration item is a string, and the optional value must be "all"
Method 3: Specifies that certain macro packages within the module should compile outputs for both the local and target platforms during cross-compilation, while other unspecified macro packages follow the default mode of Method 1.
[target.target-platform]
compile-macros-for-target = ["pkg1", "pkg2"] # The configuration item is a string array, and the optional values are macro package names
Merging Rules for “target” Related Fields
Configuration items in target may coexist with other options in cjpm.toml. For example, the compile-option field can also exist in the package field, with the difference that the field in package applies to all targets. cjpm merges these duplicate fields in a specific way, combining all applicable configurations. Taking the debug compilation mode for x86_64-unknown-linux-gnu as an example, the target configuration is as follows:
[package]
compile-option = "compile-0"
override-compile-option = "override-compile-0"
link-option = "link-0"
[dependencies]
dep0 = { path = "./dep0" }
[test-dependencies]
devDep0 = { path = "./devDep0" }
[target.x86_64-unknown-linux-gnu]
compile-option = "compile-1"
override-compile-option = "override-compile-1"
link-option = "link-1"
[target.x86_64-unknown-linux-gnu.dependencies]
dep1 = { path = "./dep1" }
[target.x86_64-unknown-linux-gnu.test-dependencies]
devDep1 = { path = "./devDep1" }
[target.x86_64-unknown-linux-gnu.bin-dependencies]
path-option = ["./test/pro1"]
[target.x86_64-unknown-linux-gnu.bin-dependencies.package-option]
"pro1.xoo" = "./test/pro1/pro1.xoo.cjo"
[target.x86_64-unknown-linux-gnu.debug]
compile-option = "compile-2"
override-compile-option = "override-compile-2"
link-option = "link-2"
[target.x86_64-unknown-linux-gnu.debug.dependencies]
dep2 = { path = "./dep2" }
[target.x86_64-unknown-linux-gnu.debug.test-dependencies]
devDep2 = { path = "./devDep2" }
[target.x86_64-unknown-linux-gnu.debug.bin-dependencies]
path-option = ["./test/pro2"]
[target.x86_64-unknown-linux-gnu.debug.bin-dependencies.package-option]
"pro2.xoo" = "./test/pro2/pro2.xoo.cjo"
When target configuration items coexist with public configuration items in cjpm.toml or other levels of configuration items for the same target, they are merged according to the following priority:
- Configuration for the corresponding
targetindebug/releasemode - Configuration for the corresponding
targetunrelated todebug/release - Public configuration items
In the above target configuration example, the target configuration items are merged according to the following rules:
compile-option: All applicable configurations with the same name are concatenated in order of priority, with higher-priority configurations appended later. In this example, the finalcompile-optionvalue indebugmode forx86_64-unknown-linux-gnuiscompile-0 compile-1 compile-2, while inreleasemode it iscompile-0 compile-1, and for othertargetsit iscompile-0.override-compile-option: Same as above. Sinceoverride-compile-optionhas higher priority thancompile-option, in the final compilation command, the concatenatedoverride-compile-optionwill be placed after the concatenatedcompile-option.link-option: Same as above.dependencies: Source dependencies are merged directly, and conflicts will result in errors. In this example, the finaldependenciesindebugmode forx86_64-unknown-linux-gnuaredep0,dep1, anddep2, while inreleasemode onlydep0anddep1are active. For othertargets, onlydep0is active.test-dependencies: Same as above.bin-dependencies: Binary dependencies are merged by priority, with conflicts resolved by keeping only the higher-priority dependency.package-optionconfigurations are added first for configurations with the same priority. In this example, indebugmode forx86_64-unknown-linux-gnu, binary dependencies from./test/pro1and./test/pro2are added, while inreleasemode only./test/pro1is added. Sincebin-dependencieshas no public configuration, no binary dependencies are active for othertargets.
In cross-compilation scenarios for this example, if x86_64-unknown-linux-gnu is specified as the target target on other platforms, the configuration for target.x86_64-unknown-linux-gnu will also be merged with public configuration items according to the above rules. If in debug mode, the configuration items for target.x86_64-unknown-linux-gnu.debug will also be applied.
Environment Variable Configuration
Environment variables can be used in cjpm.toml to configure field values. cjpm will retrieve the corresponding environment variable values from the current runtime environment and substitute them into the actual configuration values. For example, the following dependencies field uses an environment variable for path configuration:
[dependencies]
aoo = { path = "${DEPENDENCY_PATH}/aoo" }
When importing module aoo, cjpm will retrieve the DEPENDENCY_PATH variable value and substitute it to obtain the final path for module aoo.
The list of fields that support environment variable configuration includes:
- The following fields in the single-module configuration field
package:- Single-package compilation option
compile-optioninpackage-configuration
- Single-package compilation option
- The following fields in the workspace management field
workspace:- Member module list
members - Compilation module list
build-members - Test module list
test-members
- Member module list
- The following fields common to both
packageandworkspace:- Compilation option
compile-option - Global compilation option
override-compile-option - Linking option
link-option - Output directory path
target-dir
- Compilation option
- The
pathfield for local dependencies in the build dependency listdependencies - The
pathfield for local dependencies in the test dependency listtest-dependencies - The
pathfield for local dependencies in the build script dependency listscript-dependencies - Custom passthrough options
customized-optionin the command profile configurationprofile - The
pathfield in external C library configurationffi.c - The following fields in the platform isolation option
target:- Compilation option
compile-option - Global compilation option
override-compile-option - Linking option
link-option - The
pathfield for local dependencies in the build dependency listdependencies - The
pathfield for local dependencies in the test dependency listtest-dependencies - The
pathfield for local dependencies in the build script dependency listscript-dependencies - The
path-optionandpackage-optionfields in the binary dependency fieldbin-dependencies
- Compilation option
Project Management Configuration File Specification
The project management configuration file, cangjie-repo.toml, is utilized to configure settings including the central repository URL and local repository cache. The cjpm tool primarily leverages this file to interface with the central repository and manage dependency modules downloaded from the central repository.
The cangjie-repo.toml file can be configured in three locations. When executing the cjpm command, it reads the configuration files in the following priority order from highest to lowest:
- A
cangjie-repo.tomlfile located alongsidecjpm.toml: In the currentcjpmmodule directory where the command is executed. - A
cangjie-repo.tomlfile under the.cjpmdirectory of user’s home directory.- For
Linux/macOS:$HOME/.cjpm - For
Windows:%USERPROFILE%/.cjpm
- For
- A
cangjie-repo.tomlfile in the Cangjie SDK directory at the pathtools/config/cangjie-repo.toml.
Upon successfully locating a valid cangjie-repo.toml file, cjpm will utilize this file as the configuration source for the current command execution and will disregard all configuration files of lower precedence.
The configuration file format is as follows:
[repository.cache]
path = "/path/to/repository/cache"
[repository.home]
registry = "central/repo/url"
token = "user-token"
[global]
strict-tls = true
The configuration content is described as follows:
repository.homeis used to configure the central repository URL and the user’s personal token. Thecjpmtool interacts with the central repository address specified in theregistryfield, and all interaction requests will include the user’s token information for authentication.repository.cacheis used to configure the local path for storing source code modules downloaded from the central repository or Git. Environment variables can be used to configure field values, Refer toEnvironment Variable Configuration, If not configured, it defaults to the.cjpmdirectory in the user’s home directory. Once the local path is determined, Git source code modules are downloaded to thegitsubdirectory under this path. Central repository source code modules are downloaded to therepository/sourcesubdirectory under this path.global.strict-tlsis used to configure the TLS certificate verification method. The default is normal verification; when set tofalse, certificate verification is disabled.
Configuration and Cache Directories
The storage path for files downloaded by cjpm via git can be specified using the CJPM_CONFIG environment variable. If not specified, the default location on Linux/macOS is $HOME/.cjpm, and on Windows it is %USERPROFILE%/.cjpm.
Note:
- This configuration functions identically to
repository.cacheincangjie-repo.toml. It only takes effect if no validcangjie-repo.tomlconfiguration exists, or if the valid configuration is the one located attools/config/cangjie-repo.tomlwithin the Cangjie SDK.- This configuration is deprecated and will be removed in a future release. Please use
cangjie-repo.tomlinstead.
Cangjie Package Management Specification
In the Cangjie package management specification, for a file directory to be recognized as a valid source package, the following requirements must be met:
- It must directly contain at least one Cangjie code file;
- Its parent package (including the parent’s parent package, up to the
rootpackage) must also be a valid source package. Note that the modulerootpackage has no parent package, so it only needs to satisfy condition 1.
For example, consider the following cjpm project named demo:
demo
├──src
│ ├── main.cj
│ └── pkg0
│ ├── aoo
│ │ └── aoo.cj
│ └── boo
│ └── boo.cj
└── cjpm.toml
Here, the directory corresponding to demo.pkg0 does not directly contain any Cangjie code, so demo.pkg0 is not a valid source package. Although demo.pkg0.aoo and demo.pkg0.boo directly contain Cangjie code files aoo.cj and boo.cj, their upstream package demo.pkg0 is not a valid source package, so these two packages are also not valid source packages.
When cjpm identifies a package like demo.pkg0 that does not directly contain Cangjie files, it treats it as a non-source package, ignores all its subpackages, and prints the following warning:
Warning: there is no '.cj' file in directory 'demo/src/pkg0', and its subdirectories will not be scanned as source code
Therefore, if developers need to configure a valid source package, the package must directly contain at least one Cangjie code file, and all its upstream packages must be valid source packages. Taking the above demo project as an example, to make demo.pkg0, demo.pkg0.aoo, and demo.pkg0.boo all recognized as valid source packages, a Cangjie code file can be added inside demo/src/pkg0, as shown below:
demo
├── src
│ ├── main.cj
│ └── pkg0
│ ├── pkg0.cj
│ ├── aoo
│ │ └── aoo.cj
│ └── boo
│ └── boo.cj
└── cjpm.toml
demo/src/pkg0/pkg0.cj must be a Cangjie code file that complies with the package management specification and may not contain functional code, such as the following form:
package demo.pkg0
Command Extension
cjpm provides a command extension mechanism, allowing developers to extend cjpm commands via executable files named in the format cjpm-xxx(.exe).
For an executable file cjpm-xxx (cjpm-xxx.exe on Windows), if the file’s directory is configured in the system environment variable PATH, the following command can be used to execute it:
cjpm xxx [args]
Here, args represents the list of arguments that may be required by cjpm-xxx(.exe). The above command is equivalent to:
cjpm-xxx(.exe) [args]
Running cjpm-xxx(.exe) may depend on certain dynamic libraries. In such cases, developers need to manually add the directory containing the required dynamic libraries to the environment variables.
Below is an example using cjpm-demo, an executable file compiled from the following Cangjie code:
import std.process.*
import std.collection.*
main(): Int64 {
var args = ArrayList<String>(Process.current.arguments)
if (args.size < 1) {
eprintln("Error: failed to get parameters")
return 1
}
println("Output: ${args[0]}")
return 0
}
After adding its directory to PATH, running the corresponding command will execute the file and produce the expected output.
Input: cjpm demo hello,world
Output: Output: hello,world
Built-in cjpm commands have higher priority, so these commands cannot be extended this way. For example, even if an executable file named cjpm-build exists in the system environment variables, cjpm build will not execute this file but will instead run cjpm with build as an argument.
Build Scripts
cjpm provides a build script mechanism, allowing developers to define behaviors for cjpm before or after executing certain commands.
The build script source file is fixed as build.cj and is located in the Cangjie project’s root directory, at the same level as cjpm.toml. When creating a new Cangjie project using the init command, cjpm does not create build.cj by default. Developers who need it can manually create and edit build.cj in the specified location using the following template format:
// build.cj
import std.process.*
// Case of pre/post codes for 'cjpm build'.
/* called before `cjpm build`
* Success: return 0
* Error: return any number except 0
*/
// func stagePreBuild(): Int64 {
// // process before "cjpm build"
// 0
// }
/*
* called after `cjpm build`
*/
// func stagePostBuild(): Int64 {
// // process after "cjpm build"
// 0
// }
// Case of pre codes for 'cjpm clean'.
/* called before `cjpm clean`
* Success: return 0
* Error: return any number except 0
*/
// func stagePreClean(): Int64 {
// // process before "cjpm clean"
// 0
// }
// For other options, define stagePreXXX and stagePostXXX in the same way.
/*
* Error code:
* 0: success.
* other: cjpm will finish running command. Check target-dir/build-script-cache/module-name/script-log for error outputs defind by user in functions.
*/
main(): Int64 {
match (Process.current.arguments[0]) {
// Add operation here with format: "pre-"/"post-" + optionName
// case "pre-build" => stagePreBuild()
// case "post-build" => stagePostBuild()
// case "pre-clean" => stagePreClean()
case _ => 0
}
}
cjpm supports using build scripts to define pre- and post-command behaviors for a series of commands. For example, for the build command, you can define pre-build in the match block within the main function to execute the desired pre-build functionality function stagePreBuild (the function name is not restricted). Post-build behavior can be similarly defined by adding a post-build case. Other commands can be extended in the same way by adding corresponding pre/post options and functionality functions.
After defining pre- and post-command behaviors, cjpm will first compile build.cj when executing the command and then execute the corresponding behaviors before and after the command. For example, with pre-build and post-build defined, running cjpm build will follow these steps:
- Before the build process, compile
build.cj; - Execute the functionality function corresponding to
pre-build; - Proceed with the
cjpm buildcompilation process; - After successful compilation,
cjpmwill execute the functionality function corresponding topost-build.
The commands supported by build scripts are as follows:
build,test,bench: Support executing bothpreandpostprocesses defined in dependent modules’ build scripts.run,install: Only support running thepreandpostbuild script processes of the corresponding module or executing thepre-buildandpost-buildprocesses of dependent modules during compilation.check,tree,update: Only support running thepreandpostbuild script processes of the corresponding module.clean: Only support running theprebuild script process of the corresponding module.
When executing these commands, if the --skip-script option is configured, all build script compilation and execution will be skipped, including those of dependent modules.
Usage notes for build scripts:
- The return value of functionality functions must meet certain requirements: a successful execution should return
0, while a failure should return anyInt64value except0. - All outputs from
build.cjwill be redirected to the project directory atbuild-script-cache/[target|release]/[module-name]/bin/script-log. Developers can check this file for output content added in functionality functions. - If
build.cjdoes not exist in the project root directory,cjpmwill proceed with normal execution. Ifbuild.cjexists and defines pre- or post-command behaviors, the command will abort abnormally ifbuild.cjfails to compile or the functionality function returns a non-zero value, even if the command itself could execute successfully. - In multi-module scenarios, the build scripts (
build.cj) of dependent modules take effect during compilation and unit testing. Outputs from dependent module build scripts are also redirected to log files in the corresponding module directory underbuild-script-cache/[target|release].
For example, the following build script build.cj defines pre- and post-build behaviors:
import std.process.*
func stagePreBuild(): Int64 {
println("PRE-BUILD")
0
}
func stagePostBuild(): Int64 {
println("POST-BUILD")
0
}
main(): Int64 {
match (Process.current.arguments[0]) {
case "pre-build" => stagePreBuild()
case "post-build" => stagePostBuild()
case _ => 0
}
}
When executing cjpm build, cjpm will execute stagePreBuild and stagePostBuild. After cjpm build completes, the script-log file will contain the following output:
PRE-BUILD
POST-BUILD
Build scripts can import dependent modules via the script-dependencies field in cjpm.toml, with the same format as dependencies. For example, the following configuration in cjpm.toml imports the aoo module, which contains a method named aaa():
[script-dependencies]
aoo = { path = "./aoo" }
The build script can then import this dependency and use the interface aaa():
import std.process.*
import aoo.*
func stagePreBuild(): Int64 {
aaa()
0
}
func stagePostBuild(): Int64 {
println("POST-BUILD")
0
}
main(): Int64 {
match (Process.current.arguments[0]) {
case "pre-build" => stagePreBuild()
case "post-build" => stagePostBuild()
case _ => 0
}
}
Build script dependencies (script-dependencies) are independent of source code-related dependencies (dependencies and test-dependencies). Source and test code cannot use modules from script-dependencies, and build scripts cannot use modules from dependencies or test-dependencies. If the same module is needed in both build scripts and source/test code, it must be configured in both script-dependencies and dependencies/test-dependencies.
Usage Examples
The following example demonstrates how to use cjpm with a Cangjie project directory structure. The corresponding source code examples can be found in Source Code. The module name for this Cangjie project is test.
cj_project
├── pro0
│ ├── cjpm.toml
│ └── src
│ ├── zoo
│ │ ├── zoo.cj
│ │ └── zoo_test.cj
│ └── pro0.cj
├── src
│ ├── koo
│ │ ├── koo.cj
│ │ └── koo_test.cj
│ ├── main.cj
│ └── main_test.cj
└── cjpm.toml
Using init and build
-
Create a new Cangjie project and write source code
xxx.cjfiles, such as thekoopackage andmain.cjfile shown in the example structure.cjpm init --name test --path ./cj_project cd cj_project mkdir src/kooAt this point, a
cj_projectdirectory will be created in the current command execution directory, and thesrcfolder along with the defaultcjpm.tomlconfiguration file will be automatically generated within it. Developers can manually create sub-packages (e.g.,src/koo) in the source code directorysrc, or add new source files and test files in each package as needed. -
When the current module depends on an external
pro0module, create thepro0module and its configuration file. Then write the module’s source code files, manually creating thesrcdirectory underpro0, and place the Cangjie packages undersrc, such as thezoopackage in the example structure.mkdir pro0 && cd pro0 cjpm init --name pro0 --type=static mkdir src/zoo -
When the main module depends on
pro0, configure thedependenciesfield in the main module’s configuration file as described in the manual. After correct configuration, executecjpm build. The generated executable will be in thetarget/release/bin/directory.cd cj_project vim cjpm.toml cjpm build cjpm run
Using test and clean
-
After writing the corresponding
xxx_test.cjunit test files for each file as shown in the example structure, execute the following code to run unit tests. The generated files will be in thetarget/release/unittest_bindirectory.cjpm testOr:
cjpm test src src/koo pro0/src/zoo -
To manually delete intermediate files such as the
targetandcov_outputdirectories,*.gcno, and*.gcda, execute:cjpm clean
Example Source Code
// cj_project/src/main.cj
package test
import pro0.zoo.*
import test.koo.*
main(): Int64 {
let res = z + k
println(res)
let res2 = concatM("a", "b")
println(res2)
return 0
}
func concatM(s1: String, s2: String): String {
return s1 + s2
}
// cj_project/src/main_test.cj
package test
import std.unittest.* // testfame
import std.unittest.testmacro.* // macro_Defintion
@Test
public class TestM{
@TestCase
func sayhi(): Unit {
@Assert(concatM("1", "2"), "12")
@Assert(concatM("1", "3"), "13")
}
}
// cj_project/src/koo/koo.cj
package test.koo
public let k: Int32 = 12
func concatk(s1: String, s2: String): String {
return s1 + s2
}
// cj_project/src/koo/koo_test.cj
package test.koo
import std.unittest.* // testfame
import std.unittest.testmacro.* // macro_Defintion
@Test
public class TestK{
@TestCase
func sayhi(): Unit {
@Assert(concatk("1", "2"), "12")
@Assert(concatk("1", "3"), "13")
}
}
// cj_project/pro0/src/pro0.cj
package pro0
// cj_project/pro0/src/zoo/zoo.cj
package pro0.zoo
public let z: Int32 = 26
func concatZ(s1: String, s2: String): String {
return s1 + s2
}
// cj_project/pro0/src/zoo/zoo_test.cj
package pro0.zoo
import std.unittest.* // test framework
import std.unittest.testmacro.* // macro definition
@Test
public class TestZ{
@TestCase
func sayhi(): Unit {
@Assert(concatZ("1", "2"), "12")
@Assert(concatZ("1", "3"), "13")
}
}
# cj_project/cjpm.toml
[package]
cjc-version = "1.0.0"
description = "nothing here"
version = "1.0.0"
name = "test"
output-type = "executable"
[dependencies]
pro0 = { path = "pro0" }
# cj_project/pro0/cjpm.toml
[package]
cjc-version = "1.0.0"
description = "nothing here"
version = "1.0.0"
name = "pro0"
output-type = "static"
Appendix
Cross-Compilation Instructions
cjpm supports cross-compilation and execution between certain platforms. For example, assuming the target platform is arch-sys-abi, the compilation steps are as follows:
-
Configure the toolchain required for the target platform.
-
In the
cjpm.tomlof the entry module, add the compilation option configuration needed for the target platform:[target.arch-sys-abi] override-compile-option = "value"This configuration will apply to all dependent modules. In single-module compilation mode, it can be replaced with
compile-option. -
If the project has binary dependencies, configure them as follows:
[target.arch-sys-abi.bin-dependencies] path-option = [...] [target.arch-sys-abi.bin-dependencies.package-option] "..." = "..." -
Use the following commands to compile and build or test the code:
cjpm build --target=arch-sys-abi# Cross-compile artifacts for the target platform cjpm test --target=arch-sys-abi # Cross-compile executable test files for the target platform -
Import the binary artifacts into the target platform for normal execution.
Note:
- The compiled artifacts are located in the
target-dirdirectory configured by the user, under a subdirectory named after the target platform (target).- If dynamic library dependencies exist, configure them in the runtime environment variable
LD_LIBRARY_PATH.
Multi-Platform Build Instructions
To support multi-platform project builds, cjpm introduces new entities such as features and source-sets to enhance development efficiency for multi-platform projects. This is an experimental feature and requires specifying experimental = true in the [profile] field.
Feature
A Feature is a named flag used to specify the source code to be compiled. Below is a list of all available features in cjpm:
feature.os.posix
feature.os.epoll
feature.os.kqueue
feature.os.windows
feature.os.linux
# OS is harmony
feature.os.hm
feature.os.darwin
# The CPU architecture is `big-endian`
feature.arch.big
# The CPU architecture is `little-endian`
feature.arch.little
feature.arch.x64
feature.arch.aarch64
# The CPU uses `sse` instruction set
feature.arch.sse1
feature.arch.sse2
feature.arch.sse3
feature.arch.sse4.1
feature.arch.sse4.2
# The CPU uses `avx` instruction set
feature.arch.avx1
feature.arch.avx2
feature.arch.avx512
feature.arch.neon
feature.env.ohos
feature.env.gnu
feature.env.mingw32
feature.env.hos
feature.env.android
feature.cj.cjnative
feature.cj.v0_54_3 # Anything after `v` is interpreted as `cjc` version
These values are not validated in any way. It is the developer’s responsibility to ensure that the code compiled with these features executes correctly.
Developers can define custom feature values and meanings.
Developers can use the --enable-features option in cjpm’s build, run, and test commands, providing a comma-separated list of feature values.
For example:
cjpm build --enable-features=feature.os.linux,feature.env.gnu
Feature Deduction
The following feature values can be inferred from the cjc or other compilation options (e.g., --target) used in cjpm, so they typically do not need to be explicitly specified:
feature.os.posix
feature.os.windows
feature.os.linux
feature.os.hm
feature.os.darwin
feature.arch.x64
feature.arch.aarch64
feature.env.ohos
feature.env.gnu
feature.env.mingw32
feature.env.hos
feature.env.android
feature.cj.cjvm
feature.cj.cjnative
feature.cj.v0_54_3
If developing a multi-platform project on a GNU/Linux machine, the source code can be compiled and run using the following commands:
# Short command
cjpm run
# Full command
cjpm run --enable-features=feature.os.linux,feature.env.gnu
For cross-compilation scenarios, specify --target.
For example:
# Short command
cjpm build --target=aarch64-linux-android
# Full command
cjpm build --target=aarch64-linux-android --enable-features=feature.arch.aarch64,feature.os.linux,feature.env.android
If cjpm deduces multiple source directories (resulting in a compilation error), use the --no-feature-deduce option to disable deduction and explicitly specify features with --enable-features:
# Will display the error message "No source set was selected"
cjpm build --target=aarch64-linux-android --no-feature-deduce
# Specify appropriate feature values:
# 1. [..., "feature.os.linux", "feature.env.android"]
# 2. [..., "feature.os.linux"]
# 3. [..., "feature.env.android"]
cjpm build --no-feature-deduce --target=aarch64-linux-android --enable-features=feature.os.linux,feature.env.android
Source Sets and Their Configuration
The default cjpm.toml is as follows:
[package]
cjc-version = "0.57.1"
compile-option = ""
description = "nothing here"
link-option = ""
name = "cmp_lib"
output-type = "dynamic"
override-compile-option = ""
target-dir = ""
version = "1.0.0"
package-configuration = {}
# New field indicating a multi-platform project
# [source-set]
For the source-set field configuration, an example is provided below:
# Example syntax:
# Source file directory for Cangjie code
[source-set.epoll]
src-dir = "src/net/select/epoll"
condition = [ "feature.os.epoll" ]
[source-set.kqueue]
src-dir = "src/net/select/kqueue"
condition = [ "feature.os.kqueue" ]
Each source set declaration consists of three parts: the source-set identifier, src-dir, and condition.
Source-Set Identifier
The source-set identifier is the unique “path” of the source set. This “path” is separated by ., and each path should start with source-set as it is the root of all declarations.
src-dir
Specifies the location to search for the package’s source code when this source set is enabled.
# Possible syntax
# 1. Single directory
[source-set.${source set fully qualified name}]
src-dir = "./src/linux/common"
# 2. Multiple directories
# All specified directories will be compiled as a single compilation unit, similar to compiling a single directory.
[source-set.${source set fully qualified name}]
src-dir = ["./src/linux/dirA", "./src/linux/dirB"]
condition
The condition specifies one or more features that must be enabled to compile the corresponding code.
# If the source set declaration does not include a `condition` field, no constraints are set.
# Possible syntax:
# 1. Multiple features must be satisfied simultaneously.
[source-set.${source set fully qualified name}]
condition = ["feature.arch.aarch64", "feature.env.ohos", "feature.os.linux"]
# 2. Any one of multiple features must be satisfied.
[source-set.${source set fully qualified name}]
condition.1 = ["feature.arch.little"]
condiiton.alpha = ["feature.env.ohos"]
condiiton.beta= ["feature.os.linux", "feature.os.posix"]# Both features must be satisfied.
Special Source Sets and Their Configuration
Common Source Set
Only one source set can be selected at the same level to ensure code isolation across platforms. However, some code should always be included. This is called the common source set, whose code is always included in the compilation process, and the condition configuration is not applicable. Example:
[source-set.common]
src-dir = "./common"
[source-set.socketSelection.common]
src-dir = "./socket/common"
[source-set.socketSelection.kqueue]
src-dir = "./socket/kqueue"
condition = [ "feature.os.kqueue" ]
Nested Source Sets
When source sets have nested levels, the directory hierarchy of the code must correspond to the source set hierarchy (i.e., the parent source set path must be a prefix of the child source set path). Additionally, nested source sets do not support multiple paths.
[source-set.common]
src-dir = "./common"
[source-set.socketSelection]
src-dir = "./socketSelection/weird"
[source-set.socketSelection.a]
src-dir = "./socketSelection/weird/a"
[source-set.socketSelection.b]
src-dir = "./socketSelection/weird/b"
Other Source Set
If the configuration file contains only single-level source sets and cjpm cannot match any source set, a compilation error will occur. However, for nested source sets, cjpm implicitly generates a special source set named other, whose path is derived from the parent path. To customize the path, explicitly configure this source set, but its condition cannot be configured. Example:
[source-set.common]
src-dir = "./common"
[source-set.socketSelection]
src-dir = "./socketSelection"
[source-set.socketSelection.weird]
src-dir = "./socketSelection/weird"
condition = [ "feature.os.linux", "feature.os.windows" ] # Practically impossible condition to meet
# This source set will be selected as fallback
[source-set.socketSelection.other]
src-dir = "./socketSelection/other"
If no top-level source set is selected, the following error message will appear:
"No source set specified in ${path/to/cjpm.toml} was selected"
Default Source Set
The default source set represents the types of source sets supported by default in cjpm. If no modifications are needed, they do not need to be specified in cjpm.toml. To modify them, specify and configure the corresponding source set in the configuration file, which will override the default configuration.
For example, setting an empty source-set configuration indicates that the project is a multi-platform project.
[source-set]
In this case, cjpm will implicitly generate the default configuration as follows:
[source-set.common]
src-dir = "./common"
[source-set.windows]
src-dir = "./windows"
condition = ["feature.os.windows"]
[source-set.linux]
src-dir = "./linux"
condition = ["feature.os.linux", "feature.env.gnu"]
[source-set.darwin]
src-dir = "./darwin"
condition = ["feature.os.darwin"]
[source-set.android]
src-dir = "./android"
condition = ["feature.os.linux", "feature.env.android"]
[source-set.hos]
src-dir = "./hos"
condition = ["feature.os.linux", "feature.env.hos"]
[source-set.ohos]
src-dir = "./ohos"
condition = ["feature.os.linux", "feature.env.ohos"]