refactor: use logger in repo_utils.execute_* functions (#2082)

This is to unify how it handles printing log messages.

This also updates repo rules using repo_utils to create loggers and set
the `_rule_name` attribute for the logger to use.

* Also removes defunct repo_utils.debug_print
* Also makes some functions private that aren't used elsewhere
diff --git a/python/private/local_runtime_repo.bzl b/python/private/local_runtime_repo.bzl
index a206e6d..4e7edde 100644
--- a/python/private/local_runtime_repo.bzl
+++ b/python/private/local_runtime_repo.bzl
@@ -69,6 +69,7 @@
             rctx.path(rctx.attr._get_local_runtime_info),
         ],
         quiet = True,
+        logger = logger,
     )
     if exec_result.return_code != 0:
         if on_failure == "fail":
diff --git a/python/private/pypi/whl_library.bzl b/python/private/pypi/whl_library.bzl
index 0419926..c4e1f76 100644
--- a/python/private/pypi/whl_library.bzl
+++ b/python/private/pypi/whl_library.bzl
@@ -60,7 +60,7 @@
         "-isysroot {}/SDKs/MacOSX.sdk".format(xcode_root),
     ]
 
-def _get_toolchain_unix_cflags(rctx, python_interpreter):
+def _get_toolchain_unix_cflags(rctx, python_interpreter, logger = None):
     """Gather cflags from a standalone toolchain for unix systems.
 
     Pip won't be able to compile c extensions from sdists with the pre built python distributions from indygreg
@@ -72,7 +72,7 @@
         return []
 
     # Only update the location when using a standalone toolchain.
-    if not is_standalone_interpreter(rctx, python_interpreter):
+    if not is_standalone_interpreter(rctx, python_interpreter, logger = logger):
         return []
 
     stdout = repo_utils.execute_checked_stdout(
@@ -147,12 +147,13 @@
 
     return args
 
-def _create_repository_execution_environment(rctx, python_interpreter):
+def _create_repository_execution_environment(rctx, python_interpreter, logger = None):
     """Create a environment dictionary for processes we spawn with rctx.execute.
 
     Args:
         rctx (repository_ctx): The repository context.
         python_interpreter (path): The resolved python interpreter.
+        logger: Optional logger to use for operations.
     Returns:
         Dictionary of environment variable suitable to pass to rctx.execute.
     """
@@ -160,7 +161,7 @@
     # Gather any available CPPFLAGS values
     cppflags = []
     cppflags.extend(_get_xcode_location_cflags(rctx))
-    cppflags.extend(_get_toolchain_unix_cflags(rctx, python_interpreter))
+    cppflags.extend(_get_toolchain_unix_cflags(rctx, python_interpreter, logger = logger))
 
     env = {
         "PYTHONPATH": pypi_repo_utils.construct_pythonpath(
@@ -173,6 +174,7 @@
     return env
 
 def _whl_library_impl(rctx):
+    logger = repo_utils.logger(rctx)
     python_interpreter = pypi_repo_utils.resolve_python_interpreter(
         rctx,
         python_interpreter = rctx.attr.python_interpreter,
@@ -189,7 +191,7 @@
     extra_pip_args.extend(rctx.attr.extra_pip_args)
 
     # Manually construct the PYTHONPATH since we cannot use the toolchain here
-    environment = _create_repository_execution_environment(rctx, python_interpreter)
+    environment = _create_repository_execution_environment(rctx, python_interpreter, logger = logger)
 
     whl_path = None
     if rctx.attr.whl_file:
@@ -245,6 +247,7 @@
             environment = environment,
             quiet = rctx.attr.quiet,
             timeout = rctx.attr.timeout,
+            logger = logger,
         )
 
         whl_path = rctx.path(json.decode(rctx.read("whl_file.json"))["whl_file"])
@@ -292,6 +295,7 @@
         environment = environment,
         quiet = rctx.attr.quiet,
         timeout = rctx.attr.timeout,
+        logger = logger,
     )
 
     metadata = json.decode(rctx.read("metadata.json"))
@@ -440,6 +444,7 @@
             for repo in all_repo_names
         ],
     ),
+    "_rule_name": attr.string(default = "whl_library"),
 }, **ATTRS)
 whl_library_attrs.update(AUTH_ATTRS)
 
diff --git a/python/private/repo_utils.bzl b/python/private/repo_utils.bzl
index 3c07027..1c50ac6 100644
--- a/python/private/repo_utils.bzl
+++ b/python/private/repo_utils.bzl
@@ -31,17 +31,6 @@
     """
     return _getenv(rctx, REPO_DEBUG_ENV_VAR) == "1"
 
