Browse Source

Python 3 support, and a compile fix

rdb 13 years ago
parent
commit
62a4e4122c
3 changed files with 355 additions and 312 deletions
  1. 33 38
      makepanda/installpanda.py
  2. 193 187
      makepanda/makepanda.py
  3. 129 87
      makepanda/makepandacore.py

+ 33 - 38
makepanda/installpanda.py

@@ -39,59 +39,56 @@ APP_INFO_PLUGIN = (
 def WriteApplicationsFile(fname, appinfo, mimeinfo):
     fhandle = open(fname, "w")
     for app, desc, exts in appinfo:
-        print >>fhandle, app
-        print >>fhandle, "\tcommand=" + app
-        print >>fhandle, "\tname=" + desc
-        print >>fhandle, "\tcan_open_multiple_files=true"
-        print >>fhandle, "\texpects_uris=false"
-        print >>fhandle, "\trequires_terminal=false"
-        print >>fhandle, "\tmime_types=",
+        fhandle.write("%s\n" % (app))
+        fhandle.write("\tcommand=%s\n" % (app))
+        fhandle.write("\tname=%s\n" % (desc))
+        fhandle.write("\tcan_open_multiple_files=true\n")
+        fhandle.write("\texpects_uris=false\n")
+        fhandle.write("\trequires_terminal=false\n")
+        fhandle.write("\tmime_types=")
         first = True
         for ext, mime, desc2, app2 in mimeinfo:
-            if (ext in exts):
-                if (first):
+            if ext in exts:
+                if first:
                     fhandle.write(mime)
                     first = False
                 else:
                     fhandle.write("," + mime)
-        fhandle.write("\n")
-        print >>fhandle
+        fhandle.write("\n\n")
     fhandle.close()
 
 def WriteMimeXMLFile(fname, info):
     fhandle = open(fname, "w")
-    print >>fhandle, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-    print >>fhandle
-    print >>fhandle, "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">"
+    fhandle.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+    fhandle.write("<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n")
     for ext, mime, desc, app in info:
-        print >>fhandle, "\t<mime-type type=\"" + mime + "\">"
-        print >>fhandle, "\t\t<comment xml:lang=\"en\">" + desc + "</comment>"
-        print >>fhandle, "\t\t<glob pattern=\"*." + ext + "\"/>"
-        print >>fhandle, "\t</mime-type>"
-    print >>fhandle, "</mime-info>"
-    print >>fhandle
+        fhandle.write("\t<mime-type type=\"%s\">\n" % (mime))
+        fhandle.write("\t\t<comment xml:lang=\"en\">%s</comment>\n" % (desc))
+        fhandle.write("\t\t<glob pattern=\"*.%s\"/>\n" % (ext))
+        fhandle.write("\t</mime-type>\s")
+    fhandle.write("</mime-info>\n")
     fhandle.close()
 
 def WriteMimeFile(fname, info):
     fhandle = open(fname, "w")
     for ext, mime, desc, app in info:
-        print >>fhandle, mime + ":"
-        if ("." in ext):
-            print >>fhandle, "\tregex,2: " + ext.replace(".", "\\.") + "$"
-        print >>fhandle, "\text: " + ext
-        print >>fhandle
+        fhandle.write("%s:\n" % (mime))
+        if "." in ext:
+            fhandle.write("\tregex,2: %s$\n" % (ext.replace(".", "\\.")))
+        fhandle.write("\text: %s\n" % (ext))
+        fhandle.write("\n")
     fhandle.close()
 
 def WriteKeysFile(fname, info):
     fhandle = open(fname, "w")
     for ext, mime, desc, app in info:
-        print >>fhandle, mime + ":"
-        print >>fhandle, "\tdescription=" + desc
-        print >>fhandle, "\tdefault_action_type=application"
-        print >>fhandle, "\tshort_list_application_ids_for_novice_user_level=" + app
-        print >>fhandle, "\topen=" + app + " %f"
-        print >>fhandle, "\tview=" + app + " %f"
-        print >>fhandle
+        fhandle.write("%s:\n" % (mime))
+        fhandle.write("\tdescription=%s\n" % (desc))
+        fhandle.write("\tdefault_action_type=application\n")
+        fhandle.write("\tshort_list_application_ids_for_novice_user_level=%s\n" % (app))
+        fhandle.write("\topen=%s %%f\n" % (app))
+        fhandle.write("\tview=%s %%f\n" % (app))
+        fhandle.write("\n")
     fhandle.close()
 
 def GetLibDir():
@@ -229,9 +226,7 @@ if (__name__ == "__main__"):
     if (sys.platform.startswith("win") or sys.platform == "darwin"):
         exit("This script is not supported on Windows or Mac OS X at the moment!")
 
-    destdir = "/"
-    if (os.environ.has_key("DESTDIR")):
-        destdir = os.environ["DESTDIR"]
+    destdir = os.environ.get("DESTDIR", "/")
 
     parser = OptionParser()
     parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built')
@@ -249,10 +244,10 @@ if (__name__ == "__main__"):
         exit("Directory '%s' does not exist!" % destdir)
 
     if (options.runtime):
-        print "Installing Panda3D Runtime into " + destdir + options.prefix
+        print("Installing Panda3D Runtime into " + destdir + options.prefix)
         InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir)
     else:
-        print "Installing Panda3D into " + destdir + options.prefix
+        print("Installing Panda3D into " + destdir + options.prefix)
         InstallPanda(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir)
-    print "Installation finished!"
+    print("Installation finished!")
 

+ 193 - 187
makepanda/makepanda.py

@@ -12,12 +12,16 @@
 #
 ########################################################################
 try:
-    import sys,os,platform,time,stat,string,re,getopt,fnmatch,threading,Queue,signal,shutil
+    import sys,os,platform,time,stat,string,re,getopt,fnmatch,threading,signal,shutil
     if sys.platform == "darwin" or sys.version_info >= (2, 6):
         import plistlib
+    if sys.version_info >= (3, 0):
+        import queue
+    else:
+        import Queue as queue
 except:
-    print "You are either using an incomplete or an old version of Python!"
-    print "Please install the development package of Python 2.x and try again."
+    print("You are either using an incomplete or an old version of Python!")
+    print("Please install the development package of Python 2.x and try again.")
     exit(1)
 
 from makepandacore import *
@@ -29,8 +33,7 @@ import sys
 ## jGenPyCode tries to get the directory for Direct from the sys.path. This only works if you 
 ## have installed the sdk using a installer. This would not work if the installer was 
 ## never used and everything was grabbed into a virgin environment using cvs.
-sys.path.append( os.getcwd() )
-import __builtin__
+sys.path.append(os.getcwd())
 
 ########################################################################
 ##
@@ -115,45 +118,45 @@ signal.signal(signal.SIGINT, keyboardInterruptHandler)
 
 def usage(problem):
     if (problem):
-        print ""
-        print "Error parsing commandline input", problem
-
-    print ""
-    print "Makepanda generates a 'built' subdirectory containing a"
-    print "compiled copy of Panda3D.  Command-line arguments are:"
-    print ""
-    print "  --help            (print the help message you're reading now)"
-    print "  --verbose         (print out more information)"
-    print "  --runtime         (build a runtime build instead of an SDK build)"
-    print "  --installer       (build an installer)"
-    print "  --optimize X      (optimization level can be 1,2,3,4)"
-    print "  --version X       (set the panda version number)"
-    print "  --lzma            (use lzma compression when building Windows installer)"
-    print "  --distributor X   (short string identifying the distributor of the build)"
-    print "  --outputdir X     (use the specified directory instead of 'built')"
-    print "  --host URL        (set the host url (runtime build only))"
-    print "  --threads N       (use the multithreaded build system. see manual)"
-    print "  --osxtarget N     (the OSX version number to build for (OSX only))"
-    print "  --universal       (build universal binaries (OSX only))"
-    print "  --override \"O=V\"  (override dtool_config/prc option value)"
-    print "  --static          (builds libraries for static linking)"
-    print "  --target X        (experimental cross-compilation (android only))"
-    print "  --arch X          (target architecture for cross-compilation)"
-    print ""
+        print("")
+        print("Error parsing commandline input", problem)
+
+    print("")
+    print("Makepanda generates a 'built' subdirectory containing a")
+    print("compiled copy of Panda3D.  Command-line arguments are:")
+    print("")
+    print("  --help            (print the help message you're reading now)")
+    print("  --verbose         (print out more information)")
+    print("  --runtime         (build a runtime build instead of an SDK build)")
+    print("  --installer       (build an installer)")
+    print("  --optimize X      (optimization level can be 1,2,3,4)")
+    print("  --version X       (set the panda version number)")
+    print("  --lzma            (use lzma compression when building Windows installer)")
+    print("  --distributor X   (short string identifying the distributor of the build)")
+    print("  --outputdir X     (use the specified directory instead of 'built')")
+    print("  --host URL        (set the host url (runtime build only))")
+    print("  --threads N       (use the multithreaded build system. see manual)")
+    print("  --osxtarget N     (the OSX version number to build for (OSX only))")
+    print("  --universal       (build universal binaries (OSX only))")
+    print("  --override \"O=V\"  (override dtool_config/prc option value)")
+    print("  --static          (builds libraries for static linking)")
+    print("  --target X        (experimental cross-compilation (android only))")
+    print("  --arch X          (target architecture for cross-compilation)")
+    print("")
     for pkg in PkgListGet():
         p = pkg.lower()
