Skip to content

Commit 79e753d

Browse files
authored
Merge pull request #268 from serpent-os/version
all: Version improvements
2 parents 89f63c7 + c8e6d12 commit 79e753d

File tree

23 files changed

+414
-55
lines changed

23 files changed

+414
-55
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ default-members = [
1010
resolver = "2"
1111

1212
[workspace.package]
13+
version = "0.1.0"
1314
edition = "2021"
15+
rust-version = "1.78"
1416

1517
[workspace.dependencies]
1618
blsforme = { git = "https://github.com/serpent-os/blsforme.git", rev = "4a2171a8c3f9b1e58aad59ddba91181ff7836d16" }

boulder/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
[package]
22
name = "boulder"
3-
version = "0.1.0"
43
edition.workspace = true
4+
version.workspace = true
5+
rust-version.workspace = true
56

67
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
78

89
[dependencies]
910
config = { path = "../crates/config" }
1011
container = { path = "../crates/container" }
1112
moss = { path = "../moss" }
13+
serpent_buildinfo = { path = "../crates/serpent_buildinfo" }
1214
stone = { path = "../crates/stone" }
1315
stone_recipe = { path = "../crates/stone_recipe" }
1416
tui = { path = "../crates/tui" }

boulder/src/cli.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ mod build;
1111
mod chroot;
1212
mod profile;
1313
mod recipe;
14+
mod version;
1415

1516
#[derive(Debug, Parser)]
16-
#[command(version = version())]
1717
pub struct Command {
1818
#[command(flatten)]
1919
pub global: Global,
@@ -23,6 +23,14 @@ pub struct Command {
2323

2424
#[derive(Debug, Args)]
2525
pub struct Global {
26+
#[arg(
27+
short,
28+
long = "verbose",
29+
help = "Prints additional information about what boulder is doing",
30+
default_value = "false",
31+
global = true
32+
)]
33+
pub verbose: bool,
2634
#[arg(long, global = true)]
2735
pub cache_dir: Option<PathBuf>,
2836
#[arg(long, global = true)]
@@ -39,19 +47,28 @@ pub enum Subcommand {
3947
Chroot(chroot::Command),
4048
Profile(profile::Command),
4149
Recipe(recipe::Command),
50+
Version(version::Command),
4251
}
4352

4453
pub fn process() -> Result<(), Error> {
4554
let args = replace_aliases(std::env::args());
4655
let Command { global, subcommand } = Command::parse_from(args);
4756

57+
if global.verbose {
58+
match subcommand {
59+
Subcommand::Version(_) => (),
60+
_ => version::print(),
61+
}
62+
}
63+
4864
let env = Env::new(global.cache_dir, global.config_dir, global.data_dir, global.moss_root)?;
4965

5066
match subcommand {
5167
Subcommand::Build(command) => build::handle(command, env)?,
5268
Subcommand::Chroot(command) => chroot::handle(command, env)?,
5369
Subcommand::Profile(command) => profile::handle(command, env)?,
5470
Subcommand::Recipe(command) => recipe::handle(command, env)?,
71+
Subcommand::Version(command) => version::handle(command),
5572
}
5673

5774
Ok(())
@@ -94,15 +111,3 @@ pub enum Error {
94111
#[error("recipe")]
95112
Recipe(#[from] recipe::Error),
96113
}
97-
98-
fn version() -> String {
99-
use moss::environment;
100-
101-
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
102-
103-
let hash = environment::GIT_HASH
104-
.map(|hash| format!(" ({hash})"))
105-
.unwrap_or_default();
106-
107-
format!("{VERSION}{hash}")
108-
}

boulder/src/cli/version.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-FileCopyrightText: Copyright © 2020-2024 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use clap::Parser;
6+
7+
#[derive(Debug, Parser)]
8+
#[command(about = "Print version info and exit")]
9+
pub struct Command {
10+
#[arg(
11+
long = "full",
12+
help = "Print the full build and version info",
13+
default_value = "false"
14+
)]
15+
full: bool,
16+
}
17+
18+
pub fn handle(command: Command) {
19+
if command.full {
20+
print_full()
21+
} else {
22+
print()
23+
}
24+
}
25+
26+
/// Print program version
27+
pub fn print() {
28+
println!("boulder {}", serpent_buildinfo::get_simple_version());
29+
}
30+
31+
/// Print additional build information
32+
pub fn print_full() {
33+
println!("boulder {}", serpent_buildinfo::get_full_version());
34+
}

crates/config/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[package]
22
name = "config"
3-
version = "0.1.0"
43
edition.workspace = true
4+
version.workspace = true
5+
rust-version.workspace = true
56

67
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
78

crates/container/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[package]
22
name = "container"
3-
version = "0.1.0"
43
edition.workspace = true
4+
version.workspace = true
5+
rust-version.workspace = true
56

67
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
78

crates/dag/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[package]
22
name = "dag"
3-
version = "0.1.0"
43
edition.workspace = true
4+
version.workspace = true
5+
rust-version.workspace = true
56

67
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
78

crates/fnmatch/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[package]
22
name = "fnmatch"
3-
version = "0.1.0"
43
edition.workspace = true
4+
version.workspace = true
5+
rust-version.workspace = true
56

67
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
78

crates/serpent_buildinfo/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "serpent_buildinfo"
3+
edition.workspace = true
4+
version.workspace = true
5+
rust-version.workspace = true
6+
build = "build.rs"
7+
8+
[dependencies]
9+
chrono.workspace = true
10+
11+
[build-dependencies]
12+
chrono.workspace = true
13+
thiserror.workspace = true

crates/serpent_buildinfo/build.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// build.rs
2+
use std::os::unix::ffi::OsStringExt;
3+
4+
use chrono::{DateTime, Utc};
5+
6+
/// Returns value of given environment variable or error if missing.
7+
///
8+
/// This also outputs necessary ‘cargo:rerun-if-env-changed’ tag to make sure
9+
/// build script is rerun if the environment variable changes.
10+
fn env(key: &str) -> Result<std::ffi::OsString, Box<dyn std::error::Error>> {
11+
println!("cargo:rerun-if-env-changed={}", key);
12+
std::env::var_os(key).ok_or_else(|| Box::from(format!("Missing `{}` environmental variable", key)))
13+
}
14+
15+
/// Calls program with given arguments and returns its standard output. If
16+
/// calling the program fails or it exits with non-zero exit status returns an
17+
/// error.
18+
fn command(prog: &str, args: &[&str], cwd: Option<std::path::PathBuf>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
19+
println!("cargo:rerun-if-env-changed=PATH");
20+
let mut cmd = std::process::Command::new(prog);
21+
cmd.args(args);
22+
cmd.stderr(std::process::Stdio::inherit());
23+
if let Some(cwd) = cwd {
24+
cmd.current_dir(cwd);
25+
}
26+
let out = cmd.output()?;
27+
if out.status.success() {
28+
let mut stdout = out.stdout;
29+
if let Some(b'\n') = stdout.last() {
30+
stdout.pop();
31+
if let Some(b'\r') = stdout.last() {
32+
stdout.pop();
33+
}
34+
}
35+
Ok(stdout)
36+
} else if let Some(code) = out.status.code() {
37+
Err(Box::from(format!("{}: terminated with {}", prog, code)))
38+
} else {
39+
Err(Box::from(format!("{}: killed by signal", prog)))
40+
}
41+
}
42+
43+
/// Checks to see if we're building from a git source and if so attempts to gather information about the git status
44+
fn get_git_info() -> Result<(), Box<dyn std::error::Error>> {
45+
let pkg_dir = std::path::PathBuf::from(env("CARGO_MANIFEST_DIR")?);
46+
let git_dir = command("git", &["rev-parse", "--git-dir"], Some(pkg_dir.clone()));
47+
let git_dir = match git_dir {
48+
Ok(git_dir) => {
49+
println!("cargo:rustc-cfg=BUILDINFO_IS_GIT_BUILD");
50+
51+
std::path::PathBuf::from(std::ffi::OsString::from_vec(git_dir))
52+
}
53+
Err(msg) => {
54+
// We're not in a git repo, most likely we're building from a source archive
55+
println!("cargo:warning=unable to determine git version (not in git repository?)");
56+
println!("cargo:warning={}", msg);
57+
58+
// It's unlikely, but possible that someone could run git init. Might as well catch that.
59+
println!("cargo::rerun-if-changed={}/.git", pkg_dir.display());
60+
return Ok(());
61+
}
62+
};
63+
64+
// Make Cargo rerun us if currently checked out commit or the state of the
65+
// working tree changes. We try to accomplish that by looking at a few
66+
// crucial git state files. This probably may result in some false
67+
// negatives but it’s best we’ve got.
68+
for subpath in ["HEAD", "logs/HEAD", "index"] {
69+
let path = git_dir.join(subpath).canonicalize()?;
70+
println!("cargo:rerun-if-changed={}", path.display());
71+
}
72+
73+
// Get the full git hash
74+
let args = &["rev-parse", "--output-object-format=sha1", "HEAD"];
75+
let out = command("git", args, None)?;
76+
match String::from_utf8_lossy(&out) {
77+
std::borrow::Cow::Borrowed(full_hash) => {
78+
println!("cargo:rustc-env=BUILDINFO_GIT_FULL_HASH={}", full_hash.trim());
79+
}
80+
std::borrow::Cow::Owned(full_hash) => return Err(Box::from(format!("git: Invalid output: {}", full_hash))),
81+
}
82+
83+
// Get the short git hash
84+
let args = &["rev-parse", "--output-object-format=sha1", "--short", "HEAD"];
85+
let out = command("git", args, None)?;
86+
match String::from_utf8_lossy(&out) {
87+
std::borrow::Cow::Borrowed(short_hash) => {
88+
println!("cargo:rustc-env=BUILDINFO_GIT_SHORT_HASH={}", short_hash.trim());
89+
}
90+
std::borrow::Cow::Owned(short_hash) => return Err(Box::from(format!("git: Invalid output: {}", short_hash))),
91+
}
92+
93+
// Get whether this is built from a dirty tree
94+
let args = &["status", "--porcelain"];
95+
let out = command("git", args, None)?;
96+
match String::from_utf8_lossy(&out) {
97+
std::borrow::Cow::Borrowed(output) => match output.trim().len() {
98+
0 => {}
99+
_ => println!("cargo:rustc-cfg=BUILDINFO_IS_DIRTY"),
100+
},
101+
std::borrow::Cow::Owned(output) => return Err(Box::from(format!("git: Invalid output: {}", output))),
102+
}
103+
104+
// Get the commit summary
105+
let args = &["show", "--format=\"%s\"", "-s"];
106+
let out = command("git", args, None)?;
107+
match String::from_utf8_lossy(&out) {
108+
std::borrow::Cow::Borrowed(summary) => {
109+
println!("cargo:rustc-env=BUILDINFO_GIT_SUMMARY={}", summary.trim());
110+
}
111+
std::borrow::Cow::Owned(summary) => return Err(Box::from(format!("git: Invalid output: {}", summary))),
112+
}
113+
114+
Ok(())
115+
}
116+
117+
fn get_build_time() -> Result<(), Box<dyn std::error::Error>> {
118+
// Propagate SOURCE_DATE_EPOCH if set
119+
if let Ok(epoch_env) = env("SOURCE_DATE_EPOCH") {
120+
if let Ok(seconds) = epoch_env.to_string_lossy().parse::<i64>() {
121+
if let Some(time) = DateTime::from_timestamp(seconds, 0) {
122+
println!("cargo:rustc-env=BUILDINFO_BUILD_TIME={}", time.timestamp());
123+
return Ok(());
124+
}
125+
}
126+
}
127+
128+
println!("cargo:rustc-env=BUILDINFO_BUILD_TIME={}", Utc::now().timestamp());
129+
Ok(())
130+
}
131+
132+
fn main() -> Result<(), Box<dyn std::error::Error>> {
133+
// This should include all top-level directories that contain source code or otherwise modify the build in meaningful ways
134+
let top_level = std::path::PathBuf::from("../..").canonicalize()?;
135+
println!("cargo::rerun-if-changed={}/boulder", top_level.display());
136+
println!("cargo::rerun-if-changed={}/crates", top_level.display());
137+
println!("cargo::rerun-if-changed={}/moss", top_level.display());
138+
println!("cargo::rerun-if-changed={}/test", top_level.display());
139+
println!("cargo::rerun-if-changed={}/Cargo.toml", top_level.display());
140+
141+
let version = env("CARGO_PKG_VERSION")?;
142+
println!("cargo:rustc-env=BUILDINFO_VERSION={}", version.to_string_lossy());
143+
144+
get_build_time()?;
145+
146+
get_git_info()?;
147+
148+
Ok(())
149+
}

0 commit comments

Comments
 (0)