Browse Source

Fix Python 3 crash on Windows, and fix some Python 3-related compile errors

rdb 11 years ago
parent
commit
902bedc314

+ 41 - 10
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -31,6 +31,9 @@
 // And this is for GetModuleFileName().
 #include <windows.h>
 
+// And this is for CommandLineToArgvW.
+#include <shellapi.h>
+
 // SHGetSpecialFolderPath()
 #include <shlobj.h>
 #endif
@@ -74,12 +77,11 @@ extern char **environ;
 #define PREREAD_ENVIRONMENT
 #endif
 
-
 // We define the symbol HAVE_GLOBAL_ARGV if we have global variables
 // named GLOBAL_ARGC/GLOBAL_ARGV that we can read at static init time
 // to determine our command-line arguments.
 
-#if defined(HAVE_GLOBAL_ARGV) && defined(PROTOTYPE_GLOBAL_ARGV)
+#if !defined(WIN32_VC) && defined(HAVE_GLOBAL_ARGV) && defined(PROTOTYPE_GLOBAL_ARGV)
 extern char **GLOBAL_ARGV;
 extern int GLOBAL_ARGC;
 #endif
@@ -298,7 +300,7 @@ ns_get_environment_variable(const string &var) const {
 #endif
         }
       }
-      
+
       PyGILState_Release(state);
 
       if (main_dir.empty()) {
@@ -762,7 +764,32 @@ read_args() {
   // Next we need to fill in _args, which is a vector containing
   // the command-line arguments that the executable was invoked with.
 
-#if defined(IS_FREEBSD)
+#if defined(WIN32_VC)
+
+  // We cannot rely on __argv when Python is linked in Unicode mode.
+  // Instead, let's use GetCommandLine.
+
+  LPWSTR cmdline = GetCommandLineW();
+  int argc = 0;
+  LPWSTR *argv = CommandLineToArgvW(cmdline, &argc);
+
+  TextEncoder encoder;
+  encoder.set_encoding(Filename::get_filesystem_encoding());
+
+  for (int i = 0; i < argc; ++i) {
+    wstring wtext(argv[i]);
+    encoder.set_wtext(wtext);
+
+    if (i == 0) {
+      if (_binary_name.empty()) {
+        _binary_name = encoder.get_text();
+      }
+    } else {
+      _args.push_back(encoder.get_text());
+    }
+  }
+
+#elif defined(IS_FREEBSD)
   // In FreeBSD, we can use sysctl to determine the command-line arguments.
 
   size_t bufsize = 4096;
@@ -786,13 +813,17 @@ read_args() {
 #elif defined(HAVE_GLOBAL_ARGV)
   int argc = GLOBAL_ARGC;
 
-  if (_binary_name.empty() && argc > 0) {
-    _binary_name = GLOBAL_ARGV[0];
-    // This really needs to be resolved against PATH.
-  }
+  // On Windows, __argv can be NULL when the main entry point is
+  // compiled in Unicode mode (as is the case with Python 3)
+  if (GLOBAL_ARGV != NULL) {
+    if (_binary_name.empty() && argc > 0) {
+      _binary_name = GLOBAL_ARGV[0];
+      // This really needs to be resolved against PATH.
+    }
 
-  for (int i = 1; i < argc; i++) {
-    _args.push_back(GLOBAL_ARGV[i]);
+    for (int i = 1; i < argc; i++) {
+      _args.push_back(GLOBAL_ARGV[i]);
+    }
   }
 
 #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)

+ 15 - 0
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -3292,6 +3292,21 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
       expected_params += "string";
       ++num_params;
 
+    } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) {
+      if (args_type == AT_single_arg) {
+        // This is a single-arg function, so there's no need
+        // to convert anything.
+        param_name = "arg";
+        extra_param_check += " && PyUnicode_Check(arg)";
+      } else {
+        indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
+        format_specifiers += "U";
+        parameter_list += ", &" + param_name;
+      }
+      pexpr_string = param_name;
+      expected_params += "unicode";
+      ++num_params;
+
     } else if (TypeManager::is_pointer_to_PyObject(type)) {
       if (args_type == AT_single_arg) {
         // This is a single-arg function, so there's no need

+ 39 - 0
dtool/src/interrogate/typeManager.cxx

@@ -1251,6 +1251,45 @@ is_PyStringObject(CPPType *type) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TypeManager::is_pointer_to_PyUnicodeObject
+//       Access: Public, Static
+//  Description: Returns true if the indicated type is PyStringObject *.
+////////////////////////////////////////////////////////////////////
+bool TypeManager::
+is_pointer_to_PyUnicodeObject(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_pointer_to_PyUnicodeObject(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_pointer:
+    return is_PyUnicodeObject(type->as_pointer_type()->_pointing_at);
+
+  default:
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypeManager::is_PyUnicodeObject
+//       Access: Public, Static
+//  Description: Returns true if the indicated type is PyUnicodeObject.
+////////////////////////////////////////////////////////////////////
+bool TypeManager::
+is_PyUnicodeObject(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_PyUnicodeObject(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_extension:
+  case CPPDeclaration::ST_struct:
+    return (type->get_local_name(&parser) == "PyUnicodeObject");
+
+  default:
+    return false;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TypeManager::is_pointer_to_Py_buffer
 //       Access: Public, Static

+ 2 - 0
dtool/src/interrogate/typeManager.h

@@ -96,6 +96,8 @@ public:
   static bool is_PyObject(CPPType *type);
   static bool is_pointer_to_PyStringObject(CPPType *type);
   static bool is_PyStringObject(CPPType *type);
+  static bool is_pointer_to_PyUnicodeObject(CPPType *type);
+  static bool is_PyUnicodeObject(CPPType *type);
   static bool is_pointer_to_Py_buffer(CPPType *type);
   static bool is_Py_buffer(CPPType *type);
   static bool involves_unpublished(CPPType *type);

+ 8 - 6
makepanda/makepandacore.py

@@ -221,7 +221,7 @@ def ProgressOutput(progress, msg, target = None):
             prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
     else:
         global THREADS
-        
+
         ident = thread.get_ident()
         if (ident not in THREADS):
             THREADS[ident] = len(THREADS) + 1
@@ -469,7 +469,7 @@ def LocateBinary(binary):
         p = os.environ["PATH"]
 
     pathList = p.split(os.pathsep)
-    
+
     if GetHost() == 'windows':
         if not binary.endswith('.exe'):
             # Append .exe if necessary
@@ -871,7 +871,7 @@ def CxxCalcDependencies(srcfile, ipath, ignore):
 ########################################################################
 
 if sys.platform == "win32":
-    # Note: not supported on cygwin. 
+    # Note: not supported on cygwin.
     if sys.version_info >= (3, 0):
         import winreg
     else:
@@ -1132,7 +1132,7 @@ def GetThirdpartyBase():
     THIRDPARTYBASE = "thirdparty"
     if "MAKEPANDA_THIRDPARTY" in os.environ:
         THIRDPARTYBASE = os.environ["MAKEPANDA_THIRDPARTY"]
-    
+
     return THIRDPARTYBASE
 
 def GetThirdpartyDir():
@@ -1853,7 +1853,7 @@ def SdkLocatePython(force_use_sys_executable = False):
         force_use_sys_executable = False
 
     if (GetTarget() == 'windows' and not force_use_sys_executable):
-        SDK["PYTHON"] = GetThirdpartyBase()+"/win-python"
+        SDK["PYTHON"] = GetThirdpartyBase() + "/win-python"
         if (GetOptimize() <= 2):
             SDK["PYTHON"] += "-dbg"
         if (GetTargetArch() == 'x64' and os.path.isdir(SDK["PYTHON"] + "-x64")):
@@ -1882,6 +1882,8 @@ def SdkLocatePython(force_use_sys_executable = False):
         py_dll = os.path.basename(py_dlls[0])
         SDK["PYTHONVERSION"] = "python" + py_dll[6] + "." + py_dll[7]
 
+        os.environ["PYTHONHOME"] = SDK["PYTHON"]
+
     elif CrossCompiling():
         tp_python = os.path.join(GetThirdpartyDir(), "python")
         SDK["PYTHON"] = tp_python + "/include"
@@ -2804,4 +2806,4 @@ def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0, winrc=0):
         t.deps[FindLocation("dtool_have_python.dat", [])] = 1
 
     if target.endswith(".pz") and not CrossCompiling():
-        t.deps[FindLocation("pzip.exe", [])] = 1     
+        t.deps[FindLocation("pzip.exe", [])] = 1

+ 8 - 3
panda/src/gobj/internalName_ext.cxx

@@ -30,8 +30,13 @@ make(PyUnicodeObject *str) {
   if (!PyUnicode_CHECK_INTERNED(str)) {
     // Not an interned string; don't bother.
     Py_ssize_t len = 0;
-    char *c_str = PyUnicode_AsUTF8AndSize(str, &len);
-    return InternalName::make(name);
+    char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
+    if (c_str == NULL) {
+      return NULL;
+    }
+
+    string name(c_str, len);
+    return InternalName::make(c_str, len);
   }
 
   InternalName::PyInternTable::const_iterator it;
@@ -42,7 +47,7 @@ make(PyUnicodeObject *str) {
 
   } else {
     Py_ssize_t len = 0;
-    char *c_str = PyUnicode_AsUTF8AndSize(str, &len);
+    char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
     string name(c_str, len);
 
 #else