internal: repos to create a toolchain from a locally installed Python (#2000)

This adds the primitives for defining a toolchain based on a locally
installed Python.
Doing this consists of two parts:

* A repo rule to define a Python runtime pointing to a local Python
installation.
* A repo rule to define toolchains for those runtimes.

The runtime repos create platform runtimes, i.e, it sets
py_runtime.interpreter_path.
This means the runtime isn't included in the runfiles.

Note that these repo rules are largely implementation details, and are
definitely not
stable API-wise. Creating public APIs to use them through WORKSPACE or
bzlmod will
be done in a separate change (there's a few design and behavior
questions to discuss).

This is definitely experimental quality. In particular, the code that
tries
to figure out the C headers/libraries is very finicky. I couldn't find
solid docs about
how to do this, and there's a lot of undocumented settings, so what's
there is what
I was able to piece together from my laptop's behavior.

Misc other changes:
* Also fixes a bug if a pyenv-backed interpreter path is used for
precompiling:
pyenv uses `$0` to determine what to re-exec. The
`:current_interpreter_executable`
  target used its own name, which pyenv didn't understand.
* The repo logger now also accepts a string. This should help prevent
accidentally
passing a string causing an error. It's also just a bit more convenient
when
  doing development.
* Repo loggers will automatically include their rule name and repo name.
This
  makes following logging output easier.
* Makes `repo_utils.execute()` report progress.
* Adds `repo_utils.getenv`, `repo_utils.watch`, and
`repo_utils.watch_tree`:
  backwards compatibility functions for their `rctx` equivalents.
* Adds `repo_utils.which_unchecked`: calls `which`, but allows for
failure.
* Adds `repo_utils.get_platforms_os_name()`: Returns the name used in
`@platforms` for
  the OS reported by `rctx`.
* Makes several repo util functions call `watch()` or `getenv()`, if
available. This
  makes repository rules better respect environmental changes.
* Adds more detail to the definition of an in-build vs platform runtime
* Adds a README for the integration tests directory. Setting up and
using one is a bit
  more involved than other tests, so some docs help.
* Allows integration tests to specify bazel versions to use.
diff --git a/python/private/full_version.bzl b/python/private/full_version.bzl
index 68c9694..98eeee5 100644
--- a/python/private/full_version.bzl
+++ b/python/private/full_version.bzl
@@ -40,4 +40,4 @@
             ),
         )
     else:
-        fail("Unknown version format: {}".format(version))
+        fail("Unknown version format: '{}'".format(version))