Skip to content

Commit 8f4fedf

Browse files
committed
boulder/analysis: Add compress man handler
compresses man/info pages
1 parent e4d7cda commit 8f4fedf

File tree

6 files changed

+148
-11
lines changed

6 files changed

+148
-11
lines changed

Cargo.lock

Lines changed: 24 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: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
[workspace]
2-
members = [
3-
"boulder",
4-
"moss",
5-
"crates/*",
6-
]
7-
default-members = [
8-
"moss"
9-
]
2+
members = ["boulder", "moss", "crates/*"]
3+
default-members = ["moss"]
104
resolver = "2"
115

126
[workspace.package]
@@ -23,25 +17,47 @@ criterion = { version = "0.5.1", features = ["html_reports"] }
2317
crossterm = "0.27.0"
2418
derive_more = "0.99.18"
2519
dialoguer = "0.11.0"
26-
diesel = { version = "2.2.1", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] }
20+
diesel = { version = "2.2.1", features = [
21+
"sqlite",
22+
"returning_clauses_for_sqlite_3_35",
23+
] }
2724
diesel_migrations = "2.2.0"
2825
dirs = "5.0.1"
2926
elf = "0.7.4"
3027
indicatif = "0.17.8"
3128
itertools = "0.13.0"
3229
fs-err = { version = "2.11.0", features = ["tokio"] }
30+
filetime = "0.2.24"
3331
futures = "0.3.30"
3432
glob = "0.3.1"
3533
hex = "0.4.3"
3634
indextree = "4.6.1"
3735
libsqlite3-sys = { version = "0.28.0", features = ["bundled"] }
3836
log = "0.4.22"
3937
nom = "7.1.3"
40-
nix = { version = "0.27.1", features = ["user", "fs", "sched", "process", "mount", "hostname", "signal", "term"] }
38+
nix = { version = "0.27.1", features = [
39+
"user",
40+
"fs",
41+
"sched",
42+
"process",
43+
"mount",
44+
"hostname",
45+
"signal",
46+
"term",
47+
] }
4148
petgraph = "0.6.5"
4249
rayon = "1.10.0"
4350
regex = "1.10.5"
44-
reqwest = { version = "0.12.5", default-features = false, features = ["brotli", "charset", "deflate", "gzip", "http2", "rustls-tls", "stream", "zstd"] }
51+
reqwest = { version = "0.12.5", default-features = false, features = [
52+
"brotli",
53+
"charset",
54+
"deflate",
55+
"gzip",
56+
"http2",
57+
"rustls-tls",
58+
"stream",
59+
"zstd",
60+
] }
4561
serde = { version = "1.0.204", features = ["derive"] }
4662
serde_json = "1.0.120"
4763
serde_yaml = "0.9.34"

boulder/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ clap.workspace = true
2929
derive_more.workspace = true
3030
dirs.workspace = true
3131
elf.workspace = true
32+
filetime.workspace = true
3233
glob.workspace = true
3334
fs-err.workspace = true
3435
futures.workspace = true
@@ -45,3 +46,4 @@ thiserror.workspace = true
4546
tokio.workspace = true
4647
url.workspace = true
4748
mailparse.workspace = true
49+
zstd.workspace = true

boulder/src/package/analysis.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ impl<'a> Chain<'a> {
3939
Box::new(handler::pkg_config),
4040
Box::new(handler::python),
4141
Box::new(handler::cmake),
42+
Box::new(handler::compressman),
4243
// Catch-all if not excluded
4344
Box::new(handler::include_any),
4445
],

boulder/src/package/analysis/handler.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
use filetime::FileTime;
2+
use std::fs::File;
3+
use std::io::{BufReader, BufWriter, Write};
14
use std::{
25
path::PathBuf,
6+
fs,
7+
os::unix::fs::symlink,
8+
path::{Component, PathBuf},
39
process::{Command, Stdio},
410
};
511

612
use fs_err as fs;
13+
use itertools::Itertools;
714
use moss::{dependency, Dependency, Provider};
815

