deploy-ng: support logging via log_filename (and log_append) setting

This commit is contained in:
rdb 2018-04-08 17:57:48 +02:00
parent f5932bf070
commit 742c143a01
6 changed files with 251 additions and 6 deletions

View File

@ -1642,7 +1642,8 @@ class Freezer:
return target
def generateRuntimeFromStub(self, target, stub_file, use_console, fields={}):
def generateRuntimeFromStub(self, target, stub_file, use_console, fields={},
log_append=False):
# We must have a __main__ module to make an exe file.
if not self.__writingModule('__main__'):
message = "Can't generate an executable without a __main__ module."
@ -1735,7 +1736,7 @@ class Freezer:
# Determine the format of the header and module list entries depending
# on the platform.
num_pointers = 11
num_pointers = 12
stub_data = bytearray(stub_file.read())
bitnesses = self._get_executable_bitnesses(stub_data)
@ -1808,6 +1809,10 @@ class Freezer:
# A null entry marks the end of the module table.
blob += struct.pack(entry_layout, 0, 0, 0)
flags = 0
if log_append:
flags |= 1
# Compose the header we will be writing to the stub, to tell it
# where to find the module data blob, as well as other variables.
header = struct.pack(header_layout,
@ -1816,7 +1821,7 @@ class Freezer:
1, # Version number
num_pointers, # Number of pointers that follow
0, # Codepage, not yet used
0, # Flags, not yet used
flags,
table_offset, # Module table pointer.
# The following variables need to be set before static init
# time. See configPageManager.cxx, where they are read.
@ -1830,6 +1835,7 @@ class Freezer:
field_offsets.get('prc_executable_patterns', 0),
field_offsets.get('prc_executable_args_envvar', 0),
field_offsets.get('main_dir', 0),
field_offsets.get('log_filename', 0),
0)
# Now, find the location of the 'blobinfo' symbol in the binary,

View File

