Use a (mostly) hermetic Python interpreter

It still requires python to be installed on the host to run the wrapper
scripts, but it's close to being fully hermetic.

This also requires/enables the following changes, unfortunately all at
the same time:
 * Use a downloaded f2py
 * Use a downloaded scipy/numpy/matplotlib
 * Fix a few things that don't run with the python version in their #!
 * Stop using bazel-generated __init__.py files, because those interfere
   with importing matplotlib nicely

Change-Id: Ife280464613d67cece9587b7d947f0b1d5466d7e
diff --git a/tools/bazel.rc b/tools/bazel.rc
index a0dd7a2..1dee9b5 100644
--- a/tools/bazel.rc
+++ b/tools/bazel.rc
@@ -9,6 +9,9 @@
 # Use the malloc we want.
 build --custom_malloc=//tools/cpp:malloc
 
+# Use our hermetic Python runtime.
+build --python_top=//tools/python:runtime
+
 build:asan --copt -fsanitize=address
 build:asan --linkopt -fsanitize=address --linkopt -ldl
 build:asan --platform_suffix=-asan
diff --git a/tools/python/BUILD b/tools/python/BUILD
new file mode 100644
index 0000000..2181b31
--- /dev/null
+++ b/tools/python/BUILD
@@ -0,0 +1,9 @@
+py_runtime(
+    name = "runtime",
+    files = [
+        "runtime_binary.sh",
+        "@python_repo//:all_files",
+    ],
+    interpreter = "runtime_binary.sh",
+    visibility = ["//visibility:public"],
+)
diff --git a/tools/python/runtime_binary.sh b/tools/python/runtime_binary.sh
new file mode 100755
index 0000000..6c96fcb
--- /dev/null
+++ b/tools/python/runtime_binary.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+set -e
+set -u
+set -o pipefail
+
+# We disable writing .pyc files here so that the invocation is more
+# deterministic. If we get a corrupted .pyc file (for some reason) in the
+# .runfiles directory the corresponding Python invocation would crash with an
+# EOFError. You can try this by calling truncate(1) on a .pyc file and running
+# your Python script.
+# In the bazel sandbox none of the .pyc files are preserved anyway.
+# Sandboxing also means that Python's entire standard library got cached which
+# normally doesn't happen. That can lead to higher memory usage during the
+# individual build steps.
+export PYTHONDONTWRITEBYTECODE=1
+
+# Find the path that contains the Python runtime. It's not always obvious. For
+# example in a genrule the Python runtime is in the runfiles folder of the
+# tool, not of the genrule.
+# TODO(philipp): Is there a better way to do this?
+BASE_PATH=""
+for path in ${PYTHONPATH//:/ }; do
+  if [[ "$path" == *.runfiles/python_repo ]]; then
+    BASE_PATH="$path"
+    export LD_LIBRARY_PATH="$path"/lib/x86_64-linux-gnu:"$path"/usr/lib:"$path"/usr/lib/x86_64-linux-gnu
+    break
+  fi
+done
+
+if [[ -z "$BASE_PATH" ]]; then
+  echo "Could not find Python base path." >&2
+  echo "More sophisticated logic may be needed." >&2
+  exit 1
+fi
+
+export LD_LIBRARY_PATH="${BASE_PATH}/usr/lib/lapack:${BASE_PATH}/usr/lib/libblas:${BASE_PATH}/usr/lib/x86_64-linux-gnu"
+
+if head -n 1 "$1" | grep -q python3; then
+  exec "$BASE_PATH"/usr/bin/python3 "$@"
+else
+  exec "$BASE_PATH"/usr/bin/python2 "$@"
+fi
diff --git a/tools/ruby/BUILD b/tools/ruby/BUILD
index 426a9f1..b8b2543 100644
--- a/tools/ruby/BUILD
+++ b/tools/ruby/BUILD
@@ -1,5 +1,5 @@
 py_binary(
-  name = 'standalone_ruby',
-  srcs = ['standalone_ruby.py'],
-  visibility = ['//visibility:public'],
+    name = "standalone_ruby",
+    srcs = ["standalone_ruby.py"],
+    visibility = ["//visibility:public"],
 )