xref: /aosp_15_r20/external/bazelbuild-rules_rust/util/dir_zipper/dir_zipper.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1 use std::ffi::OsString;
2 use std::path::PathBuf;
3 use std::process::Command;
4 
5 const USAGE: &str = r#"usage: dir_zipper <zipper> <output> <root-dir> [<file>...]
6 
7 Creates a zip archive, stripping a directory prefix from each file name.
8 
9 Args:
10   zipper: Path to @bazel_tools//tools/zip:zipper.
11   output: Path to zip file to create: e.g., "/tmp/out.zip".
12   root_dir: Directory to strip from each archive name, with no trailing
13     slash: e.g., "/tmp/myfiles".
14   files: List of files to include in the archive, all under `root_dir`:
15     e.g., ["/tmp/myfiles/a", "/tmp/myfiles/b/c"].
16 
17 Example:
18   dir_zipper \
19     bazel-rules_rust/external/bazel_tools/tools/zip/zipper/zipper \
20     /tmp/out.zip \
21     /tmp/myfiles \
22     /tmp/myfiles/a /tmp/myfiles/b/c
23 
24 This will create /tmp/out.zip with file entries "a" and "b/c".
25 "#;
26 
27 macro_rules! die {
28     ($($arg:tt)*) => {
29         {
30             eprintln!($($arg)*);
31             std::process::exit(1);
32         }
33     };
34 }
35 
main()36 fn main() {
37     let mut args = std::env::args_os().skip(1);
38     let (zipper, output, root_dir) = match args.next().zip(args.next()).zip(args.next()) {
39         Some(((zipper, output), root_dir)) => (
40             PathBuf::from(zipper),
41             PathBuf::from(output),
42             PathBuf::from(root_dir),
43         ),
44         _ => {
45             die!("{}", USAGE);
46         }
47     };
48     let files = args.map(PathBuf::from).collect::<Vec<_>>();
49     let mut comm = Command::new(zipper);
50     comm.arg("c"); // create, but don't compress
51     comm.arg(output);
52     for f in files {
53         let rel = f.strip_prefix(&root_dir).unwrap_or_else(|_e| {
54             die!(
55                 "fatal: non-descendant: {} not under {}",
56                 f.display(),
57                 root_dir.display()
58             );
59         });
60         let mut spec = OsString::new();
61         spec.push(rel);
62         spec.push("=");
63         spec.push(f);
64         comm.arg(spec);
65     }
66     let exit_status = comm
67         .spawn()
68         .unwrap_or_else(|e| die!("fatal: could not spawn zipper: {}", e))
69         .wait()
70         .unwrap_or_else(|e| die!("fatal: could not wait on zipper: {}", e));
71     if !exit_status.success() {
72         match exit_status.code() {
73             Some(c) => std::process::exit(c),
74             None => die!("fatal: zipper terminated by signal"),
75         }
76     }
77 }
78