和大部分语言一样,cargo也可以创建一个工作空间,以便可以包含多个二进制单元和库单元,从而构建较为复杂的工程。
构建这样一个空间空间主要依赖两个手段:Cargo.toml和单元之间的目录结构
从效果上看,rust的Cargo在工作空间上的管理和maven大体相似,但是还不如maven那么的人性化。
一、如何编写顶级Cargo.toml
我们先看看新版本的工作空间的规定:https://doc.rust-lang.org/cargo/reference/workspaces.html
翻译一下。
工作空间的关键点
- 规定成员允许执行的命令
- 所有成员共享一个Cargo.lock,看图1可以明白
- 所有成员共享一个输出目录,看图1可以明白
- 所有成员可以共享包元数据,这个通过定义workspace.package实现
- patch,replace,profile.*只能在顶级toml(工作空间)toml中定义,成员的无效
虚拟工作空间
如果你的工作空间配置(toml)不包含package部分,那么者就是一个虚拟的工作空间。这种情况下,所有的成员的目录都是空间的子目录。
反之,就不是一个虚拟工作空间,也就是我们如果在顶级包中包含了根包(root package),那么就不是一个虚拟工作空间。
作为一个非虚拟空间,也就意味着可以在工作空间配置中定义package有关的一些配置,具体略。
到底要不要虚拟工作空间,纯看个人喜好。
个人倾向于构建一个虚拟的工作空间,因为这个和maven工程更像。
不能做的事情
如果你定义了一个虚拟的工作空间,那么就不能在工作空间配置文件中包含常规单元包所具有的一些配置,例如package,dependencies之类的。
否则编译通过不过
二、单元Cargo.toml
在一个工作空间中,可以有一个二进制包,若干个库单元包。
如果你执行cargo run,那么cargo会自动找到二进制包并运行。
二进制的配置和单独的一个二进制配置并没有什么区别,还有更多,主要是两点:
- 共享工作空间的包配置(package),这个通过 xxx.workspace=true实现,这个xxx可以是name之外的一些包属性
- 共享依赖,这也是工作空间的一个极其重要的目的,避免引入不同的外部单元包,导致版本冲突。这个通过dependencies中xxx.workspace=true实现
其它还包括受限的命令、编译工具(lint)、外部工具元数据。
库单元配置同二进制单元配置,具体略
三、其它
工作空间所涉及的,不止本文提到的一些内容,完整的请参考 https://doc.rust-lang.org/cargo/reference/workspaces.html
四、例子
本文模仿了有关例子,并结合https://doc.rust-lang.org/cargo/reference/workspaces.html的内容,实现了一个简单的工作空间。
4.1、概述
整个空间包含一个二进制库wsmain,以及两个库单元:teacher,student
整个目录结构如下:
图1_示例工作空间wsexample
4.2、配置及其步骤
共有四个配置文件,一个是工作空间的,三个是单元包的。
- 工作空间的toml必须手动添加,换言之工作空间只能手动创建
- 单元包则通过cargo new xxx 添加 ,如果在vscode中,会自动修改工作空间的Cargo.toml文件
单元包在new的时候,可以根据需要添加 --lib还是--bin。但是记住一个工作空间中只能有一个单元包是二进制的(可执行的)。
例如 cargo new wsmain --bin,这样会创建一个二进制单元包wsmain
不幸的是,如果要移除其中一个单元包,好像没有什么高效的方式,只能先修改工作空间的toml,然后再手动删除单元包的目录和文件!
好消息是,这个操作也不算复杂。rust没有直接提供这个,估计是为了安全考虑!
重点看三个
1.wsexample(工作空间配置)- [workspace]
- members = [
- "student",
- "teacher", "wsmain",
- ]
- resolver = "2"
- [workspace.package]
- version = "0.1.0"
- edition = "2021"
- [workspace.dependencies]
- rand = "0.9.0-beta.1"
复制代码
这个配置为所有的成员限定了包的发行版本和版本,并指定了共享的依赖rand。
2.wsmain(二进制单元包)- [package]
- name = "wsmain"
- version.workspace=true
- edition.workspace=true
- [dependencies]
- student={path="../student"}
- teacher={path="../teacher"}
复制代码 这个配置指定了wsmain依赖的两个库包,并且包部分共享了工作的包配置(version,edition)
对工作空间内其它库单元依赖的定义是让我失望的地方,不够优雅,个人建议这样:
member.student=true
teacher.sutdent=true
3.student(其中一个库单元)- [package]
- name = "student"
- version.workspace=true
- edition.workspace=true
- [dependencies]
- rand.workspace=true
复制代码
这个配置指定了student共享了工作空间的包配置,并指定了共享工作空间的依赖rand
4.3、代码
二进制单元main.rs代码- use teacher::*;
- use student::*;
- fn main() {
- let lml=studentinfo{
- name:String::from("lml"),
- age:18,
- gender:String::from("女"),
- no:String::from("12101")
- };
- print_student(&lml);
- lml.learn();
- lml.sleep();
- let lu=teacherinfo{
- name:String::from("lu"),
- age:46,
- gender:String::from("男"),
- position:String::from("教研组长"),
- };
- lu.teach_student(&lml);
- print_teacher(&lu);
- }
复制代码 其余两个库单元代码略。
4.4、运行
在一个虚拟工作空间内,无需指定包名,即可运行,例如
cargo run
如果硬要指定也可以,通过参数-p来指定包名,或者--package,例如:
cargo run -p wsmain
结果同cargo run 一样,具体略。
五、其它工具
如果习惯手动管理工作空间问题也不大,但是又那么一点小小的不方便。
是否有工具? 肯定的
5.1、cargo workspace
https://github.com/pksunkara/cargo-workspaces
安装
cargo install cargo-workspaces
可以使用cargo workspaces 或者cargo ws使用。
a.创建工作空间
cargo workspaces create --bin ai 创建一个有二进制包的工作空间,名字是ai
cargo workspaces create --lib txt 创建一个只有库包的工作空间 ,名字是txt
b.其它命令
List -- 列出成员
Changed -- 列出有变更的成员
Exec --在每一个单元包内运行特定命令
cargo ws exec dir -- 列出每个单元包下的文件
Publish --发布
Rename --重命名
这种功能,估计各种ide会很快支持
Plan --计划 ,实际是根据发布顺序列出单元包
Config --配置,不太清楚做什么,没有试过.
c.使用场景(收集自网络,没有体会过)
- 大型项目:管理包含数十个包的 monorepo。
- 库维护者:统一发布多个关联包。
- CI/CD 流水线:自动化测试、构建和部署。
- 依赖清理:优化工作区依赖树。
暂时没有什么体会!
这个东西的主要优点是免费,其次如果你喜欢命令行也可以用。 不想安装庞大的ide的情况下,也算是一个不错的选择。
随着ide/插件的发展,这个工具可能会失去不少关注。
六、小结
- 运用cargo也可以构建一个工作空间,以便包含一个二进制单元和多个库单元
- 在效果上,cargo的构建过程和效果类似maven,但某些方面不如maven来得优雅
- 本文所提到的知识,已经足以解决工程常见问题:模块化、共享(配置和依赖)
- 更多高级特性,包括编译,发布等等需要阅读更多相关资料
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |