diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl index 3213e20f04..54b024505b 100644 --- a/rust/private/rustdoc.bzl +++ b/rust/private/rustdoc.bzl @@ -119,15 +119,16 @@ def _zip_action(ctx, input_dir, output_zip): output_zip (File): The location of the output archive containing generated documentation """ args = ctx.actions.args() - - # Create but not compress. - args.add("c", output_zip) + args.add(ctx.executable._zipper) + args.add(output_zip) + args.add(ctx.bin_dir.path) args.add_all([input_dir], expand_directories = True) ctx.actions.run( - executable = ctx.executable._zipper, + executable = ctx.executable._dir_zipper, inputs = [input_dir], outputs = [output_zip], arguments = [args], + tools = [ctx.executable._zipper], ) rust_doc = rule( @@ -159,6 +160,11 @@ rust_doc = rule( doc = "File to add in ``, after content.", allow_single_file = [".html", ".md"], ), + "_dir_zipper": attr.label( + default = Label("//util/dir_zipper"), + cfg = "exec", + executable = True, + ), "_zipper": attr.label( default = Label("@bazel_tools//tools/zip:zipper"), cfg = "exec", diff --git a/util/dir_zipper/BUILD b/util/dir_zipper/BUILD new file mode 100644 index 0000000000..f8d7eb2f64 --- /dev/null +++ b/util/dir_zipper/BUILD @@ -0,0 +1,8 @@ +load("//rust:rust.bzl", "rust_binary") + +rust_binary( + name = "dir_zipper", + srcs = ["dir_zipper.rs"], + edition = "2018", + visibility = ["//visibility:public"], +) diff --git a/util/dir_zipper/dir_zipper.rs b/util/dir_zipper/dir_zipper.rs new file mode 100644 index 0000000000..fbc4938e2a --- /dev/null +++ b/util/dir_zipper/dir_zipper.rs @@ -0,0 +1,77 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::Command; + +const USAGE: &str = r#"usage: dir_zipper [...] + +Creates a zip archive, stripping a directory prefix from each file name. + +Args: + zipper: Path to @bazel_tools//tools/zip:zipper. + output: Path to zip file to create: e.g., "/tmp/out.zip". + root_dir: Directory to strip from each archive name, with no trailing + slash: e.g., "/tmp/myfiles". + files: List of files to include in the archive, all under `root_dir`: + e.g., ["/tmp/myfiles/a", "/tmp/myfiles/b/c"]. + +Example: + dir_zipper \ + bazel-rules_rust/external/bazel_tools/tools/zip/zipper/zipper \ + /tmp/out.zip \ + /tmp/myfiles \ + /tmp/myfiles/a /tmp/myfiles/b/c + +This will create /tmp/out.zip with file entries "a" and "b/c". +"#; + +macro_rules! die { + ($($arg:tt)*) => { + { + eprintln!($($arg)*); + std::process::exit(1); + } + }; +} + +fn main() { + let mut args = std::env::args_os().skip(1); + let (zipper, output, root_dir) = match args.next().zip(args.next()).zip(args.next()) { + Some(((zipper, output), root_dir)) => ( + PathBuf::from(zipper), + PathBuf::from(output), + PathBuf::from(root_dir), + ), + _ => { + die!("{}", USAGE); + } + }; + let files = args.map(PathBuf::from).collect::>(); + let mut comm = Command::new(zipper); + comm.arg("c"); // create, but don't compress + comm.arg(output); + for f in files { + let rel = f.strip_prefix(&root_dir).unwrap_or_else(|_e| { + die!( + "fatal: non-descendant: {} not under {}", + f.display(), + root_dir.display() + ); + }); + let mut spec = OsString::new(); + spec.push(rel); + spec.push("="); + spec.push(f); + comm.arg(spec); + } + let exit_status = comm + .spawn() + .unwrap_or_else(|e| die!("fatal: could not spawn zipper: {}", e)) + .wait() + .unwrap_or_else(|e| die!("fatal: could not wait on zipper: {}", e)); + if !exit_status.success() { + match exit_status.code() { + Some(c) => std::process::exit(c), + None => die!("fatal: zipper terminated by signal"), + } + } +}