Skip to content

Commit

Permalink
Merge pull request #43 from beeware/console-app
Browse files Browse the repository at this point in the history
Add support for BRIEFCASE_DEBUG.
  • Loading branch information
mhsmith authored Jun 3, 2024
2 parents 6d3a1b8 + 5ccec1f commit ab18e99
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 19 deletions.
1 change: 1 addition & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"bundle_identifier": "{{ cookiecutter.bundle }}.{{ cookiecutter.app_name|replace('_', '-') }}",
"url": "https://example.com",
"description": "Short description of app",
"console_app": false,
"flatpak_runtime": "org.freedesktop.Platform",
"flatpak_runtime_version": "21.08",
"flatpak_sdk": "org.freedesktop.Sdk",
Expand Down
53 changes: 34 additions & 19 deletions {{ cookiecutter.format }}/src/bootstrap/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
#include <stdio.h>
#include <Python.h>

// A global indicator of the debug level
char *debug_mode;

void debug_log(const char *format, ...);

int main(int argc, char *argv[]) {
int ret = 0;
PyStatus status;
Expand All @@ -25,6 +30,9 @@ int main(int argc, char *argv[]) {
PyObject *exc_traceback;
PyObject *systemExit_code;

// Set the global debug state based on the runtime environment
debug_mode = getenv("BRIEFCASE_DEBUG");

// Generate an isolated Python configuration.
PyPreConfig_InitIsolatedConfig(&preconfig);
PyConfig_InitIsolatedConfig(&config);
Expand All @@ -41,7 +49,7 @@ int main(int argc, char *argv[]) {
// Isolated apps need to set the full PYTHONPATH manually.
config.module_search_paths_set = 1;

printf("Pre-initializing Python runtime...\n");
debug_log("Pre-initializing Python runtime...\n");
status = Py_PreInitialize(&preconfig);
if (PyStatus_Exception(status)) {
// crash_dialog("Unable to pre-initialize Python interpreter: %s", status.err_msg, nil]);
Expand All @@ -51,7 +59,7 @@ int main(int argc, char *argv[]) {

// Set the home for the Python interpreter
python_home = "/app";
printf("PYTHONHOME: %s\n", python_home);
debug_log("PYTHONHOME: %s\n", python_home);
wtmp_str = Py_DecodeLocale(python_home, NULL);
status = PyConfig_SetString(&config, &config.home, wtmp_str);
if (PyStatus_Exception(status)) {
Expand All @@ -62,6 +70,7 @@ int main(int argc, char *argv[]) {
PyMem_RawFree(wtmp_str);

// Set the executable to match the known path of the app binary in the flatpak.
debug_log("config.executable: /app/bin/{{ cookiecutter.app_name }}\n");
status = PyConfig_SetBytesString(&config, &config.executable, "/app/bin/{{ cookiecutter.app_name }}");
if (PyStatus_Exception(status)) {
// crash_dialog("Unable to set executable name: %s", status.err_msg);
Expand All @@ -77,6 +86,7 @@ int main(int argc, char *argv[]) {
if (app_module_name == NULL) {
app_module_name = "{{ cookiecutter.module_name }}";
}
debug_log("config.run_module: %s\n", app_module_name);
status = PyConfig_SetBytesString(&config, &config.run_module, app_module_name);
if (PyStatus_Exception(status)) {
// crash_dialog("Unable to set app module name: %s", status.err_msg);
Expand All @@ -93,10 +103,10 @@ int main(int argc, char *argv[]) {
}

// Set the full module path. This includes the stdlib, site-packages, and app code.
printf("PYTHONPATH:\n");
debug_log("PYTHONPATH:\n");
// // The .zip form of the stdlib
path = "/app/lib/python{{ ''.join(cookiecutter.python_version.split('.')[:2]) }}.zip";
printf("- %s\n", path);
debug_log("- %s\n", path);
wtmp_str = Py_DecodeLocale(path, NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
if (PyStatus_Exception(status)) {
Expand All @@ -108,7 +118,7 @@ int main(int argc, char *argv[]) {

// The unpacked form of the stdlib
path = "/app/lib/python{{ '.'.join(cookiecutter.python_version.split('.')[:2]) }}";
printf("- %s\n", path);
debug_log("- %s\n", path);
wtmp_str = Py_DecodeLocale(path, NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
if (PyStatus_Exception(status)) {
Expand All @@ -120,7 +130,7 @@ int main(int argc, char *argv[]) {

// Add the stdlib binary modules path
path = "/app/lib/python{{ '.'.join(cookiecutter.python_version.split('.')[:2]) }}/lib-dynload";
printf("- %s\n", path);
debug_log("- %s\n", path);
wtmp_str = Py_DecodeLocale(path, NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
if (PyStatus_Exception(status)) {
Expand All @@ -132,7 +142,7 @@ int main(int argc, char *argv[]) {

// Add the app_packages path
path = "/app/briefcase/app_packages";
printf("- %s\n", path);
debug_log("- %s\n", path);
wtmp_str = Py_DecodeLocale(path, NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
if (PyStatus_Exception(status)) {
Expand All @@ -144,7 +154,7 @@ int main(int argc, char *argv[]) {

// Add the app path
path = "/app/briefcase/app";
printf("- %s\n", path);
debug_log("- %s\n", path);
wtmp_str = Py_DecodeLocale(path, NULL);
status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
if (PyStatus_Exception(status)) {
Expand All @@ -154,15 +164,15 @@ int main(int argc, char *argv[]) {
}
PyMem_RawFree(wtmp_str);

printf("Configure argc/argv...\n");
debug_log("Configure argc/argv...\n");
status = PyConfig_SetBytesArgv(&config, argc, argv);
if (PyStatus_Exception(status)) {
// crash_dialog("Unable to configured argc/argv: %s", status.err_msg);
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}

printf("Initializing Python runtime...\n");
debug_log("Initializing Python runtime...\n");
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
// crash_dialog("Unable to initialize Python interpreter: %s", status.err_msg);
Expand All @@ -177,7 +187,7 @@ int main(int argc, char *argv[]) {
// pymain_run_module() method); we need to re-implement it
// because we need to be able to inspect the error state of
// the interpreter, not just the return code of the module.
printf("Running app module: %s\n", app_module_name);
debug_log("Running app module: %s\n", app_module_name);
module = PyImport_ImportModule("runpy");
if (module == NULL) {
// crash_dialog(@"Could not import runpy module");
Expand All @@ -204,7 +214,7 @@ int main(int argc, char *argv[]) {

// Print a separator to differentiate Python startup logs from app logs,
// then flush stdout/stderr to ensure all startup logs have been output.
printf("---------------------------------------------------------------------------\n");
debug_log("---------------------------------------------------------------------------\n");
fflush(stdout);
fflush(stderr);

Expand All @@ -224,31 +234,36 @@ int main(int argc, char *argv[]) {
if (PyErr_GivenExceptionMatches(exc_value, PyExc_SystemExit)) {
systemExit_code = PyObject_GetAttrString(exc_value, "code");
if (systemExit_code == NULL) {
printf("Could not determine exit code\n");
debug_log("Could not determine exit code\n");
ret = -10;
}
else {
ret = (int) PyLong_AsLong(systemExit_code);
}
} else {
ret = -6;
}

if (ret != 0) {
printf("Application quit abnormally (Exit code %d)!\n", ret);
printf("---------------------------------------------------------------------------\n");
printf("Application quit abnormally!\n");

// Restore the error state of the interpreter.
PyErr_Restore(exc_type, exc_value, exc_traceback);

// Print exception to stderr.
// In case of SystemExit, this will call exit()
PyErr_Print();

exit(ret);
}
}

Py_Finalize();

return ret;
}

void debug_log(const char *format, ...) {
if (debug_mode) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
}

0 comments on commit ab18e99

Please sign in to comment.