@ -11,6 +11,7 @@ import shutil
import struct
import io
import imp
import string
import distutils.core
import distutils.log
@ -86,6 +87,8 @@ class build_apps(distutils.core.Command):
self.extra_prc_files = []
self.extra_prc_data = ''
self.default_prc_dir = None
self.log_filename = None
self.log_append = False
self.requirements_path = './requirements.txt'
self.pypi_extra_indexes = []
self.file_handlers= {
@ -382,7 +385,9 @@ class build_apps(distutils.core.Command):
'prc_encryption_key': None,
'prc_executable_patterns': None,
'prc_executable_args_envvar': None,
})
'main_dir': None,
'log_filename': self.expand_path(self.log_filename, platform),
}, self.log_append)
stub_file.close()
# Copy the dependencies.
@ -818,6 +823,20 @@ class build_apps(distutils.core.Command):
return deps
def expand_path(self, path, platform):
"Substitutes variables in the given path string."
if path is None:
return None
t = string.Template(path)
if platform.startswith('win'):
return t.substitute(HOME='~', USER_APPDATA='~/AppData/Local')
elif platform.startswith('macosx'):
return t.substitute(HOME='~', USER_APPDATA='~/Documents')
else:
return t.substitute(HOME='~', USER_APPDATA='~/.local/share')
class bdist_apps(distutils.core.Command):
DEFAULT_INSTALLERS = {

View File

@ -115,6 +115,7 @@ reload_implicit_pages() {
const char *prc_executable_patterns;
const char *prc_executable_args_envvar;
const char *main_dir;
const char *log_filename;
};
#ifdef _MSC_VER
const BlobInfo *blobinfo = (const BlobInfo *)GetProcAddress(GetModuleHandle(NULL), "blobinfo");

View File

@ -6712,12 +6712,12 @@ if PkgSkip("PYTHON") == 0:
TargetAdd('deploy-stub.exe', input='deploy-stub.obj')
if GetTarget() == 'windows':
TargetAdd('deploy-stub.exe', input='frozen_dllmain.obj')
TargetAdd('deploy-stub.exe', opts=['PYTHON', 'DEPLOYSTUB', 'NOICON'])
TargetAdd('deploy-stub.exe', opts=['WINSHELL', 'PYTHON', 'DEPLOYSTUB', 'NOICON'])
if GetTarget() == 'windows':
TargetAdd('deploy-stubw.exe', input='deploy-stub.obj')
TargetAdd('deploy-stubw.exe', input='frozen_dllmain.obj')
TargetAdd('deploy-stubw.exe', opts=['SUBSYSTEM:WINDOWS', 'PYTHON', 'DEPLOYSTUB', 'NOICON'])
TargetAdd('deploy-stubw.exe', opts=['SUBSYSTEM:WINDOWS', 'WINSHELL', 'PYTHON', 'DEPLOYSTUB', 'NOICON'])
elif GetTarget() == 'darwin':
DefSymbol('MACOS_APP_BUNDLE', 'MACOS_APP_BUNDLE')
OPTS = OPTS + ['MACOS_APP_BUNDLE']

View File

@ -3,8 +3,10 @@
#include "Python.h"
#ifdef _WIN32
# include "malloc.h"
# include <Shlobj.h>
#else
# include <sys/mman.h>
# include <pwd.h>
#endif
#ifdef __FreeBSD__
@ -18,6 +20,7 @@
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#if PY_MAJOR_VERSION >= 3
# include <locale.h>
@ -31,6 +34,11 @@
other pointers that are being read by configPageManager.cxx. */
#define MAX_NUM_POINTERS 24
/* Stored in the flags field of the blobinfo structure below. */
enum Flags {
F_log_append = 1,
};
/* Define an exposed symbol where we store the offset to the module data. */
#ifdef _MSC_VER
__declspec(dllexport)
@ -113,6 +121,206 @@ static void set_main_dir(char *main_dir) {
}
}
/**
* Creates the parent directories of the given path. Returns 1 on success.
*/
#ifdef _WIN32
static int mkdir_parent(const wchar_t *path) {
// Copy the path to a temporary buffer.
wchar_t buffer[4096];
size_t buflen = wcslen(path);
if (buflen + 1 >= _countof(buffer)) {
return 0;
}
wcscpy_s(buffer, _countof(buffer), path);
// Seek back to find the last path separator.
while (buflen-- > 0) {
if (buffer[buflen] == '/' || buffer[buflen] == '\\') {
buffer[buflen] = 0;
break;
}
}
if (buflen == 0) {
// There was no path separator.
return 0;
}
if (CreateDirectoryW(buffer, NULL) != 0) {
// Success!
return 1;
}
// Failed.
DWORD last_error = GetLastError();
if (last_error == ERROR_ALREADY_EXISTS) {
// Not really an error: the directory is already there.
return 1;
}
if (last_error == ERROR_PATH_NOT_FOUND) {
// We need to make the parent directory first.
if (mkdir_parent(buffer)) {
// Parent successfully created. Try again to make the child.
if (CreateDirectoryW(buffer, NULL) != 0) {
// Got it!
return 1;
}
}
}
return 0;
}
#else
static int mkdir_parent(const char *path) {
// Copy the path to a temporary buffer.
char buffer[4096];
size_t buflen = strlen(path);
if (buflen + 1 >= sizeof(buffer)) {
return 0;
}
strcpy(buffer, path);
// Seek back to find the last path separator.
while (buflen-- > 0) {
if (buffer[buflen] == '/') {
buffer[buflen] = 0;
break;
}
}
if (buflen == 0) {
// There was no path separator.
return 0;
}
if (mkdir(buffer, 0755) == 0) {
// Success!
return 1;
}
// Failed.
if (errno == EEXIST) {
// Not really an error: the directory is already there.
return 1;
}
if (errno == ENOENT || errno == EACCES) {
// We need to make the parent directory first.
if (mkdir_parent(buffer)) {
// Parent successfully created. Try again to make the child.
if (mkdir(buffer, 0755) == 0) {
// Got it!
return 1;
}
}
}
return 0;
}
#endif
/**
* Redirects the output streams to point to the log file with the given path.
*
* @param path specifies the location of log file, may start with ~
* @param append should be nonzero if it should not truncate the log file.
*/
static int setup_logging(const char *path, int append) {
#ifdef _WIN32
// Does it start with a tilde? Perform tilde expansion if so.
wchar_t pathw[MAX_PATH * 2];
size_t offset = 0;
if (path[0] == '~' && (path[1] == 0 || path[1] == '/' || path[1] == '\\')) {
// Strip off the tilde.
++path;
// Get the home directory path for the current user.
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathw))) {
return 0;
}
offset = wcslen(pathw);
}
// We need to convert the rest of the path from UTF-8 to UTF-16.
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw + offset,
(int)(_countof(pathw) - offset)) == 0) {
return 0;
}
DWORD access = append ? FILE_APPEND_DATA : (GENERIC_READ | GENERIC_WRITE);
int creation = append ? OPEN_ALWAYS : CREATE_ALWAYS;
HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ,
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
// Make the parent directories first.
mkdir_parent(pathw);
handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ,
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
}
if (handle == INVALID_HANDLE_VALUE) {
return 0;
}
if (append) {
SetFilePointer(handle, 0, NULL, FILE_END);
}
fflush(stdout);
fflush(stderr);
int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | (append ? _O_APPEND : 0));
SetStdHandle(STD_OUTPUT_HANDLE, handle);
_dup2(fd, 1);
SetStdHandle(STD_ERROR_HANDLE, handle);
_dup2(fd, 2);
_close(fd);
return 1;
#else
// Does it start with a tilde? Perform tilde expansion if so.
char buffer[PATH_MAX * 2];
size_t offset = 0;
if (path[0] == '~' && (path[1] == 0 || path[1] == '/')) {
// Strip off the tilde.
++path;
// Get the home directory path for the current user.
const char *home_dir = getenv("HOME");
if (home_dir == NULL) {
home_dir = getpwuid(getuid())->pw_dir;
}
offset = strlen(home_dir);
assert(offset < sizeof(buffer));
strncpy(buffer, home_dir, sizeof(buffer));
}
// Copy over the rest of the path.
strcpy(buffer + offset, path);
mode_t mode = O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC);
int fd = open(buffer, mode, 0644);
if (fd == -1) {
// Make the parent directories first.
mkdir_parent(buffer);
fd = open(buffer, mode, 0644);
}
if (fd == -1) {
perror(buffer);
return 0;
}
fflush(stdout);
fflush(stderr);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
return 1;
#endif
}
/* Main program */
#ifdef WIN_UNICODE
@ -389,7 +597,9 @@ int main(int argc, char *argv[]) {
#endif
int retval;
struct _frozen *moddef;
const char *log_filename;
void *blob = NULL;
log_filename = NULL;
/*
printf("blob_offset: %d\n", (int)blobinfo.blob_offset);
@ -418,6 +628,9 @@ int main(int argc, char *argv[]) {
blobinfo.pointers[i] = (void *)((uintptr_t)blobinfo.pointers[i] + (uintptr_t)blob);
}
}
if (blobinfo.num_pointers >= 12) {
log_filename = blobinfo.pointers[11];
}
} else {
blobinfo.pointers[0] = blob;
}
@ -434,6 +647,10 @@ int main(int argc, char *argv[]) {
}
}
if (log_filename != NULL) {
setup_logging(log_filename, (blobinfo.flags & F_log_append) != 0);
}
#ifdef _WIN32
if (blobinfo.codepage != 0) {
SetConsoleCP(blobinfo.codepage);

View File

@ -12,6 +12,8 @@ setup(
'gui_apps': {
'asteroids': 'main.py',
},
'log_filename': '$USER_APPDATA/Asteroids/output.log',
'log_append': False,
'plugins': [
'pandagl',
'p3openal_audio',