-        print "  --use-%-9s   --no-%-9s (enable/disable use of %s)"%(p, p, pkg)
-    print ""
-    print "  --nothing         (disable every third-party lib)"
-    print "  --everything      (enable every third-party lib)"
-    print "  --directx-sdk=X   (specify version of DX9 SDK to use: jun2010, aug2009, mar2009, aug2006)"
-    print "  --platform-sdk=X  (specify MSPlatSdk to use: win71, win61, win60A, winserver2003r2)"
-    print "  --use-icl         (experimental setting to use an intel compiler instead of MSVC on Windows)"
-    print ""
-    print "The simplest way to compile panda is to just type:"
-    print ""
-    print "  makepanda --everything"
-    print ""
+        print("  --use-%-9s   --no-%-9s (enable/disable use of %s)"%(p, p, pkg))
+    print("")
+    print("  --nothing         (disable every third-party lib)")
+    print("  --everything      (enable every third-party lib)")
+    print("  --directx-sdk=X   (specify version of DX9 SDK to use: jun2010, aug2009, mar2009, aug2006)")
+    print("  --platform-sdk=X  (specify MSPlatSdk to use: win71, win61, win60A, winserver2003r2)")
+    print("  --use-icl         (experimental setting to use an intel compiler instead of MSVC on Windows)")
+    print("")
+    print("The simplest way to compile panda is to just type:")
+    print("")
+    print("  makepanda --everything")
+    print("")
     os._exit(1)
 
 def parseopts(args):
@@ -177,7 +180,7 @@ def parseopts(args):
     try:
         opts, extras = getopt.getopt(args, "", longopts)
         for option,value in opts:
-            if (option=="--help"): raise "usage"
+            if (option=="--help"): raise Exception
             elif (option=="--optimize"): optimize=value
             elif (option=="--installer"): INSTALLER=1
             elif (option=="--verbose"): SetVerbose(True)
@@ -196,7 +199,7 @@ def parseopts(args):
             elif (option=="--nocolor"): DisableColors()
             elif (option=="--version"):
                 VERSION=value
-                if (len(VERSION.split(".")) != 3): raise "usage"
+                if (len(VERSION.split(".")) != 3): raise Exception
             elif (option=="--lzma"): COMPRESSOR="lzma"
             elif (option=="--override"): AddOverride(value.strip())
             elif (option=="--static"): SetLinkAllStatic(True)
@@ -210,12 +213,12 @@ def parseopts(args):
             elif (option=="--directx-sdk"):
                 STRDXSDKVERSION = value.strip().lower()
                 if STRDXSDKVERSION == '':
-                    print "No DirectX SDK version specified. Using 'default' DirectX SDK search"
+                    print("No DirectX SDK version specified. Using 'default' DirectX SDK search")
                     STRDXSDKVERSION = 'default'
             elif (option=="--platform-sdk"): 
                 STRMSPLATFORMVERSION = value.strip().lower()
                 if STRMSPLATFORMVERSION == '':
-                    print "No MS Platform SDK version specified. Using 'default' MS Platform SDK search"
+                    print("No MS Platform SDK version specified. Using 'default' MS Platform SDK search")
                     STRMSPLATFORMVERSION = 'default'
             elif (option=="--use-icl"): BOOUSEINTELCOMPILER = True
             else:
@@ -232,7 +235,7 @@ def parseopts(args):
               anything = 1
     except: 
         usage(0)
-        print "Exception while parsing commandline:", sys.exc_info()[0]
+        print("Exception while parsing commandline:", sys.exc_info()[0])
     if (anything==0): usage(0)
     if (RTDIST and RUNTIME):
         usage("Options --runtime and --rtdist cannot be specified at the same time!")
@@ -809,30 +812,30 @@ if GetTarget() == 'android':
 
 def printStatus(header,warnings):
     if GetVerbose():
-        print ""
-        print "-------------------------------------------------------------------"
-        print header
+        print("")
+        print("-------------------------------------------------------------------")
+        print(header)
         tkeep = ""
         tomit = ""
         for x in PkgListGet():
             if (PkgSkip(x)==0): tkeep = tkeep + x + " "
             else:                  tomit = tomit + x + " "
-        if RTDIST:  print "Makepanda: Runtime distribution build"
-        elif RUNTIME: print "Makepanda: Runtime build"
-        else:        print "Makepanda: Regular build"
-        print "Makepanda: Compiler:",COMPILER
-        print "Makepanda: Optimize:",GetOptimize()
-        print "Makepanda: Keep Pkg:",tkeep
-        print "Makepanda: Omit Pkg:",tomit
-        if (GENMAN): print "Makepanda: Generate API reference manual"
-        else       : print "Makepanda: Don't generate API reference manual"
+        if RTDIST:  print("Makepanda: Runtime distribution build")
+        elif RUNTIME: print("Makepanda: Runtime build")
+        else:        print("Makepanda: Regular build")
+        print("Makepanda: Compiler:",COMPILER)
+        print("Makepanda: Optimize:",GetOptimize())
+        print("Makepanda: Keep Pkg:",tkeep)
+        print("Makepanda: Omit Pkg:",tomit)
+        if (GENMAN): print("Makepanda: Generate API reference manual")
+        else       : print("Makepanda: Don't generate API reference manual")
         if (sys.platform == "win32" and not RTDIST):
-            if INSTALLER:  print "Makepanda: Build installer, using",COMPRESSOR
-            else        :  print "Makepanda: Don't build installer"
-        print "Makepanda: Version ID: "+VERSION
-        for x in warnings: print "Makepanda: "+x
-        print "-------------------------------------------------------------------"
-        print ""
+            if INSTALLER:  print("Makepanda: Build installer, using",COMPRESSOR)
+            else        :  print("Makepanda: Don't build installer")
+        print("Makepanda: Version ID: "+VERSION)
+        for x in warnings: print("Makepanda: "+x)
+        print("-------------------------------------------------------------------")
+        print("")
         sys.stdout.flush()
 
 ########################################################################
@@ -1133,10 +1136,10 @@ def CompileIgate(woutd,wsrc,opts):
     library = GetValueOption(opts, "ILIB:")
     ipath = GetListOption(opts, "DIR:")
     if (PkgSkip("PYTHON")):
-        WriteFile(woutc,"")
-        WriteFile(woutd,"")
-        CompileCxx(wobj,woutc,opts)
-        ConditionalWriteFile(woutd,"")
+        WriteFile(woutc, "")
+        WriteFile(woutd, "")
+        CompileCxx(wobj, woutc, opts)
+        ConditionalWriteFile(woutd, "")
         return
 
     if not CrossCompiling():
@@ -1202,8 +1205,8 @@ def CompileImod(wobj, wsrc, opts):
     if (COMPILER=="GCC"):
         woutc = wobj[:-2]+".cxx"
     if (PkgSkip("PYTHON")):
-        WriteFile(woutc,"")
-        CompileCxx(wobj,woutc,opts)
+        WriteFile(woutc, "")
+        CompileCxx(wobj, woutc, opts)
         return
 
     if not CrossCompiling():
@@ -1709,15 +1712,15 @@ def CompileAnything(target, inputs, opts, progress = None):
 
         # Add version number to the dynamic library, on unix
         if origsuffix==".dll" and "MODULE" not in opts and not RTDIST:
-            target = GetTarget()
-            if target == "darwin":
+            tplatform = GetTarget()
+            if tplatform == "darwin":
                 # On Mac, libraries are named like libpanda.1.2.dylib
-                if target.lower().endswith(".dylib"):
-                    target = target[:-5] + MAJOR_VERSION + ".dylib"
+                if tplatform.lower().endswith(".dylib"):
+                    tplatform = tplatform[:-5] + MAJOR_VERSION + ".dylib"
                     SetOrigExt(target, origsuffix)
-            elif target != "windows" and target != "android":
+            elif tplatform != "windows" and tplatform != "android":
                 # On Linux, libraries are named like libpanda.so.1.2
-                target = target + "." + MAJOR_VERSION
+                tplatform += "." + MAJOR_VERSION
                 SetOrigExt(target, origsuffix)
         return CompileLink(target, inputs, opts)
     elif (origsuffix==".in"):
@@ -2100,7 +2103,6 @@ def WriteConfigSettings():
             val = OverrideValue(key, prc_parameters[key])
             if (val == 'UNDEF'): conf = conf + "#undef " + key + "\n"
             else:                conf = conf + "#define " + key + " " + val + "\n"
-            del prc_parameters[key]
     ConditionalWriteFile(GetOutputDir() + '/include/prc_parameters.h', conf)
 
     conf = "/* dtool_config.h.  Generated automatically by makepanda.py */\n"
@@ -2108,7 +2110,6 @@ def WriteConfigSettings():
         val = OverrideValue(key, dtool_config[key])
         if (val == 'UNDEF'): conf = conf + "#undef " + key + "\n"
         else:                conf = conf + "#define " + key + " " + val + "\n"
-        del dtool_config[key]
     ConditionalWriteFile(GetOutputDir() + '/include/dtool_config.h', conf)
 
     if (RTDIST or RUNTIME):
@@ -2117,7 +2118,6 @@ def WriteConfigSettings():
             val = plugin_config[key]
             if (val == 'UNDEF'): conf = conf + "#undef " + key + "\n"
             else:                conf = conf + "#define " + key + " \"" + val.replace("\\", "\\\\") + "\"\n"
-            del plugin_config[key]
         ConditionalWriteFile(GetOutputDir() + '/include/p3d_plugin_config.h', conf)
 
     if (PkgSkip("SPEEDTREE")==0):
@@ -2126,7 +2126,6 @@ def WriteConfigSettings():
             val = OverrideValue(key, speedtree_parameters[key])
             if (val == 'UNDEF'): conf = conf + "#undef " + key + "\n"
             else:                conf = conf + "#define " + key + " \"" + val.replace("\\", "\\\\") + "\"\n"
-            del speedtree_parameters[key]
         ConditionalWriteFile(GetOutputDir() + '/include/speedtree_parameters.h', conf)
 
     for x in PkgListGet():
@@ -2137,9 +2136,9 @@ WriteConfigSettings()
 
 MoveAwayConflictingFiles()
 if "libdtoolbase" in GetLibCache():
-    print "%sWARNING:%s Found conflicting Panda3D libraries from other ppremake build!" % (GetColor("red"), GetColor())
+    print("%sWARNING:%s Found conflicting Panda3D libraries from other ppremake build!" % (GetColor("red"), GetColor()))
 if "libp3dtoolconfig" in GetLibCache():
-    print "%sWARNING:%s Found conflicting Panda3D libraries from other makepanda build!" % (GetColor("red"), GetColor())
+    print("%sWARNING:%s Found conflicting Panda3D libraries from other makepanda build!" % (GetColor("red"), GetColor()))
 
 ##########################################################################################
 #
@@ -2672,7 +2671,7 @@ COMMON_EGG2X_LIBS_PYSTUB = COMMON_EGG2X_LIBS + ['libp3pystub.lib']
 #
 ########################################################################
 
-print "Generating dependencies..."
+print("Generating dependencies...")
 sys.stdout.flush()
 
 #
@@ -5677,7 +5676,7 @@ def BuildWorker(taskqueue, donequeue):
         sys.stdout.flush()
         if (task == 0): return
         try:
-            apply(task[0],task[1])
+            task[0](*task[1])
             donequeue.put(task)
         except:
             donequeue.put(0)
@@ -5699,8 +5698,8 @@ def AllSourcesReady(task, pending):
 
 def ParallelMake(tasklist):
     # Create the communication queues.
-    donequeue=Queue.Queue()
-    taskqueue=Queue.Queue()
+    donequeue = Queue.Queue()
+    taskqueue = Queue.Queue()
     # Build up a table listing all the pending targets
     #task = [CompileAnything, [name, inputs, opts], [name], deps, []]
     # task[2] = [name]
@@ -5727,12 +5726,12 @@ def ParallelMake(tasklist):
             pending[target] = 1
     # Create the workers
     for slave in range(THREADCOUNT):
-        th = threading.Thread(target=BuildWorker, args=[taskqueue,donequeue])
+        th = threading.Thread(target=BuildWorker, args=[taskqueue, donequeue])
         th.setDaemon(1)
         th.start()
     # Feed tasks to the workers.
     tasksqueued = 0
-    while (1):
+    while True:
         if (tasksqueued < THREADCOUNT):
             extras = []
             for task in tasklist:
@@ -5760,7 +5759,7 @@ def ParallelMake(tasklist):
     for slave in range(THREADCOUNT):
         taskqueue.put(0)
     # Make sure there aren't any unsatisfied tasks
-    if (len(tasklist)>0):
+    if len(tasklist) > 0:
         exit("Dependency problems: " + str(len(tasklist)) + " tasks not finished. First task unsatisfied: "+str(tasklist[0][2]))
     SequentialMake(tasklist_seq)
 
@@ -5769,7 +5768,7 @@ def SequentialMake(tasklist):
     i = 0
     for task in tasklist:
         if (NeedsBuild(task[2], task[3])):
-            apply(task[0], task[1] + [(i * 100.0) / len(tasklist)])
+            task[0](*task[1] + [(i * 100.0) / len(tasklist)])
             JustBuilt(task[2], task[3])
         i += 1
 
@@ -5809,7 +5808,7 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
         shutil.move("direct\\src\\plugin_installer\\p3d-setup.exe", file)
         return
 
-    print "Building "+fullname+" installer. This can take up to an hour."
+    print("Building "+fullname+" installer. This can take up to an hour.")
     if (COMPRESSOR != "lzma"):
         print("Note: you are using zlib, which is faster, but lzma gives better compression.")
     if (os.path.exists("nsis-output.exe")):
@@ -5838,11 +5837,11 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
 
     if GetHost() == 'windows':
         cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2'
-        for item in nsis_defs.iteritems():
+        for item in nsis_defs.items():
             cmd += ' /D%s="%s"' % item
     else:
         cmd = 'makensis -V2'
-        for item in nsis_defs.iteritems():
+        for item in nsis_defs.items():
             cmd += ' -D%s="%s"' % item
 
     cmd += ' "%s"' % (os.path.join(psource, 'direct', 'src', 'directscripts', 'packpanda.nsi'))
@@ -5969,6 +5968,41 @@ Info_plist = """<?xml version="1.0" encoding="UTF-8"?>
 </plist>
 """
 
