diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py index 2d929d6b5f..ee53960139 100644 --- a/direct/src/p3d/AppRunner.py +++ b/direct/src/p3d/AppRunner.py @@ -113,6 +113,11 @@ class AppRunner(DirectObject): # hosts we have imported packages from. self.hosts = {} + # Application code can assign a callable object here; if so, + # it will be invoked when an uncaught exception propagates to + # the top of the TaskMgr.run() loop. + self.exceptionHandler = None + # Managing packages for runtime download. self.downloadingPackages = [] self.downloadTask = None @@ -157,6 +162,49 @@ class AppRunner(DirectObject): # call back to the main thread. self.accept('AppRunner_startIfReady', self.__startIfReady) + def getToken(self, tokenName): + """ Returns the value of the indicated web token as a string, + if it was set, or None if it was not. """ + + return self.tokenDict.get(tokenName.lower(), None) + + def getTokenInt(self, tokenName): + """ Returns the value of the indicated web token as an integer + value, if it was set, or None if it was not, or not an + integer. """ + + value = self.getToken(tokenName) + if value is not None: + try: + value = int(value) + except ValueError: + value = None + return value + + def getTokenFloat(self, tokenName): + """ Returns the value of the indicated web token as a + floating-point value value, if it was set, or None if it was + not, or not a number. """ + + value = self.getToken(tokenName) + if value is not None: + try: + value = float(value) + except ValueError: + value = None + return value + + def getTokenBool(self, tokenName): + """ Returns the value of the indicated web token as a boolean + value, if it was set, or None if it was not. """ + + value = self.getTokenInt(tokenName) + if value is not None: + value = bool(value) + return value + + + def installPackage(self, packageName, version = None, hostUrl = None): """ Installs the named package, downloading it first if @@ -248,6 +296,33 @@ class AppRunner(DirectObject): # shuts down. taskMgr.doMethodLater(0.5, sys.exit, 'exit') + def run(self): + """ This method calls taskMgr.run(), with an optional + exception handler. This is generally the program's main loop + when running in a p3d environment (except on unusual platforms + like the iPhone, which have to hand the main loop off to the + OS, and don't use this interface. """ + + try: + taskMgr.run() + + except SystemExit: + # Presumably the window has already been shut down here, but shut + # it down again for good measure. + if hasattr(__builtin__, "base"): + base.destroy() + + self.notify.info("Normal exit.") + raise + + except: + # Some unexpected Python exception; pass it to the + # optional handler, if it is defined. + if self.exceptionHandler: + self.exceptionHandler() + else: + raise + def setSessionId(self, sessionId): """ This message should come in at startup. """ self.sessionId = sessionId diff --git a/direct/src/plugin/handleStream.I b/direct/src/plugin/handleStream.I index f99835ddf8..9fb1b2ebe3 100644 --- a/direct/src/plugin/handleStream.I +++ b/direct/src/plugin/handleStream.I @@ -73,6 +73,17 @@ close() { _buf.close(); } +//////////////////////////////////////////////////////////////////// +// Function: HandleStream::close_handle +// Access: Public +// Description: Closes the underlying handle, *without* attempting to +// flush the stream. +//////////////////////////////////////////////////////////////////// +inline void HandleStream:: +close_handle() { + _buf.close_handle(); +} + //////////////////////////////////////////////////////////////////// // Function: HandleStream::get_handle // Access: Public diff --git a/direct/src/plugin/handleStream.h b/direct/src/plugin/handleStream.h index bceaad39e9..0ef794e970 100644 --- a/direct/src/plugin/handleStream.h +++ b/direct/src/plugin/handleStream.h @@ -32,6 +32,7 @@ public: inline void open_read(FHandle handle); inline void open_write(FHandle handle); inline void close(); + inline void close_handle(); inline FHandle get_handle() const; diff --git a/direct/src/plugin/handleStreamBuf.cxx b/direct/src/plugin/handleStreamBuf.cxx index 7761bfa71b..2ed06f3b2b 100644 --- a/direct/src/plugin/handleStreamBuf.cxx +++ b/direct/src/plugin/handleStreamBuf.cxx @@ -36,12 +36,9 @@ HandleStreamBuf() { _is_open_read = false; _is_open_write = false; -#ifdef _WIN32 - // Windows case. - _handle = NULL; -#else - _handle = -1; -#endif // _WIN32 + _handle = INVALID_HANDLE_VALUE; + + INIT_LOCK(_lock); _buffer = new char[handle_buffer_size]; char *ebuf = _buffer + handle_buffer_size; @@ -59,6 +56,8 @@ HandleStreamBuf:: close(); delete[] _buffer; + + DESTROY_LOCK(_lock); } //////////////////////////////////////////////////////////////////// @@ -123,6 +122,20 @@ close() { // Make sure the write buffer is flushed. sync(); + close_handle(); + + pbump(pbase() - pptr()); + gbump(egptr() - gptr()); +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::close_handle +// Access: Public +// Description: Closes the underlying handle, *without* attempting to +// flush the stream. +//////////////////////////////////////////////////////////////////// +void HandleStreamBuf:: +close_handle() { #ifdef _WIN32 if (_handle != NULL) { CloseHandle(_handle); @@ -137,9 +150,6 @@ close() { _is_open_read = false; _is_open_write = false; - - pbump(pbase() - pptr()); - gbump(egptr() - gptr()); } //////////////////////////////////////////////////////////////////// @@ -150,11 +160,15 @@ close() { //////////////////////////////////////////////////////////////////// int HandleStreamBuf:: overflow(int ch) { + ACQUIRE_LOCK(_lock); + bool okflag = true; + assert(pptr() >= pbase()); size_t n = pptr() - pbase(); if (n != 0) { size_t wrote = write_chars(pbase(), n); + assert(wrote <= n); pbump(-(int)wrote); if (wrote != n) { okflag = false; @@ -172,6 +186,8 @@ overflow(int ch) { } } + RELEASE_LOCK(_lock); + if (!okflag) { return EOF; } @@ -186,11 +202,16 @@ overflow(int ch) { //////////////////////////////////////////////////////////////////// int HandleStreamBuf:: sync() { + ACQUIRE_LOCK(_lock); + assert(pptr() >= pbase()); size_t n = pptr() - pbase(); size_t wrote = write_chars(pbase(), n); + assert(wrote <= n); pbump(-(int)wrote); + RELEASE_LOCK(_lock); + if (n != wrote) { return EOF; } @@ -205,6 +226,7 @@ sync() { //////////////////////////////////////////////////////////////////// int HandleStreamBuf:: underflow() { + ACQUIRE_LOCK(_lock); // Sometimes underflow() is called even if the buffer is not empty. if (gptr() >= egptr()) { // Mark the buffer filled (with buffer_size bytes). @@ -218,6 +240,7 @@ underflow() { // Oops, we didn't read what we thought we would. if (read_count == 0) { gbump(num_bytes); + RELEASE_LOCK(_lock); return EOF; } @@ -229,7 +252,10 @@ underflow() { } } - return (unsigned char)*gptr(); + unsigned char next = *gptr(); + RELEASE_LOCK(_lock); + + return next; } //////////////////////////////////////////////////////////////////// @@ -309,6 +335,7 @@ write_chars(const char *start, size_t length) { DWORD bytes_written = 0; BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL); if (!success) { + assert(bytes_written <= length); DWORD error = GetLastError(); if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) { cerr << "Error writing " << length diff --git a/direct/src/plugin/handleStreamBuf.h b/direct/src/plugin/handleStreamBuf.h index 661564570a..ff1d2f30dd 100644 --- a/direct/src/plugin/handleStreamBuf.h +++ b/direct/src/plugin/handleStreamBuf.h @@ -16,6 +16,7 @@ #define HANDLESTREAMBUF_H #include "fhandle.h" +#include "p3d_lock.h" #include using namespace std; @@ -34,6 +35,7 @@ public: bool is_open_read() const; bool is_open_write() const; void close(); + void close_handle(); inline FHandle get_handle() const; @@ -51,6 +53,7 @@ private: bool _is_open_write; FHandle _handle; + LOCK _lock; char *_buffer; }; diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I index 5e57520da7..a3e629fec2 100644 --- a/direct/src/plugin/p3dInstance.I +++ b/direct/src/plugin/p3dInstance.I @@ -61,14 +61,14 @@ get_session_key() const { } //////////////////////////////////////////////////////////////////// -// Function: P3DInstance::get_python_version +// Function: P3DInstance::get_session // Access: Public -// Description: Returns a string that uniquely identifies this -// instance's required Python version. +// Description: Returns the P3DSession that is hosting this instance, +// or NULL if the instance is not running. //////////////////////////////////////////////////////////////////// -inline const string &P3DInstance:: -get_python_version() const { - return _python_version; +inline P3DSession *P3DInstance:: +get_session() const { + return _session; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 57a89cb0fc..01e10322cd 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -58,6 +58,7 @@ P3DInstance(P3D_request_ready_func *func, { _browser_script_object = NULL; _panda_script_object = new P3DMainObject; + _panda_script_object->set_instance(this); _user_data = user_data; _request_pending = false; _temp_p3d_filename = NULL; @@ -113,6 +114,7 @@ P3DInstance:: nout << "panda_script_object ref = " << _panda_script_object->_ref_count << "\n"; + _panda_script_object->set_instance(NULL); P3D_OBJECT_DECREF(_panda_script_object); // Tell all of the packages that we're no longer in business for @@ -458,7 +460,7 @@ bake_requests() { // No more requests to process right now. return; } - + // Now we've got a request in XML form; convert it to P3D_request // form. TiXmlElement *xrequest = doc->FirstChildElement("request"); @@ -1000,6 +1002,11 @@ make_p3d_request(TiXmlElement *xrequest) { // We no longer need to keep this reference. _session->drop_p3dobj(object_id); } + + } else if (strcmp(rtype, "stop") == 0) { + // A stop request from Python code. This is kind of weird, but OK. + request = new P3D_request; + request->_request_type = P3D_RT_stop; } else { nout << "Ignoring request of type " << rtype << "\n"; diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 2ca71650f1..1ee3fc5eea 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -82,7 +82,8 @@ public: inline int get_instance_id() const; inline const string &get_session_key() const; - inline const string &get_python_version() const; + + inline P3DSession *get_session() const; inline P3D_request_ready_func *get_request_ready_func() const; @@ -158,7 +159,6 @@ private: int _instance_id; string _session_key; - string _python_version; string _log_basename; bool _hidden; bool _allow_python_dev; diff --git a/direct/src/plugin/p3dInstanceManager.I b/direct/src/plugin/p3dInstanceManager.I index 11b3427eef..b6571a9e4e 100644 --- a/direct/src/plugin/p3dInstanceManager.I +++ b/direct/src/plugin/p3dInstanceManager.I @@ -89,6 +89,21 @@ get_log_directory() const { return _log_directory; } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::get_log_pathname +// Access: Public +// Description: Returns the filename of the system log file; this +// file is responsible for downloading and installing +// updates, and launching applications. This is +// different from the session log file(s), which +// represent the output from a particular Python +// session. +//////////////////////////////////////////////////////////////////// +inline const string &P3DInstanceManager:: +get_log_pathname() const { + return _log_pathname; +} + //////////////////////////////////////////////////////////////////// // Function: P3DInstanceManager::get_keep_cwd // Access: Public diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h index 6fc6972ce5..a6da79cbf4 100644 --- a/direct/src/plugin/p3dInstanceManager.h +++ b/direct/src/plugin/p3dInstanceManager.h @@ -58,6 +58,7 @@ public: inline const string &get_root_dir() const; inline const string &get_platform() const; inline const string &get_log_directory() const; + inline const string &get_log_pathname() const; inline bool get_keep_cwd() const; P3DInstance * diff --git a/direct/src/plugin/p3dMainObject.cxx b/direct/src/plugin/p3dMainObject.cxx index 8d5fa19512..aa16f4bc3f 100644 --- a/direct/src/plugin/p3dMainObject.cxx +++ b/direct/src/plugin/p3dMainObject.cxx @@ -13,6 +13,11 @@ //////////////////////////////////////////////////////////////////// #include "p3dMainObject.h" +#include "p3dInstance.h" +#include "p3dSession.h" +#include "p3dUndefinedObject.h" +#include "p3dStringObject.h" +#include "p3dInstanceManager.h" //////////////////////////////////////////////////////////////////// // Function: P3DMainObject::Constructor @@ -178,6 +183,13 @@ set_property(const string &property, P3D_object *value) { //////////////////////////////////////////////////////////////////// bool P3DMainObject:: has_method(const string &method_name) { + // Some special-case methods implemented in-place. + if (method_name == "read_game_log") { + return true; + } else if (method_name == "read_system_log") { + return true; + } + if (_pyobj == NULL) { // No methods until we get our pyobj. return false; @@ -202,6 +214,12 @@ has_method(const string &method_name) { P3D_object *P3DMainObject:: call(const string &method_name, bool needs_response, P3D_object *params[], int num_params) { + if (method_name == "read_game_log") { + return call_read_game_log(); + } else if (method_name == "read_system_log") { + return call_read_system_log(); + } + if (_pyobj == NULL) { // No methods until we get our pyobj. return NULL; @@ -263,3 +281,88 @@ P3D_object *P3DMainObject:: get_pyobj() const { return _pyobj; } + +//////////////////////////////////////////////////////////////////// +// Function: P3DMainObject::set_instance +// Access: Public +// Description: Sets a callback pointer to the instance that owns +// this object. When this instance destructs, it clears +// this pointer to NULL. +//////////////////////////////////////////////////////////////////// +void P3DMainObject:: +set_instance(P3DInstance *inst) { + _inst = inst; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DMainObject::call_read_game_log +// Access: Private +// Description: Reads the entire logfile as a string, and returns it +// to the calling JavaScript process. +//////////////////////////////////////////////////////////////////// +P3D_object *P3DMainObject:: +call_read_game_log() { + nout << "call_read_game_log: " << _inst << "\n"; + if (_inst == NULL) { + return new P3DUndefinedObject(); + } + + P3DSession *session = _inst->get_session(); + nout << "session = " << session << "\n"; + + string log_pathname = session->get_log_pathname(); + nout << "log_pathname = " << log_pathname << "\n"; + + ifstream log(log_pathname.c_str(), ios::in); + + // Get the size of the file. + log.seekg(0, ios::end); + size_t size = (size_t)log.tellg(); + log.seekg(0, ios::beg); + + // Read the entire file into memory all at once. + char *buffer = new char[size]; + if (buffer == NULL) { + return NULL; + } + + log.read(buffer, size); + P3D_object *result = new P3DStringObject(buffer, size); + delete[] buffer; + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DMainObject::call_read_system_log +// Access: Private +// Description: As above, but reads the system log, the logfile for +// the installation process. +//////////////////////////////////////////////////////////////////// +P3D_object *P3DMainObject:: +call_read_system_log() { + nout << "call_read_system_log: " << _inst << "\n"; + + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + string log_pathname = inst_mgr->get_log_pathname(); + nout << "log_pathname = " << log_pathname << "\n"; + + ifstream log(log_pathname.c_str(), ios::in); + + // Get the size of the file. + log.seekg(0, ios::end); + size_t size = (size_t)log.tellg(); + log.seekg(0, ios::beg); + + // Read the entire file into memory all at once. + char *buffer = new char[size]; + if (buffer == NULL) { + return NULL; + } + + log.read(buffer, size); + P3D_object *result = new P3DStringObject(buffer, size); + delete[] buffer; + + return result; +} diff --git a/direct/src/plugin/p3dMainObject.h b/direct/src/plugin/p3dMainObject.h index eeb1fde396..e9a778a2e6 100644 --- a/direct/src/plugin/p3dMainObject.h +++ b/direct/src/plugin/p3dMainObject.h @@ -20,6 +20,7 @@ #include class P3DSession; +class P3DInstance; //////////////////////////////////////////////////////////////////// // Class : P3DMainObject @@ -63,8 +64,15 @@ public: void set_pyobj(P3D_object *pyobj); P3D_object *get_pyobj() const; + void set_instance(P3DInstance *inst); + +private: + P3D_object *call_read_game_log(); + P3D_object *call_read_system_log(); + private: P3D_object *_pyobj; + P3DInstance *_inst; // This map is used to store properties and retrieve until // set_pyobj() is called for the firs ttime. At that point, the diff --git a/direct/src/plugin/p3dPythonMain.cxx b/direct/src/plugin/p3dPythonMain.cxx index ad479db2cf..7e72a5fc27 100644 --- a/direct/src/plugin/p3dPythonMain.cxx +++ b/direct/src/plugin/p3dPythonMain.cxx @@ -32,7 +32,6 @@ main(int argc, char *argv[]) { const char *archive_file = NULL; const char *input_handle_str = NULL; const char *output_handle_str = NULL; - const char *error_handle_str = NULL; const char *interactive_console_str = NULL; if (argc > 1) { @@ -45,10 +44,7 @@ main(int argc, char *argv[]) { output_handle_str = argv[3]; } if (argc > 4) { - error_handle_str = argv[4]; - } - if (argc > 5) { - interactive_console_str = argv[5]; + interactive_console_str = argv[4]; } if (archive_file == NULL || *archive_file == '\0') { @@ -74,15 +70,6 @@ main(int argc, char *argv[]) { } } - FHandle error_handle = invalid_fhandle; - if (error_handle_str != NULL && *error_handle_str) { - stringstream stream(error_handle_str); - stream >> error_handle; - if (!stream) { - error_handle = invalid_fhandle; - } - } - bool interactive_console = false; if (interactive_console_str != NULL && *interactive_console_str) { stringstream stream(interactive_console_str); @@ -93,12 +80,11 @@ main(int argc, char *argv[]) { } } - cerr << "handles: " << input_handle << ", " << output_handle - << ", " << error_handle << "\n"; + cerr << "handles: " << input_handle << ", " << output_handle << "\n"; cerr << "interactive_console = " << interactive_console << "\n"; if (!run_p3dpython(program_name, archive_file, input_handle, output_handle, - error_handle, interactive_console)) { + NULL, interactive_console)) { cerr << "Failure on startup.\n"; return 1; } diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 9ef6ce6c36..9e9fd01781 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -31,7 +31,7 @@ P3DPythonRun *P3DPythonRun::_global_ptr = NULL; P3DPythonRun:: P3DPythonRun(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, - FHandle error_handle, bool interactive_console) { + const char *log_pathname, bool interactive_console) { _read_thread_continue = false; _program_continue = true; _session_terminated = false; @@ -74,11 +74,14 @@ P3DPythonRun(const char *program_name, const char *archive_file, PySys_SetArgv(_py_argc, _py_argv); // Open the error output before we do too much more. - _error_log.open_write(error_handle); - if (!_error_log.fail()) { - // Set up the indicated error log as the Notify output. - _error_log.setf(ios::unitbuf); - Notify::ptr()->set_ostream_ptr(&_error_log, false); + if (log_pathname != NULL && *log_pathname != '\0') { + Filename f = Filename::from_os_specific(log_pathname); + f.set_text(); + if (f.open_write(_error_log)) { + // Set up the indicated error log as the Notify output. + _error_log.setf(ios::unitbuf); + Notify::ptr()->set_ostream_ptr(&_error_log, false); + } } // Open the pipe streams with the input and output handles from the @@ -103,6 +106,9 @@ P3DPythonRun(const char *program_name, const char *archive_file, //////////////////////////////////////////////////////////////////// P3DPythonRun:: ~P3DPythonRun() { + // Close the write pipe, so the parent process will terminate us. + _pipe_write.close(); + Py_Finalize(); join_read_thread(); @@ -339,22 +345,32 @@ run_python() { Py_DECREF(check_comm); - // Finally, get lost in taskMgr.run(). - bool okflag = true; - PyObject *done = PyObject_CallMethod(_taskMgr, (char *)"run", (char *)""); + // Finally, get lost in AppRunner.run() (which is really a call to + // taskMgr.run()). + PyObject *done = PyObject_CallMethod(_runner, (char *)"run", (char *)""); if (done == NULL) { + // An uncaught application exception, and not handled by + // appRunner.exceptionHandler. PyErr_Print(); - okflag = false; - } else { - Py_DECREF(done); + + if (_interactive_console) { + // Give an interactive user a chance to explore the exception. + run_interactive_console(); + return true; + } + + // We're done. + return false; } + // A normal exit from the taskManager. We're presumably done. + Py_DECREF(done); + if (_interactive_console) { run_interactive_console(); - okflag = true; } - return okflag; + return true; } //////////////////////////////////////////////////////////////////// @@ -1032,8 +1048,6 @@ spawn_read_thread() { void P3DPythonRun:: join_read_thread() { _read_thread_continue = false; - _pipe_read.close(); - JOIN_THREAD(_read_thread); } diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index e31d607696..02331386fd 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -26,6 +26,7 @@ #include "p3d_lock.h" #include "handleStream.h" #include "p3dCInstance.h" +#include "pandaFileStreamBuf.h" #include "pythonTask.h" #include "pmap.h" #include "pdeque.h" @@ -68,7 +69,7 @@ class P3DPythonRun { public: P3DPythonRun(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, - FHandle error_handle, bool interactive_console); + const char *log_pathname, bool interactive_console); ~P3DPythonRun(); bool run_python(); @@ -155,7 +156,7 @@ private: HandleStream _pipe_read; HandleStream _pipe_write; - HandleStream _error_log; + pofstream _error_log; bool _read_thread_continue; bool _program_continue; diff --git a/direct/src/plugin/p3dSession.I b/direct/src/plugin/p3dSession.I index 2f607a0038..70afed80fa 100644 --- a/direct/src/plugin/p3dSession.I +++ b/direct/src/plugin/p3dSession.I @@ -25,14 +25,16 @@ get_session_key() const { } //////////////////////////////////////////////////////////////////// -// Function: P3DSession::get_python_version +// Function: P3DSession::get_log_pathname // Access: Public -// Description: Returns a string that uniquely identifies this -// session's required Python version. +// Description: Returns the log filename for this particular session, +// if the session was started and if it has a log file. +// Returns empty string if the session never started or +// if it lacks a log file. //////////////////////////////////////////////////////////////////// inline const string &P3DSession:: -get_python_version() const { - return _python_version; +get_log_pathname() const { + return _log_pathname; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 5903c3dc09..d42bb40151 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -52,7 +52,6 @@ P3DSession(P3DInstance *inst) { P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); _session_id = inst_mgr->get_unique_id(); _session_key = inst->get_session_key(); - _python_version = inst->get_python_version(); _start_dir = inst_mgr->get_root_dir() + "/start"; _p3dpython_one_process = false; @@ -180,9 +179,6 @@ shutdown() { _p3dpython_running = false; _p3dpython_started = false; - - // Close the pipe now. - _pipe_read.close(); } // If there are any leftover commands in the queue (presumably @@ -195,6 +191,9 @@ shutdown() { _commands.clear(); join_read_thread(); + + // Close the pipe now. + _pipe_read.close(); } //////////////////////////////////////////////////////////////////// @@ -211,7 +210,6 @@ void P3DSession:: start_instance(P3DInstance *inst) { assert(inst->_session == NULL); assert(inst->get_session_key() == _session_key); - assert(inst->get_python_version() == _python_version); inst->ref(); ACQUIRE_LOCK(_instances_lock); @@ -684,6 +682,10 @@ start_p3dpython(P3DInstance *inst) { mkdir_complete(_start_dir, nout); } + // Also make sure the prc directory is present. + string prc_root = inst_mgr->get_root_dir() + "/prc"; + mkdir_complete(prc_root, nout); + #ifdef _WIN32 char sep = ';'; #else @@ -716,7 +718,14 @@ start_p3dpython(P3DInstance *inst) { string dyld_path = search_path; string python_path = search_path; - string prc_path = search_path; + string prc_path = prc_root + sep + search_path; + + string prc_name = inst->get_fparams().lookup_token("prc_name"); + if (!prc_name.empty()) { + // Add the prc_name to the path too, even if this directory doesn't + // actually exist. + prc_path = inst_mgr->get_root_dir() + "/" + prc_name + sep + prc_path; + } if (keep_pythonpath) { // With keep_pythonpath true, we preserve the PYTHONPATH setting @@ -929,34 +938,8 @@ start_p3dpython(P3DInstance *inst) { // Create the error stream for log output. This means opening the // logfile, if we have one, or keeping the standard error if we // don't. - _got_error_handle = false; #ifdef _WIN32 - _error_handle = GetStdHandle(STD_ERROR_HANDLE); - if (!_log_pathname.empty()) { - HANDLE handle = CreateFile - (_log_pathname.c_str(), GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, CREATE_ALWAYS, 0, NULL); - if (handle != INVALID_HANDLE_VALUE) { - _error_handle = handle; - SetHandleInformation(_error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - _got_error_handle = true; - } else { - nout << "Unable to open " << _log_pathname << "\n"; - } - } #else // _WIN32 - _error_handle = STDERR_FILENO; - if (!_log_pathname.empty()) { - int logfile_fd = open(_log_pathname.c_str(), - O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (logfile_fd < 0) { - nout << "Unable to open " << _log_pathname << "\n"; - } else { - _error_handle = logfile_fd; - _got_error_handle = true; - } - } #endif // _WIN32 // Get the filename of the Panda3D multifile. We need to pass this @@ -1049,7 +1032,6 @@ join_read_thread() { } _read_thread_continue = false; - _pipe_read.close(); JOIN_THREAD(_read_thread); _started_read_thread = false; @@ -1171,10 +1153,34 @@ win_create_process() { // Make sure we see an error dialog if there is a missing DLL. SetErrorMode(0); + // Open the log file. + HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE); + bool got_error_handle = false; + if (!_log_pathname.empty()) { + HANDLE handle = CreateFile + (_log_pathname.c_str(), GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, CREATE_ALWAYS, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) { + error_handle = handle; + SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + got_error_handle = true; + } else { + nout << "Unable to open " << _log_pathname << "\n"; + } + } + STARTUPINFO startup_info; ZeroMemory(&startup_info, sizeof(STARTUPINFO)); startup_info.cb = sizeof(startup_info); + // Set up the I/O handles. We send stderr and stdout to our + // error_handle. + startup_info.hStdError = error_handle; + startup_info.hStdOutput = error_handle; + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + // Make sure the "python" console window is hidden. startup_info.wShowWindow = SW_HIDE; startup_info.dwFlags |= STARTF_USESHOWWINDOW; @@ -1191,8 +1197,7 @@ win_create_process() { ostringstream stream; stream << "\"" << _p3dpython_exe << "\" \"" << _mf_filename << "\" \"" << _input_handle << "\" \"" << _output_handle - << "\" \"" << _error_handle << "\" \"" << _interactive_console - << "\""; + << "\" \"" << _interactive_console << "\""; // I'm not sure why CreateProcess wants a non-const char pointer for // its command-line string, but I'm not taking chances. It gets a @@ -1213,8 +1218,8 @@ win_create_process() { // Close the pipe handles that are now owned by the child. CloseHandle(_output_handle); CloseHandle(_input_handle); - if (_got_error_handle) { - CloseHandle(_error_handle); + if (got_error_handle) { + CloseHandle(error_handle); } if (!started_program) { @@ -1263,6 +1268,20 @@ posix_create_process() { _pipe_read.close(); _pipe_write.close(); + if (!_log_pathname.empty()) { + // Open a logfile. + int logfile_fd = open(_log_pathname.c_str(), + O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (logfile_fd < 0) { + nout << "Unable to open " << _log_pathname << "\n"; + } else { + // Redirect stderr and stdout onto our logfile. + dup2(logfile_fd, STDERR_FILENO); + dup2(logfile_fd, STDOUT_FILENO); + close(logfile_fd); + } + } + if (_use_start_dir) { if (chdir(_start_dir.c_str()) < 0) { nout << "Could not chdir to " << _start_dir << "\n"; @@ -1290,13 +1309,9 @@ posix_create_process() { output_handle_stream << _output_handle; string output_handle_str = output_handle_stream.str(); - stringstream error_handle_stream; - error_handle_stream << _error_handle; - string error_handle_str = error_handle_stream.str(); - execle(_p3dpython_exe.c_str(), _p3dpython_exe.c_str(), _mf_filename.c_str(), input_handle_str.c_str(), - output_handle_str.c_str(), error_handle_str.c_str(), + output_handle_str.c_str(), _interactive_console ? "1" : "0", (char *)0, &ptrs[0]); nout << "Failed to exec " << _p3dpython_exe << "\n"; _exit(1); @@ -1305,9 +1320,6 @@ posix_create_process() { // Close the handles that are now owned by the child. close(_input_handle); close(_output_handle); - if (_got_error_handle) { - close(_error_handle); - } return child; } @@ -1391,7 +1403,7 @@ p3dpython_thread_run() { } if (!run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(), - _input_handle, _output_handle, _error_handle, + _input_handle, _output_handle, _log_pathname.c_str(), _interactive_console)) { nout << "Failure on startup.\n"; } diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index f330b32e0a..75006ffebd 100644 --- a/direct/src/plugin/p3dSession.h +++ b/direct/src/plugin/p3dSession.h @@ -43,7 +43,7 @@ public: void shutdown(); inline const string &get_session_key() const; - inline const string &get_python_version() const; + inline const string &get_log_pathname() const; void start_instance(P3DInstance *inst); void terminate_instance(P3DInstance *inst); @@ -88,7 +88,6 @@ private: private: int _session_id; string _session_key; - string _python_version; string _log_pathname; string _python_root_dir; string _start_dir; @@ -100,8 +99,7 @@ private: string _p3dpython_dll; string _mf_filename; string _env; - FHandle _input_handle, _output_handle, _error_handle; - bool _got_error_handle; + FHandle _input_handle, _output_handle; bool _interactive_console; typedef map Instances; diff --git a/direct/src/plugin/p3dStringObject.cxx b/direct/src/plugin/p3dStringObject.cxx index bb89f1ca22..45bbed1f2e 100644 --- a/direct/src/plugin/p3dStringObject.cxx +++ b/direct/src/plugin/p3dStringObject.cxx @@ -23,6 +23,15 @@ P3DStringObject:: P3DStringObject(const string &value) : _value(value) { } +//////////////////////////////////////////////////////////////////// +// Function: P3DStringObject::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DStringObject:: +P3DStringObject(const char *data, size_t size) : _value(data, size) { +} + //////////////////////////////////////////////////////////////////// // Function: P3DStringObject::Copy Constructor // Access: Public diff --git a/direct/src/plugin/p3dStringObject.h b/direct/src/plugin/p3dStringObject.h index 1f12cd2c26..bce0b62f92 100644 --- a/direct/src/plugin/p3dStringObject.h +++ b/direct/src/plugin/p3dStringObject.h @@ -25,6 +25,7 @@ class P3DStringObject : public P3DObject { public: P3DStringObject(const string &value); + P3DStringObject(const char *data, size_t size); P3DStringObject(const P3DStringObject ©); public: diff --git a/direct/src/plugin/run_p3dpython.cxx b/direct/src/plugin/run_p3dpython.cxx index c2a54769be..e5db32d10e 100644 --- a/direct/src/plugin/run_p3dpython.cxx +++ b/direct/src/plugin/run_p3dpython.cxx @@ -24,10 +24,10 @@ bool run_p3dpython(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, - FHandle error_handle, bool interactive_console) { + const char *log_pathname, bool interactive_console) { P3DPythonRun::_global_ptr = new P3DPythonRun(program_name, archive_file, input_handle, output_handle, - error_handle, interactive_console); + log_pathname, interactive_console); bool result = P3DPythonRun::_global_ptr->run_python(); delete P3DPythonRun::_global_ptr; P3DPythonRun::_global_ptr = NULL; diff --git a/direct/src/plugin/run_p3dpython.h b/direct/src/plugin/run_p3dpython.h index ee3b605441..21d5430f54 100644 --- a/direct/src/plugin/run_p3dpython.h +++ b/direct/src/plugin/run_p3dpython.h @@ -29,12 +29,12 @@ typedef bool run_p3dpython_func(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, - FHandle error_handle, bool interactive_console); + const char *log_pathname, bool interactive_console); extern "C" EXPCL_P3DPYTHON bool run_p3dpython(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, - FHandle error_handle, bool interactive_console); + const char *log_pathname, bool interactive_console); #endif diff --git a/direct/src/showbase/VFSImporter.py b/direct/src/showbase/VFSImporter.py index e432db9b12..9864607210 100644 --- a/direct/src/showbase/VFSImporter.py +++ b/direct/src/showbase/VFSImporter.py @@ -182,6 +182,7 @@ class VFSLoader: filename = Filename(self.filename) filename.setExtension('py') + filename.setText() vfile = vfs.getFile(filename) if not vfile: raise IOError