Rust: from zero to your own organizer Lesson 58 of 60
Building and sharing the program
Lesson 58. Build for your OS, show a version, and hand over a clear archive.
This text was translated with AI.
Revisit settings and README and automatic checks. Distribute only a checked practice project from a separate folder.
Familiar image and recall map
A recipe and baked bread are different things. Source code resembles the recipe; an executable file resembles bread baked for a particular oven. Recall map: check source → build a release → run the file → include README and licence notice → hand over an archive → test after extraction. The analogy has a limit: a file built for one OS usually cannot run directly on another.
cargo build --release builds with release settings. It may take longer than a practice cargo build, but the result is prepared for use. Cargo puts it under target/release: organizer.exe on Windows, organizer on macOS/Linux. version = "0.1.0" in Cargo.toml is the project version. env!("CARGO_PKG_VERSION") is a macro: Cargo inserts that version at compile time. It is embedded in the program and does not change when a runtime environment variable changes. #[test] checks that the version has three parts; three parts alone promise no compatibility. An archive bundles a program, instructions, and required notices into one file.
Run and inspect
The manifest field license = "MIT" declares the terms for this course example code; choose terms deliberately for your own new code. In a separate Cargo project set version = "0.1.0" and put the first Rust block in src/main.rs. Run cargo test --locked, then cargo build --release. Run the built file from target/release; the output below is for this manifest. Rebuild after changing version, or the old executable will still show the old number. Create README.md in the practice folder: state the required OS, launch command, and that organizer data is separate from the executable. The course examples use the MIT section of LICENSE-COURSE; retain its copyright and permission notice when sharing a substantial part of that code.
A manually tested ZIP archive made by one tool lost executable permission after extraction on macOS. On macOS/Linux, tar.gz is convenient because it preserves that permission. If the extracted file cannot run because of permissions, chmod +x organizer in its folder adds execute permission (chmod changes permissions and +x adds execution). Windows handles this differently; test organizer.exe there.
For a repeatable macOS/Linux step, run tar -czf organizer.tar.gz organizer README.md LICENSE in the folder holding those three files, then create an empty extracted subfolder, enter it, and run tar -xzf ../organizer.tar.gz. tar packs or extracts files; -c creates, -x extracts, -z uses gzip compression, and -f gives the archive name. On Windows, make a ZIP in File Explorer and test the extracted .exe.
[package]
name = "organizer"
version = "0.1.0"
edition = "2024"
license = "MIT"
[dependencies]
fn main() {
println!("Version: {}", env!("CARGO_PKG_VERSION"));
}
#[test]
fn package_version_has_three_parts() {
let parts: Vec<&str> = env!("CARGO_PKG_VERSION").split('.').collect();
assert_eq!(parts.len(), 3);
}
Version: 0.1.0
Recall without looking
- Why does editing
Cargo.tomlnot change an executable already built? - Why test an archive after extracting it?
- What else must you give the recipient besides the executable?
Exercise
Required. Add --version handling and make an unknown argument exit with status 2. Build a release, put the executable, README.md, and licence notice in a separate folder, and archive that folder with your OS tools. Extract it into another folder on the same OS and run --version. Predict the output before running.
Answers
Show the answers
Read words after the program name with std::env::args().skip(1). For errors, use eprintln! and std::process::exit(2). Take the course-code permission text from LICENSE-COURSE.
fn version_text() -> String {
format!("Version: {}", env!("CARGO_PKG_VERSION"))
}
fn run(args: &[String]) -> Result<String, String> {
match args {
[] => Ok(version_text()),
[flag] if flag == "--version" => Ok(version_text()),
_ => Err(String::from("Unknown argument")),
}
}
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
match run(&args) {
Ok(message) => println!("{message}"),
Err(message) => {
eprintln!("{message}");
std::process::exit(2);
}
}
}
#[test]
fn prints_version_for_flag() {
assert!(run(&[String::from("--version")]).unwrap().contains("0.1.0"));
}
#[test]
fn rejects_unknown_argument() {
assert!(run(&[String::from("--unknown")]).is_err());
}
Version: 0.1.0
README.md:
# Organizer 0.1.0
Built for the OS on which this archive was made. Extract all files, then run `organizer --version` (Windows: `organizer.exe --version`). This practice program only shows its version and does not yet open a task file. Keep the future organizer task file separate from the program and back it up. On macOS/Linux, if the extracted file lacks execute permission, run `chmod +x organizer` in this folder. On failure, read stderr and never replace data with an empty file.
After checking
Do not promise that a Windows executable works on macOS or Linux. Build a separate file for another OS and check it there; lesson 57 CI checks code but does not automatically hand over a release archive. Keep secrets and personal data out of the practice archive. cargo build documentation.
If you have found a mistake or a typo in this article, tell us about it
Comments (0)
Log in to leave a comment →
No comments yet. Be the first.