+MAC_POSTINSTALL = """#!/usr/bin/python
+import os, sys, plistlib
+home = os.environ['HOME']
+if not os.path.isdir(os.path.join(home, '.MacOSX')):
+    sys.exit()
+plist = dict()
+envfile = os.path.join(home, '.MacOSX', 'environment.plist')
+if os.path.exists(envfile):
+    try:
+        plist = plistlib.readPlist(envfile)
+    except: sys.exit(0)
+else:
+    sys.exit(0)
+paths = {'PATH' : '/Developer/Tools/Panda3D', 'DYLD_LIBRARY_PATH' : '/Developer/Panda3D/lib', 'PYTHONPATH' : '/Developer/Panda3D/lib',
+         'MAYA_SCRIPT_PATH' : '/Developer/Panda3D/plugins', 'MAYA_PLUG_IN_PATH' : '/Developer/Panda3D/plugins'}
+for env, path in dict(paths).items():
+    if env in plist:
+        paths = plist[env].split(':')
+        if '' in paths: paths.remove('')
+        if path in paths: paths.remove(path)
+        if len(paths) == 0:
+            del plist[env]
+        else:
+            plist[env] = ':'.join(paths)
+if len(plist) == 0:
+    os.remove(envfile)
+else:
+    plistlib.writePlist(plist, envfile)
+"""
+
+MAC_POSTFLIGHT = """#!/usr/bin/env bash"
+RESULT=`/usr/bin/open 'http://www.panda3d.org/wiki/index.php/Getting_Started_on_OSX'`"
+exit 0
+"""
+
 # FreeBSD pkg-descr
 INSTALLER_PKG_DESCR_FILE = """
 Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost.
@@ -6115,39 +6149,10 @@ def MakeInstallerOSX():
     # Temporary script that should clean up the poison that the early 1.7.0 builds injected into environment.plist
     oscmd("mkdir -p dstroot/scripts/base/")
     postinstall = open("dstroot/scripts/base/postinstall", "w")
-    print >>postinstall, "#!/usr/bin/python"
-    print >>postinstall, "import os, sys, plistlib"
-    print >>postinstall, "home = os.environ['HOME']"
-    print >>postinstall, "if not os.path.isdir(os.path.join(home, '.MacOSX')):"
-    print >>postinstall, "    sys.exit()"
-    print >>postinstall, "plist = dict()"
-    print >>postinstall, "envfile = os.path.join(home, '.MacOSX', 'environment.plist')"
-    print >>postinstall, "if os.path.exists(envfile):"
-    print >>postinstall, "    try:"
-    print >>postinstall, "        plist = plistlib.readPlist(envfile)"
-    print >>postinstall, "    except: sys.exit(0)"
-    print >>postinstall, "else:"
-    print >>postinstall, "    sys.exit(0)"
-    print >>postinstall, "paths = {'PATH' : '/Developer/Tools/Panda3D', 'DYLD_LIBRARY_PATH' : '/Developer/Panda3D/lib', 'PYTHONPATH' : '/Developer/Panda3D/lib',"
-    print >>postinstall, "         'MAYA_SCRIPT_PATH' : '/Developer/Panda3D/plugins', 'MAYA_PLUG_IN_PATH' : '/Developer/Panda3D/plugins'}"
-    print >>postinstall, "for env, path in paths.items():"
-    print >>postinstall, "    if env in plist:"
-    print >>postinstall, "        paths = plist[env].split(':')"
-    print >>postinstall, "        if '' in paths: paths.remove('')"
-    print >>postinstall, "        if path in paths: paths.remove(path)"
-    print >>postinstall, "        if len(paths) == 0:"
-    print >>postinstall, "            del plist[env]"
-    print >>postinstall, "        else:"
-    print >>postinstall, "            plist[env] = ':'.join(paths)"
-    print >>postinstall, "if len(plist) == 0:"
-    print >>postinstall, "    os.remove(envfile)"
-    print >>postinstall, "else:"
-    print >>postinstall, "    plistlib.writePlist(plist, envfile)"
+    postinstall.write(MAC_POSTINSTALL)
     postinstall.close()
     postflight = open("dstroot/scripts/base/postflight", "w")
-    print >>postflight, "#!/usr/bin/env bash\n"
-    print >>postflight, "RESULT=`/usr/bin/open 'http://www.panda3d.org/wiki/index.php/Getting_Started_on_OSX'`"
-    print >>postflight, "\nexit 0"
+    postflight.write(MAC_POSTFLIGHT)
     postflight.close()
     oscmd("chmod +x dstroot/scripts/base/postinstall")
     oscmd("chmod +x dstroot/scripts/base/postflight")
@@ -6200,24 +6205,25 @@ def MakeInstallerOSX():
     # Dummy package uninstall16 which just contains a preflight script to remove /Applications/Panda3D/ .
     oscmd("mkdir -p dstroot/scripts/uninstall16/")
     preflight = open("dstroot/scripts/uninstall16/preflight", "w")
-    print >>preflight, "#!/usr/bin/python"
-    print >>preflight, "import os, re, sys, shutil"
-    print >>preflight, "if os.path.isdir('/Applications/Panda3D'): shutil.rmtree('/Applications/Panda3D')"
-    print >>preflight, "bash_profile = os.path.join(os.environ['HOME'], '.bash_profile')"
-    print >>preflight, "if not os.path.isfile(bash_profile): sys.exit(0)"
-    print >>preflight, "pattern = re.compile('''PANDA_VERSION=[0-9][.][0-9][.][0-9]"
-    print >>preflight, "PANDA_PATH=/Applications/Panda3D/[$A-Z.0-9_]+"
-    print >>preflight, "if \[ -d \$PANDA_PATH \]"
-    print >>preflight, "then(.+?)fi"
-    print >>preflight, "''', flags = re.DOTALL | re.MULTILINE)"
-    print >>preflight, "bpfile = open(bash_profile, 'r')"
-    print >>preflight, "bpdata = bpfile.read()"
-    print >>preflight, "bpfile.close()"
-    print >>preflight, "newbpdata = pattern.sub('', bpdata)"
-    print >>preflight, "if newbpdata == bpdata: sys.exit(0)"
-    print >>preflight, "bpfile = open(bash_profile, 'w')"
-    print >>preflight, "bpfile.write(newbpdata)"
-    print >>preflight, "bpfile.close()"
+    preflight.write(
+        "#!/usr/bin/python\n"
+        "import os, re, sys, shutil\n"
+        "if os.path.isdir('/Applications/Panda3D'): shutil.rmtree('/Applications/Panda3D')\n"
+        "bash_profile = os.path.join(os.environ['HOME'], '.bash_profile')\n"
+        "if not os.path.isfile(bash_profile): sys.exit(0)\n"
+        "pattern = re.compile('''PANDA_VERSION=[0-9][.][0-9][.][0-9]\n"
+        "PANDA_PATH=/Applications/Panda3D/[$A-Z.0-9_]+\n"
+        "if \[ -d \$PANDA_PATH \]\n"
+        "then(.+?)fi\n"
+        "''', flags = re.DOTALL | re.MULTILINE)\n"
+        "bpfile = open(bash_profile, 'r')\n"
+        "bpdata = bpfile.read()\n"
+        "bpfile.close()\n"
+        "newbpdata = pattern.sub('', bpdata)\n"
+        "if newbpdata == bpdata: sys.exit(0)\n"
+        "bpfile = open(bash_profile, 'w')\n"
+        "bpfile.write(newbpdata)\n"
+        "bpfile.close()\n")
     preflight.close()
     oscmd("chmod +x dstroot/scripts/uninstall16/preflight")
 
@@ -6260,55 +6266,55 @@ def MakeInstallerOSX():
 
     # Now that we've built all of the individual packages, build the metapackage.
     dist = open("dstroot/Panda3D/Panda3D.mpkg/Contents/distribution.dist", "w")
-    print >>dist, '<?xml version="1.0" encoding="utf-8"?>'
-    print >>dist, '<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.3" authoringToolBuild="174">'
-    print >>dist, '    <title>Panda3D</title>'
-    print >>dist, '    <options customize="always" allow-external-scripts="no" rootVolumeOnly="false"/>'
-    # The following script is to enable the "Uninstall 1.6.x" option only when Panda3D 1.6.x is actually installed.
-    print >>dist, '''    <script>
-function have16installed() {
-  return system.files.fileExistsAtPath(my.target.mountpoint + '/Applications/Panda3D');
-}</script>'''
-    print >>dist, '    <license language="en" mime-type="text/plain">%s</license>' % ReadFile("doc/LICENSE")
-    print >>dist, '    <choices-outline>'
-    print >>dist, '        <line choice="uninstall16"/>'
-    print >>dist, '        <line choice="base"/>'
-    print >>dist, '        <line choice="tools"/>'
+    dist.write('<?xml version="1.0" encoding="utf-8"?>\n')
+    dist.write('<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.3" authoringToolBuild="174">\n')
+    dist.write('    <title>Panda3D</title>\n')
+    dist.write('    <options customize="always" allow-external-scripts="no" rootVolumeOnly="false"/>\n')
+    dist.write('    <script>\n')
+    dist.write('    function have16installed() {\n')
+    dist.write('        return system.files.fileExistsAtPath(my.target.mountpoint + \'/Applications/Panda3D\');\n')
+    dist.write('    }\n')
+    dist.write('    </script>\n')
+    dist.write('    <license language="en" mime-type="text/plain">%s</license>\n' % ReadFile("doc/LICENSE"))
+    dist.write('    <choices-outline>\n')
+    dist.write('        <line choice="uninstall16"/>\n')
+    dist.write('        <line choice="base"/>\n')
+    dist.write('        <line choice="tools"/>\n')
     if PkgSkip("PYTHON")==0:
-        print >>dist, '        <line choice="pythoncode"/>'
+        dist.write('        <line choice="pythoncode"/>\n')
     if os.path.isdir("samples"):
-        print >>dist, '        <line choice="samples"/>'
-    print >>dist, '        <line choice="headers"/>'
-    print >>dist, '    </choices-outline>'
-    print >>dist, '    <choice id="uninstall16" title="Uninstall Panda3D 1.6.x" tooltip="Uninstalls Panda3D 1.6.x before installing Panda3D %s" description="If this option is checked, Panda3D 1.6.x is removed from /Applications/Panda3D/ before the new version is installed. This is recommended to avoid library conflicts. WARNING: EVERYTHING UNDER /Applications/Panda3D WILL BE DELETED. MAKE SURE YOU HAVE BACKED UP IMPORTANT DATA!" selected="have16installed()" enabled="have16installed()" visible="have16installed()">' % VERSION
-    print >>dist, '        <pkg-ref id="org.panda3d.panda3d.uninstall16.pkg"/>'
-    print >>dist, '    </choice>'
-    print >>dist, '    <choice id="base" title="Panda3D Base Installation" description="This package contains the Panda3D libraries, configuration files and models/textures that are needed to use Panda3D. Location: /Developer/Panda3D/" start_enabled="false">'
-    print >>dist, '        <pkg-ref id="org.panda3d.panda3d.base.pkg"/>'
-    print >>dist, '    </choice>'
-    print >>dist, '    <choice id="tools" title="Tools" tooltip="Useful tools and model converters to help with Panda3D development" description="This package contains the various utilities that ship with Panda3D, including packaging tools, model converters, and many more. Location: /Developer/Tools/Panda3D/">'
-    print >>dist, '        <pkg-ref id="org.panda3d.panda3d.tools.pkg"/>'
-    print >>dist, '    </choice>'
+        dist.write('        <line choice="samples"/>\n')
+    dist.write('        <line choice="headers"/>\n')
+    dist.write('    </choices-outline>\n')
+    dist.write('    <choice id="uninstall16" title="Uninstall Panda3D 1.6.x" tooltip="Uninstalls Panda3D 1.6.x before installing Panda3D %s" description="If this option is checked, Panda3D 1.6.x is removed from /Applications/Panda3D/ before the new version is installed. This is recommended to avoid library conflicts. WARNING: EVERYTHING UNDER /Applications/Panda3D WILL BE DELETED. MAKE SURE YOU HAVE BACKED UP IMPORTANT DATA!" selected="have16installed()" enabled="have16installed()" visible="have16installed()">\n' % VERSION)
+    dist.write('        <pkg-ref id="org.panda3d.panda3d.uninstall16.pkg"/>\n')
+    dist.write('    </choice>\n')
+    dist.write('    <choice id="base" title="Panda3D Base Installation" description="This package contains the Panda3D libraries, configuration files and models/textures that are needed to use Panda3D. Location: /Developer/Panda3D/" start_enabled="false">\n')
+    dist.write('        <pkg-ref id="org.panda3d.panda3d.base.pkg"/>\n')
+    dist.write('    </choice>\n')
+    dist.write('    <choice id="tools" title="Tools" tooltip="Useful tools and model converters to help with Panda3D development" description="This package contains the various utilities that ship with Panda3D, including packaging tools, model converters, and many more. Location: /Developer/Tools/Panda3D/">\n')
+    dist.write('        <pkg-ref id="org.panda3d.panda3d.tools.pkg"/>\n')
+    dist.write('    </choice>\n')
     if PkgSkip("PYTHON")==0:
-        print >>dist, '    <choice id="pythoncode" title="Python Code" tooltip="Code you\'ll need for Python development" description="This package contains the \'direct\', \'pandac\' and \'panda3d\' python packages that are needed to do Python development with Panda3D. Location: /Developer/Panda3D/">'
-        print >>dist, '        <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg"/>'
-        print >>dist, '    </choice>'
+        dist.write('    <choice id="pythoncode" title="Python Code" tooltip="Code you\'ll need for Python development" description="This package contains the \'direct\', \'pandac\' and \'panda3d\' python packages that are needed to do Python development with Panda3D. Location: /Developer/Panda3D/">\n')
+        dist.write('        <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg"/>\n')
+        dist.write('    </choice>\n')
     if os.path.isdir("samples"):
-        print >>dist, '    <choice id="samples" title="Sample Programs" tooltip="Python sample programs that use Panda3D" description="This package contains the Python sample programs that can help you with learning how to use Panda3D. Location: /Developer/Examples/Panda3D/">'
-        print >>dist, '        <pkg-ref id="org.panda3d.panda3d.samples.pkg"/>'
-        print >>dist, '    </choice>'
-    print >>dist, '    <choice id="headers" title="C++ Header Files" tooltip="Header files for C++ development with Panda3D" description="This package contains the C++ header files that are needed in order to do C++ development with Panda3D. You don\'t need this if you want to develop in Python. Location: /Developer/Panda3D/include/" start_selected="false">'
-    print >>dist, '        <pkg-ref id="org.panda3d.panda3d.headers.pkg"/>'
-    print >>dist, '    </choice>'
-    print >>dist, '    <pkg-ref id="org.panda3d.panda3d.uninstall16.pkg" installKBytes="0" version="1" auth="Root">file:./Contents/Packages/uninstall16.pkg</pkg-ref>'
-    print >>dist, '    <pkg-ref id="org.panda3d.panda3d.base.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/base.pkg</pkg-ref>' % (GetDirectorySize("dstroot/base") / 1024)
-    print >>dist, '    <pkg-ref id="org.panda3d.panda3d.tools.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/tools.pkg</pkg-ref>' % (GetDirectorySize("dstroot/tools") / 1024)
+        dist.write('    <choice id="samples" title="Sample Programs" tooltip="Python sample programs that use Panda3D" description="This package contains the Python sample programs that can help you with learning how to use Panda3D. Location: /Developer/Examples/Panda3D/">\n')
+        dist.write('        <pkg-ref id="org.panda3d.panda3d.samples.pkg"/>\n')
+        dist.write('    </choice>\n')
+    dist.write('    <choice id="headers" title="C++ Header Files" tooltip="Header files for C++ development with Panda3D" description="This package contains the C++ header files that are needed in order to do C++ development with Panda3D. You don\'t need this if you want to develop in Python. Location: /Developer/Panda3D/include/" start_selected="false">\n')
+    dist.write('        <pkg-ref id="org.panda3d.panda3d.headers.pkg"/>\n')
+    dist.write('    </choice>\n')
+    dist.write('    <pkg-ref id="org.panda3d.panda3d.uninstall16.pkg" installKBytes="0" version="1" auth="Root">file:./Contents/Packages/uninstall16.pkg</pkg-ref>\n')
+    dist.write('    <pkg-ref id="org.panda3d.panda3d.base.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/base.pkg</pkg-ref>\n' % (GetDirectorySize("dstroot/base") // 1024))
+    dist.write('    <pkg-ref id="org.panda3d.panda3d.tools.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/tools.pkg</pkg-ref>\n' % (GetDirectorySize("dstroot/tools") // 1024))
     if PkgSkip("PYTHON")==0:
-        print >>dist, '    <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/pythoncode.pkg</pkg-ref>' % (GetDirectorySize("dstroot/pythoncode") / 1024)
+        dist.write('    <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/pythoncode.pkg</pkg-ref>\n' % (GetDirectorySize("dstroot/pythoncode") // 1024))
     if os.path.isdir("samples"):
-        print >>dist, '    <pkg-ref id="org.panda3d.panda3d.samples.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/samples.pkg</pkg-ref>' % (GetDirectorySize("dstroot/samples") / 1024)
-    print >>dist, '    <pkg-ref id="org.panda3d.panda3d.headers.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/headers.pkg</pkg-ref>' % (GetDirectorySize("dstroot/headers") / 1024)
-    print >>dist, '</installer-script>'
+        dist.write('    <pkg-ref id="org.panda3d.panda3d.samples.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/samples.pkg</pkg-ref>\n' % (GetDirectorySize("dstroot/samples") // 1024))
+    dist.write('    <pkg-ref id="org.panda3d.panda3d.headers.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/headers.pkg</pkg-ref>\n' % (GetDirectorySize("dstroot/headers") // 1024))
+    dist.write('</installer-script>\n')
     dist.close()
 
     oscmd('hdiutil create Panda3D-rw.dmg -srcfolder dstroot/Panda3D')
@@ -6410,4 +6416,4 @@ MoveBackConflictingFiles()
 WARNINGS.append("Elapsed Time: "+PrettyTime(time.time() - STARTTIME))
 
 printStatus("Makepanda Final Status Report", WARNINGS)
-print GetColor("green") + "Build successfully finished, elapsed time: " + PrettyTime(time.time() - STARTTIME) + GetColor()
+print(GetColor("green") + "Build successfully finished, elapsed time: " + PrettyTime(time.time() - STARTTIME) + GetColor())

+ 129 - 87
makepanda/makepandacore.py

@@ -9,9 +9,14 @@
 ##
 ########################################################################
 
-import sys,os,time,stat,string,re,getopt,cPickle,fnmatch,threading,Queue,signal,shutil,platform,glob,getpass,signal
+import sys,os,time,stat,string,re,getopt,fnmatch,threading,signal,shutil,platform,glob,getpass,signal
 from distutils import sysconfig
 
+if sys.version_info >= (3, 0):
+    import pickle
+else:
+    import cPickle as pickle
+
 SUFFIX_INC = [".cxx",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"]
 SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"]
 SUFFIX_LIB = [".lib",".ilb"]
@@ -36,7 +41,10 @@ if sys.platform == 'darwin':
     # On OSX, platform.architecture reports '64bit' even if it is
     # currently running in 32-bit mode.  But sys.maxint is a reliable
     # indicator.
-    is_64 = (sys.maxint > 0x100000000L)
+    if sys.version_info >= (3, 0):
+        is_64 = (sys.maxsize > 0x100000000)
+    else:
+        is_64 = (sys.maxint > 0x100000000)
 else:
     # On Windows (and Linux?) sys.maxint reports 0x7fffffff even on a
     # 64-bit build.  So we stick with platform.architecture in that
@@ -167,37 +175,49 @@ def GetColor(color = None):
     else:
       return curses.tparm(curses.tigetstr("sgr0"))
 
+def ColorText(color, text, reset=True):
+    if reset is True:
+        return ''.join((GetColor(color), text, GetColor()))
+    else:
+        return ''.join((GetColor(color), text))
+
 def PrettyTime(t):
     t = int(t)
-    hours = t/3600
-    t -= hours*3600
-    minutes = t/60
-    t -= minutes*60
+    hours = t // 3600
+    t -= hours * 3600
+    minutes = t // 60
+    t -= minutes * 60
     seconds = t
-    if (hours): return str(hours)+" hours "+str(minutes)+" min"
-    if (minutes): return str(minutes)+" min "+str(seconds)+" sec"
-    return str(seconds)+" sec"
+    if hours:
+        return "%d hours %d min" % (hours, minutes)
+    if minutes:
+        return "%d min %d sec" % (minutes, seconds)
+    return "%d sec" % (seconds)
 
 def ProgressOutput(progress, msg, target = None):
-    if (threading.currentThread() == MAINTHREAD):
-        if (progress == None):
-            print msg
+    prefix = ""
+    if (threading.currentThread() is MAINTHREAD):
+        if progress is None:
+            prefix = ""
         elif (progress >= 100.0):
-            print "%s[%s%d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
+            prefix = "%s[%s%d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
         elif (progress < 10.0):
-            print "%s[%s  %d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
+            prefix = "%s[%s  %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
         else:
-            print "%s[%s %d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
+            prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
     else:
         global THREADS
         ident = threading.currentThread().ident
         if (ident not in THREADS):
             THREADS[ident] = len(THREADS) + 1
-        print "%s[%sT%d%s] %s" % (GetColor("yellow"), GetColor("cyan"), THREADS[ident], GetColor("yellow"), msg),
-    if (target == None):
-        print GetColor()
+        prefix = "%s[%sT%d%s] " % (GetColor("yellow"), GetColor("cyan"), THREADS[ident], GetColor("yellow"))
+
+    if target is not None:
+        suffix = ' ' + ColorText("green", target)
     else:
-        print "%s%s%s" % (GetColor("green"), target, GetColor())
+        suffix = GetColor()
+
+    print(''.join((prefix, msg, suffix)))
 
 def exit(msg = ""):
     sys.stdout.flush()
@@ -205,16 +225,16 @@ def exit(msg = ""):
     if (threading.currentThread() == MAINTHREAD):
         SaveDependencyCache()
         MoveBackConflictingFiles()
-        print "Elapsed Time: "+PrettyTime(time.time() - STARTTIME)
-        print msg
-        print GetColor("red") + "Build terminated." + GetColor()
+        print("Elapsed Time: " + PrettyTime(time.time() - STARTTIME))
+        print(msg)
+        print(ColorText("red", "Build terminated."))
         sys.stdout.flush()
         sys.stderr.flush()
         ##Don't quit the interperter if I'm running this file directly (debugging)
         if __name__ != '__main__':
             os._exit(1)
     else:
-        print msg
+        print(msg)
         raise "initiate-exit"
 
 ########################################################################
@@ -435,7 +455,7 @@ def LocateBinary(binary):
 
 def oscmd(cmd, ignoreError = False):
     if VERBOSE:
-        print GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor()
+        print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
     sys.stdout.flush()
 
     if sys.platform == "win32":
@@ -448,21 +468,21 @@ def oscmd(cmd, ignoreError = False):
         res = os.system(cmd)
         sig = res & 0x7F
         if (GetVerbose() and res != 0):
-            print GetColor("red") + "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig) + GetColor()
+            print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
         if (sig == signal.SIGINT):
             exit("keyboard interrupt")
         # Don't ask me where the 35584 or 34304 come from...
         if (sig == signal.SIGSEGV or res == 35584 or res == 34304):
             if (LocateBinary("gdb") and GetVerbose()):
-                print GetColor("red") + "Received SIGSEGV, retrieving traceback..." + GetColor()
+                print(ColorText("red", "Received SIGSEGV, retrieving traceback..."))
                 os.system("gdb -batch -ex 'handle SIG33 pass nostop noprint' -ex 'set pagination 0' -ex 'run' -ex 'bt full' -ex 'info registers' -ex 'thread apply all backtrace' -ex 'quit' --args %s < /dev/null" % cmd)
             else:
-                print GetColor("red") + "Received SIGSEGV" + GetColor()
+                print(ColorText("red", "Received SIGSEGV"))
             exit("")
 
     if res != 0 and not ignoreError:
         if "interrogate" in cmd.split(" ", 1)[0] and GetVerbose():
-            print GetColor("red") + "Interrogate failed, retrieving debug output..." + GetColor()
+            print(ColorText("red", "Interrogate failed, retrieving debug output..."))
             if sys.platform == "win32":
                 os.spawnl(os.P_WAIT, exe, cmd.split(" ", 1)[0] + " -v " + cmd.split(" ", 1)[1])
             else:
@@ -498,7 +518,7 @@ def GetDirectoryContents(dir, filters="*", skip=[]):
                 actual[file] = 1
     if (os.path.isfile(dir + "/CVS/Entries")):
         cvs = {}
-        srchandle = open(dir+"/CVS/Entries", "r")
+        srchandle = open(dir + "/CVS/Entries", "r")
         files = []
         for line in srchandle:
             if (line[0]=="/"):
@@ -514,21 +534,23 @@ def GetDirectoryContents(dir, filters="*", skip=[]):
         #XXX this happens all the time, do we really need to warn about this?
         #for file in actual.keys():
         #    if (file not in cvs and VERBOSE):
-        #        msg = "%sWARNING: %s is in %s, but not in CVS%s" % (GetColor("red"), GetColor("green") + file + GetColor(), GetColor("green") + dir + GetColor(), GetColor())
+        #        msg = "%sWARNING: %s is in %s, but not in CVS%s" % (GetColor("red"), ColorText("green", file), ColorText("green", dir), GetColor())
         #        print msg
         #        WARNINGS.append(msg)
 
         for file in cvs.keys():
             if (file not in actual and VERBOSE):
-                msg = "%sWARNING: %s is not in %s, but is in CVS%s" % (GetColor("red"), GetColor("green") + file + GetColor(), GetColor("green") + dir + GetColor(), GetColor())
-                print msg
+                msg = "%sWARNING: %s is not in %s, but is in CVS%s" % (GetColor("red"), ColorText("green", file), ColorText("green", dir), GetColor())
+                print(msg)
                 WARNINGS.append(msg)
 
-    results = actual.keys()
+    results = list(actual.keys())
     results.sort()
     return results
 
 def GetDirectorySize(dir):
+    if not os.path.isdir(dir):
+        return 0
     size = 0
     for (path, dirs, files) in os.walk(dir):
         for file in files:
@@ -600,7 +622,7 @@ def NeedsBuild(files,others):
         else:
             oldothers = BUILTFROMCACHE[key][0]
             if (oldothers != others and VERBOSE):
-                print "%sWARNING:%s file dependencies changed: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), str(files), GetColor())
+                print("%sWARNING:%s file dependencies changed: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), str(files), GetColor()))
     return 1
 
 ########################################################################
@@ -634,7 +656,7 @@ def CxxGetIncludes(path):
     if (path in CXXINCLUDECACHE):
         cached = CXXINCLUDECACHE[path]
         if (cached[0]==date): return cached[1]
-    try: sfile = open(path, 'rb')
+    try: sfile = open(path, 'r')
     except:
         exit("Cannot open source file \""+path+"\" for reading.")
     include = []
@@ -669,9 +691,9 @@ def SaveDependencyCache():
     try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'wb')
     except: icache = 0
     if (icache!=0):
-        print "Storing dependency cache."
-        cPickle.dump(CXXINCLUDECACHE, icache, 1)
-        cPickle.dump(BUILTFROMCACHE, icache, 1)
+        print("Storing dependency cache.")
+        pickle.dump(CXXINCLUDECACHE, icache, 1)
+        pickle.dump(BUILTFROMCACHE, icache, 1)
         icache.close()
 
 def LoadDependencyCache():
@@ -680,8 +702,8 @@ def LoadDependencyCache():
     try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'rb')
     except: icache = 0
     if (icache!=0):
-        CXXINCLUDECACHE = cPickle.load(icache)
-        BUILTFROMCACHE = cPickle.load(icache)
+        CXXINCLUDECACHE = pickle.load(icache)
+        BUILTFROMCACHE = pickle.load(icache)
         icache.close()
 
 ########################################################################
@@ -759,7 +781,7 @@ def CxxCalcDependencies(srcfile, ipath, ignore):
             if (ignore.count(header)==0):
                 hdeps = CxxCalcDependencies(header, ipath, [srcfile]+ignore)
                 for x in hdeps: dep[x] = 1
-    result = dep.keys()
+    result = list(dep.keys())
     CxxDependencyCache[srcfile] = result
     return result
 
@@ -775,15 +797,18 @@ def CxxCalcDependencies(srcfile, ipath, ignore):
 ########################################################################
 
 if sys.platform == "win32":
-    import _winreg
+    if sys.version_info >= (3, 0):
+        import winreg
+    else:
+        import _winreg as winreg
 
 def TryRegistryKey(path):
     try:
-        key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ)
+        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ)
         return key
     except: pass
     try:
-        key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ | 256)
+        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ | 256)
         return key
     except: pass
     return 0
@@ -795,10 +820,10 @@ def ListRegistryKeys(path):
     if (key != 0):
         try:
             while (1):
-                result.append(_winreg.EnumKey(key, index))
+                result.append(winreg.EnumKey(key, index))
                 index = index + 1
         except: pass
-        _winreg.CloseKey(key)
+        winreg.CloseKey(key)
     return result
 
 def ListRegistryValues(path):
@@ -808,10 +833,10 @@ def ListRegistryValues(path):
     if (key != 0):
         try:
             while (1):
-                result.append(_winreg.EnumValue(key, index)[0])
+                result.append(winreg.EnumValue(key, index)[0])
                 index = index + 1
         except: pass
-        _winreg.CloseKey(key)
+        winreg.CloseKey(key)
     return result
 
 def GetRegistryKey(path, subkey, override64=True):
@@ -821,9 +846,9 @@ def GetRegistryKey(path, subkey, override64=True):
     key = TryRegistryKey(path)
     if (key != 0):
         try:
-            k1, k2 = _winreg.QueryValueEx(key, subkey)
+            k1, k2 = winreg.QueryValueEx(key, subkey)
         except: pass
-        _winreg.CloseKey(key)
+        winreg.CloseKey(key)
     return k1
 
 def GetProgramFiles():
@@ -873,32 +898,49 @@ def MakeDirectory(path):
     os.mkdir(path)
 
 def ReadFile(wfile):
+    try:
+        srchandle = open(wfile, "r")
+        data = srchandle.read()
+        srchandle.close()
+        return data
+    except ex:
+        exit("Cannot read %s: %s" % (wfile, ex))
+
+def ReadBinaryFile(wfile):
     try:
         srchandle = open(wfile, "rb")
         data = srchandle.read()
         srchandle.close()
         return data
-    except Exception, ex:
+    except ex:
         exit("Cannot read %s: %s" % (wfile, ex))
 
 def WriteFile(wfile, data):
+    try:
+        dsthandle = open(wfile, "w")
+        dsthandle.write(data)
+        dsthandle.close()
+    except ex:
+        exit("Cannot write to %s: %s" % (wfile, ex))
+
+def WriteBinaryFile(wfile, data):
     try:
         dsthandle = open(wfile, "wb")
         dsthandle.write(data)
         dsthandle.close()
-    except Exception, ex:
+    except ex:
         exit("Cannot write to %s: %s" % (wfile, ex))
 
-def ConditionalWriteFile(dest,desiredcontents):
+def ConditionalWriteFile(dest, desiredcontents):
     try:
-        rfile = open(dest, 'rb')
+        rfile = open(dest, 'r')
         contents = rfile.read(-1)
         rfile.close()
     except:
         contents=0
     if contents != desiredcontents:
         sys.stdout.flush()
-        WriteFile(dest,desiredcontents)
+        WriteFile(dest, desiredcontents)
 
 def DeleteCVS(dir):
     if dir == "": dir = "."
@@ -940,7 +982,7 @@ def DeleteEmptyDirs(dir):
 
 def CreateFile(file):
     if (os.path.exists(file)==0):
-        WriteFile(file,"")
+        WriteFile(file, "")
 
 ########################################################################
 #
@@ -1053,7 +1095,7 @@ def GetThirdpartyDir():
         THIRDPARTYDIR = GetThirdpartyBase()+"/android-libs-%s/" % (GetTargetArch())
 
     else:
-        print GetColor("red") + "WARNING:" + GetColor("Unsupported platform: " + target)
+        print("%s Unsupported platform: %s" % (ColorText("red", "WARNING:"), target))
     return THIRDPARTYDIR
 
 ########################################################################
@@ -1202,7 +1244,7 @@ def AddOverride(spec):
 
 def OverrideValue(parameter, value):
     if parameter in OVERRIDES_LIST:
-        print "Overriding value of key \"" + parameter + "\" with value \"" + OVERRIDES_LIST[parameter] + "\""
+        print("Overriding value of key \"" + parameter + "\" with value \"" + OVERRIDES_LIST[parameter] + "\"")
         return OVERRIDES_LIST[parameter]
     else:
         return value
@@ -1338,7 +1380,7 @@ def GetLibCache():
     if (LD_CACHE == None):
         LD_CACHE = []
         sysroot = SDK.get('SYSROOT', '')
-        print "Generating library cache..."
+        print("Generating library cache...")
 
         # If not cross compiling, use ldconfig to get a list of libraries in the LD cache
         if (not CrossCompiling() and LocateBinary("ldconfig") != None and GetTarget() != 'freebsd'):
@@ -1386,7 +1428,7 @@ def ChooseLib(*libs):
             return libname
     if (len(libs) > 0):
         if (VERBOSE):
-            print GetColor("cyan") + "Couldn't find any of the libraries " + ", ".join(libs) + GetColor()
+            print(ColorText("cyan", "Couldn't find any of the libraries " + ", ".join(libs)))
         return libs[0]
 
 def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, framework = None, target_pkg = None, tool = "pkg-config"):
@@ -1462,10 +1504,10 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
             for d, v in defs.values():
                 DefSymbol(target_pkg, d, v)
         elif (pkg in PkgListGet()):
-            print "%sWARNING:%s Could not locate framework %s, excluding from build" % (GetColor("red"), GetColor(), framework)
+            print("%sWARNING:%s Could not locate framework %s, excluding from build" % (GetColor("red"), GetColor(), framework))
             PkgDisable(pkg)
         else:
-            print "%sERROR:%s Could not locate framework %s, aborting build" % (GetColor("red"), GetColor(), framework)
+            print("%sERROR:%s Could not locate framework %s, aborting build" % (GetColor("red"), GetColor(), framework))
             exit()
         return
     elif (LocateBinary(tool) != None and (tool != "pkg-config" or pkgconfig != None)):
@@ -1484,10 +1526,10 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
 
     if (pkgconfig != None and (libs == None or len(libs) == 0)):
         if (pkg in PkgListGet()):
-            print "%sWARNING:%s Could not locate pkg-config package %s, excluding from build" % (GetColor("red"), GetColor(), pkgconfig)
+            print("%sWARNING:%s Could not locate pkg-config package %s, excluding from build" % (GetColor("red"), GetColor(), pkgconfig))
             PkgDisable(pkg)
         else:
-            print "%sERROR:%s Could not locate pkg-config package %s, aborting build" % (GetColor("red"), GetColor(), pkgconfig)
+            print("%sERROR:%s Could not locate pkg-config package %s, aborting build" % (GetColor("red"), GetColor(), pkgconfig))
             exit()
     else:
         # Okay, our pkg-config attempts failed. Let's try locating the libs by ourselves.
@@ -1500,7 +1542,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
                 LibName(target_pkg, "-l" + libname)
             else:
                 if (VERBOSE):
-                    print GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()
+                    print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor())
                 have_pkg = False
 
         for i in incs:
@@ -1520,7 +1562,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
                         incdir = sorted(glob.glob(os.path.join(pdir, i)))[-1]
                         have_pkg = True
                 if (incdir == None and VERBOSE and i.endswith(".h")):
-                    print GetColor("cyan") + "Couldn't find header file " + i + GetColor()
+                    print(GetColor("cyan") + "Couldn't find header file " + i + GetColor())
 
             # Note: It's possible to specify a file instead of a dir, for the sake of checking if it exists.
             if (incdir != None and os.path.isdir(incdir)):
@@ -1528,10 +1570,10 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
 
         if (not have_pkg):
             if (pkg in PkgListGet()):
-                print "%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), pkg.lower())
+                print("%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), pkg.lower()))
                 PkgDisable(pkg)
             else:
-                print "%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), pkg.lower())
+                print("%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), pkg.lower()))
                 exit()
 
 ########################################################################
@@ -1600,32 +1642,32 @@ def SdkLocateDirectX( strMode = 'default' ):
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
             if (dir != 0):
-                print "Using DirectX SDK June 2010"
+                print("Using DirectX SDK June 2010")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
             if (dir != 0):
-                print "Using DirectX SDK June 2010"
+                print("Using DirectX SDK June 2010")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
             if (dir != 0):
-                print "Using DirectX SDK Aug 2009"
+                print("Using DirectX SDK Aug 2009")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
             if (dir != 0):
-                print "Using DirectX SDK Aug 2009"
+                print("Using DirectX SDK Aug 2009")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
             ## Try to locate the key within the "new" March 2009 location in the registry (yecch):
             dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath")
             if (dir != 0):
-                print "Using DirectX SDK March 2009"
+                print("Using DirectX SDK March 2009")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
         archStr = "x86"
         if (is_64): archStr = "x64"
@@ -1654,13 +1696,13 @@ def SdkLocateDirectX( strMode = 'default' ):
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
             if (dir != 0):
-                print "Found DirectX SDK June 2010"
+                print("Found DirectX SDK June 2010")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
             if (dir != 0):
-                print "Found DirectX SDK June 2010"
+                print("Found DirectX SDK June 2010")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
@@ -1669,13 +1711,13 @@ def SdkLocateDirectX( strMode = 'default' ):
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
             if (dir != 0):
-                print "Found DirectX SDK Aug 2009"
+                print("Found DirectX SDK Aug 2009")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
             dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
             if (dir != 0):
-                print "Found DirectX SDK Aug 2009"
+                print("Found DirectX SDK Aug 2009")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
                 SDK["GENERIC_DXERR_LIBRARY"] = 1;
         if ("DX9" not in SDK):
@@ -1685,7 +1727,7 @@ def SdkLocateDirectX( strMode = 'default' ):
             ## Try to locate the key within the "new" March 2009 location in the registry (yecch):
             dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath")
             if (dir != 0):
-                print "Found DirectX SDK March 2009"
+                print("Found DirectX SDK March 2009")
                 SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
         if ("DX9" not in SDK):
             exit("Couldn't find DirectX March 2009 SDK")
@@ -1778,7 +1820,7 @@ def SdkLocatePython(force_use_sys_executable = False):
                 exit("Could not find %s!" % SDK["PYTHONEXEC"])
 
             os.system(SDK["PYTHONEXEC"] + " -V > "+OUTPUTDIR+"/tmp/pythonversion 2>&1")
-            pv=ReadFile(OUTPUTDIR+"/tmp/pythonversion")
+            pv = ReadFile(OUTPUTDIR + "/tmp/pythonversion")
             if (pv.startswith("Python ")==0):
                 exit("python -V did not produce the expected output")
             pv = pv[7:10]
@@ -1958,7 +2000,7 @@ def SdkLocateSpeedTree():
     for dirname in os.listdir(dir):
         if dirname.startswith('SpeedTree SDK v'):
             version = dirname[15:].split()[0]
-            version = map(int, version.split('.'))
+            version = tuple(map(int, version.split('.')))
             speedtrees.append((version, dirname))
     if not speedtrees:
         # No installed SpeedTree SDK.
@@ -2142,11 +2184,11 @@ def LibName(opt, name):
         if not os.path.exists(name):
             WARNINGS.append(name + " not found.  Skipping Package " + opt)
             if (opt in PkgListGet()):
-                print "%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), opt.lower())
+                print("%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), opt.lower()))
                 PkgDisable(opt)
                 return
             else:
-                print "%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), opt.lower())
+                print("%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), opt.lower()))
                 exit()
     LIBNAMES.append((opt, name))
 
@@ -2248,20 +2290,20 @@ def CopyFile(dstfile, srcfile):
         if (fnl < 0): fn = srcfile
         else: fn = srcfile[fnl+1:]
         dstfile = dstdir + fn
-    if (NeedsBuild([dstfile],[srcfile])):
-        WriteFile(dstfile,ReadFile(srcfile))
+    if (NeedsBuild([dstfile], [srcfile])):
+        WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
         JustBuilt([dstfile], [srcfile])
 
 def CopyAllFiles(dstdir, srcdir, suffix=""):
     for x in GetDirectoryContents(srcdir, ["*"+suffix]):
-        CopyFile(dstdir+x, srcdir+x)
+        CopyFile(dstdir + x, srcdir + x)
 
 def CopyAllHeaders(dir, skip=[]):
     for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip):
         srcfile = dir + "/" + filename
         dstfile = OUTPUTDIR + "/include/" + filename
         if (NeedsBuild([dstfile], [srcfile])):
-            WriteFile(dstfile, ReadFile(srcfile))
+            WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
             JustBuilt([dstfile], [srcfile])
 
 def CopyAllJavaSources(dir, skip=[]):
@@ -2269,7 +2311,7 @@ def CopyAllJavaSources(dir, skip=[]):
         srcfile = dir + "/" + filename
         dstfile = OUTPUTDIR + "/src/org/panda3d/android/" + filename
         if (NeedsBuild([dstfile], [srcfile])):
-            WriteFile(dstfile, ReadFile(srcfile))
+            WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
             JustBuilt([dstfile], [srcfile])
 
 def CopyTree(dstdir, srcdir, omitCVS=True):