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

创建与构建项目

本页从一个空目录开始创建并构建 Clue 项目。

创建项目

clue new hello

new 要求目标目录不存在。要在已有目录中初始化项目,可以运行 clue init .--bin 是默认值,也可以用 clue new --lib math 创建库项目。要创建只负责注册子 crate 的虚拟工作区,可以运行 clue new workspace --workspace

这会创建 hello/Clue.tomlhello/.gitignorehello/src/main.ridClue.toml 的初始内容如下:

[package]
name = "hello"
version = "0.1.0"

[[bin]]
name = "hello"
path = "src/main.rid"

[dependencies]

.gitignore 会忽略 /.clue,也就是 Clue 的构建缓存和输出目录。

库项目改为生成 [lib] 目标和 src/lib.rid

[lib]
name = "math"
path = "src/lib.rid"

initnew 都不会覆盖已有的 Clue.toml 或目标入口源码。默认生成的二进制入口为:

fun main() {
}

构建项目

clue build hello

也可以在项目目录内运行:

clue build

清单中的 [[bin]].path[lib].path 指定入口。单个二进制目标时,旧式 [package].entry 的优先级更高;多个 [[bin]] 各自使用 pathclue build 默认构建全部,也可以用 clue build --bin <name> 只构建一个。没有显式目标时,clue build 按包类型寻找入口文件。二进制包依次检查 src/main.ridsrc/lib.rid<package-name>.ridmain.rid;库和过程宏包依次检查 src/lib.rid<package-name>.ridlib.ridsrc/main.rid

旧清单也可以在 [package] 中指定入口文件:

[package]
name = "hello"
entry = "src/bin/hello.rid"
version = "0.1.0"

构建时会按 Rust 风格展开 mod name;,只有被 mod 声明的文件会加入编译:

  • 在当前模块目录下读取 name.ridname/mod.rid
  • name.ridname/mod.rid 同时存在会报错;
  • 进入 name 模块后,子模块会继续从 name/ 目录下寻找。

例如 src/main.rid 写了 mod foo;,会读取 src/foo.ridsrc/foo/mod.rid。如果 src/foo/mod.rid 里再写 mod bar;,会读取 src/foo/bar.ridsrc/foo/bar/mod.rid。仅仅存在 src/foo/mod.rid 但没有 mod foo; 时,这个目录不会被编译。

如果找不到入口文件,Clue 会报错:

missing entry file; expected src/main.rid, src/lib.rid, <package>.rid, main.rid, or lib.rid

使用依赖

Clue 支持 path、git 和 sparse registry 依赖。假设有两个相邻的本地包:

workspace/
  hello/
    Clue.toml
    src/main.rid
  math/
    Clue.toml
    src/lib.rid

hello/Clue.toml 可以写成:

[package]
name = "hello"
version = "0.1.0"

[dependencies]
math = { path = "../math" }

如果依赖包名不能直接当模块名,可以像 Cargo 一样用 package 指向真实包名:

[dependencies]
math = { package = "math-core", path = "../math-core" }

依赖键会作为模块名出现在 hello 包中:

fun main() -> i32 {
    math::one()
}

依赖包会使用自己的入口规则,也可以在依赖包的 [package] 中设置 entry。依赖声明还可以使用 semver、git 的 branch/tag/rev、registry、feature 和 optional 配置:

[dependencies]
math = { path = "../math", version = "^1.0" }
json = "^1.2"
codec = { git = "https://example.com/codec.git", tag = "v1.0.0" }
log = { version = "^1", optional = true, default-features = false }

单包项目会在项目根目录维护 Clue.lock v3;工作区在根目录维护一个统一的锁文件。clue fetch 获取并校验依赖,普通构建复用锁定版本,clue update 才重新选择版本。--locked 拒绝缺失或过期的锁文件,--offline 只使用缓存。

作为依赖加载时,Clue 会优先读取依赖包的 [lib].path;没有 [lib] 目标时,默认先寻找 src/lib.rid。依赖包中要被外部包使用的项需要写成 pub

pub fun one() -> i32 {
    1
}

也就是说,math/src/lib.rid 中的私有函数仍只能被 math 包内部使用,hello 只能通过 math::one() 访问 pub 导出的项。模块、use 和可见性规则见模块、use 与包

使用工作区

工作区根目录的 Clue.toml 只注册子 crate:

[workspace]
crates = ["hello", "math"]

hello/Clue.tomlmath/Clue.toml 仍分别声明自己的 [package]、目标和依赖。根目录执行 clue checkclue build 会按依赖顺序处理所有注册 crate;在子 crate 目录执行时默认只处理当前 crate,--workspace 选择全部,--package <name> 选择一个。工作区中的包和包内二进制分别用 --package <name>--bin <name> 选择。

Clue 会在根目录生成 Clue.lock v3,记录 path 包的相对路径与源码指纹、git revision、registry checksum、依赖关系和启用的 feature。--locked 会拒绝缺失或过期的锁文件。子 crate 不生成锁文件;工作区内部的 path 依赖必须同时出现在 workspace.crates 中。

输出文件

二进制项目会保留生成的 C 源码,并输出本机可执行文件。默认宿主 debug 构建使用兼容旧版本的目录;--release 和交叉目标使用独立目录:

.clue/build/<bin-name>.c
.clue/build/<bin-name>       # Linux/macOS
.clue/build/<bin-name>.exe   # Windows
.clue/build/<target>/release/<bin-name>[.exe]

构建成功时会输出:

clue: built .clue/build/hello.exe

实际路径会随平台变化。源码、Clue.toml、编译器版本和 C 编译器选择都没变化时,再次运行会复用缓存:

clue: fresh .clue/build/hello.exe

库项目默认生成 C、目标文件、.rmeta.rlibcrate-type = ["riddlelib", "staticlib", "cdylib"] 还会生成可复用的静态库和动态库。二进制项目可以直接运行:

clue run hello
clue run hello -- arg1 arg2

设置 CC 时 Clue 会严格使用指定的 C 编译器;否则先尝试目标组件 c-toolchain.toml 中配置的编译器(由 ridup target configure 设置),再探测 clangccgcc(Windows 目标额外尝试 clang-clcl),最后尝试带版本后缀的 GCC/Clang。只有能完成 C11 编译和链接的候选才会被采用。