<Hermetic> Implement CPython2 Launcher in C++.

The launcher is actually a wrapper which will statically link Python
interpreter within the .par file. It is used to bootstrap embedded
interpreter.

The next step is to change/integrate with Soong to make this hermetic .par
generation process more automatic.

Bug: b/62380596

Test: The launcher has been tested using real files:
zip -r hermetic.zip entry_point.txt Stdlib/ runfiles/
cat launcher | cat - hermetic.zip > executable && chmod u+x executable

Change-Id: I293cae2fe74d46766044f3e3c4b654a54d319b67
diff --git a/Launcher/launcher_internal.cpp b/Launcher/launcher_internal.cpp
new file mode 100644
index 0000000..be4d48e
--- /dev/null
+++ b/Launcher/launcher_internal.cpp
@@ -0,0 +1,216 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "launcher_internal.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" {
+// Cpython built-in C functions.
+/*
+   read_directory(archive) -> files dict (new reference)
+
+   Given a path to a Zip archive, build a dict, mapping file names
+   (local to the archive, using SEP as a separator) to toc entries.
+*/
+PyObject *read_directory(const char *archive);
+
+/* Given a path to a Zip file and a toc_entry, return the (uncompressed)
+   data as a new reference. */
+PyObject *get_data(const char *archive, PyObject *toc_entry);
+}
+
+namespace android {
+namespace cpython2 {
+namespace python_launcher {
+namespace internal{
+
+int RunModule(const char *module, int set_argv0) {
+  PyObject *runpy, *runmodule, *runargs, *result;
+  runpy = PyImport_ImportModule("runpy");
+  if (runpy == NULL) {
+    fprintf(stderr, "Could not import runpy module\n");
+    return -1;
+  }
+  runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
+  if (runmodule == NULL) {
+    fprintf(stderr, "Could not access runpy._run_module_as_main\n");
+    Py_DECREF(runpy);
+    return -1;
+  }
+  runargs = Py_BuildValue("(si)", module, set_argv0);
+  if (runargs == NULL) {
+    fprintf(stderr,
+            "Could not create arguments for runpy._run_module_as_main\n");
+    Py_DECREF(runpy);
+    Py_DECREF(runmodule);
+    return -1;
+  }
+  result = PyObject_Call(runmodule, runargs, NULL);
+  if (result == NULL) {
+    PyErr_Print();
+  }
+  Py_DECREF(runpy);
+  Py_DECREF(runmodule);
+  Py_DECREF(runargs);
+  if (result == NULL) {
+    return -1;
+  }
+  Py_DECREF(result);
+  return 0;
+}
+
+std::string GetEntryPointFilePath(const char *launcher_path) {
+  PyObject *files;
+  files = read_directory(launcher_path);
+  if (files == NULL) {
+    return std::string();
+  }
+  PyObject *toc_entry;
+  // Return value: Borrowed reference.
+  toc_entry = PyDict_GetItemString(files, ENTRYPOINT_FILE);
+  if (toc_entry == NULL) {
+    Py_DECREF(files);
+    return std::string();
+  }
+  PyObject *py_data;
+  py_data = get_data(launcher_path, toc_entry);
+  if (py_data == NULL) {
+    Py_DECREF(files);
+    return std::string();
+  }
+  // PyString_AsString returns a NUL-terminated representation of the "py_data",
+  // "data" must not be modified in any way. And it must not be deallocated.
+  char *data = PyString_AsString(py_data);
+  if (data == NULL) {
+    Py_DECREF(py_data);
+    Py_DECREF(files);
+    return std::string();
+  }
+
+  char *res = strdup(data); /* deep copy of data */
+  Py_DECREF(py_data);
+  Py_DECREF(files);
+
+  int i = 0;
+  /* Strip newline and other trailing whitespace. */
+  for (i = strlen(res) - 1; i >= 0 && isspace(res[i]); i--) {
+    res[i] = '\0';
+  }
+  /* Check for the file extension. */
+  i = strlen(res);
+  if (i > 3 && strcmp(res + i - 3, ".py") == 0) {
+    res[i - 3] = '\0';
+  } else {
+    PyErr_Format(PyExc_ValueError, "Invalid entrypoint in %s: %s",
+                 ENTRYPOINT_FILE, res);
+    return std::string();
+  }
+  return std::string(res);
+}
+
+int RunModuleNameFromEntryPoint(const char *launcher_path, std::string entrypoint) {
+  if (entrypoint.empty()) {
+    return -1;
+  }
+  // Has to pass to free to avoid a memory leak after use.
+  char *arr = strdup(entrypoint.c_str());
+  // Replace file system path seperator with Python package/module seperator.
+  char *ch;
+  for (ch = arr; *ch; ch++) {
+    if (*ch == '/') {
+      *ch = '.';
+    }
+  }
+
+  if (AddPathToPythonSysPath(launcher_path) < 0) {
+    free(arr);
+    return -1;
+  }
+  // Calculate the runfiles path size. Extra space for '\0'.
+  size_t size = snprintf(nullptr, 0, "%s/%s", launcher_path, RUNFILES) + 1;
+  char runfiles_path[size];
+  snprintf(runfiles_path, size, "%s/%s", launcher_path, RUNFILES);
+  if (AddPathToPythonSysPath(runfiles_path) < 0) {
+    free(arr);
+    return -1;
+  }
+  int ret =  RunModule(arr, 0);
+  free(arr);
+  return ret;
+}
+
+int AddPathToPythonSysPath(const char *path) {
+  if (path == NULL) {
+    return -1;
+  }
+  PyObject *py_path;
+  py_path = PyString_FromString(path);
+  if (py_path == NULL) {
+    return -1;
+  }
+  PyObject *sys_path;
+  // Return value: Borrowed reference.
+  sys_path = PySys_GetObject(const_cast<char*>("path"));
+  if (sys_path == NULL) {
+    Py_DECREF(py_path);
+    return -1;
+  }
+  PyList_Insert(sys_path, 0, py_path);
+  Py_DECREF(py_path);
+  return 0;
+}
+
+int RunMainFromImporter(const char *launcher_path) {
+  PyObject *py_launcher_path, *importer;
+  py_launcher_path = PyString_FromString(launcher_path);
+  if (py_launcher_path == NULL) {
+    return -1;
+  }
+  importer = PyImport_GetImporter(py_launcher_path);
+  if (importer == NULL) {
+    Py_DECREF(py_launcher_path);
+    return -1;
+  }
+  if (importer != Py_None && importer->ob_type != &PyNullImporter_Type) {
+    /* Launcher path is usable as an import source, so
+       put it in sys.path[0] and import __main__ */
+    if (AddPathToPythonSysPath(launcher_path) < 0) {
+      Py_DECREF(importer);
+      Py_DECREF(py_launcher_path);
+      return -1;
+    }
+  }
+  Py_DECREF(importer);
+  Py_DECREF(py_launcher_path);
+  return RunModule("__main__", 0);
+}
+}  // namespace internal
+
+int RunEntryPointOrMainModule(const char *launcher_path) {
+  std::string entrypoint = internal::GetEntryPointFilePath(launcher_path);
+  if (entrypoint.empty()) {
+    // If entry point can not be found or can not be executed, we try to
+    // run __main__.py within the .par file.
+    fprintf(stderr, "Cannot find valid entry point to execute par file!\n");
+    fprintf(stdout, "Start trying to run __main__ module within par file.\n");
+    return internal::RunMainFromImporter(launcher_path);
+  }
+  return internal::RunModuleNameFromEntryPoint(launcher_path, entrypoint);
+}
+}  // namespace python_launcher
+}  // namespace cpython2
+}  // namespace android