916
use crate::package::collect::PathInfo;
@@ -180,3 +187,88 @@ pub fn cmake(bucket: &mut BucketMut, info: &mut PathInfo) -> Result<Response, Bo
180187

181188
Ok(Decision::NextHandler.into())
182189
}
190+
191+
pub fn compressman(bucket: &mut BucketMut, info: &mut PathInfo) -> Result<Response, BoxError> {
192+
if !bucket.recipe.parsed.options.compressman {
193+
return Ok(Decision::NextHandler.into());
194+
}
195+
196+
let is_man_file = info.path.components().contains(&Component::Normal("man".as_ref()))
197+
&& info.file_name().ends_with(|c| ('1'..'9').contains(&c));
198+
let is_info_file =
199+
info.path.components().contains(&Component::Normal("info".as_ref())) && info.file_name().ends_with(".info");
200+
201+
if !(is_man_file || is_info_file) {
202+
return Ok(Decision::NextHandler.into());
203+
}
204+
205+
let mut generated_path = PathBuf::new();
206+
207+
let metadata = fs::metadata(&info.path)?;
208+
let atime = metadata.accessed()?;
209+
let mtime = metadata.modified()?;
210+
211+
/* If we have a man/info symlink update the link to the compressed file */
212+
if info.path.is_symlink() {
213+
let new_original = format!("{}.zst", fs::canonicalize(&info.path)?.display());
214+
let new_link = format!("{}.zst", &info.path.display());
215+
216+
/*
217+
* Depending on the order the files get analysed the new compressed file may not yet exist,
218+
* compress it _now_ so the correct metadata src info is returned to the binary writer.
219+
*/
220+
if !std::path::Path::new(&new_original).exists() {
221+
let compressed_file = compress_file_zstd(fs::canonicalize(&info.path)?)?;
222+
let _ = bucket.paths.install().guest.join(compressed_file);
223+
}
224+
225+
symlink(format!("{}.zst", fs::read_link(&info.path)?.display()), &new_link)?;
226+
227+
/* Restore the original {a,m}times for reproducibility */
228+
filetime::set_symlink_file_times(
229+
&new_link,
230+
FileTime::from_system_time(atime),
231+
FileTime::from_system_time(mtime),
232+
)?;
233+
234+
generated_path.push(bucket.paths.install().guest.join(new_link));
235+
return Ok(Decision::ReplaceFile {
236+
newpath: generated_path,
237+
}
238+
.into());
239+
}
240+
241+
let mut compressed_file = PathBuf::from(format!("{}.zst", info.path.display()));
242+
243+
/* We may have already compressed the file if we encountered a symlink to this file first */
244+
if !&compressed_file.exists() {
245+
compressed_file = compress_file_zstd(info.path.clone())?;
246+
}
247+
248+
/* Restore the original {a,m}times for reproducibility */
249+
filetime::set_file_handle_times(
250+
&File::open(&compressed_file)?,
251+
Some(FileTime::from_system_time(atime)),
252+
Some(FileTime::from_system_time(mtime)),
253+
)?;
254+
255+
generated_path.push(bucket.paths.install().guest.join(compressed_file));
256+
257+
pub fn compress_file_zstd(path: PathBuf) -> Result<PathBuf, BoxError> {
258+
let output_path = PathBuf::from(format!("{}.zst", path.display()));
259+
let input = File::create(&output_path)?;
260+
let mut reader = BufReader::new(File::open(&path)?);
261+
let mut writer = BufWriter::new(input);
262+
263+
zstd::stream::copy_encode(&mut reader, &mut writer, 16)?;
264+
265+
writer.flush()?;
266+
267+
Ok(output_path)
268+
}
269+
270+
Ok(Decision::ReplaceFile {
271+
newpath: generated_path,
272+
}
273+
.into())
274+
}

crates/stone_recipe/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ pub struct Options {
9393
pub strip: bool,
9494
#[serde(default, deserialize_with = "stringy_bool")]
9595
pub networking: bool,
96+
#[serde(default, deserialize_with = "stringy_bool")]
97+
pub compressman: bool,
9698
}
9799

98100
#[derive(Debug, Clone, Deserialize)]

0 commit comments

Comments
 (0)