Modernize and relocate iree/runtime Python package to runtime/bindings/python. (#8912)

* Modernize and relocate iree/runtime Python package to iree/runtime/python.

Non-functional changes:

* Moves `bindings/python/iree/runtime` -> `iree/runtime/python/iree/runtime`.
* Fixes dash vs undescore inconsistency in compile install path. Now both use underscores (python_packages/iree_compiler and python_packages/iree_runtime).
* Moves build directory for iree/compiler/python to iree/compiler/python (was outputting to bindings/python). Updates locations that were hard-coded.

Functional changes:

* Removes the old build-dir only setup.py in favor of an iree/runtime/setup.py that works from either the source or build dir.
* Reworks the releases to use the new setup.py as-is vs scripting the build manually.
* iree.runtime.version is now generated in the same way as iree.compiler.version.
* Users can now run iree/runtime/setup.py with pip themselves to generate a wheel (i.e. `pip wheel iree/runtime`).

It is now possible to integrate python package testing into the presubmit and have build jobs that generated Python installable binaries for subsequent steps.

The only file left in bindings/python is build_requirements.txt. It is referred to by some docs and CI jobs so leaving as-is for the moment (will find it a new home in a followup).
diff --git a/.github/workflows/build_package.yml b/.github/workflows/build_package.yml
index 75eaf98..2f4a6bc 100644
--- a/.github/workflows/build_package.yml
+++ b/.github/workflows/build_package.yml
@@ -69,7 +69,7 @@
       # Note that on Linux, we run under docker with an altered path.
       # Note that on Windows, we need to configure the compiler api project to
       # but its CMake build directory on a short path to avoid path overflow.
-      CIBW_ENVIRONMENT_LINUX: "REPO_DIR=/project/main_checkout BINDIST_DIR=/output CMAKE_GENERATOR=Ninja IREE_TARGET_BACKEND_CUDA=ON"
+      CIBW_ENVIRONMENT_LINUX: "REPO_DIR=/project/main_checkout BINDIST_DIR=/output CMAKE_GENERATOR=Ninja IREE_TARGET_BACKEND_CUDA=ON IREE_HAL_DRIVER_CUDA=ON"
       CIBW_ENVIRONMENT_MACOS: "REPO_DIR=${{ github.workspace }}/main_checkout CMAKE_GENERATOR=Ninja"
       CIBW_ENVIRONMENT_WINDOWS: "REPO_DIR='${{ github.workspace }}\\main_checkout' CMAKE_GENERATOR=Ninja IREE_COMPILER_API_CMAKE_BUILD_DIR=D:/b"
 
@@ -167,24 +167,21 @@
         if: "matrix.build_package == 'py-runtime-pkg'"
         shell: bash
         run: |
-          package_dir="./iree-install/python_packages/iree_runtime"
-          export CIBW_BEFORE_BUILD="python ./main_checkout/build_tools/github_actions/build_dist.py py-runtime-pkg"
-          # TODO: cibuildwheel sanity checks this, but our setup.py is the
-          # *output* of the build :( Make source packages.
-          mkdir -p $package_dir && touch $package_dir/setup.py
-          python -m cibuildwheel --output-dir bindist $package_dir
+          export CIBW_BEFORE_BUILD="python -m pip install --upgrade pip>=21.3"
+          python -m cibuildwheel --output-dir bindist ./runtime/
 
       - name: Build runtime wheels (instrumented)
         if: "matrix.build_package == 'instrumented-py-runtime-pkg'"
         shell: bash
         run: |
-          package_dir="./iree-install/python_packages/iree_runtime"
           export CIBW_BEFORE_ALL_LINUX="./main_checkout/build_tools/github_actions/install_tracy_cli_deps_manylinux2014.sh"
-          export CIBW_BEFORE_BUILD="python ./main_checkout/build_tools/github_actions/build_dist.py instrumented-py-runtime-pkg"
-          # TODO: cibuildwheel sanity checks this, but our setup.py is the
-          # *output* of the build :( Make source packages.
-          mkdir -p $package_dir && touch $package_dir/setup.py
-          python -m cibuildwheel --output-dir bindist $package_dir
+          export CIBW_BEFORE_BUILD="python -m pip install --upgrade pip>=21.3"
+          # I really dislike how cibuildwheel and GitHub actions make tunneling
+          # environment variables so obtuse. Burn this all with fire.
+          export CIBW_ENVIRONMENT_LINUX="$CIBW_ENVIRONMENT_LINUX IREE_BUILD_TRACY=ON IREE_ENABLE_RUNTIME_TRACING=ON"
+          export CIBW_ENVIRONMENT_MACOS="$CIBW_ENVIRONMENT_LINUX IREE_BUILD_TRACY=ON IREE_ENABLE_RUNTIME_TRACING=ON"
+          export CIBW_ENVIRONMENT_WINDOWS="$CIBW_ENVIRONMENT_LINUX IREE_BUILD_TRACY=ON IREE_ENABLE_RUNTIME_TRACING=ON"
+          python -m cibuildwheel --output-dir bindist ./runtime/
 
       # Experimental iree.compiler package.
       - name: Build compiler wheels
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff9e985..3d0b65a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -654,12 +654,11 @@
 
 # Order constraint: The python bindings install tools targets from iree/tools
 # and tracy, and must come after it.
-if(${IREE_BUILD_PYTHON_BINDINGS})
-  add_subdirectory(bindings/python)
+if(IREE_BUILD_PYTHON_BINDINGS)
   # Write out a .env file to make IDEs and developers happy.
   # Yes, we are writing this to the source dir. It is only for IDEs and if
   # it gets clobbered, it is fine.
-  set(_pythonpath_env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/bindings/python\n")
+  set(_pythonpath_env "PYTHONPATH=$<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}/iree/compiler/python;${CMAKE_CURRENT_BINARY_DIR}/runtime/bindings/python>\n")
   file(GENERATE OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/.env"
     CONTENT "${_pythonpath_env}"
   )
@@ -699,3 +698,13 @@
 #-------------------------------------------------------------------------------
 
 add_subdirectory(build_tools/benchmarks)
+
+#-------------------------------------------------------------------------------
+# Transitional directories.
+#-------------------------------------------------------------------------------
+
+# The runtime uses tools and targets from elsewhere in the tree in a way
+# that is not supported by CMake, so we include it last (it aggregates
+# many things into top-level APIs). This should resolve once we finish
+# relayering everything.
+add_subdirectory(runtime)
diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt
deleted file mode 100644
index 545a800..0000000
--- a/bindings/python/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2020 The IREE Authors
-#
-# Licensed under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-set(NUMPY_DEPS "")
-
-set(PYBIND_COPTS "-fexceptions")
-set(PYBIND_EXTENSION_COPTS "-fvisibility=hidden")
-
-# Namespace packages.
-add_subdirectory(iree/runtime)
diff --git a/bindings/python/README.md b/bindings/python/README.md
deleted file mode 100644
index 9f633c2..0000000
--- a/bindings/python/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# IREE Python API
-
-Top-level packages:
-
-* `iree.compiler` : Main compiler API.
-* `iree.runtime` : Runtime components for executing binaries.
-* `iree.tools.core` : Core tools for executing the compiler.
-* `iree.tools.tf` : TensorFlow compiler tools (if enabled).
-
-## Installing
-
-First perform a normal CMake build/install with the following options:
-
-* `-DCMAKE_INSTALL_PREFIX=...path to install to...` : Sets up installation
-  prefix.
-* `-DIREE_BUILD_PYTHON_BINDINGS=ON` : Enables Python Bindings
-
-Then from the install directory, run:
-
-```shell
-# Multiple packages will exist under python_packages. Choose the one you want.
-cd python_packages/iree_compiler
-# Install into a local installation or virtualenv.
-python setup.py install
-python -m pip wheel .
-```
-
-## Development mode
-
-For development, just set your `PYTHONPATH` environment variable to the
-`bindings/python` directory in your CMake build dir.
-
-## Run tests
-
-Tests under `bindings/python/tests` can be run directly once installed.
-Additional tests under `integrations/tensorflow/e2e` will be runnable soon.
-
-## Documentation
-
-API reference pages for IREE's runtime and compiler Python APIs are hosted on
-[readthedocs](https://iree-python-api.readthedocs.io/en/latest/).
diff --git a/bindings/python/iree/runtime/setup.py.in b/bindings/python/iree/runtime/setup.py.in
deleted file mode 100644
index 6518fd4..0000000
--- a/bindings/python/iree/runtime/setup.py.in
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2020 The IREE Authors
-#
-# Licensed under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-# Build platform specific wheel files for the iree.runtime package.
-# Built artifacts are per-platform and build out of the build tree.
-
-import os
-from setuptools import setup, find_namespace_packages, Extension
-import sysconfig
-
-with open(os.path.join(os.path.dirname(__file__), "README.md"), "r") as f:
-  README = f.read()
-
-setup(
-    name="iree-runtime@IREE_RELEASE_PACKAGE_SUFFIX@",
-    version="@IREE_RELEASE_VERSION@",
-    author="The IREE Team",
-    author_email="iree-discuss@googlegroups.com",
-    description="IREE Python Runtime Components",
-    long_description=README,
-    long_description_content_type="text/markdown",
-    license="Apache-2.0",
-    classifiers=[
-        "Development Status :: 3 - Alpha",
-        "License :: OSI Approved :: Apache Software License",
-        "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
-        "Programming Language :: Python :: 3.9",
-    ],
-    url="https://github.com/google/iree",
-    python_requires=">=3.7",
-    packages=find_namespace_packages(
-        include=["iree.runtime", "iree.runtime.*"]),
-    ext_modules=[
-        Extension(name="iree.runtime.binding", sources=[]),
-    ],
-    # Matching the native extension as a data file keeps setuptools from
-    # "building" it (i.e. turning it into a static binary).
-    package_data={
-        "": [
-            f"*{sysconfig.get_config_var('EXT_SUFFIX')}",
-            "iree-run-module*",
-            "iree-run-trace*",
-            "iree-benchmark-trace*",
-            "iree-tracy-capture*",
-        ],
-    },
-    entry_points={
-        "console_scripts": [
-            "iree-run-module = iree.runtime.scripts.iree_run_module.__main__:main",
-            "iree-run-trace = iree.runtime.scripts.iree_run_trace.__main__:main",
-            "iree-benchmark-trace = iree.runtime.scripts.iree_benchmark_trace.__main__:main",
-            "iree-tracy-capture = iree.runtime.scripts.iree_tracy_capture.__main__:main",
-        ],
-    },
-    zip_safe=False,
-    install_requires=[
-        "numpy",
-        "PyYAML",
-    ],
-)
diff --git a/bindings/python/iree/runtime/version.py.in b/bindings/python/iree/runtime/version.py.in
deleted file mode 100644
index e660b02..0000000
--- a/bindings/python/iree/runtime/version.py.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2020 The IREE Authors
-#
-# Licensed under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-PACKAGE_SUFFIX = "@IREE_RELEASE_PACKAGE_SUFFIX@"
-VERSION = "@IREE_RELEASE_VERSION@"
-REVISION = "@IREE_RELEASE_REVISION@"
diff --git a/build_tools/cmake/iree_python.cmake b/build_tools/cmake/iree_python.cmake
index 18d66b0..d23c355 100644
--- a/build_tools/cmake/iree_python.cmake
+++ b/build_tools/cmake/iree_python.cmake
@@ -39,8 +39,6 @@
 #     scoped)
 #   - Be installed under python_packages/PACKAGE_NAME
 #   - Have a local path of MODULE_PATH (i.e. namespace package path)
-#   - Process a setup.py.in from the current directory (if NOT AUGMENT_EXISTING_PACKAGE)
-#   - Process a version.py.in from the current directory (if NOT AUGMENT_EXISTING_PACKAGE)
 # Will set parent scope variables:
 #   - PY_INSTALL_COMPONENT: Install component. Echoed back from the argument
 #     for easier addition after this call.
@@ -73,7 +71,7 @@
     "DEPS;ADDL_PACKAGE_FILES;FILES_MATCHING"
     ${ARGN})
   set(_install_component ${ARG_COMPONENT})
-  set(_install_packages_dir "${CMAKE_INSTALL_PREFIX}/python_packages/${ARG_PACKAGE_NAME}")
+  set(_install_packages_dir "python_packages/${ARG_PACKAGE_NAME}")
   set(_install_module_dir "${_install_packages_dir}/${ARG_MODULE_PATH}")
   set(_target_name install-${_install_component})
 
@@ -83,44 +81,6 @@
     set(_files_matching ${ARG_FILES_MATCHING})
   endif()
 
-  if(NOT ARG_AUGMENT_EXISTING_PACKAGE)
-    configure_file(setup.py.in setup.py)
-    install(
-      FILES
-        ${CMAKE_CURRENT_BINARY_DIR}/setup.py
-        ${ARG_ADDL_PACKAGE_FILES}
-      COMPONENT ${_install_component}
-      DESTINATION "${_install_packages_dir}"
-    )
-    configure_file(version.py.in version.py)
-    install(
-      FILES
-        ${CMAKE_CURRENT_BINARY_DIR}/version.py
-      COMPONENT ${_install_component}
-      DESTINATION "${_install_module_dir}"
-    )
-
-    set(_component_option -DCMAKE_INSTALL_COMPONENT="${ARG_COMPONENT}")
-    add_custom_target(${_target_name}
-      COMMAND "${CMAKE_COMMAND}"
-              ${_component_option}
-              -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
-      USES_TERMINAL)
-    add_custom_target(${_target_name}-stripped
-      COMMAND "${CMAKE_COMMAND}"
-              ${_component_option}
-              -DCMAKE_INSTALL_DO_STRIP=1
-              -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
-      USES_TERMINAL)
-  endif()
-
-  # Explicit add dependencies in case if we are just extending a package
-  # vs adding the targets.
-  if(ARG_DEPS)
-    add_dependencies(${_target_name} ${ARG_DEPS})
-    add_dependencies(${_target_name}-stripped ${ARG_DEPS})
-  endif()
-
   install(
     DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
     COMPONENT ${_install_component}
@@ -336,7 +296,7 @@
 
   set_property(TEST ${_NAME_PATH} PROPERTY LABELS "${_RULE_LABELS}")
   set_property(TEST ${_NAME_PATH} PROPERTY ENVIRONMENT
-      "PYTHONPATH=${IREE_BINARY_DIR}/bindings/python:$ENV{PYTHONPATH}"
+      "PYTHONPATH=${IREE_BINARY_DIR}/iree/compiler/python:${IREE_BINARY_DIR}/runtime/bindings/python:$ENV{PYTHONPATH}"
       "TEST_TMPDIR=${IREE_BINARY_DIR}/tmp/${_NAME}_test_tmpdir"
   )
   iree_add_test_environment_properties(${_NAME_PATH})
diff --git a/build_tools/github_actions/README.md b/build_tools/github_actions/README.md
index 3e147f2..c7e9d25 100644
--- a/build_tools/github_actions/README.md
+++ b/build_tools/github_actions/README.md
@@ -2,18 +2,3 @@
 
 ## Debugging releases cookbook
 
-### Build in the same Linux container as the release
-
-```
-docker run --rm -it -v $(pwd):/work stellaraccident/manylinux2014_x86_64-bazel-4.2.2:latest /bin/bash
-
-# From within docker.
-export PATH=/opt/python/cp38-cp38/bin:$PATH
-python -m pip install -r bindings/python/build_requirements.txt
-cd /work
-python ./build_tools/gitub_actions/cmake_ci.py -B../iree-build \
-  -DCMAKE_INSTALL_PREFIX=../iree-install -DCMAKE_BUILD_TYPE=Release \
-  -DIREE_BUILD_SAMPLES=OFF -DIREE_BUILD_PYTHON_BINDINGS=ON \
-  -DIREE_BUILD_TENSORFLOW_COMPILER=OFF -DIREE_BUILD_TFLITE_COMPILER=OFF -DIREE_BUILD_XLA_COMPILER=OFF
-python ./build_tools/github_actions/cmake_ci.py --build ../iree-build
-```
diff --git a/build_tools/github_actions/build_dist.py b/build_tools/github_actions/build_dist.py
index 965779c..31d46d1 100644
--- a/build_tools/github_actions/build_dist.py
+++ b/build_tools/github_actions/build_dist.py
@@ -38,7 +38,6 @@
   source .venv/bin/activate
 
   python ./main_checkout/build_tools/github_actions/build_dist.py main-dist
-  python ./main_checkout/build_tools/github_actions/build_dist.py py-runtime-pkg
   python ./main_checkout/build_tools/github_actions/build_dist.py py-xla-compiler-tools-pkg
   python ./main_checkout/build_tools/github_actions/build_dist.py py-tflite-compiler-tools-pkg
   python ./main_checkout/build_tools/github_actions/build_dist.py py-tf-compiler-tools-pkg
@@ -178,67 +177,6 @@
       tf.add(os.path.join(INSTALL_DIR, entry), arcname=entry, recursive=True)
 
 
-def build_py_runtime_pkg(instrumented: bool = False):
-  """Builds the iree-install/python_packages/iree_runtime package.
-
-  This includes native, python-version dependent code and is designed to
-  be built multiple times.
-
-  Note that an instrumented build may require additional dependencies.
-  See: install_tracy_cli_deps_manylinux2014.sh for how to set up on that
-  container.
-  """
-  install_python_requirements()
-
-  # Clean up install and build trees.
-  shutil.rmtree(INSTALL_DIR, ignore_errors=True)
-  remove_cmake_cache()
-  extra_cmake_flags = []
-
-  # Extra options for instrumentation.
-  if instrumented:
-    print("*** Enabling options for instrumented build ***")
-    extra_cmake_flags.extend([
-        f"-DIREE_ENABLE_RUNTIME_TRACING=ON",
-        f"-DIREE_BUILD_TRACY=ON",
-    ])
-
-  # Enable CUDA if on platforms where we expect to have the deps and produce
-  # such binaries.
-  if platform.system() == "Linux":
-    print("*** Enabling CUDA runtime ***")
-    extra_cmake_flags.extend([
-        "-DIREE_HAL_DRIVER_CUDA=ON",
-    ])
-
-  # CMake configure.
-  print("*** Configuring ***")
-  subprocess.run([
-      sys.executable,
-      CMAKE_CI_SCRIPT,
-      f"-B{BUILD_DIR}",
-      "--log-level=VERBOSE",
-      f"-DCMAKE_INSTALL_PREFIX={INSTALL_DIR}",
-      f"-DCMAKE_BUILD_TYPE=Release",
-      f"-DIREE_BUILD_COMPILER=OFF",
-      f"-DIREE_BUILD_PYTHON_BINDINGS=ON",
-      f"-DIREE_BUILD_SAMPLES=OFF",
-      f"-DIREE_BUILD_TESTS=OFF",
-  ] + extra_cmake_flags,
-                 check=True)
-
-  print("*** Building ***")
-  subprocess.run([
-      sys.executable,
-      CMAKE_CI_SCRIPT,
-      "--build",
-      BUILD_DIR,
-      "--target",
-      "install-IreePythonPackage-runtime-stripped",
-  ],
-                 check=True)
-
-
 def build_py_tf_compiler_tools_pkg():
   """Builds the iree-install/python_packages/iree_tools_tf package."""
   install_python_requirements()
@@ -279,10 +217,6 @@
 command = sys.argv[1]
 if command == "main-dist":
   build_main_dist()
-elif command == "py-runtime-pkg":
-  build_py_runtime_pkg()
-elif command == "instrumented-py-runtime-pkg":
-  build_py_runtime_pkg(instrumented=True)
 elif command == "py-tf-compiler-tools-pkg":
   build_py_tf_compiler_tools_pkg()
 else:
diff --git a/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-swiftshader/build.sh b/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-swiftshader/build.sh
index b6c9bb8..d666553 100755
--- a/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-swiftshader/build.sh
+++ b/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-swiftshader/build.sh
@@ -76,7 +76,7 @@
 
 echo "***** Testing with CTest *****"
 if ! ctest --timeout 900 --output-on-failure \
-   --tests-regex "^integrations/tensorflow/|^bindings/python/" \
+   --tests-regex "^integrations/tensorflow/|^runtime/bindings/python/" \
    --label-exclude "^nokokoro$|^vulkan_uses_vk_khr_shader_float16_int8$"
 then
    tests_passed=false
diff --git a/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-turing/build.sh b/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-turing/build.sh
index 200be0f..6953bbd 100755
--- a/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-turing/build.sh
+++ b/build_tools/kokoro/gcp_ubuntu/cmake-bazel/linux/x86-turing/build.sh
@@ -83,7 +83,7 @@
 # as well.
 echo "***** Testing with CTest *****"
 if ! ctest --timeout 900 --output-on-failure \
-   --tests-regex "^integrations/tensorflow/|^bindings/python/" \
+   --tests-regex "^integrations/tensorflow/|^runtime/bindings/python/" \
    --label-regex "^driver=vulkan$|^driver=cuda$" \
    --label-exclude "^nokokoro$"
 then
diff --git a/iree/compiler/API/python/CMakeLists.txt b/iree/compiler/API/python/CMakeLists.txt
index 1587cc7..932ca06 100644
--- a/iree/compiler/API/python/CMakeLists.txt
+++ b/iree/compiler/API/python/CMakeLists.txt
@@ -10,8 +10,8 @@
 # TODO: Add an upstream cmake param for this vs having a global here.
 add_compile_definitions("MLIR_PYTHON_PACKAGE_PREFIX=iree.compiler.")
 
-set(_PYTHON_BUILD_PREFIX "${IREE_BINARY_DIR}/bindings/python")
-set(_PYTHON_INSTALL_PREFIX "python_packages/iree-compiler")
+set(_PYTHON_BUILD_PREFIX "${IREE_BINARY_DIR}/iree/compiler/python")
+set(_PYTHON_INSTALL_PREFIX "python_packages/iree_compiler")
 
 # HACK: This should not be necessary, but add_mlir_python_extension is
 # accidentally closing over an errant include_directories from up-tree, so
diff --git a/iree/compiler/setup.py b/iree/compiler/setup.py
index 5156477..5fd3cdc 100644
--- a/iree/compiler/setup.py
+++ b/iree/compiler/setup.py
@@ -208,7 +208,7 @@
 
   # Write version.py directly into install dir.
   version_py_file = os.path.join(CMAKE_INSTALL_DIR_ABS, "python_packages",
-                                 "iree-compiler", "iree", "compiler",
+                                 "iree_compiler", "iree", "compiler",
                                  "version.py")
   os.makedirs(os.path.dirname(version_py_file), exist_ok=True)
   with open(version_py_file, "wt") as f:
@@ -231,7 +231,7 @@
     if os.path.exists(target_dir):
       shutil.rmtree(target_dir)
     shutil.copytree(os.path.join(CMAKE_INSTALL_DIR_ABS, "python_packages",
-                                 "iree-compiler"),
+                                 "iree_compiler"),
                     target_dir,
                     symlinks=False)
     print("Target populated.", file=sys.stderr)
@@ -301,7 +301,7 @@
 
 packages = find_namespace_packages(where=os.path.join(CMAKE_INSTALL_DIR_ABS,
                                                       "python_packages",
-                                                      "iree-compiler"),
+                                                      "iree_compiler"),
                                    include=[
                                        "iree.compiler",
                                        "iree.compiler.*",
@@ -339,7 +339,7 @@
     package_dir={
         # Note: Must be relative path, so we line this up with the absolute
         # path built above. Note that this must exist prior to the call.
-        "": f"{CMAKE_INSTALL_DIR_REL}/python_packages/iree-compiler",
+        "": f"{CMAKE_INSTALL_DIR_REL}/python_packages/iree_compiler",
     },
     packages=packages,
     entry_points={
diff --git a/iree/runtime/BUILD b/iree/runtime/BUILD.bazel
similarity index 100%
rename from iree/runtime/BUILD
rename to iree/runtime/BUILD.bazel
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
new file mode 100644
index 0000000..8a3ce7a
--- /dev/null
+++ b/runtime/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright 2022 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+if(IREE_BUILD_PYTHON_BINDINGS)
+  # Copy Python packaging files to the build dir so that we can install from
+  # there.
+  configure_file(pyproject.toml pyproject.toml COPYONLY)
+  configure_file(setup.py setup.py @ONLY)
+
+  add_subdirectory(bindings/python/iree/runtime)
+endif()
diff --git a/runtime/README.md b/runtime/README.md
new file mode 100644
index 0000000..e395753
--- /dev/null
+++ b/runtime/README.md
@@ -0,0 +1,43 @@
+# IREE runtime
+
+Note that this directory is in a transitional state. The C code still lives
+in directories under `iree/` and will be relocated here in the future.
+
+## Language Bindings
+
+### Python
+
+The included `setup.py` file can be used to build Python binaries or directly
+install the IREE runtime API. Do note that the runtime is quite heavy and
+unless you are developing it and on a significant machine, you will want to
+use released binaries.
+
+There are two ways to build/install Python packages:
+
+* Directly from the source tree (this is how official releases are done).
+* From the build directory while developing.
+
+It is recommended to use your favorite method for managing
+[virtual environments](https://docs.python.org/3/library/venv.html) instead
+of modifying the system installation.
+
+Only relatively recent versions of `pip` are supported. Always use the latest
+via `pip install --upgrade pip`.
+
+You can build either from the source or build tree (assumes that CMake has
+been configured and the project built). The latter is typically used by
+project developers who are already setup for development and want to
+incrementally generate Python packages without rebuilding.
+
+To build a wheel that can be installed on the same Python version and OS:
+
+```
+python -m pip wheel runtime/
+```
+
+To directly install:
+
+```
+python -m pip install runtime/
+```
+
diff --git a/bindings/python/iree/runtime/CMakeLists.txt b/runtime/bindings/python/iree/runtime/CMakeLists.txt
similarity index 89%
rename from bindings/python/iree/runtime/CMakeLists.txt
rename to runtime/bindings/python/iree/runtime/CMakeLists.txt
index 95cab2e..e93f41f 100644
--- a/bindings/python/iree/runtime/CMakeLists.txt
+++ b/runtime/bindings/python/iree/runtime/CMakeLists.txt
@@ -4,6 +4,10 @@
 # See https://llvm.org/LICENSE.txt for license information.
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+set(NUMPY_DEPS "")
+set(PYBIND_COPTS "-fexceptions")
+set(PYBIND_EXTENSION_COPTS "-fvisibility=hidden")
+
 set(_python_extra_srcs)
 set(_extra_install_tool_targets)
 set(_tracy_enabled OFF)
@@ -151,7 +155,9 @@
   PACKAGE_NAME iree_runtime
   MODULE_PATH iree/runtime
   DEPS
-    bindings_python_iree_runtime_PyExtRt
+    # TODO: Update CMake target/path mangling rules to make this syntactically
+    # rooted on "iree" in some way.
+    runtime_bindings_python_iree_runtime_PyExtRt
     iree_tools_iree-benchmark-trace
     iree_tools_iree-run-module
     iree_tools_iree-run-trace
@@ -161,7 +167,9 @@
 )
 
 install(
-  TARGETS bindings_python_iree_runtime_PyExtRt
+  # TODO: Update CMake target/path mangling rules to make this syntactically
+  # rooted on "iree" in some way.
+  TARGETS runtime_bindings_python_iree_runtime_PyExtRt
   COMPONENT ${PY_INSTALL_COMPONENT}
   DESTINATION "${PY_INSTALL_MODULE_DIR}"
 )
diff --git a/bindings/python/iree/runtime/README.md b/runtime/bindings/python/iree/runtime/README.md
similarity index 100%
rename from bindings/python/iree/runtime/README.md
rename to runtime/bindings/python/iree/runtime/README.md
diff --git a/bindings/python/iree/runtime/__init__.py b/runtime/bindings/python/iree/runtime/__init__.py
similarity index 100%
rename from bindings/python/iree/runtime/__init__.py
rename to runtime/bindings/python/iree/runtime/__init__.py
diff --git a/bindings/python/iree/runtime/array_interop.py b/runtime/bindings/python/iree/runtime/array_interop.py
similarity index 100%
rename from bindings/python/iree/runtime/array_interop.py
rename to runtime/bindings/python/iree/runtime/array_interop.py
diff --git a/bindings/python/iree/runtime/array_interop_test.py b/runtime/bindings/python/iree/runtime/array_interop_test.py
similarity index 100%
rename from bindings/python/iree/runtime/array_interop_test.py
rename to runtime/bindings/python/iree/runtime/array_interop_test.py
diff --git a/bindings/python/iree/runtime/binding.h b/runtime/bindings/python/iree/runtime/binding.h
similarity index 100%
rename from bindings/python/iree/runtime/binding.h
rename to runtime/bindings/python/iree/runtime/binding.h
diff --git a/bindings/python/iree/runtime/export.def b/runtime/bindings/python/iree/runtime/export.def
similarity index 100%
rename from bindings/python/iree/runtime/export.def
rename to runtime/bindings/python/iree/runtime/export.def
diff --git a/bindings/python/iree/runtime/flags.py b/runtime/bindings/python/iree/runtime/flags.py
similarity index 100%
rename from bindings/python/iree/runtime/flags.py
rename to runtime/bindings/python/iree/runtime/flags.py
diff --git a/bindings/python/iree/runtime/flags_test.py b/runtime/bindings/python/iree/runtime/flags_test.py
similarity index 100%
rename from bindings/python/iree/runtime/flags_test.py
rename to runtime/bindings/python/iree/runtime/flags_test.py
diff --git a/bindings/python/iree/runtime/function.py b/runtime/bindings/python/iree/runtime/function.py
similarity index 100%
rename from bindings/python/iree/runtime/function.py
rename to runtime/bindings/python/iree/runtime/function.py
diff --git a/bindings/python/iree/runtime/function_test.py b/runtime/bindings/python/iree/runtime/function_test.py
similarity index 100%
rename from bindings/python/iree/runtime/function_test.py
rename to runtime/bindings/python/iree/runtime/function_test.py
diff --git a/bindings/python/iree/runtime/hal.cc b/runtime/bindings/python/iree/runtime/hal.cc
similarity index 99%
rename from bindings/python/iree/runtime/hal.cc
rename to runtime/bindings/python/iree/runtime/hal.cc
index 27fc669..1d577a6 100644
--- a/bindings/python/iree/runtime/hal.cc
+++ b/runtime/bindings/python/iree/runtime/hal.cc
@@ -4,7 +4,7 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "bindings/python/iree/runtime/hal.h"
+#include "./hal.h"
 
 #include "iree/base/tracing.h"
 #include "iree/hal/api.h"
diff --git a/bindings/python/iree/runtime/hal.h b/runtime/bindings/python/iree/runtime/hal.h
similarity index 97%
rename from bindings/python/iree/runtime/hal.h
rename to runtime/bindings/python/iree/runtime/hal.h
index 4230260..02201a9 100644
--- a/bindings/python/iree/runtime/hal.h
+++ b/runtime/bindings/python/iree/runtime/hal.h
@@ -9,8 +9,8 @@
 
 #include <vector>
 
-#include "bindings/python/iree/runtime/binding.h"
-#include "bindings/python/iree/runtime/status_utils.h"
+#include "./binding.h"
+#include "./status_utils.h"
 #include "iree/hal/api.h"
 
 namespace iree {
@@ -143,7 +143,6 @@
   }
   ~HalMappedMemory() {
     if (bv_) {
-      iree_hal_buffer_t* buffer = iree_hal_buffer_view_buffer(bv_);
       iree_hal_buffer_unmap_range(&mapped_memory_);
       iree_hal_buffer_view_release(bv_);
     }
diff --git a/bindings/python/iree/runtime/hal_test.py b/runtime/bindings/python/iree/runtime/hal_test.py
similarity index 100%
rename from bindings/python/iree/runtime/hal_test.py
rename to runtime/bindings/python/iree/runtime/hal_test.py
diff --git a/bindings/python/iree/runtime/initialize_module.cc b/runtime/bindings/python/iree/runtime/initialize_module.cc
similarity index 84%
rename from bindings/python/iree/runtime/initialize_module.cc
rename to runtime/bindings/python/iree/runtime/initialize_module.cc
index 6c4487d..211e7dc 100644
--- a/bindings/python/iree/runtime/initialize_module.cc
+++ b/runtime/bindings/python/iree/runtime/initialize_module.cc
@@ -4,11 +4,11 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "bindings/python/iree/runtime/binding.h"
-#include "bindings/python/iree/runtime/hal.h"
-#include "bindings/python/iree/runtime/invoke.h"
-#include "bindings/python/iree/runtime/status_utils.h"
-#include "bindings/python/iree/runtime/vm.h"
+#include "./binding.h"
+#include "./hal.h"
+#include "./invoke.h"
+#include "./status_utils.h"
+#include "./vm.h"
 #include "iree/base/internal/flags.h"
 #include "iree/base/status_cc.h"
 #include "iree/hal/drivers/init.h"
diff --git a/bindings/python/iree/runtime/invoke.cc b/runtime/bindings/python/iree/runtime/invoke.cc
similarity index 99%
rename from bindings/python/iree/runtime/invoke.cc
rename to runtime/bindings/python/iree/runtime/invoke.cc
index 8e8c4a9..b0b9a59 100644
--- a/bindings/python/iree/runtime/invoke.cc
+++ b/runtime/bindings/python/iree/runtime/invoke.cc
@@ -4,10 +4,10 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "bindings/python/iree/runtime/invoke.h"
+#include "./invoke.h"
 
-#include "bindings/python/iree/runtime/hal.h"
-#include "bindings/python/iree/runtime/vm.h"
+#include "./hal.h"
+#include "./vm.h"
 #include "iree/base/api.h"
 #include "iree/base/tracing.h"
 #include "iree/hal/api.h"
diff --git a/bindings/python/iree/runtime/invoke.h b/runtime/bindings/python/iree/runtime/invoke.h
similarity index 90%
rename from bindings/python/iree/runtime/invoke.h
rename to runtime/bindings/python/iree/runtime/invoke.h
index 65e86b7..206524f 100644
--- a/bindings/python/iree/runtime/invoke.h
+++ b/runtime/bindings/python/iree/runtime/invoke.h
@@ -7,7 +7,7 @@
 #ifndef IREE_BINDINGS_PYTHON_IREE_RT_INVOKE_H_
 #define IREE_BINDINGS_PYTHON_IREE_RT_INVOKE_H_
 
-#include "bindings/python/iree/runtime/binding.h"
+#include "./binding.h"
 
 namespace iree {
 namespace python {
diff --git a/bindings/python/iree/runtime/scripts/iree_benchmark_trace/__main__.py b/runtime/bindings/python/iree/runtime/scripts/iree_benchmark_trace/__main__.py
similarity index 100%
rename from bindings/python/iree/runtime/scripts/iree_benchmark_trace/__main__.py
rename to runtime/bindings/python/iree/runtime/scripts/iree_benchmark_trace/__main__.py
diff --git a/bindings/python/iree/runtime/scripts/iree_run_module/__main__.py b/runtime/bindings/python/iree/runtime/scripts/iree_run_module/__main__.py
similarity index 100%
rename from bindings/python/iree/runtime/scripts/iree_run_module/__main__.py
rename to runtime/bindings/python/iree/runtime/scripts/iree_run_module/__main__.py
diff --git a/bindings/python/iree/runtime/scripts/iree_run_trace/__main__.py b/runtime/bindings/python/iree/runtime/scripts/iree_run_trace/__main__.py
similarity index 100%
rename from bindings/python/iree/runtime/scripts/iree_run_trace/__main__.py
rename to runtime/bindings/python/iree/runtime/scripts/iree_run_trace/__main__.py
diff --git a/bindings/python/iree/runtime/scripts/iree_tracy_capture/__main__.py b/runtime/bindings/python/iree/runtime/scripts/iree_tracy_capture/__main__.py
similarity index 100%
rename from bindings/python/iree/runtime/scripts/iree_tracy_capture/__main__.py
rename to runtime/bindings/python/iree/runtime/scripts/iree_tracy_capture/__main__.py
diff --git a/bindings/python/iree/runtime/status_utils.cc b/runtime/bindings/python/iree/runtime/status_utils.cc
similarity index 97%
rename from bindings/python/iree/runtime/status_utils.cc
rename to runtime/bindings/python/iree/runtime/status_utils.cc
index b12ccd2..a05bfd5 100644
--- a/bindings/python/iree/runtime/status_utils.cc
+++ b/runtime/bindings/python/iree/runtime/status_utils.cc
@@ -4,7 +4,7 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "bindings/python/iree/runtime/status_utils.h"
+#include "./status_utils.h"
 
 namespace iree {
 namespace python {
diff --git a/bindings/python/iree/runtime/status_utils.h b/runtime/bindings/python/iree/runtime/status_utils.h
similarity index 100%
rename from bindings/python/iree/runtime/status_utils.h
rename to runtime/bindings/python/iree/runtime/status_utils.h
diff --git a/bindings/python/iree/runtime/system_api.py b/runtime/bindings/python/iree/runtime/system_api.py
similarity index 100%
rename from bindings/python/iree/runtime/system_api.py
rename to runtime/bindings/python/iree/runtime/system_api.py
diff --git a/bindings/python/iree/runtime/system_api_test.py b/runtime/bindings/python/iree/runtime/system_api_test.py
similarity index 100%
rename from bindings/python/iree/runtime/system_api_test.py
rename to runtime/bindings/python/iree/runtime/system_api_test.py
diff --git a/bindings/python/iree/runtime/tracing.py b/runtime/bindings/python/iree/runtime/tracing.py
similarity index 100%
rename from bindings/python/iree/runtime/tracing.py
rename to runtime/bindings/python/iree/runtime/tracing.py
diff --git a/bindings/python/iree/runtime/unix_version.lds b/runtime/bindings/python/iree/runtime/unix_version.lds
similarity index 100%
rename from bindings/python/iree/runtime/unix_version.lds
rename to runtime/bindings/python/iree/runtime/unix_version.lds
diff --git a/bindings/python/iree/runtime/vm.cc b/runtime/bindings/python/iree/runtime/vm.cc
similarity index 99%
rename from bindings/python/iree/runtime/vm.cc
rename to runtime/bindings/python/iree/runtime/vm.cc
index 34cb5aa..53dbbd1 100644
--- a/bindings/python/iree/runtime/vm.cc
+++ b/runtime/bindings/python/iree/runtime/vm.cc
@@ -4,9 +4,9 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "bindings/python/iree/runtime/vm.h"
+#include "./vm.h"
 
-#include "bindings/python/iree/runtime/status_utils.h"
+#include "./status_utils.h"
 #include "iree/base/api.h"
 #include "iree/base/status_cc.h"
 #include "iree/base/tracing.h"
diff --git a/bindings/python/iree/runtime/vm.h b/runtime/bindings/python/iree/runtime/vm.h
similarity index 97%
rename from bindings/python/iree/runtime/vm.h
rename to runtime/bindings/python/iree/runtime/vm.h
index aa6c62a..48fdbab 100644
--- a/bindings/python/iree/runtime/vm.h
+++ b/runtime/bindings/python/iree/runtime/vm.h
@@ -9,8 +9,8 @@
 
 #include <optional>
 
-#include "bindings/python/iree/runtime/binding.h"
-#include "bindings/python/iree/runtime/hal.h"
+#include "./binding.h"
+#include "./hal.h"
 #include "iree/base/api.h"
 #include "iree/vm/api.h"
 #include "iree/vm/bytecode_module.h"
diff --git a/bindings/python/iree/runtime/vm_test.py b/runtime/bindings/python/iree/runtime/vm_test.py
similarity index 100%
rename from bindings/python/iree/runtime/vm_test.py
rename to runtime/bindings/python/iree/runtime/vm_test.py
diff --git a/runtime/pyproject.toml b/runtime/pyproject.toml
new file mode 100644
index 0000000..22b4019
--- /dev/null
+++ b/runtime/pyproject.toml
@@ -0,0 +1,14 @@
+[build-system]
+requires = [
+    "setuptools>=42",
+    "wheel",
+    # There is no fundamental reason to pin this CMake version, beyond
+    # build stability.
+    "cmake==3.22.2",
+    "ninja==1.10.2",
+    "packaging",
+    # Version 2.7.0 excluded: https://github.com/pybind/pybind11/issues/3136
+    "pybind11>=2.6.0,!=2.7.0",
+    "PyYAML",
+]
+build-backend = "setuptools.build_meta"
diff --git a/runtime/setup.py b/runtime/setup.py
new file mode 100644
index 0000000..fa684bc
--- /dev/null
+++ b/runtime/setup.py
@@ -0,0 +1,391 @@
+# Copyright 2022 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# This builds just the runtime API and is relatively quick to build.
+# To install:
+#   pip install .
+# To build a wheel:
+#   pip wheel .
+#
+# It is recommended to build with Ninja and ccache. To do so, set environment
+# variables by prefixing to above invocations:
+#   CMAKE_C_COMPILER_LAUNCHER=ccache CMAKE_CXX_COMPILER_LAUNCHER=ccache
+#
+# On CIs, it is often advantageous to re-use/control the CMake build directory.
+# This can be set with the IREE_RUNTIME_API_CMAKE_BUILD_DIR env var.
+#
+# A custom package suffix can be specified with the environment variable:
+#   IREE_RUNTIME_CUSTOM_PACKAGE_SUFFIX
+#
+# Select CMake options are available from environment variables:
+#   IREE_HAL_DRIVER_CUDA
+#   IREE_HAL_DRIVER_VULKAN
+#   IREE_ENABLE_RUNTIME_TRACING
+#   IREE_BUILD_TRACY
+
+from gettext import install
+import json
+from multiprocessing.spawn import prepare
+import os
+import platform
+import re
+import shutil
+import subprocess
+import sys
+import sysconfig
+
+from distutils.command.build import build as _build
+from setuptools import find_namespace_packages, setup, Extension
+from setuptools.command.build_ext import build_ext as _build_ext
+from setuptools.command.build_py import build_py as _build_py
+
+
+def check_pip_version():
+  from packaging import version
+  # Pip versions < 22.0.3 default to out of tree builds, which is quite
+  # incompatible with what we do (and has other issues). Pip >= 22.0.4
+  # removed this option entirely and are only in-tree builds. Since the
+  # old behavior can silently produce unworking installations, we aggressively
+  # suppress it.
+  try:
+    import pip
+  except ModuleNotFoundError:
+    # If pip not installed, we are obviously not trying to package via pip.
+    pass
+  else:
+    if (version.parse(pip.__version__) < version.parse("21.3")):
+      print("ERROR: pip version >= 21.3 required")
+      print("Upgrade: pip install pip --upgrade")
+      sys.exit(2)
+
+
+check_pip_version()
+
+# This file can be run directly from the source tree or it can be CMake
+# configured so it can run from the build tree with an already existing
+# build tree. We detect the difference based on whether the following
+# are expanded by CMake.
+CONFIGURED_SOURCE_DIR = "@IREE_SOURCE_DIR@"
+CONFIGURED_BINARY_DIR = "@IREE_BINARY_DIR@"
+
+IREE_SOURCE_DIR = None
+IREE_BINARY_DIR = None
+
+# We must do the intermediate installation to a fixed location that agrees
+# between what we pass to setup() and cmake. So hard-code it here.
+# Note that setup() needs a relative path (to the setup.py file).
+SETUPPY_DIR = os.path.realpath(os.path.dirname(__file__))
+CMAKE_INSTALL_DIR_REL = os.path.join("build", "cmake_install")
+CMAKE_INSTALL_DIR_ABS = os.path.join(SETUPPY_DIR, CMAKE_INSTALL_DIR_REL)
+
+IS_CONFIGURED = CONFIGURED_SOURCE_DIR[0] != "@"
+if IS_CONFIGURED:
+  IREE_SOURCE_DIR = CONFIGURED_SOURCE_DIR
+  IREE_BINARY_DIR = CONFIGURED_BINARY_DIR
+  print(
+      f"Running setup.py from build tree: "
+      f"SOURCE_DIR = {IREE_SOURCE_DIR} "
+      f"BINARY_DIR = {IREE_BINARY_DIR}",
+      file=sys.stderr)
+else:
+  IREE_SOURCE_DIR = os.path.join(SETUPPY_DIR, "..")
+  IREE_BINARY_DIR = os.getenv("IREE_RUNTIME_API_CMAKE_BUILD_DIR")
+  if not IREE_BINARY_DIR:
+    # Note that setuptools always builds into a "build" directory that
+    # is a sibling of setup.py, so we just colonize a sub-directory of that
+    # by default.
+    IREE_BINARY_DIR = os.path.join(SETUPPY_DIR, "build", "cmake_build")
+  print(
+      f"Running setup.py from source tree: "
+      f"SOURCE_DIR = {IREE_SOURCE_DIR} "
+      f"BINARY_DIR = {IREE_BINARY_DIR}",
+      file=sys.stderr)
+
+# Setup and get version information.
+VERSION_INFO_FILE = os.path.join(IREE_SOURCE_DIR, "version_info.json")
+
+
+def load_version_info():
+  with open(VERSION_INFO_FILE, "rt") as f:
+    return json.load(f)
+
+
+try:
+  version_info = load_version_info()
+except FileNotFoundError:
+  print("version_info.json not found. Using defaults", file=sys.stderr)
+  version_info = {}
+
+PACKAGE_SUFFIX = version_info.get("package-suffix") or ""
+PACKAGE_VERSION = version_info.get("package-version") or "0.1dev1"
+
+
+def maybe_nuke_cmake_cache():
+  # From run to run under pip, we can end up with different paths to ninja,
+  # which isn't great and will confuse cmake. Detect if the location of
+  # ninja changes and force a cache flush.
+  ninja_path = ""
+  try:
+    import ninja
+  except ModuleNotFoundError:
+    pass
+  else:
+    ninja_path = ninja.__file__
+  expected_stamp_contents = f"{sys.executable}\n{ninja_path}"
+
+  # In order to speed things up on CI and not rebuild everything, we nuke
+  # the CMakeCache.txt file if the path to the Python interpreter changed.
+  # Ideally, CMake would let us reconfigure this dynamically... but it does
+  # not (and gets very confused).
+  PYTHON_STAMP_FILE = os.path.join(IREE_BINARY_DIR, "python_stamp.txt")
+  if os.path.exists(PYTHON_STAMP_FILE):
+    with open(PYTHON_STAMP_FILE, "rt") as f:
+      actual_stamp_contents = f.read()
+      if actual_stamp_contents == expected_stamp_contents:
+        # All good.
+        return
+
+  # Mismatch or not found. Clean it.
+  cmake_cache_file = os.path.join(IREE_BINARY_DIR, "CMakeCache.txt")
+  if os.path.exists(cmake_cache_file):
+    print("Removing CMakeCache.txt because Python version changed",
+          file=sys.stderr)
+    os.remove(cmake_cache_file)
+
+  # And write.
+  with open(PYTHON_STAMP_FILE, "wt") as f:
+    f.write(expected_stamp_contents)
+
+
+def get_env_cmake_option(name: str, default_value: bool = False) -> bool:
+  svalue = os.getenv(name)
+  if not svalue:
+    svalue = "ON" if default_value else "OFF"
+  return f"-D{name}={svalue}"
+
+
+def prepare_installation():
+  subprocess.check_call(["cmake", "--version"])
+  version_py_content = generate_version_py()
+  print(f"Generating version.py:\n{version_py_content}", file=sys.stderr)
+
+  if not IS_CONFIGURED:
+    # Build from source tree.
+    os.makedirs(IREE_BINARY_DIR, exist_ok=True)
+    maybe_nuke_cmake_cache()
+    print(f"CMake build dir: {IREE_BINARY_DIR}", file=sys.stderr)
+    print(f"CMake install dir: {CMAKE_INSTALL_DIR_ABS}", file=sys.stderr)
+    cfg = "Release"
+    cmake_args = [
+        "-GNinja",
+        "--log-level=VERBOSE",
+        "-DIREE_BUILD_PYTHON_BINDINGS=ON",
+        "-DIREE_BUILD_COMPILER=OFF",
+        "-DIREE_BUILD_SAMPLES=OFF",
+        "-DIREE_BUILD_TESTS=OFF",
+        "-DPython3_EXECUTABLE={}".format(sys.executable),
+        "-DCMAKE_BUILD_TYPE={}".format(cfg),
+        get_env_cmake_option("IREE_HAL_DRIVER_CUDA"),
+        get_env_cmake_option("IREE_HAL_DRIVER_VULKAN",
+                             "OFF" if platform.system() == "Darwin" else "ON"),
+        get_env_cmake_option("IREE_ENABLE_RUNTIME_TRACING"),
+        get_env_cmake_option("IREE_BUILD_TRACY"),
+    ]
+
+    # Only do a from-scratch configure if not already configured.
+    cmake_cache_file = os.path.join(IREE_BINARY_DIR, "CMakeCache.txt")
+    if not os.path.exists(cmake_cache_file):
+      print(f"Configuring with: {cmake_args}", file=sys.stderr)
+      subprocess.check_call(["cmake", IREE_SOURCE_DIR] + cmake_args,
+                            cwd=IREE_BINARY_DIR)
+    else:
+      print(f"Not re-configuring (already configured)", file=sys.stderr)
+
+    # Build.
+    subprocess.check_call([
+        "cmake", "--build", ".", "--target",
+        "runtime/bindings/python/iree/runtime/all"
+    ],
+                          cwd=IREE_BINARY_DIR)
+    print("Build complete.", file=sys.stderr)
+
+  # Install the directory we care about.
+  install_subdirectory = os.path.join(IREE_BINARY_DIR, "runtime", "bindings",
+                                      "python", "iree", "runtime")
+  install_args = [
+      "-DCMAKE_INSTALL_DO_STRIP=ON",
+      f"-DCMAKE_INSTALL_PREFIX={CMAKE_INSTALL_DIR_ABS}/",
+      "-P",
+      os.path.join(install_subdirectory, "cmake_install.cmake"),
+  ]
+  print(f"Installing with: {install_args}", file=sys.stderr)
+  subprocess.check_call(["cmake"] + install_args, cwd=install_subdirectory)
+
+  # Write version.py directly into install dir.
+  version_py_file = os.path.join(CMAKE_INSTALL_DIR_ABS, "python_packages",
+                                 "iree_runtime", "iree", "runtime",
+                                 "version.py")
+  os.makedirs(os.path.dirname(version_py_file), exist_ok=True)
+  with open(version_py_file, "wt") as f:
+    f.write(version_py_content)
+
+  print(f"Installation prepared: {CMAKE_INSTALL_DIR_ABS}", file=sys.stderr)
+
+
+class CMakeBuildPy(_build_py):
+
+  def run(self):
+    # It is critical that the target directory contain all built extensions,
+    # or else setuptools will helpfully compile an empty binary for us
+    # (this is the **worst** possible thing it could do). We just copy
+    # everything. What's another hundred megs between friends?
+    target_dir = os.path.abspath(self.build_lib)
+    print(f"Building in target dir: {target_dir}", file=sys.stderr)
+    os.makedirs(target_dir, exist_ok=True)
+    print("Copying install to target.", file=sys.stderr)
+    if os.path.exists(target_dir):
+      shutil.rmtree(target_dir)
+    shutil.copytree(os.path.join(CMAKE_INSTALL_DIR_ABS, "python_packages",
+                                 "iree_runtime"),
+                    target_dir,
+                    symlinks=False)
+    print("Target populated.", file=sys.stderr)
+
+
+class CustomBuild(_build):
+
+  def run(self):
+    self.run_command("build_py")
+    self.run_command("build_ext")
+    self.run_command("build_scripts")
+
+
+class CMakeExtension(Extension):
+
+  def __init__(self, name, sourcedir=""):
+    Extension.__init__(self, name, sources=[])
+    self.sourcedir = os.path.abspath(sourcedir)
+
+
+class NoopBuildExtension(_build_ext):
+
+  def __init__(self, *args, **kwargs):
+    assert False
+
+  def build_extension(self, ext):
+    pass
+
+
+def generate_version_py():
+  return f"""# Auto-generated version info.
+PACKAGE_SUFFIX = "{PACKAGE_SUFFIX}"
+VERSION = "{PACKAGE_VERSION}"
+REVISIONS = {json.dumps(find_git_versions())}
+"""
+
+
+def find_git_versions():
+  revisions = {}
+  try:
+    revisions["IREE"] = subprocess.check_output(
+        ["git", "rev-parse", "HEAD"],
+        cwd=IREE_SOURCE_DIR).decode("utf-8").strip()
+  except subprocess.SubprocessError as e:
+    print(f"ERROR: Could not get IREE revision: {e}", file=sys.stderr)
+  return revisions
+
+
+def find_git_submodule_revision(submodule_path):
+  try:
+    data = subprocess.check_output(["git", "ls-tree", "HEAD", submodule_path],
+                                   cwd=IREE_SOURCE_DIR).decode("utf-8").strip()
+    columns = re.split("\\s+", data)
+    return columns[2]
+  except Exception as e:
+    print(
+        f"ERROR: Could not get submodule revision for {submodule_path}"
+        f" ({e})",
+        file=sys.stderr)
+    return ""
+
+
+prepare_installation()
+
+packages = find_namespace_packages(where=os.path.join(CMAKE_INSTALL_DIR_ABS,
+                                                      "python_packages",
+                                                      "iree_runtime"),
+                                   include=[
+                                       "iree.runtime",
+                                       "iree.runtime.*",
+                                   ])
+print(f"Found runtime packages: {packages}")
+
+with open(
+    os.path.join(IREE_SOURCE_DIR, "runtime", "bindings", "python", "iree",
+                 "runtime", "README.md"), "rt") as f:
+  README = f.read()
+
+custom_package_suffix = os.getenv("IREE_RUNTIME_CUSTOM_PACKAGE_SUFFIX")
+if not custom_package_suffix:
+  custom_package_suffix = ""
+
+setup(
+    name=f"iree-runtime{PACKAGE_SUFFIX}{custom_package_suffix}",
+    version=f"{PACKAGE_VERSION}",
+    author="IREE Authors",
+    author_email="iree-discuss@googlegroups.com",
+    description="IREE Python Runtime Components",
+    long_description=README,
+    long_description_content_type="text/markdown",
+    license="Apache-2.0",
+    classifiers=[
+        "Development Status :: 3 - Alpha",
+        "License :: OSI Approved :: Apache Software License",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+    ],
+    url="https://github.com/google/iree",
+    python_requires=">=3.7",
+    ext_modules=[
+        CMakeExtension("iree.runtime.binding"),
+    ],
+    cmdclass={
+        "build": CustomBuild,
+        "built_ext": NoopBuildExtension,
+        "build_py": CMakeBuildPy,
+    },
+    zip_safe=False,
+    package_dir={
+        # Note: Must be relative path, so we line this up with the absolute
+        # path built above. Note that this must exist prior to the call.
+        "": f"{CMAKE_INSTALL_DIR_REL}/python_packages/iree_runtime",
+    },
+    packages=packages,
+    # Matching the native extension as a data file keeps setuptools from
+    # "building" it (i.e. turning it into a static binary).
+    package_data={
+        "": [
+            f"*{sysconfig.get_config_var('EXT_SUFFIX')}",
+            "iree-run-module*",
+            "iree-run-trace*",
+            "iree-benchmark-trace*",
+            "iree-tracy-capture*",
+        ],
+    },
+    entry_points={
+        "console_scripts": [
+            "iree-run-module = iree.runtime.scripts.iree_run_module.__main__:main",
+            "iree-run-trace = iree.runtime.scripts.iree_run_trace.__main__:main",
+            "iree-benchmark-trace = iree.runtime.scripts.iree_benchmark_trace.__main__:main",
+            "iree-tracy-capture = iree.runtime.scripts.iree_tracy_capture.__main__:main",
+        ],
+    },
+    install_requires=[
+        "numpy",
+        "PyYAML",
+    ],
+)