-def _debug_print(rctx, message_cb):
-    """Prints a message if repo debugging is enabled.
-
-    Args:
-        rctx: repository_ctx
-        message_cb: Callable that returns the string to print. Takes
-            no arguments.
-    """
-    if _is_repo_debug_enabled(rctx):
-        print(message_cb())  # buildifier: disable=print
-
 def _logger(ctx, name = None):
     """Creates a logger instance for printing messages.
 
@@ -73,7 +62,7 @@
     elif not name:
         fail("The name has to be specified when using the logger with `module_ctx`")
 
-    def _log(enabled_on_verbosity, level, message_cb_or_str):
+    def _log(enabled_on_verbosity, level, message_cb_or_str, printer = print):
         if verbosity < enabled_on_verbosity:
             return
 
@@ -82,7 +71,8 @@
         else:
             message = message_cb_or_str()
 
-        print("\nrules_python:{} {}:".format(
+        # NOTE: printer may be the `fail` function.
+        printer("\nrules_python:{} {}:".format(
             name,
             level.upper(),
         ), message)  # buildifier: disable=print
@@ -92,6 +82,7 @@
         debug = lambda message_cb: _log(2, "DEBUG", message_cb),
         info = lambda message_cb: _log(1, "INFO", message_cb),
         warn = lambda message_cb: _log(0, "WARNING", message_cb),
+        fail = lambda message_cb: _log(-1, "FAIL", message_cb, fail),
     )
 
 def _execute_internal(
@@ -101,6 +92,7 @@
         fail_on_error = False,
         arguments,
         environment = {},
+        logger = None,
         **kwargs):
     """Execute a subprocess with debugging instrumentation.
 
@@ -114,12 +106,15 @@
         arguments: list of arguments; see rctx.execute#arguments.
         environment: optional dict of the environment to run the command
             in; see rctx.execute#environment.
+        logger: optional `Logger` to use for logging execution details. If
+            not specified, a default will be created.
         **kwargs: additional kwargs to pass onto rctx.execute
 
     Returns:
         exec_result object, see repository_ctx.execute return type.
     """
-    _debug_print(rctx, lambda: (
+    logger = logger or _logger(rctx)
+    logger.debug(lambda: (
         "repo.execute: {op}: start\n" +
         "  command: {cmd}\n" +
         "  working dir: {cwd}\n" +
@@ -137,7 +132,7 @@
     result = rctx.execute(arguments, environment = environment, **kwargs)
 
     if fail_on_error and result.return_code != 0:
-        fail((
+        logger.fail((
             "repo.execute: {op}: end: failure:\n" +
             "  command: {cmd}\n" +
             "  return code: {return_code}\n" +
@@ -155,8 +150,7 @@
             output = _outputs_to_str(result),
         ))
     elif _is_repo_debug_enabled(rctx):
-        # buildifier: disable=print
-        print((
+        logger.debug((
             "repo.execute: {op}: end: {status}\n" +
             "  return code: {return_code}\n" +
             "{output}"
@@ -413,7 +407,6 @@
 
 repo_utils = struct(
     # keep sorted
-    debug_print = _debug_print,
     execute_checked = _execute_checked,
     execute_checked_stdout = _execute_checked_stdout,
     execute_unchecked = _execute_unchecked,
diff --git a/python/private/toolchains_repo.bzl b/python/private/toolchains_repo.bzl
index dd59e47..df16fb8 100644
--- a/python/private/toolchains_repo.bzl
+++ b/python/private/toolchains_repo.bzl
@@ -120,9 +120,10 @@
 )
 
 def _toolchain_aliases_impl(rctx):
-    (os_name, arch) = get_host_os_arch(rctx)
+    logger = repo_utils.logger(rctx)
+    (os_name, arch) = _get_host_os_arch(rctx, logger)
 
-    host_platform = get_host_platform(os_name, arch)
+    host_platform = _get_host_platform(os_name, arch)
 
     is_windows = (os_name == WINDOWS_NAME)
     python3_binary_path = "python.exe" if is_windows else "bin/python3"
@@ -236,14 +237,15 @@
 )
 
 def _host_toolchain_impl(rctx):
+    logger = repo_utils.logger(rctx)
     rctx.file("BUILD.bazel", """\
 # Generated by python/private/toolchains_repo.bzl
 
 exports_files(["python"], visibility = ["//visibility:public"])
 """)
 
-    (os_name, arch) = get_host_os_arch(rctx)
-    host_platform = get_host_platform(os_name, arch)
+    (os_name, arch) = _get_host_os_arch(rctx, logger)
+    host_platform = _get_host_platform(os_name, arch)
     repo = "@@{py_repository}_{host_platform}".format(
         py_repository = rctx.attr.name[:-len("_host")],
         host_platform = host_platform,
@@ -320,6 +322,7 @@
             mandatory = True,
             doc = "The base name for all created repositories, like 'python38'.",
         ),
+        "_rule_name": attr.string(default = "host_toolchain"),
         "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
     },
 )
@@ -384,7 +387,7 @@
 def sanitize_platform_name(platform):
     return platform.replace("-", "_")
 
-def get_host_platform(os_name, arch):
+def _get_host_platform(os_name, arch):
     """Gets the host platform.
 
     Args:
@@ -401,11 +404,13 @@
         fail("No platform declared for host OS {} on arch {}".format(os_name, arch))
     return host_platform
 
-def get_host_os_arch(rctx):
+def _get_host_os_arch(rctx, logger):
     """Infer the host OS name and arch from a repository context.
 
     Args:
         rctx: Bazel's repository_ctx.
+        logger: Logger to use for operations.
+
     Returns:
         A tuple with the host OS name and arch.
     """
@@ -423,6 +428,7 @@
             rctx,
             op = "GetUname",
             arguments = [repo_utils.which_checked(rctx, "uname"), "-m"],
+            logger = logger,
         ).stdout.strip()
 
         # Normalize the os_name.
diff --git a/python/repositories.bzl b/python/repositories.bzl
index d58feef..baa5f5b 100644
--- a/python/repositories.bzl
+++ b/python/repositories.bzl
@@ -76,12 +76,13 @@
 
 STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER"
 
-def is_standalone_interpreter(rctx, python_interpreter_path):
+def is_standalone_interpreter(rctx, python_interpreter_path, *, logger = None):
     """Query a python interpreter target for whether or not it's a rules_rust provided toolchain
 
     Args:
         rctx (repository_ctx): The repository rule's context object.
         python_interpreter_path (path): A path representing the interpreter.
+        logger: Optional logger to use for operations.
 
     Returns:
         bool: Whether or not the target is from a rules_python generated toolchain.
@@ -102,6 +103,7 @@
                 STANDALONE_INTERPRETER_FILENAME,
             ),
         ],
+        logger = logger,
     ).return_code == 0
 
 def _python_repository_impl(rctx):
@@ -110,6 +112,8 @@
     if bool(rctx.attr.url) == bool(rctx.attr.urls):
         fail("Exactly one of (url, urls) must be set.")
 
+    logger = repo_utils.logger(rctx)
+
     platform = rctx.attr.platform
     python_version = rctx.attr.python_version
     python_version_info = python_version.split(".")
@@ -145,6 +149,7 @@
                 timeout = 600,
                 quiet = True,
                 working_directory = working_directory,
+                logger = logger,
             )
             zstd = "{working_directory}/zstd".format(working_directory = working_directory)
             unzstd = "./unzstd"
@@ -160,6 +165,7 @@
                 "--use-compress-program={unzstd}".format(unzstd = unzstd),
                 "--file={}".format(release_filename),
             ],
+            logger = logger,
         )
     else:
         rctx.download_and_extract(
@@ -197,11 +203,13 @@
                 rctx,
                 op = "python_repository.MakeReadOnly",
                 arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
+                logger = logger,
             )
             exec_result = repo_utils.execute_unchecked(
                 rctx,
                 op = "python_repository.TestReadOnly",
                 arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
+                logger = logger,
             )
 
             # The issue with running as root is the installation is no longer
@@ -211,6 +219,7 @@
                     rctx,
                     op = "python_repository.GetUserId",
                     arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
+                    logger = logger,
                 )
                 uid = int(stdout.strip())
                 if uid == 0:
@@ -529,6 +538,7 @@
         "zstd_version": attr.string(
             default = "1.5.2",
         ),
+        "_rule_name": attr.string(default = "python_repository"),
     },
     environ = [REPO_DEBUG_ENV_VAR],
 )