浏览代码

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

rdb 7 年之前
父节点
当前提交
742c143a01

+ 9 - 3
direct/src/showutil/FreezeTool.py

@@ -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,

+ 20 - 1
direct/src/showutil/dist.py

@@ -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 = {

+ 1 - 0
dtool/src/prc/configPageManager.cxx

@@ -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");

+ 2 - 2
makepanda/makepanda.py

@@ -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']

+ 217 - 0
pandatool/src/deploy-stub/deploy-stub.c

@@ -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);

+ 2 - 0
samples/asteroids/setup.py

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