mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-28 07:48:37 -04:00
dist: Implement more reliable solution for logging stdout/stderr
The previous trick doesn't seem to work on one of my devices. The disadvantage of the new solution, however, is that it doesn't capture anything until site.py is successfully executed.
This commit is contained in:
parent
cc865e6d21
commit
b8c301164a
47
direct/src/dist/commands.py
vendored
47
direct/src/dist/commands.py
vendored
@ -163,12 +163,57 @@ from _frozen_importlib import _imp, FrozenImporter
|
||||
from importlib import _bootstrap_external
|
||||
from importlib.abc import Loader, MetaPathFinder
|
||||
from importlib.machinery import ModuleSpec
|
||||
from io import RawIOBase, TextIOWrapper
|
||||
|
||||
from android_log import write as android_log_write
|
||||
|
||||
|
||||
sys.frozen = True
|
||||
sys.platform = "android"
|
||||
|
||||
|
||||
# Replace stdout/stderr with something that writes to the Android log.
|
||||
class AndroidLogStream:
|
||||
closed = False
|
||||
encoding = 'utf-8'
|
||||
|
||||
def __init__(self, prio, tag):
|
||||
self.prio = prio
|
||||
self.tag = tag
|
||||
self.buffer = ''
|
||||
|
||||
def isatty(self):
|
||||
return False
|
||||
|
||||
def write(self, text):
|
||||
self.writelines(text.split('\\n'))
|
||||
|
||||
def writelines(self, lines):
|
||||
num_lines = len(lines)
|
||||
if num_lines == 1:
|
||||
self.buffer += lines[0]
|
||||
elif num_lines > 1:
|
||||
android_log_write(self.prio, self.tag, self.buffer + lines[0])
|
||||
for line in lines[1:-1]:
|
||||
android_log_write(self.prio, self.tag, line)
|
||||
self.buffer = lines[-1]
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def seekable(self):
|
||||
return False
|
||||
|
||||
def readable(self):
|
||||
return False
|
||||
|
||||
def writable(self):
|
||||
return True
|
||||
|
||||
sys.stdout = AndroidLogStream(2, 'Python')
|
||||
sys.stderr = AndroidLogStream(3, 'Python')
|
||||
|
||||
|
||||
# Alter FrozenImporter to give a __file__ property to frozen modules.
|
||||
_find_spec = FrozenImporter.find_spec
|
||||
|
||||
@ -191,7 +236,7 @@ class AndroidExtensionFinder(MetaPathFinder):
|
||||
@classmethod
|
||||
def find_spec(cls, fullname, path=None, target=None):
|
||||
soname = 'libpy.' + fullname + '.so'
|
||||
path = os.path.join(sys._native_library_dir, soname)
|
||||
path = os.path.join(os.path.dirname(sys.executable), soname)
|
||||
|
||||
if os.path.exists(path):
|
||||
loader = _bootstrap_external.ExtensionFileLoader(fullname, path)
|
||||
|
@ -6052,8 +6052,10 @@ if PkgSkip("PYTHON") == 0:
|
||||
PyTargetAdd('deploy-stubw.exe', opts=['MACOS_APP_BUNDLE', 'DEPLOYSTUB', 'NOICON'])
|
||||
elif GetTarget() == 'android':
|
||||
PyTargetAdd('deploy-stubw_android_main.obj', opts=OPTS, input='android_main.cxx')
|
||||
PyTargetAdd('deploy-stubw_android_log.obj', opts=OPTS, input='android_log.c')
|
||||
PyTargetAdd('libdeploy-stubw.dll', input='android_native_app_glue.obj')
|
||||
PyTargetAdd('libdeploy-stubw.dll', input='deploy-stubw_android_main.obj')
|
||||
PyTargetAdd('libdeploy-stubw.dll', input='deploy-stubw_android_log.obj')
|
||||
PyTargetAdd('libdeploy-stubw.dll', input=COMMON_PANDA_LIBS)
|
||||
PyTargetAdd('libdeploy-stubw.dll', input='libp3android.dll')
|
||||
PyTargetAdd('libdeploy-stubw.dll', opts=['DEPLOYSTUB', 'ANDROID'])
|
||||
|
55
pandatool/src/deploy-stub/android_log.c
Normal file
55
pandatool/src/deploy-stub/android_log.c
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file android_log.c
|
||||
* @author rdb
|
||||
* @date 2021-12-10
|
||||
*/
|
||||
|
||||
#undef _POSIX_C_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#define PY_SSIZE_T_CLEAN 1
|
||||
|
||||
#include "Python.h"
|
||||
#include <android/log.h>
|
||||
|
||||
/**
|
||||
* Writes a message to the Android log.
|
||||
*/
|
||||
static PyObject *
|
||||
_py_write(PyObject *self, PyObject *args) {
|
||||
int prio;
|
||||
char *tag;
|
||||
char *text;
|
||||
if (PyArg_ParseTuple(args, "iss", &prio, &tag, &text)) {
|
||||
__android_log_write(prio, tag, text);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
static PyMethodDef python_simple_funcs[] = {
|
||||
{ "write", &_py_write, METH_VARARGS },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static struct PyModuleDef android_log_module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"android_log",
|
||||
NULL,
|
||||
-1,
|
||||
python_simple_funcs,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
PyObject *PyInit_android_log() {
|
||||
return PyModule_Create(&android_log_module);
|
||||
}
|
@ -50,6 +50,9 @@ extern "C" {
|
||||
} blobinfo = {(uint64_t)-1};
|
||||
}
|
||||
|
||||
// Defined in android_log.c
|
||||
extern "C" PyObject *PyInit_android_log();
|
||||
|
||||
/**
|
||||
* Maps the binary blob at the given memory address to memory, and returns the
|
||||
* pointer to the beginning of it.
|
||||
@ -96,36 +99,6 @@ void android_main(struct android_app *app) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pipe stdout/stderr to the Android log stream, for convenience.
|
||||
int pfd[2];
|
||||
setvbuf(stdout, 0, _IOLBF, 0);
|
||||
setvbuf(stderr, 0, _IOLBF, 0);
|
||||
|
||||
pipe(pfd);
|
||||
dup2(pfd[1], 1);
|
||||
dup2(pfd[1], 2);
|
||||
|
||||
std::thread t([=] {
|
||||
ssize_t size;
|
||||
char buf[4096] = {0};
|
||||
char *bufstart = buf;
|
||||
char *const bufend = buf + sizeof(buf) - 1;
|
||||
|
||||
while ((size = read(pfd[0], bufstart, bufend - bufstart)) > 0) {
|
||||
bufstart[size] = 0;
|
||||
bufstart += size;
|
||||
|
||||
while (char *nl = (char *)memchr(buf, '\n', strnlen(buf, bufend - buf))) {
|
||||
*nl = 0;
|
||||
__android_log_write(ANDROID_LOG_VERBOSE, "Python", buf);
|
||||
|
||||
// Move everything after the newline to the beginning of the buffer.
|
||||
memmove(buf, nl + 1, bufend - (nl + 1));
|
||||
bufstart -= (nl + 1) - buf;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
jclass activity_class = env->GetObjectClass(activity->clazz);
|
||||
|
||||
// Get the current Java thread name. This just helps with debugging.
|
||||
@ -179,6 +152,19 @@ void android_main(struct android_app *app) {
|
||||
env->ReleaseStringUTFChars(jcache_dir, cache_dir);
|
||||
}
|
||||
|
||||
// Fetch the path to the library directory.
|
||||
jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;");
|
||||
jstring libdir_jstr = (jstring) env->GetObjectField(appinfo, libdir_field);
|
||||
const char *libdir = env->GetStringUTFChars(libdir_jstr, nullptr);
|
||||
|
||||
if (libdir != nullptr) {
|
||||
std::string dtool_name = std::string(libdir) + "/libp3dtool.so";
|
||||
ExecutionEnvironment::set_dtool_name(dtool_name);
|
||||
android_cat.info() << "Path to dtool: " << dtool_name << "\n";
|
||||
|
||||
env->ReleaseStringUTFChars(libdir_jstr, libdir);
|
||||
}
|
||||
|
||||
// Get the path to the APK.
|
||||
methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
|
||||
jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||
@ -198,7 +184,6 @@ void android_main(struct android_app *app) {
|
||||
|
||||
// Map the blob to memory
|
||||
void *blob = map_blob(lib_path, (off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size);
|
||||
env->ReleaseStringUTFChars(lib_path_jstr, lib_path);
|
||||
assert(blob != NULL);
|
||||
|
||||
assert(blobinfo.num_pointers <= MAX_NUM_POINTERS);
|
||||
@ -261,12 +246,21 @@ void android_main(struct android_app *app) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the android_log module.
|
||||
if (PyImport_AppendInittab("android_log", &PyInit_android_log) < 0) {
|
||||
android_cat.error()
|
||||
<< "Failed to register android_log module.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
PyConfig config;
|
||||
PyConfig_InitIsolatedConfig(&config);
|
||||
config.pathconfig_warnings = 0; /* Suppress errors from getpath.c */
|
||||
config.buffered_stdio = 0;
|
||||
config.configure_c_stdio = 0;
|
||||
config.write_bytecode = 0;
|
||||
PyConfig_SetBytesString(&config, &config.executable, lib_path);
|
||||
env->ReleaseStringUTFChars(lib_path_jstr, lib_path);
|
||||
|
||||
status = Py_InitializeFromConfig(&config);
|
||||
PyConfig_Clear(&config);
|
||||
@ -275,26 +269,6 @@ void android_main(struct android_app *app) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the path to the library directory.
|
||||
jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;");
|
||||
jstring libdir_jstr = (jstring) env->GetObjectField(appinfo, libdir_field);
|
||||
const char *libdir = env->GetStringUTFChars(libdir_jstr, nullptr);
|
||||
|
||||
if (libdir != nullptr) {
|
||||
// This is used by the import hook to locate the module libraries.
|
||||
PyObject *py_native_dir = PyUnicode_FromString(libdir);
|
||||
PySys_SetObject("_native_library_dir", py_native_dir);
|
||||
Py_DECREF(py_native_dir);
|
||||
|
||||
if (ExecutionEnvironment::get_dtool_name().empty()) {
|
||||
std::string dtool_name = std::string(libdir) + "/libp3dtool.so";
|
||||
ExecutionEnvironment::set_dtool_name(dtool_name);
|
||||
android_cat.info() << "Path to dtool: " << dtool_name << "\n";
|
||||
}
|
||||
|
||||
env->ReleaseStringUTFChars(libdir_jstr, libdir);
|
||||
}
|
||||
|
||||
while (!app->destroyRequested) {
|
||||
// Call the main module. This will not return until the app is done.
|
||||
android_cat.info() << "Importing __main__\n";
|
||||
@ -305,12 +279,12 @@ void android_main(struct android_app *app) {
|
||||
break;
|
||||
}
|
||||
if (n < 0) {
|
||||
if (_PyErr_OCCURRED() != PyExc_SystemExit) {
|
||||
PyErr_Print();
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
|
||||
fsync(1);
|
||||
fsync(2);
|
||||
sched_yield();
|
||||
|
||||
if (app->destroyRequested) {
|
||||
// The app closed responding to a destroy request.
|
||||
|
Loading…
x
Reference in New Issue
Block a user