Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add package-based dependency tracing #7

Merged
merged 20 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/clidef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ pub fn cli(version: &'static str) -> Command {
.action(clap::ArgAction::SetTrue)
.help("Do not remove anything, only display what will be removed")
)
.arg(
Arg::new("autodeps")
.short('a')
.long("autodeps")
.action(clap::ArgAction::SetTrue)
.help(format!("Include graph of package dependencies\n{}", "NOTE: This can increase the size, but might not always be useful".yellow()))
)
.arg(
Arg::new("root")
.short('r')
Expand Down Expand Up @@ -78,6 +85,12 @@ pub fn cli(version: &'static str) -> Command {
.arg(
Arg::new("f_log").long("logs").action(clap::ArgAction::SetTrue).help("Leave any kind of logs")
)
.arg(
Arg::new("f_pic").long("pic").action(clap::ArgAction::SetTrue).help("Leave any graphics (pictures)")
)
.arg(
Arg::new("f_arc").long("arc").action(clap::ArgAction::SetTrue).help("Leave any kind of archives/tarballs")
)

// Other
.next_help_heading("Other")
Expand Down
23 changes: 23 additions & 0 deletions src/filters/defs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Definitions, constants
*/

/// Stub doc files
pub const DOC_STUB_FILES: &[&str] =
&["AUTHORS", "COPYING", "LICENSE", "DEBUG", "DISTRIB", "DOC", "HISTORY", "README", "TERMS", "TODO"];

/// Docfiles
pub const DOC_F_EXT: &[&str] = &[".txt", ".doc", ".rtf", ".md", ".rtx", ".tex", ".xml"];

/// Docfiles portable
pub const DOC_FP_EXT: &[&str] = &[".eps", ".pdf", ".ps"];

/// Headers
pub const H_SRC_F_EXT: &[&str] = &[".h", ".hpp"];

/// Archives
pub const ARC_F_EXT: &[&str] = &[".gz", ".bz2", ".xz", ".zip", ".tar"];

/// Graphic files
pub const IMG_F_EXT: &[&str] =
&[".bmp", ".jpg", ".jpeg", ".png", ".gif", ".xpm", ".xbm", ".tif", ".tiff", ".pbm", ".svg", ".ico"];
1 change: 1 addition & 0 deletions src/filters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod defs;
pub mod dirs;
pub mod intf;
pub mod resources;
Expand Down
10 changes: 4 additions & 6 deletions src/filters/resources.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use super::{defs, intf::DataFilter};
use crate::profile::Profile;
use std::{
collections::HashSet,
path::{Path, PathBuf},
};

use crate::profile::Profile;

use super::intf::DataFilter;

pub struct ResourcesDataFilter {
data: Vec<PathBuf>,
remove_archives: bool,
Expand Down Expand Up @@ -37,7 +35,7 @@ impl ResourcesDataFilter {

let p = p.to_str().unwrap();

for s in [".gz", ".bz2", ".xz", ".zip", ".tar"] {
for s in defs::ARC_F_EXT {
if p.ends_with(s) {
return true;
}
Expand All @@ -53,7 +51,7 @@ impl ResourcesDataFilter {
}

let p = p.to_str().unwrap();
for s in [".bmp", ".jpg", ".jpeg", ".png", ".gif", ".xpm", ".tif", ".tiff", ".pbm", ".svg", ".ico"] {
for s in defs::IMG_F_EXT {
if p.ends_with(s) {
return true;
}
Expand Down
30 changes: 24 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ mod procdata;
mod profile;
mod rootfs;
mod scanner;

use crate::profile::Profile;
use clap::{ArgMatches, Command};
use colored::Colorize;

use crate::profile::Profile;
use std::{
env,
path::{Path, PathBuf},
Expand Down Expand Up @@ -54,7 +52,9 @@ fn get_profile(mut cli: Command, params: &ArgMatches) -> Profile {
.set_doc(f(params, "f_doc"))
.set_i18n(f(params, "f_i18n"))
.set_l10n(f(params, "f_l10n"))
.set_log(f(params, "f_log"));
.set_log(f(params, "f_log"))
.set_arch(f(params, "f_arc"))
.set_img(f(params, "f_pic"));
} else if let Some(profile_path) = profile_path {
log::info!("Getting profile at {profile_path}");
match Profile::new(Path::new(profile_path)) {
Expand All @@ -80,6 +80,12 @@ fn get_profile(mut cli: Command, params: &ArgMatches) -> Profile {
if is_f(params, "f_log") {
profile.set_manpages(f(params, "f_log"));
}
if is_f(params, "f_pic") {
profile.set_img(f(params, "f_pic"));
}
if is_f(params, "f_arc") {
profile.set_arch(f(params, "f_arc"));
}
}
Err(err) => {
log::error!("{}", err);
Expand Down Expand Up @@ -129,12 +135,24 @@ fn main() -> Result<(), std::io::Error> {
log::error!("Mountpoint \"{}\" does not exist or is not accessible", rpth.to_str().unwrap().bright_yellow());
process::exit(exitcode::IOERR);
}
if let Err(err) =
procdata::TintProcessor::new(rpth).set_profile(get_profile(cli, &params)).set_dry_run(params.get_flag("dry-run")).start()

log::info!("Launching scanner and data processor");

if let Err(err) = procdata::TintProcessor::new(rpth)
.set_profile(get_profile(cli, &params))
.set_dry_run(params.get_flag("dry-run"))
.set_autodeps(params.get_flag("autodeps"))
.start()
{
log::error!("{}", err);
process::exit(exitcode::IOERR);
}

if params.get_flag("dry-run") {
log::warn!("This was a dry-run. Changes were not applied.");
} else {
log::info!("Finished. Hopefully it even works :-)");
}

Ok(())
}
41 changes: 22 additions & 19 deletions src/procdata.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
use std::fs::{self, canonicalize, remove_file, DirEntry};
use std::{
collections::HashSet,
io::Error,
os::unix,
path::{Path, PathBuf},
};

use crate::{
filters::{dirs::PathsDataFilter, intf::DataFilter, resources::ResourcesDataFilter, texts::TextDataFilter},
profile::Profile,
rootfs,
scanner::{binlib::ElfScanner, debpkg::DebPackageScanner, general::Scanner},
scanner::{binlib::ElfScanner, debpkg::DebPackageScanner, dlst::ContentFormatter, general::Scanner},
};

use bytesize::ByteSize;
use filesize::PathExt;
use std::fs::{self, canonicalize, remove_file, DirEntry};
use std::{
collections::HashSet,
io::Error,
os::unix,
path::{Path, PathBuf},
};

/// Main processing of profiles or other data
#[derive(Clone)]
pub struct TintProcessor {
profile: Profile,
root: PathBuf,
dry_run: bool,
autodeps: bool,
}

impl TintProcessor {
pub fn new(root: PathBuf) -> Self {
TintProcessor { profile: Profile::default(), root, dry_run: true }
TintProcessor { profile: Profile::default(), root, dry_run: true, autodeps: false }
}

/// Set configuration from a profile
Expand All @@ -35,11 +34,18 @@ impl TintProcessor {
self
}

/// Set dry-run flag (no actual writes on the target image)
pub fn set_dry_run(&mut self, dr: bool) -> &mut Self {
self.dry_run = dr;
self
}

/// Set flag for automatic dependency tracing
pub fn set_autodeps(&mut self, ad: bool) -> &mut Self {
self.autodeps = ad;
self
}

// Chroot to the mount point
fn switch_root(&self) -> Result<(), Error> {
unix::fs::chroot(self.root.to_str().unwrap())?;
Expand Down Expand Up @@ -107,7 +113,7 @@ impl TintProcessor {
for p in paths {
total_size += p.size_on_disk_fast(&p.metadata().unwrap()).unwrap();
total_files += 1;
log::info!(" - {}", p.to_str().unwrap());
log::debug!(" - {}", p.to_str().unwrap());
}

println!("\nTotal files to be removed: {}, disk size freed: {}\n", total_files, ByteSize::b(total_size));
Expand Down Expand Up @@ -143,7 +149,8 @@ impl TintProcessor {
paths.extend(ElfScanner::new().scan(Path::new(target_path).to_owned()));

log::debug!("Find package dependencies for {target_path}");
paths.extend(DebPackageScanner::new().scan(Path::new(target_path).to_owned()));
// XXX: This will re-scan again and again, if target_path belongs to the same package
paths.extend(DebPackageScanner::new(self.autodeps).scan(Path::new(target_path).to_owned()));

// Add the target itself
paths.insert(Path::new(target_path).to_owned());
Expand All @@ -153,7 +160,7 @@ impl TintProcessor {
// and then let TextDataFilter removes what still should be removed.
// The idea is to keep parts only relevant to the runtime.
log::debug!("Filtering packages");
let pscan = DebPackageScanner::new();
let pscan = DebPackageScanner::new(false); // XXX: Maybe --autodeps=LEVEL to optionally include these too?
for p in self.profile.get_packages() {
log::debug!("Getting content of package \"{}\"", p);
paths.extend(pscan.get_package_contents(p.to_string())?);
Expand Down Expand Up @@ -195,11 +202,7 @@ impl TintProcessor {

if self.dry_run {
self.dry_run(p)?;

log::info!("Preserve:");
for x in paths {
log::info!(" + {}", x.to_str().unwrap());
}
ContentFormatter::new(&paths).format();
} else {
self.apply_changes(p)?;
}
Expand Down
38 changes: 31 additions & 7 deletions src/scanner/debpkg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use crate::scanner::general::{Scanner, ScannerCommons};
use crate::scanner::{
general::{Scanner, ScannerCommons},
tracedeb,
traceitf::PkgDepTrace,
};
use colored::Colorize;
use std::{
io::{Error, ErrorKind},
path::PathBuf,
Expand All @@ -8,12 +13,13 @@ use std::{
/// a target belongs to.
pub struct DebPackageScanner {
commons: ScannerCommons,
autodeps: bool,
}

impl DebPackageScanner {
/// Constructor
pub fn new() -> Self {
DebPackageScanner { commons: ScannerCommons::new() }
pub fn new(autodeps: bool) -> Self {
DebPackageScanner { commons: ScannerCommons::new(), autodeps }
}

/// Expands target taking to the account Linux /bin symlinks to /usr/bin etc.
Expand Down Expand Up @@ -84,20 +90,38 @@ impl DebPackageScanner {
impl Scanner for DebPackageScanner {
fn scan(&mut self, pth: PathBuf) -> Vec<PathBuf> {
log::debug!("Scanning package contents for {:?}", pth.to_str());

let mut out: Vec<PathBuf> = vec![];
let pkgname = self.get_package_for(pth.to_str().unwrap().to_string());

if let Ok(Some(pkgname)) = pkgname {
log::debug!("{} corresponds to {}", pth.to_str().unwrap(), pkgname);
match self.get_package_contents(pkgname) {

match self.get_package_contents(pkgname.to_owned()) {
Ok(fp) => {
return fp;
out.extend(fp);
}
Err(err) => {
log::error!("{}", err);
log::error!("Failed getting contents of {}: {}", pkgname, err);
}
}

if self.autodeps {
// Trace dependencies graph for the package
for p in tracedeb::DebPackageTrace::new().trace(pkgname.to_owned()) {
log::info!("Keeping dependency package: {}", p.bright_yellow());
match self.get_package_contents(p.to_owned()) {
Ok(fp) => {
out.extend(fp);
}
Err(err) => {
log::error!("Failed getting contents of {}: {}", p, err);
}
}
}
}
}

vec![]
out
}
}
Loading