dist: Allow to suppress logs by default.

Add a log argument to copy_to_dist_dir that specifies
the default log level.

- If --log is specified in the command line, it takes the highest priority.
- Otherwise, if log is specified in copy_to_dist_dir, its value is used.
- Otherwise, the script defaults to DEBUG log level.

Test: run with and without --log
Bug: 231368217
Change-Id: I1ba4d475080a8eb64e1ca890e59e9ad2fad14f21
diff --git a/dist/dist.bzl b/dist/dist.bzl
index 19626bc..2ecc8c2 100644
--- a/dist/dist.bzl
+++ b/dist/dist.bzl
@@ -64,6 +64,7 @@
         prefix = None,
         archive_prefix = None,
         dist_dir = None,
+        log = None,
         **kwargs):
     """A dist rule to copy files out of Bazel's output directory into a custom location.
 
@@ -91,6 +92,10 @@
           In particular, if this is a relative path, it is interpreted as a relative path
           under workspace root when the target is executed with `bazel run`.
           See details by running the target with `--help`.
+        log: If specified, `--log <log>` is provided to the script by default. This sets the
+          default log level of the script.
+
+          See `dist.py` for allowed values and the default value.
         kwargs: Additional attributes to the internal rule, e.g.
           [`visibility`](https://docs.bazel.build/versions/main/visibility.html).
 
@@ -106,6 +111,8 @@
         default_args += ["--archive_prefix", archive_prefix]
     if dist_dir != None:
         default_args += ["--dist_dir", dist_dir]
+    if log != None:
+        default_args += ["--log", log]
 
     _generate_dist_manifest(
         name = name + "_dist_manifest",
diff --git a/dist/dist.py b/dist/dist.py
index 39d3c12..c9538c4 100644
--- a/dist/dist.py
+++ b/dist/dist.py
@@ -33,6 +33,7 @@
 
 import argparse
 import glob
+import logging
 import os
 import shutil
 import sys
@@ -45,8 +46,8 @@
     dist_manifests = glob.glob(
         os.path.join(runfiles_directory, pattern))
     if not dist_manifests:
-        print("Warning: could not find a file with pattern " + pattern +
-              " in the runfiles directory: %s" % runfiles_directory)
+        logging.warning("Could not find a file with pattern %s"
+                        " in the runfiles directory: %s", pattern, runfiles_directory)
     files_to_dist = []
     for dist_manifest in dist_manifests:
         with open(dist_manifest, "r") as f:
@@ -55,7 +56,8 @@
 
 
 def copy_files_to_dist_dir(files, archives, dist_dir, flat, prefix,
-    archive_prefix):
+    archive_prefix, **ignored):
+    logging.info("Copying to %s", dist_dir)
 
     for src in files:
         src_relpath = os.path.basename(src) if flat else src
@@ -65,32 +67,40 @@
         dst = os.path.join(dist_dir, src_relpath)
         if os.path.isfile(src):
             dst_dirname = os.path.dirname(dst)
-            print("[dist] Copying file: %s" % dst)
+            logging.debug("Copying file: %s" % dst)
             if not os.path.exists(dst_dirname):
                 os.makedirs(dst_dirname)
 
             shutil.copyfile(src_abspath, dst, follow_symlinks=True)
         elif os.path.isdir(src):
-            print("[dist] Copying dir: %s" % dst)
+            logging.debug("Copying dir: %s" % dst)
             shutil.copytree(src_abspath, dst, copy_function=shutil.copyfile, dirs_exist_ok=True)
 
     for archive in archives:
         try:
             with tarfile.open(archive) as tf:
                 dst_dirname = os.path.join(dist_dir, archive_prefix)
-                print("[dist] Extracting archive: {} -> {}".format(archive,
-                                                                   dst_dirname))
+                logging.debug("Extracting archive: %s -> %s", archive, dst_dirname)
                 tf.extractall(dst_dirname)
         except tarfile.TarError:
             # toybox does not support creating empty tar files, hence the build
             # system may use empty files as empty archives.
             if os.path.getsize(archive) == 0:
-                print("Warning: skipping empty tar file: {}".format(archive))
+                logging.warning("Skipping empty tar file: %s", archive)
                 continue
              # re-raise if we do not know anything about this error
+            logging.exception("Unknown TarError.")
             raise
 
 
+def config_logging(log_level_str):
+    level = getattr(logging, log_level_str.upper(), None)
+    if not isinstance(level, int):
+        sys.stderr.write("ERROR: Invalid --log {}\n".format(log_level_str))
+        sys.exit(1)
+    logging.basicConfig(level=level, format="[dist] %(levelname)s: %(message)s")
+
+
 def main():
     parser = argparse.ArgumentParser(
         description="Dist Bazel output files into a custom directory.")
@@ -110,9 +120,12 @@
         "--archive_prefix", default="",
         help="Path prefix to apply within dist_dir for extracted archives. " +
              "Supported archives: tar.")
+    parser.add_argument("--log", help="Log level (debug, info, warning, error)", default="debug")
 
     args = parser.parse_args(sys.argv[1:])
 
+    config_logging(args.log)
+
     if not os.path.isabs(args.dist_dir):
         # BUILD_WORKSPACE_DIRECTORY is the root of the Bazel workspace containing
         # this binary target.