| // 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 |