3. 打包并发布 Rust 项目
经过,我们通过单元测试和黑盒测试后,我们确认,我们的项目已经功能完备了。是骡子是马拉出来遛遛现在是打包和发布的时候了!
下面我们看看发布Rust
的几种方式。
最快:cargo publish
使用cargo
发布应用程序是最简单的方法。我们还记得我们如何将外部依赖项添加到项目中吗?cargo
会从其默认的包管理器crates.io下载它们。借助cargo publish
,我们也可以将crate
发布到crates.io
。这适用于所有crate
,包括具有二进制目标的crate
。
将crate
发布到crates.io
相当简单:
- 如果尚未创建
crates.io
帐户,需要创建一个帐户。目前,可以通过在GitHub
上授权来完成
- 本地计算机上,登录
cargo
登录,为此,我们需要到crates.io
帐户页面,创建一个新令牌,然后运行cargo login <your-new-token>
- 确认
Cargo.toml
信息,确保我们已添加了必要的元数据
[package] name = "f789" version = "0.1.0" authors = ["Your Name <your@email.com>"] license = "MIT OR Apache-2.0" description = "文件搜索工具" readme = "README.md" homepage = "https://github.com/you/f789" repository = "https://github.com/you/f789" keywords = ["cli", "search"] categories = ["command-line-utilities"]
- 使用
cargo publish
进行发布
- 发布成功后,就可以在
crates.io
中查看
- 如果你是首次在
crates.io
发布,你需要验证一下邮箱
如果想了解可以在
cargo
的发布指南中了解更多信息。
如何从crates.io安装二进制文件
我们已经了解了如何将crate
发布到crates.io
,我们可能想知道如何安装它。与库不同,cargo
会在运行cargo build
(或类似的命令)时为我们下载和编译库,我们需要明确告诉它要安装二进制文件。
使用cargo install <crate-name>
可以实现这一点。默认情况下,它会下载crate
,编译其中包含的所有二进制目标(以release
模式进行,所以可能需要一些时间),并将它们复制到~/.cargo/bin/
目录中。
还可以从git
存储库安装crate
,仅安装crate
的特定二进制文件,并指定替代目录以进行安装。
何时使用它
cargo install
是一种安装二进制crate的简单方法。对于Rust开发人员来说非常方便,但有一些重要的缺点:由于它总是从头开始编译我们的源代码,因此使用我们的工具的用户需要在其计算机上安装Rust
、cargo
和项目所需的所有其他系统依赖项。编译大型Rust
代码库可能也需要一些时间。
使用cargo install f789
按照
安装成功,并默认存储到/Users/xxx/.cargo/bin
中
我们现在可以随意打开一个命令行,并且按照我们之前代码逻辑,f789 front text.txt
就可以查看运行结果了。
大家可以忽略上面截图中
git
部分的。我为了省事,直接在源代码的目录中,进行了上述的操作。其实上述操作可以在任何终端中运行。
分发二进制文件
Rust
是一种编译为本机代码的语言,并默认情况下静态链接所有依赖项。当我们在包含名为f789
的二进制文件的项目上运行cargo build
时,我们将得到一个名为f789
的二进制文件。
- 使用
cargo build
,它将位于target/debug/f789
, - 当我们运行
cargo build --release
时,它将位于target/release/f789
。
这意味着,我们可以将这个文件发送给与我们运行相同操作系统的人,他们就可以运行它。
它解决了cargo install
的两个缺点:用户的计算机上不需要安装Rust
,并且不是需要一分钟才能编译,他们可以立即运行二进制文件。
因此,正如我们所看到的,cargo build
已经为我们构建了二进制文件。唯一的问题是,默认情况下,这些二进制文件不能保证在所有有趣的平台上运行。如果我们在Windows
计算机上运行cargo build
,我们不会得到默认情况下在Mac
上运行的二进制文件。
在 CI 上构建二进制版本
如果我们的工具是开源的并托管在GitHub
上,那么设置免费的CI
(持续集成)服务(如Travis CI)非常容易。这基本上是在虚拟机中每次我们推送更改到我们的存储库时运行设置命令。这些命令是什么以及它们运行在哪种类型的机器上是可配置的。
我们还可以使用此功能构建二进制文件并将其上传到GitHub
!
- 首先,我们运行
cargo build --release
并将二进制文件上传到某个位置 - 其次,我们仍然需要确保我们构建的二进制文件与尽可能多的系统兼容。
- 例如,在
Linux
上,我们可以编译而不是为当前系统编译,而是为x86_64-unknown-linux-musl
目标编译,以避免依赖默认系统库。 - 在
macOS
上,我们可以将MACOSX_DEPLOYMENT_TARGET
设置为10.7
,只依赖于版本10.7
及更早版本中存在的系统功能。
另一种方法是使用包含构建二进制文件所需工具的预构建(Docker
)映像。这允许我们轻松地针对更多的异构平台进行定位。trust项目包含可以包含在我们的项目中的脚本以及设置的说明。它还包括使用AppVeyor
的Windows
支持。
如果我们只想在本地设置并在自己的计算机上生成发布文件,请仍然查看trust。它在内部使用cross,它的工作方式类似于cargo
,但将命令转发到Docker
容器内部的cargo
进程。这些映像的定义也可在cross
的存储库中找到。
何时使用它
一般来说,拥有二进制发布版本是一个好主意,几乎没有任何不利因素。它不能解决用户必须手动安装和更新工具的问题,但他们可以快速获取最新的发布版本,而无需安装Rust
。
将应用程序放入包存储库
迄今为止,我们看到的两种方法都不是我们通常在计算机上安装软件的方式。特别是大多数操作系统上的全局软件包管理器,我们可以使用这些管理器来安装命令行工具。对用户来说:如果他们可以以与安装其他工具相同的方式安装程序,那么就无需考虑如何安装我们的程序。这些软件包管理器还允许用户在新版本可用时更新其程序。
难点在于,支持不同的系统意味着我们必须查看这些不同的系统如何工作。对于某些系统,只需向存储库添加一个文件(例如,为macOS
的brew
添加一个Formula
文件),但对于其他系统,我们通常需要自己发送补丁并将我们的工具添加到它们的存储库中。有一些有用的工具,如cargo-bundle、cargo-deb和cargo-aur,但描述它们的工作原理以及如何正确为这些不同的系统打包我们的工具超出了本章的范围。
代码展示
src/main.rs
use anyhow::{Context, Result}; use clap::Parser; use std::fs::File; use std::io::{self, BufRead}; use std::path::PathBuf; /// 在文件中搜索模式并显示包含它的行。 #[derive(Parser)] struct Cli { /// 要查找的模式 pattern: String, /// 要读取的文件的路径 path: PathBuf, } fn main() -> Result<()> { let args = Cli::parse(); // 打开文件并创建一个 BufReader 来逐行读取 let file = File::open(&args.path).with_context(|| format!("无法打开文件 {:?}", &args.path))?; let reader = io::BufReader::new(file); let stdout = io::stdout(); let stdout_lock = stdout.lock(); let handle = io::BufWriter::new(stdout_lock); let content = reader .lines() .collect::<io::Result<Vec<String>>>()? .join("\n"); f789::find_matches(&content, &args.pattern, handle)?; Ok(()) }
src/lib.rs
use anyhow::Result; use indicatif::ProgressBar; use std::io::Write; pub fn find_matches(content: &str, pattern: &str, mut writer: impl Write) -> Result<()> { let pb = ProgressBar::new(100); for line in content.lines() { do_hard_work(); pb.println(format!("[+] 查找到了 #{:?}项", line)); pb.inc(1); if line.contains(pattern) { writeln!(writer, "{}", line)?; } } Ok(()) } fn do_hard_work() { std::thread::sleep(std::time::Duration::from_millis(250)); }
后记
分享是一种态度。
参考资料:
全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。