Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Conditional Compilation

Developers can achieve conditional compilation through predefined or custom conditions. Currently, Cangjie supports conditional compilation for imports and declarations.

Conditional Compilation for Imports and Declarations

Cangjie supports using the built-in compilation marker @When for conditional compilation. Compilation conditions are enclosed in [], which can contain one or multiple sets of conditions. @When can be applied to import nodes and declaration nodes (except package).

Usage Example

Taking the built-in os compilation condition as an example, its usage is as follows:

@When[os == "Linux"]
class mc{}

main(): Int64 {
    var a = mc()
    return 0
}

In the above code, developers can successfully compile and execute it in Linux systems; in non-Linux systems, they will encounter a compilation error indicating that the mc class definition cannot be found.

Important notes:

  • Cangjie does not support nested conditional compilation. The following syntax is prohibited:

    @When[os == "Windows"]
    @When[os == "Linux"]    // Error, illegal nested when conditional compilation
    import std.ast.*
    @When[os == "Windows"]
    @When[os == "Linux"]    // Error, illegal nested when conditional compilation
    func A(){}
    
  • @When[...] is a built-in compilation marker processed before imports. If code generated by macro expansion contains @When[...], it will result in a compilation error, such as:

    @Derive[ToString]
    @When[os == "Linux"]    // Error, unexpected when conditional compilation directive
    class A {}
    

Built-in Conditional Variables

Cangjie provides the following built-in conditional variables: os, arch, env, backend, cjc_version, debug and test.

os

os represents the target platform’s operating system. It supports == and != operators. Supported operating systems include: Windows, Linux, macOS, iOS.

Usage example:

@When[os == "Linux"]
func foo() {
    print("Linux, ")
}
@When[os == "Windows"]
func foo() {
    print("Windows, ")
}
@When[os != "Windows"]
func fee() {
    println("NOT Windows")
}
@When[os != "Linux"]
func fee() {
    println("NOT Linux")
}
main() {
    foo()
    fee()
}

When compiled and executed in a Windows environment, it will output Windows, NOT Linux; in a Linux environment, it will output Linux, NOT Windows.

arch

arch represents the target platform’s processor architecture. It supports == and != operators.

Supported architectures: x86_64, aarch64, arm.

Usage example:

@When[arch == "aarch64"]
var arch = "aarch64"

@When[arch == "x86_64"]
var arch = "x86_64"

@When[arch == "arm"]
var arch = "arm"

main() {
    println(arch)
}

When compiled and executed on an x86_64 architecture platform, it will output x86_64; on an aarch64 architecture platform, it will output aarch64; on an arm architecture platform, it will output arm.

env

env provides additional information, such as the ABI (Application Binary Interface) of the target platform, to eliminate ambiguities between different target platforms. It supports == and != operators.

Supported environment: ohos,gnu,simulator,android and default(empty string).

Usage example:

@When[env == "ohos"]
var env = "ohos"

@When[env != "ohos"]
var env = "other"

main() {
    println(env)
}

When compiled and executed on the OpenHarmony target platform, you will get information ohos; when compile on other target platform, you will get information other

backend

backend represents the target platform’s backend type, supporting conditional compilation for multiple backends. It supports == and != operators.

Currently supported backends: cjnative.

Usage example:

@When[backend == "cjnative"]
func foo() {
    print("cjnative backend")
}
@When[backend != "cjnative"]
func foo() {
    print("not cjnative backend")
}
main() {
    foo()
}

When compiled and executed with the cjnative backend package, it will output cjnative backend.

cjc_version

cjc_version is a built-in condition that allows developers to select code based on the current Cangjie compiler version. It supports ==, !=, >, <, >=, and <= operators. The format is xx.xx.xx, where each xx supports 1-2 digits. The comparison rule pads each part to 2 digits, e.g., 0.18.8 < 0.18.11, 0.18.8 == 0.18.08.

Usage example:

@When[cjc_version == "0.18.6"]
func foo() {
    println("cjc_version equals 0.18.6")
}
@When[cjc_version != "0.18.6"]
func foo() {
    println("cjc_version is NOT equal to 0.18.6")
}
@When[cjc_version > "0.18.6"]
func fnn() {
    println("cjc_version is greater than 0.18.6")
}
@When[cjc_version <= "0.18.6"]
func fnn() {
    println("cjc_version is less than or equal to 0.18.6")
}
@When[cjc_version < "0.18.6"]
func fee() {
    println("cjc_version is less than 0.18.6")
}
@When[cjc_version >= "0.18.6"]
func fee() {
    println("cjc_version is greater than or equal to 0.18.6")
}
main() {
    foo()
    fnn()
    fee()
}

The output of the above code will vary depending on the cjc version.

debug

debug indicates whether debug mode is enabled (i.e., the -g compilation option is used). It can be used to switch between debug and release builds. It only supports the logical NOT operator (!).

Usage example:

@When[debug]
func foo() {
    println("debug")
}
@When[!debug]
func foo() {
    println("NOT debug")
}
main() {
    foo()
}

When compiled with -g, it will output debug; without -g, it will output NOT debug.

test

test indicates whether the unit test option --test is enabled. It only supports the logical NOT operator (!). It can be used to distinguish test code from regular code.

Usage example:

@When[test]
@Test
class Tests {
    @TestCase
    public func case1(): Unit {
        @Expect("run", foo())
    }
}

func foo() {
    "run"
}

@When[!test]
main () {
    println(foo())
}

When compiled with --test, it will produce test results; without --test, it will compile and run normally, outputting run.

Custom Conditional Variables

Cangjie allows developers to define custom conditional variables and values. Custom variable names must be valid identifiers and cannot conflict with built-in variables. Their values are string literals. Custom conditions support == and != operators. Unlike built-in variables, custom conditions must be defined via the --cfg compilation option or in the cfg.toml configuration file.

Configuring Custom Conditional Variables

There are two ways to configure custom conditional variables: directly via compilation options or through a configuration file.

Developers can use --cfg <value> to pass custom compilation conditions as key-value pairs or specify the search path for the cfg.toml configuration file.

  • Option values must be enclosed in double quotes.

  • If the option value contains =, it will be treated as a key-value pair (if the path contains =, it must be escaped with \). Multiple key-value pairs can be separated by commas ,. For example:

    $ cjc --cfg "feature = lion, platform = dsp" source.cj
    
  • Multiple --cfg options can be used, e.g.:

    $ cjc --cfg "feature = lion" --cfg "platform = dsp" source.cj
    
  • Defining the same variable multiple times is prohibited, e.g.:

    $ cjc --cfg "feature = lion" --cfg "feature = meta" source.cj
    
    $ cjc --cfg "feature = lion, feature = meta" source.cj
    

    Both commands will result in errors.

  • If the option value does not contain = or contains an escaped =, it will be treated as the search path for cfg.toml. For example:

    $ cjc --cfg "./cfg" source.cj
    

    If ./cfg/cfg.toml exists, the compiler will automatically read the custom conditions defined in it. The cfg.toml file should contain key-value pairs, with each pair on a separate line. Keys must be valid Cangjie identifiers, and values must be double-quoted strings (no escape sequences). Full-line and inline comments are supported, e.g.:

    feature = "lion"
    platform = "dsp"
    # Full-line comment
    feature = "meta" # Inline comment
    
  • When multiple --cfg options specify cfg.toml search paths, they are searched in the order provided. If no cfg.toml is found in any path, the compiler will search for cfg.toml in the default path.

  • If any --cfg option directly provides key-value pairs, the cfg.toml configuration will be ignored.

  • If no --cfg option is used, the compiler will search for cfg.toml in the default path (the package directory specified by --package or -p, or the cjc execution directory).

Multi-Conditional Compilation

Cangjie allows developers to combine multiple conditional compilation options freely. Logical operators and parentheses can be used to specify precedence.

Usage example 1:

//source.cj
@When[(test || feature == "lion") && !debug]
func fee() {
    println("feature lion")
}
main() {
    fee()

Compile and run the above code using the following command:

$ cjc --cfg="feature=lion" source.cj -o runner.out

The output will be as follows:

feature lion

Usage example 2:

Cangjie cross-compiled to the target platform aarch64-linux-android31. The conditional variables are set as shown in the code below. If you need to cross-compile to other platforms, please refer to the Target platform and Conditional Compilation Mapping Table .

@When[os == "Linux" && arch == "aarch64" && env == "android"]
func foo() {
    "target aarch64-linux-android31 run"
}

main() {
    println(foo())
}

Appendix

Target platform and Conditional Compilation Mapping Table

The target platforms supported by Cangjie’s cross-compilation are determined by the build-in conditional variables os, arch and env. The mapping between this three variables and the target platforms is shown in the table below:

target platformarchosenv
x86_64-windows-gnu“x86_64”“Windows”“gnu”
x86_64-linux-gnu“x86_64”“Linux”“gnu”
x86_64-apple-darwin“x86_64”“macOS”“”
x86_64-linux-ohos“x86_64”“Linux”“ohos”
x86_64-w64-mingw32“x86_64”“Windows”“gnu”
x86_64-linux-android[26+][android target]“x86_64”“Linux”“android”
aarch64-linux-gnu“aarch64”“Linux”“gnu”
aarch64-linux-android[26+][android target]“aarch64”“Linux”“android”
aarch64-apple-darwin“aarch64”“macOS”“”
aarch64-linux-ohos“aarch64”“Linux”“ohos”
arm64-apple-ios[11+][ios target]“aarch64”“iOS”“”
arm64-apple-ios[11+]-simulator[ios target]“aarch64”“iOS”“simulator”

’[android target] x86_64-linux-android[26+], the number following the android suffix the API Level. If no number is specified, the default API Level is 26; specifying a number(e.g., x86_64-linux-android33)indicates that the Android API Level is 33, and the number of API Level must greater than or equal to 26. [ios target] arm64-apple-ios[11+], the number following the ios suffix the ios version. If no number is specified, the default ios version is 11; specifying a number(e.g., arm64-apple-ios26)indicates that the ios version is 26,and the number of ios version must greater than or equal to 11.