Browse Source

Merge branch 'master' of https://github.com/panda3d/panda3d

David Rose 9 years ago
parent
commit
63eb1636a9
100 changed files with 4477 additions and 1231 deletions
  1. 5 0
      .gitignore
  2. 0 9
      contrib/.gitignore
  3. 0 2
      contrib/src/panda3dtoolsgui/.gitignore
  4. 0 9
      direct/.gitignore
  5. 0 1
      direct/src/configfiles/.gitignore
  6. 0 3
      direct/src/dcparser/.gitignore
  7. 4 0
      direct/src/directscripts/eggcacher.py
  8. 3 0
      direct/src/directscripts/packpanda.py
  9. 1 5
      direct/src/distributed/DistributedObjectGlobalUD.py
  10. 3 3
      direct/src/distributed/cConnectionRepository.cxx
  11. 0 1
      direct/src/extensions_native/.gitignore
  12. 0 2
      direct/src/gui/.gitignore
  13. 3 3
      direct/src/gui/DirectScrolledList.py
  14. 0 8
      direct/src/p3d/.gitignore
  15. 0 1
      direct/src/plugin/.gitignore
  16. 0 1
      direct/src/plugin_activex/.gitignore
  17. 0 1
      direct/src/plugin_npapi/.gitignore
  18. 0 2
      direct/src/plugin_standalone/.gitignore
  19. 36 1
      direct/src/showbase/PythonUtil.py
  20. 0 1
      direct/src/showutil/.gitignore
  21. 0 7
      dmodels/.gitignore
  22. 0 6
      dmodels/src/.gitignore
  23. 10 0
      doc/ReleaseNotes
  24. 0 7
      dtool/.gitignore
  25. 0 2
      dtool/src/cppparser/.gitignore
  26. 10 1
      dtool/src/cppparser/cppArrayType.cxx
  27. 1 0
      dtool/src/cppparser/cppArrayType.h
  28. 551 482
      dtool/src/cppparser/cppBison.cxx.prebuilt
  29. 264 228
      dtool/src/cppparser/cppBison.h.prebuilt
  30. 339 18
      dtool/src/cppparser/cppBison.yxx
  31. 26 3
      dtool/src/cppparser/cppClassTemplateParameter.cxx
  32. 1 0
      dtool/src/cppparser/cppClassTemplateParameter.h
  33. 42 1
      dtool/src/cppparser/cppConstType.cxx
  34. 5 0
      dtool/src/cppparser/cppConstType.h
  35. 8 2
      dtool/src/cppparser/cppDeclaration.cxx
  36. 70 1
      dtool/src/cppparser/cppDeclaration.h
  37. 212 7
      dtool/src/cppparser/cppExpression.cxx
  38. 9 2
      dtool/src/cppparser/cppExpression.h
  39. 23 3
      dtool/src/cppparser/cppExtensionType.cxx
  40. 2 0
      dtool/src/cppparser/cppExtensionType.h
  41. 1 1
      dtool/src/cppparser/cppFile.h
  42. 14 6
      dtool/src/cppparser/cppFunctionType.cxx
  43. 4 1
      dtool/src/cppparser/cppFunctionType.h
  44. 8 5
      dtool/src/cppparser/cppInstance.cxx
  45. 3 0
      dtool/src/cppparser/cppInstance.h
  46. 7 7
      dtool/src/cppparser/cppInstanceIdentifier.cxx
  47. 4 0
      dtool/src/cppparser/cppInstanceIdentifier.h
  48. 14 1
      dtool/src/cppparser/cppParser.cxx
  49. 65 0
      dtool/src/cppparser/cppPointerType.cxx
  50. 2 0
      dtool/src/cppparser/cppPointerType.h
  51. 257 83
      dtool/src/cppparser/cppPreprocessor.cxx
  52. 6 0
      dtool/src/cppparser/cppPreprocessor.h
  53. 79 0
      dtool/src/cppparser/cppReferenceType.cxx
  54. 3 0
      dtool/src/cppparser/cppReferenceType.h
  55. 1 1
      dtool/src/cppparser/cppScope.cxx
  56. 3 2
      dtool/src/cppparser/cppScope.h
  57. 53 0
      dtool/src/cppparser/cppSimpleType.cxx
  58. 11 7
      dtool/src/cppparser/cppSimpleType.h
  59. 379 39
      dtool/src/cppparser/cppStructType.cxx
  60. 12 0
      dtool/src/cppparser/cppStructType.h
  61. 4 2
      dtool/src/cppparser/cppTemplateScope.cxx
  62. 175 0
      dtool/src/cppparser/cppType.cxx
  63. 17 0
      dtool/src/cppparser/cppType.h
  64. 41 0
      dtool/src/cppparser/cppTypedefType.cxx
  65. 5 0
      dtool/src/cppparser/cppTypedefType.h
  66. 9 2
      dtool/src/dtoolbase/deletedBufferChain.cxx
  67. 1 5
      dtool/src/dtoolbase/deletedBufferChain.h
  68. 6 1
      dtool/src/dtoolbase/dtoolbase.h
  69. 16 4
      dtool/src/dtoolbase/memoryHook.I
  70. 5 0
      dtool/src/dtoolbase/memoryHook.cxx
  71. 2 0
      dtool/src/dtoolbase/neverFreeMemory.I
  72. 3 7
      dtool/src/dtoolbase/neverFreeMemory.cxx
  73. 0 3
      dtool/src/dtoolutil/.gitignore
  74. 15 10
      dtool/src/dtoolutil/filename.I
  75. 8 5
      dtool/src/dtoolutil/filename.h
  76. 4 2
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  77. 7 3
      dtool/src/interrogate/interrogateBuilder.cxx
  78. 153 0
      dtool/src/parser-inc/functional
  79. 34 0
      dtool/src/parser-inc/initializer_list
  80. 163 23
      dtool/src/parser-inc/memory
  81. 34 14
      dtool/src/parser-inc/stdint.h
  82. 2 0
      dtool/src/parser-inc/stdtypedefs.h
  83. 96 0
      dtool/src/parser-inc/type_traits
  84. 84 0
      dtool/src/parser-inc/unordered_map
  85. 83 0
      dtool/src/parser-inc/unordered_set
  86. 1 0
      dtool/src/parser-inc/xmmintrin.h
  87. 0 1
      dtool/src/prc/.gitignore
  88. 42 33
      dtool/src/prc/encryptStreamBuf.cxx
  89. 2 4
      dtool/src/prc/encryptStreamBuf.h
  90. 3 0
      dtool/src/prc/notify.cxx
  91. 4 0
      dtool/src/pystub/pystub.cxx
  92. 0 2
      makepanda/.gitignore
  93. 29 3
      makepanda/installer.nsi
  94. 196 115
      makepanda/makepanda.py
  95. 51 6
      makepanda/makepandacore.py
  96. 598 0
      makepanda/makewheel.py
  97. 0 8
      panda/.gitignore
  98. 1 1
      panda/src/audiotraits/openalAudioManager.h
  99. 1 1
      panda/src/audiotraits/openalAudioSound.h
  100. 28 0
      panda/src/bullet/bulletBodyNode.cxx

+ 5 - 0
.gitignore

@@ -45,3 +45,8 @@ CTestTestfile.cmake
 # Windows
 Thumbs.db
 ehthumbs.db
+
+# Python
+__pycache__
+*.pyc
+*.pyo

+ 0 - 9
contrib/.gitignore

@@ -1,9 +0,0 @@
-*.pyc
-*.pyo
-/__init__.py
-# These are files that are generated within the source tree by the
-# ppremake system.
-Makefile
-pp.dep
-/built/
-Opt?-*

+ 0 - 2
contrib/src/panda3dtoolsgui/.gitignore

@@ -1,2 +0,0 @@
-/build
-/dist

+ 0 - 9
direct/.gitignore

@@ -1,9 +0,0 @@
-*.pyc
-*.pyo
-/__init__.py
-# These are files that are generated within the source tree by the
-# ppremake system.
-Makefile
-pp.dep
-/built/
-Opt?-*

+ 0 - 1
direct/src/configfiles/.gitignore

@@ -1 +0,0 @@
-/40_direct.prc

+ 0 - 3
direct/src/dcparser/.gitignore

@@ -1,3 +0,0 @@
-/dcLexer.cxx
-/dcParser.cxx
-/dcParser.h

+ 4 - 0
direct/src/directscripts/eggcacher.py

@@ -88,3 +88,7 @@ class EggCacher:
             progress += size
 
 cacher = EggCacher(sys.argv[1:])
+
+# Dummy main function so this can be added to console_scripts.
+def main():
+    return 0

+ 3 - 0
direct/src/directscripts/packpanda.py

@@ -419,3 +419,6 @@ else:
     if not(os.path.exists("/usr/bin/rpmbuild") or os.path.exists("/usr/bin/dpkg-deb")):
         exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
 
+# Dummy main function so this can be added to console_scripts.
+def main():
+    return 0

+ 1 - 5
direct/src/distributed/DistributedObjectGlobalUD.py

@@ -26,11 +26,7 @@ class DistributedObjectGlobalUD(DistributedObjectUD):
 
     def execCommand(self, command, mwMgrId, avId, zoneId):
         text = str(self.__execMessage(command))[:config.GetInt("ai-debug-length",300)]
-
-        dclass = uber.air.dclassesByName.get("PiratesMagicWordManagerAI")
-        dg = dclass.aiFormatUpdate(
-            "setMagicWordResponse", mwMgrId, (1<<32)+avId, uber.air.ourChannel, [text])
-        uber.air.send(dg)
+        self.notify.info(text)
 
     def __execMessage(self, message):
         if not self.ExecNamespace:

+ 3 - 3
direct/src/distributed/cConnectionRepository.cxx

@@ -403,7 +403,7 @@ send_datagram(const Datagram &dg) {
 
 #ifdef WANT_NATIVE_NET
   if (_native) {
-    bool result = _bdc.SendMessage();
+    bool result = _bdc.SendMessage(dg);
     if (!result && _bdc.IsConnected()) {
 #ifdef HAVE_PYTHON
       ostringstream s;
@@ -415,8 +415,8 @@ send_datagram(const Datagram &dg) {
 #endif
 
       s << endl << "Error sending message: " << endl;
-      msg.dump_hex(s);
-      s << "Message data: " << msg.get_data() << endl;
+      dg.dump_hex(s);
+      s << "Message data: " << dg.get_data() << endl;
 
       string message = s.str();
       PyErr_SetString(exc_type, message.c_str());

+ 0 - 1
direct/src/extensions_native/.gitignore

@@ -1 +0,0 @@
-/extensions_darwin.py

+ 0 - 2
direct/src/gui/.gitignore

@@ -1,2 +0,0 @@
-/NL*
-/Nested*

+ 3 - 3
direct/src/gui/DirectScrolledList.py

@@ -20,7 +20,7 @@ class DirectScrolledListItem(DirectButton):
 
     def __init__(self, parent=None, **kw):
         assert self.notify.debugStateCall(self)
-        self.parent = parent
+        self._parent = parent
         if "command" in kw:
             self.nextCommand = kw.get("command")
             del kw["command"]
@@ -28,7 +28,7 @@ class DirectScrolledListItem(DirectButton):
             self.nextCommandExtraArgs = kw.get("extraArgs")
             del kw["extraArgs"]
         optiondefs = (
-            ('parent', self.parent,    None),
+            ('parent', self._parent,    None),
             ('command', self.select, None),
             )
         # Merge keyword options with default options
@@ -39,7 +39,7 @@ class DirectScrolledListItem(DirectButton):
     def select(self):
         assert self.notify.debugStateCall(self)
         self.nextCommand(*self.nextCommandExtraArgs)
-        self.parent.selectListItem(self)
+        self._parent.selectListItem(self)
 
 
 class DirectScrolledList(DirectFrame):

+ 0 - 8
direct/src/p3d/.gitignore

@@ -1,8 +0,0 @@
-/_vfsimporter.exp
-/_vfsimporter.lib
-/_vfsimporter.pyd
-/_vfsimporter.pyd.manifest
-/packp3d
-/ppackage
-/ppatcher
-/vc90.pdb

+ 0 - 1
direct/src/plugin/.gitignore

@@ -1 +0,0 @@
-/p3d_plugin_config.h

+ 0 - 1
direct/src/plugin_activex/.gitignore

@@ -1 +0,0 @@
-/P3DActiveX.rc

+ 0 - 1
direct/src/plugin_npapi/.gitignore

@@ -1 +0,0 @@
-/nppanda3d.rc

+ 0 - 2
direct/src/plugin_standalone/.gitignore

@@ -1,2 +0,0 @@
-/p3d_plugin_config.h
-/panda3d.rc

+ 36 - 1
direct/src/showbase/PythonUtil.py

@@ -38,7 +38,6 @@ import os
 import sys
 import random
 import time
-import importlib
 
 __report_indent = 3
 
@@ -61,6 +60,42 @@ def Functor(function, *args, **kArgs):
     return functor
 """
 
+try:
+    import importlib
+except ImportError:
+    # Backward compatibility for Python 2.6.
+    def _resolve_name(name, package, level):
+        if not hasattr(package, 'rindex'):
+            raise ValueError("'package' not set to a string")
+        dot = len(package)
+        for x in xrange(level, 1, -1):
+            try:
+                dot = package.rindex('.', 0, dot)
+            except ValueError:
+                raise ValueError("attempted relative import beyond top-level "
+                                  "package")
+        return "%s.%s" % (package[:dot], name)
+
+    def import_module(name, package=None):
+        if name.startswith('.'):
+            if not package:
+                raise TypeError("relative imports require the 'package' argument")
+            level = 0
+            for character in name:
+                if character != '.':
+                    break
+                level += 1
+            name = _resolve_name(name[level:], package, level)
+        __import__(name)
+        return sys.modules[name]
+
+    imp = import_module('imp')
+    importlib = imp.new_module("importlib")
+    importlib._resolve_name = _resolve_name
+    importlib.import_module = import_module
+    sys.modules['importlib'] = importlib
+
+
 class Functor:
     def __init__(self, function, *args, **kargs):
         assert callable(function), "function should be a callable obj"

+ 0 - 1
direct/src/showutil/.gitignore

@@ -1 +0,0 @@
-/runp3d

+ 0 - 7
dmodels/.gitignore

@@ -1,7 +0,0 @@
-*.egg
-*.pt
-/built/
-Makefile
-bams/
-optchar/
-pal_egg/

+ 0 - 6
dmodels/src/.gitignore

@@ -1,6 +0,0 @@
-/*.buildings
-/game_options.txt
-/prefixstrip
-/retarget/
-/textures.boo
-/topstrip/

+ 10 - 0
doc/ReleaseNotes

@@ -2,6 +2,7 @@
 
 This issue fixes several bugs that were still found in 1.9.2.
 
+* Fix crash when using homebrew Python on Mac OS X
 * Fix crash when running in Steam on Linux when using OpenAL
 * Fix crash using wx/tkinter on Mac as long as want-wx/tk is set
 * Fix loading models from 'models' package with models/ prefix
@@ -45,6 +46,15 @@ This issue fixes several bugs that were still found in 1.9.2.
 * Fix rare X11 .ico cursor bug; also now supports PNG-compressed icons
 * Add keyword argument support to make() methods such as Shader.make()
 * Fix compilation errors with Bullet 2.84
+* Fix exception when trying to pickle NodePathCollection objects
+* Fix error when trying to raise vectors to a power
+* GLSL: fix error when legacy matrix generator inputs are mat3
+* Now tries to preserve refresh rate when switching fullscreen on Windows
+* Fix back-to-front sorting when gl-coordinate-system is changed
+* Now also compiles on older Linux distros (eg. CentOS 5 / manylinux1)
+* get_keyboard_map now includes keys on layouts with special characters
+* Fix crash due to incorrect alignment when compiling Eigen with AVX
+* Fix crash when writing 16-bit .tif file (now silently downsamples)
 
 ------------------------  RELEASE 1.9.2  ------------------------
 

+ 0 - 7
dtool/.gitignore

@@ -1,7 +0,0 @@
-/dtool_config.h
-# These are files that are generated within the source tree by the
-# ppremake system.
-Makefile
-pp.dep
-/built/
-Opt?-*

+ 0 - 2
dtool/src/cppparser/.gitignore

@@ -1,2 +0,0 @@
-/cppBison.cxx
-/cppBison.h

+ 10 - 1
dtool/src/cppparser/cppArrayType.cxx

@@ -13,6 +13,7 @@
 
 #include "cppArrayType.h"
 #include "cppExpression.h"
+#include "cppPointerType.h"
 
 /**
  *
@@ -63,6 +64,14 @@ is_tbd() const {
   return _element_type->is_tbd();
 }
 
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPArrayType::
+is_standard_layout() const {
+  return _element_type->is_standard_layout();
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -76,7 +85,7 @@ is_trivial() const {
  */
 bool CPPArrayType::
 is_default_constructible() const {
-  return _element_type->is_default_constructible();
+  return _bounds != NULL && _element_type->is_default_constructible();
 }
 
 /**

+ 1 - 0
dtool/src/cppparser/cppArrayType.h

@@ -38,6 +38,7 @@ public:
   virtual CPPType *resolve_type(CPPScope *current_scope,
                                 CPPScope *global_scope);
   virtual bool is_tbd() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;

File diff suppressed because it is too large
+ 551 - 482
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 264 - 228
dtool/src/cppparser/cppBison.h.prebuilt

@@ -54,120 +54,138 @@ extern int cppyydebug;
     CUSTOM_LITERAL = 264,
     IDENTIFIER = 265,
     TYPENAME_IDENTIFIER = 266,
-    SCOPING = 267,
-    TYPEDEFNAME = 268,
-    ELLIPSIS = 269,
-    OROR = 270,
-    ANDAND = 271,
-    EQCOMPARE = 272,
-    NECOMPARE = 273,
-    LECOMPARE = 274,
-    GECOMPARE = 275,
-    LSHIFT = 276,
-    RSHIFT = 277,
-    POINTSAT_STAR = 278,
-    DOT_STAR = 279,
-    UNARY = 280,
-    UNARY_NOT = 281,
-    UNARY_NEGATE = 282,
-    UNARY_MINUS = 283,
-    UNARY_PLUS = 284,
-    UNARY_STAR = 285,
-    UNARY_REF = 286,
-    POINTSAT = 287,
-    SCOPE = 288,
-    PLUSPLUS = 289,
-    MINUSMINUS = 290,
-    TIMESEQUAL = 291,
-    DIVIDEEQUAL = 292,
-    MODEQUAL = 293,
-    PLUSEQUAL = 294,
-    MINUSEQUAL = 295,
-    OREQUAL = 296,
-    ANDEQUAL = 297,
-    XOREQUAL = 298,
-    LSHIFTEQUAL = 299,
-    RSHIFTEQUAL = 300,
-    KW_ALIGNAS = 301,
-    KW_ALIGNOF = 302,
-    KW_AUTO = 303,
-    KW_BEGIN_PUBLISH = 304,
-    KW_BLOCKING = 305,
-    KW_BOOL = 306,
-    KW_CATCH = 307,
-    KW_CHAR = 308,
-    KW_CHAR16_T = 309,
-    KW_CHAR32_T = 310,
-    KW_CLASS = 311,
-    KW_CONST = 312,
-    KW_CONSTEXPR = 313,
-    KW_CONST_CAST = 314,
-    KW_DECLTYPE = 315,
-    KW_DEFAULT = 316,
-    KW_DELETE = 317,
-    KW_DOUBLE = 318,
-    KW_DYNAMIC_CAST = 319,
-    KW_ELSE = 320,
-    KW_END_PUBLISH = 321,
-    KW_ENUM = 322,
-    KW_EXTENSION = 323,
-    KW_EXTERN = 324,
-    KW_EXPLICIT = 325,
-    KW_PUBLISHED = 326,
-    KW_FALSE = 327,
-    KW_FINAL = 328,
-    KW_FLOAT = 329,
-    KW_FRIEND = 330,
-    KW_FOR = 331,
-    KW_GOTO = 332,
-    KW_IF = 333,
-    KW_INLINE = 334,
-    KW_INT = 335,
-    KW_LONG = 336,
-    KW_MAKE_MAP_PROPERTY = 337,
-    KW_MAKE_PROPERTY = 338,
-    KW_MAKE_PROPERTY2 = 339,
-    KW_MAKE_SEQ = 340,
-    KW_MAKE_SEQ_PROPERTY = 341,
-    KW_MUTABLE = 342,
-    KW_NAMESPACE = 343,
-    KW_NEW = 344,
-    KW_NOEXCEPT = 345,
-    KW_NULLPTR = 346,
-    KW_OPERATOR = 347,
-    KW_OVERRIDE = 348,
-    KW_PRIVATE = 349,
-    KW_PROTECTED = 350,
-    KW_PUBLIC = 351,
-    KW_REGISTER = 352,
-    KW_REINTERPRET_CAST = 353,
-    KW_RETURN = 354,
-    KW_SHORT = 355,
-    KW_SIGNED = 356,
-    KW_SIZEOF = 357,
-    KW_STATIC = 358,
-    KW_STATIC_ASSERT = 359,
-    KW_STATIC_CAST = 360,
-    KW_STRUCT = 361,
-    KW_TEMPLATE = 362,
-    KW_THREAD_LOCAL = 363,
-    KW_THROW = 364,
-    KW_TRUE = 365,
-    KW_TRY = 366,
-    KW_TYPEDEF = 367,
-    KW_TYPEID = 368,
-    KW_TYPENAME = 369,
-    KW_UNION = 370,
-    KW_UNSIGNED = 371,
-    KW_USING = 372,
-    KW_VIRTUAL = 373,
-    KW_VOID = 374,
-    KW_VOLATILE = 375,
-    KW_WCHAR_T = 376,
-    KW_WHILE = 377,
-    START_CPP = 378,
-    START_CONST_EXPR = 379,
-    START_TYPE = 380
+    TYPEPACK_IDENTIFIER = 267,
+    SCOPING = 268,
+    TYPEDEFNAME = 269,
+    ELLIPSIS = 270,
+    OROR = 271,
+    ANDAND = 272,
+    EQCOMPARE = 273,
+    NECOMPARE = 274,
+    LECOMPARE = 275,
+    GECOMPARE = 276,
+    LSHIFT = 277,
+    RSHIFT = 278,
+    POINTSAT_STAR = 279,
+    DOT_STAR = 280,
+    UNARY = 281,
+    UNARY_NOT = 282,
+    UNARY_NEGATE = 283,
+    UNARY_MINUS = 284,
+    UNARY_PLUS = 285,
+    UNARY_STAR = 286,
+    UNARY_REF = 287,
+    POINTSAT = 288,
+    SCOPE = 289,
+    PLUSPLUS = 290,
+    MINUSMINUS = 291,
+    TIMESEQUAL = 292,
+    DIVIDEEQUAL = 293,
+    MODEQUAL = 294,
+    PLUSEQUAL = 295,
+    MINUSEQUAL = 296,
+    OREQUAL = 297,
+    ANDEQUAL = 298,
+    XOREQUAL = 299,
+    LSHIFTEQUAL = 300,
+    RSHIFTEQUAL = 301,
+    KW_ALIGNAS = 302,
+    KW_ALIGNOF = 303,
+    KW_AUTO = 304,
+    KW_BEGIN_PUBLISH = 305,
+    KW_BLOCKING = 306,
+    KW_BOOL = 307,
+    KW_CATCH = 308,
+    KW_CHAR = 309,
+    KW_CHAR16_T = 310,
+    KW_CHAR32_T = 311,
+    KW_CLASS = 312,
+    KW_CONST = 313,
+    KW_CONSTEXPR = 314,
+    KW_CONST_CAST = 315,
+    KW_DECLTYPE = 316,
+    KW_DEFAULT = 317,
+    KW_DELETE = 318,
+    KW_DOUBLE = 319,
+    KW_DYNAMIC_CAST = 320,
+    KW_ELSE = 321,
+    KW_END_PUBLISH = 322,
+    KW_ENUM = 323,
+    KW_EXTENSION = 324,
+    KW_EXTERN = 325,
+    KW_EXPLICIT = 326,
+    KW_PUBLISHED = 327,
+    KW_FALSE = 328,
+    KW_FINAL = 329,
+    KW_FLOAT = 330,
+    KW_FRIEND = 331,
+    KW_FOR = 332,
+    KW_GOTO = 333,
+    KW_HAS_VIRTUAL_DESTRUCTOR = 334,
+    KW_IF = 335,
+    KW_INLINE = 336,
+    KW_INT = 337,
+    KW_IS_ABSTRACT = 338,
+    KW_IS_BASE_OF = 339,
+    KW_IS_CLASS = 340,
+    KW_IS_CONSTRUCTIBLE = 341,
+    KW_IS_CONVERTIBLE_TO = 342,
+    KW_IS_DESTRUCTIBLE = 343,
+    KW_IS_EMPTY = 344,
+    KW_IS_ENUM = 345,
+    KW_IS_FINAL = 346,
+    KW_IS_FUNDAMENTAL = 347,
+    KW_IS_POD = 348,
+    KW_IS_POLYMORPHIC = 349,
+    KW_IS_STANDARD_LAYOUT = 350,
+    KW_IS_TRIVIAL = 351,
+    KW_IS_UNION = 352,
+    KW_LONG = 353,
+    KW_MAKE_MAP_PROPERTY = 354,
+    KW_MAKE_PROPERTY = 355,
+    KW_MAKE_PROPERTY2 = 356,
+    KW_MAKE_SEQ = 357,
+    KW_MAKE_SEQ_PROPERTY = 358,
+    KW_MUTABLE = 359,
+    KW_NAMESPACE = 360,
+    KW_NEW = 361,
+    KW_NOEXCEPT = 362,
+    KW_NULLPTR = 363,
+    KW_OPERATOR = 364,
+    KW_OVERRIDE = 365,
+    KW_PRIVATE = 366,
+    KW_PROTECTED = 367,
+    KW_PUBLIC = 368,
+    KW_REGISTER = 369,
+    KW_REINTERPRET_CAST = 370,
+    KW_RETURN = 371,
+    KW_SHORT = 372,
+    KW_SIGNED = 373,
+    KW_SIZEOF = 374,
+    KW_STATIC = 375,
+    KW_STATIC_ASSERT = 376,
+    KW_STATIC_CAST = 377,
+    KW_STRUCT = 378,
+    KW_TEMPLATE = 379,
+    KW_THREAD_LOCAL = 380,
+    KW_THROW = 381,
+    KW_TRUE = 382,
+    KW_TRY = 383,
+    KW_TYPEDEF = 384,
+    KW_TYPEID = 385,
+    KW_TYPENAME = 386,
+    KW_UNDERLYING_TYPE = 387,
+    KW_UNION = 388,
+    KW_UNSIGNED = 389,
+    KW_USING = 390,
+    KW_VIRTUAL = 391,
+    KW_VOID = 392,
+    KW_VOLATILE = 393,
+    KW_WCHAR_T = 394,
+    KW_WHILE = 395,
+    START_CPP = 396,
+    START_CONST_EXPR = 397,
+    START_TYPE = 398
   };
 #endif
 /* Tokens.  */
@@ -180,120 +198,138 @@ extern int cppyydebug;
 #define CUSTOM_LITERAL 264
 #define IDENTIFIER 265
 #define TYPENAME_IDENTIFIER 266
-#define SCOPING 267
-#define TYPEDEFNAME 268
-#define ELLIPSIS 269
-#define OROR 270
-#define ANDAND 271
-#define EQCOMPARE 272
-#define NECOMPARE 273
-#define LECOMPARE 274
-#define GECOMPARE 275
-#define LSHIFT 276
-#define RSHIFT 277
-#define POINTSAT_STAR 278
-#define DOT_STAR 279
-#define UNARY 280
-#define UNARY_NOT 281
-#define UNARY_NEGATE 282
-#define UNARY_MINUS 283
-#define UNARY_PLUS 284
-#define UNARY_STAR 285
-#define UNARY_REF 286
-#define POINTSAT 287
-#define SCOPE 288
-#define PLUSPLUS 289
-#define MINUSMINUS 290
-#define TIMESEQUAL 291
-#define DIVIDEEQUAL 292
-#define MODEQUAL 293
-#define PLUSEQUAL 294
-#define MINUSEQUAL 295
-#define OREQUAL 296
-#define ANDEQUAL 297
-#define XOREQUAL 298
-#define LSHIFTEQUAL 299
-#define RSHIFTEQUAL 300
-#define KW_ALIGNAS 301
-#define KW_ALIGNOF 302
-#define KW_AUTO 303
-#define KW_BEGIN_PUBLISH 304
-#define KW_BLOCKING 305
-#define KW_BOOL 306
-#define KW_CATCH 307
-#define KW_CHAR 308
-#define KW_CHAR16_T 309
-#define KW_CHAR32_T 310
-#define KW_CLASS 311
-#define KW_CONST 312
-#define KW_CONSTEXPR 313
-#define KW_CONST_CAST 314
-#define KW_DECLTYPE 315
-#define KW_DEFAULT 316
-#define KW_DELETE 317
-#define KW_DOUBLE 318
-#define KW_DYNAMIC_CAST 319
-#define KW_ELSE 320
-#define KW_END_PUBLISH 321
-#define KW_ENUM 322
-#define KW_EXTENSION 323
-#define KW_EXTERN 324
-#define KW_EXPLICIT 325
-#define KW_PUBLISHED 326
-#define KW_FALSE 327
-#define KW_FINAL 328
-#define KW_FLOAT 329
-#define KW_FRIEND 330
-#define KW_FOR 331
-#define KW_GOTO 332
-#define KW_IF 333
-#define KW_INLINE 334
-#define KW_INT 335
-#define KW_LONG 336
-#define KW_MAKE_MAP_PROPERTY 337
-#define KW_MAKE_PROPERTY 338
-#define KW_MAKE_PROPERTY2 339
-#define KW_MAKE_SEQ 340
-#define KW_MAKE_SEQ_PROPERTY 341
-#define KW_MUTABLE 342
-#define KW_NAMESPACE 343
-#define KW_NEW 344
-#define KW_NOEXCEPT 345
-#define KW_NULLPTR 346
-#define KW_OPERATOR 347
-#define KW_OVERRIDE 348
-#define KW_PRIVATE 349
-#define KW_PROTECTED 350
-#define KW_PUBLIC 351
-#define KW_REGISTER 352
-#define KW_REINTERPRET_CAST 353
-#define KW_RETURN 354
-#define KW_SHORT 355
-#define KW_SIGNED 356
-#define KW_SIZEOF 357
-#define KW_STATIC 358
-#define KW_STATIC_ASSERT 359
-#define KW_STATIC_CAST 360
-#define KW_STRUCT 361
-#define KW_TEMPLATE 362
-#define KW_THREAD_LOCAL 363
-#define KW_THROW 364
-#define KW_TRUE 365
-#define KW_TRY 366
-#define KW_TYPEDEF 367
-#define KW_TYPEID 368
-#define KW_TYPENAME 369
-#define KW_UNION 370
-#define KW_UNSIGNED 371
-#define KW_USING 372
-#define KW_VIRTUAL 373
-#define KW_VOID 374
-#define KW_VOLATILE 375
-#define KW_WCHAR_T 376
-#define KW_WHILE 377
-#define START_CPP 378
-#define START_CONST_EXPR 379
-#define START_TYPE 380
+#define TYPEPACK_IDENTIFIER 267
+#define SCOPING 268
+#define TYPEDEFNAME 269
+#define ELLIPSIS 270
+#define OROR 271
+#define ANDAND 272
+#define EQCOMPARE 273
+#define NECOMPARE 274
+#define LECOMPARE 275
+#define GECOMPARE 276
+#define LSHIFT 277
+#define RSHIFT 278
+#define POINTSAT_STAR 279
+#define DOT_STAR 280
+#define UNARY 281
+#define UNARY_NOT 282
+#define UNARY_NEGATE 283
+#define UNARY_MINUS 284
+#define UNARY_PLUS 285
+#define UNARY_STAR 286
+#define UNARY_REF 287
+#define POINTSAT 288
+#define SCOPE 289
+#define PLUSPLUS 290
+#define MINUSMINUS 291
+#define TIMESEQUAL 292
+#define DIVIDEEQUAL 293
+#define MODEQUAL 294
+#define PLUSEQUAL 295
+#define MINUSEQUAL 296
+#define OREQUAL 297
+#define ANDEQUAL 298
+#define XOREQUAL 299
+#define LSHIFTEQUAL 300
+#define RSHIFTEQUAL 301
+#define KW_ALIGNAS 302
+#define KW_ALIGNOF 303
+#define KW_AUTO 304
+#define KW_BEGIN_PUBLISH 305
+#define KW_BLOCKING 306
+#define KW_BOOL 307
+#define KW_CATCH 308
+#define KW_CHAR 309
+#define KW_CHAR16_T 310
+#define KW_CHAR32_T 311
+#define KW_CLASS 312
+#define KW_CONST 313
+#define KW_CONSTEXPR 314
+#define KW_CONST_CAST 315
+#define KW_DECLTYPE 316
+#define KW_DEFAULT 317
+#define KW_DELETE 318
+#define KW_DOUBLE 319
+#define KW_DYNAMIC_CAST 320
+#define KW_ELSE 321
+#define KW_END_PUBLISH 322
+#define KW_ENUM 323
+#define KW_EXTENSION 324
+#define KW_EXTERN 325
+#define KW_EXPLICIT 326
+#define KW_PUBLISHED 327
+#define KW_FALSE 328
+#define KW_FINAL 329
+#define KW_FLOAT 330
+#define KW_FRIEND 331
+#define KW_FOR 332
+#define KW_GOTO 333
+#define KW_HAS_VIRTUAL_DESTRUCTOR 334
+#define KW_IF 335
+#define KW_INLINE 336
+#define KW_INT 337
+#define KW_IS_ABSTRACT 338
+#define KW_IS_BASE_OF 339
+#define KW_IS_CLASS 340
+#define KW_IS_CONSTRUCTIBLE 341
+#define KW_IS_CONVERTIBLE_TO 342
+#define KW_IS_DESTRUCTIBLE 343
+#define KW_IS_EMPTY 344
+#define KW_IS_ENUM 345
+#define KW_IS_FINAL 346
+#define KW_IS_FUNDAMENTAL 347
+#define KW_IS_POD 348
+#define KW_IS_POLYMORPHIC 349
+#define KW_IS_STANDARD_LAYOUT 350
+#define KW_IS_TRIVIAL 351
+#define KW_IS_UNION 352
+#define KW_LONG 353
+#define KW_MAKE_MAP_PROPERTY 354
+#define KW_MAKE_PROPERTY 355
+#define KW_MAKE_PROPERTY2 356
+#define KW_MAKE_SEQ 357
+#define KW_MAKE_SEQ_PROPERTY 358
+#define KW_MUTABLE 359
+#define KW_NAMESPACE 360
+#define KW_NEW 361
+#define KW_NOEXCEPT 362
+#define KW_NULLPTR 363
+#define KW_OPERATOR 364
+#define KW_OVERRIDE 365
+#define KW_PRIVATE 366
+#define KW_PROTECTED 367
+#define KW_PUBLIC 368
+#define KW_REGISTER 369
+#define KW_REINTERPRET_CAST 370
+#define KW_RETURN 371
+#define KW_SHORT 372
+#define KW_SIGNED 373
+#define KW_SIZEOF 374
+#define KW_STATIC 375
+#define KW_STATIC_ASSERT 376
+#define KW_STATIC_CAST 377
+#define KW_STRUCT 378
+#define KW_TEMPLATE 379
+#define KW_THREAD_LOCAL 380
+#define KW_THROW 381
+#define KW_TRUE 382
+#define KW_TRY 383
+#define KW_TYPEDEF 384
+#define KW_TYPEID 385
+#define KW_TYPENAME 386
+#define KW_UNDERLYING_TYPE 387
+#define KW_UNION 388
+#define KW_UNSIGNED 389
+#define KW_USING 390
+#define KW_VIRTUAL 391
+#define KW_VOID 392
+#define KW_VOLATILE 393
+#define KW_WCHAR_T 394
+#define KW_WHILE 395
+#define START_CPP 396
+#define START_CONST_EXPR 397
+#define START_TYPE 398
 
 /* Value type.  */
 

+ 339 - 18
dtool/src/cppparser/cppBison.yxx

@@ -212,7 +212,7 @@ pop_struct() {
 %token <u.integer> CHAR_TOK
 %token <str> SIMPLE_STRING SIMPLE_IDENTIFIER
 %token <u.expr> STRING_LITERAL CUSTOM_LITERAL
-%token <u.identifier> IDENTIFIER TYPENAME_IDENTIFIER SCOPING
+%token <u.identifier> IDENTIFIER TYPENAME_IDENTIFIER TYPEPACK_IDENTIFIER SCOPING
 %token <u.type> TYPEDEFNAME
 
 %token ELLIPSIS
@@ -280,9 +280,25 @@ pop_struct() {
 %token KW_FRIEND
 %token KW_FOR
 %token KW_GOTO
+%token KW_HAS_VIRTUAL_DESTRUCTOR
 %token KW_IF
 %token KW_INLINE
 %token KW_INT
+%token KW_IS_ABSTRACT
+%token KW_IS_BASE_OF
+%token KW_IS_CLASS
+%token KW_IS_CONSTRUCTIBLE
+%token KW_IS_CONVERTIBLE_TO
+%token KW_IS_DESTRUCTIBLE
+%token KW_IS_EMPTY
+%token KW_IS_ENUM
+%token KW_IS_FINAL
+%token KW_IS_FUNDAMENTAL
+%token KW_IS_POD
+%token KW_IS_POLYMORPHIC
+%token KW_IS_STANDARD_LAYOUT
+%token KW_IS_TRIVIAL
+%token KW_IS_UNION
 %token KW_LONG
 %token KW_MAKE_MAP_PROPERTY
 %token KW_MAKE_PROPERTY
@@ -317,6 +333,7 @@ pop_struct() {
 %token KW_TYPEDEF
 %token KW_TYPEID
 %token KW_TYPENAME
+%token KW_UNDERLYING_TYPE
 %token KW_UNION
 %token KW_UNSIGNED
 %token KW_USING
@@ -354,9 +371,10 @@ pop_struct() {
 %type <u.instance> formal_parameter
 %type <u.inst_ident> not_paren_formal_parameter_identifier
 %type <u.inst_ident> formal_parameter_identifier
+%type <u.inst_ident> parameter_pack_identifier
 %type <u.inst_ident> not_paren_empty_instance_identifier
 %type <u.inst_ident> empty_instance_identifier
-%type <u.type> type
+%type <u.type> type type_pack
 %type <u.decl> type_decl
 %type <u.decl> var_type_decl
 %type <u.type> predefined_type
@@ -447,6 +465,10 @@ constructor_init:
         name '(' optional_const_expr_comma ')'
 {
   delete $3;
+}
+        | name '(' optional_const_expr_comma ')' ELLIPSIS
+{
+  delete $3;
 }
         | name '{' optional_const_expr_comma '}'
 {
@@ -1187,26 +1209,38 @@ function_post:
 {
   $$ = 0;
 }
-        | KW_CONST
+        | function_post KW_CONST
 {
-  $$ = (int)CPPFunctionType::F_const_method;
+  $$ = $1 | (int)CPPFunctionType::F_const_method;
+}
+        | function_post KW_VOLATILE
+{
+  $$ = $1 | (int)CPPFunctionType::F_volatile_method;
 }
         | function_post KW_NOEXCEPT
 {
-  $$ = (int)CPPFunctionType::F_noexcept;
+  $$ = $1 | (int)CPPFunctionType::F_noexcept;
 }
         | function_post KW_FINAL
 {
-  $$ = (int)CPPFunctionType::F_final;
+  $$ = $1 | (int)CPPFunctionType::F_final;
 }
         | function_post KW_OVERRIDE
 {
-  $$ = (int)CPPFunctionType::F_override;
+  $$ = $1 | (int)CPPFunctionType::F_override;
+}
+        | function_post '&'
+{
+  $$ = $1 | (int)CPPFunctionType::F_lvalue_method;
+}
+        | function_post ANDAND
+{
+  $$ = $1 | (int)CPPFunctionType::F_rvalue_method;
 }
         | function_post KW_MUTABLE
 {
   // Used for lambdas, currently ignored.
-  $$ = 0;
+  $$ = $1;
 }
         | function_post KW_THROW '(' ')'
 {
@@ -1215,6 +1249,10 @@ function_post:
         | function_post KW_THROW '(' name ')'
 {
   $$ = $1;
+}
+        | function_post KW_THROW '(' name ELLIPSIS ')'
+{
+  $$ = $1;
 }
 /*        | function_post '[' '[' attribute_specifiers ']' ']'
 {
@@ -1418,22 +1456,35 @@ template_nonempty_formal_parameters:
 }
         ;
 
+typename_keyword:
+        KW_CLASS
+        | KW_TYPENAME
+        ;
+
 template_formal_parameter:
-        KW_CLASS name
+        typename_keyword
+{
+  $$ = CPPType::new_type(new CPPClassTemplateParameter((CPPIdentifier *)NULL));
+}
+        | typename_keyword name
 {
   $$ = CPPType::new_type(new CPPClassTemplateParameter($2));
 }
-        | KW_CLASS name '=' full_type
+        | typename_keyword name '=' full_type
 {
   $$ = CPPType::new_type(new CPPClassTemplateParameter($2, $4));
 }
-        | KW_TYPENAME name
+        | typename_keyword ELLIPSIS
 {
-  $$ = CPPType::new_type(new CPPClassTemplateParameter($2));
+  CPPClassTemplateParameter *ctp = new CPPClassTemplateParameter((CPPIdentifier *)NULL);
+  ctp->_packed = true;
+  $$ = CPPType::new_type(ctp);
 }
-        | KW_TYPENAME name '=' full_type
+        | typename_keyword ELLIPSIS name
 {
-  $$ = CPPType::new_type(new CPPClassTemplateParameter($2, $4));
+  CPPClassTemplateParameter *ctp = new CPPClassTemplateParameter($3);
+  ctp->_packed = true;
+  $$ = CPPType::new_type(ctp);
 }
         | template_formal_parameter_type formal_parameter_identifier template_parameter_maybe_initialize
 {
@@ -1447,6 +1498,17 @@ template_formal_parameter:
   CPPInstance *inst = new CPPInstance($2, $3, 0, @3.file);
   inst->set_initializer($4);
   $$ = inst;
+}
+        | template_formal_parameter_type parameter_pack_identifier
+{
+  CPPInstance *inst = new CPPInstance($1, $2, 0, @2.file);
+  $$ = inst;
+}
+        | KW_CONST template_formal_parameter_type parameter_pack_identifier
+{
+  $3->add_modifier(IIT_const);
+  CPPInstance *inst = new CPPInstance($2, $3, 0, @3.file);
+  $$ = inst;
 }
         ;
 
@@ -1467,6 +1529,14 @@ template_formal_parameter_type:
     yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
   }
   assert($$ != NULL);
+}
+        | TYPEPACK_IDENTIFIER
+{
+  $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
+  assert($$ != NULL);
 }
         ;
 
@@ -1547,7 +1617,16 @@ instance_identifier:
 }
         | instance_identifier '('
 {
-  push_scope($1->get_scope(current_scope, global_scope));
+  // Create a scope for this function (in case it is a function)
+  CPPScope *scope = new CPPScope($1->get_scope(current_scope, global_scope),
+                                 CPPNameComponent(""), V_private);
+
+  // It still needs to be able to pick up any template arguments, if this is
+  // a definition for a method template.  Add a fake "using" declaration to
+  // accomplish this.
+  scope->_using.insert(current_scope);
+
+  push_scope(scope);
 }
         formal_parameter_list ')' function_post
 {
@@ -1785,6 +1864,23 @@ function_parameter:
   $4->add_modifier(IIT_const);
   $$ = new CPPInstance($3, $4, 0, @3.file);
   $$->set_initializer($5);
+}
+        | type_pack parameter_pack_identifier maybe_initialize
+{
+  $$ = new CPPInstance($1, $2, 0, @2.file);
+  $$->set_initializer($3);
+}
+        | KW_CONST type_pack parameter_pack_identifier maybe_initialize
+{
+  $3->add_modifier(IIT_const);
+  $$ = new CPPInstance($2, $3, 0, @3.file);
+  $$->set_initializer($4);
+}
+        | KW_CONST KW_REGISTER type_pack parameter_pack_identifier maybe_initialize
+{
+  $4->add_modifier(IIT_const);
+  $$ = new CPPInstance($3, $4, 0, @3.file);
+  $$->set_initializer($5);
 }
         | KW_REGISTER function_parameter
 {
@@ -1913,10 +2009,79 @@ formal_parameter_identifier:
 }
         ;
 
+parameter_pack_identifier:
+        ELLIPSIS
+{
+  $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
+  $$->_packed = true;
+}
+        | ELLIPSIS name
+{
+  $$ = new CPPInstanceIdentifier($2);
+  $$->_packed = true;
+}
+        | KW_CONST parameter_pack_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE parameter_pack_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
+}
+        | '*' parameter_pack_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_pointer);
+}
+        | '&' parameter_pack_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_reference);
+}
+        | ANDAND parameter_pack_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_rvalue_reference);
+}
+        | SCOPING '*' parameter_pack_identifier  %prec UNARY
+{
+  $$ = $3;
+  $$->add_scoped_pointer_modifier($1);
+}
+        | parameter_pack_identifier '[' optional_const_expr ']'
+{
+  $$ = $1;
+  $$->add_array_modifier($3);
+}
+        | '(' parameter_pack_identifier ')' '(' function_parameter_list ')' function_post
+{
+  $$ = $2;
+  $$->add_modifier(IIT_paren);
+  $$->add_func_modifier($5, $7);
+}
+        | '(' parameter_pack_identifier ')'
+{
+  $$ = $2;
+  $$->add_modifier(IIT_paren);
+}
+        ;
+
 not_paren_empty_instance_identifier:
         empty
 {
   $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
+}
+        | ELLIPSIS
+{
+  $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
+  $$->_packed = true;
+}
+        | ELLIPSIS name
+{
+  $$ = new CPPInstanceIdentifier($2);
+  $$->_packed = true;
 }
         | KW_CONST not_paren_empty_instance_identifier  %prec UNARY
 {
@@ -1959,6 +2124,16 @@ empty_instance_identifier:
         empty
 {
   $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
+}
+        | ELLIPSIS
+{
+  $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
+  $$->_packed = true;
+}
+        | ELLIPSIS name
+{
+  $$ = new CPPInstanceIdentifier($2);
+  $$->_packed = true;
 }
         | KW_CONST empty_instance_identifier  %prec UNARY
 {
@@ -2093,6 +2268,16 @@ type:
     str << *$3;
     yyerror("could not determine type of " + str.str(), @3);
   }
+}
+        | KW_UNDERLYING_TYPE '(' full_type ')'
+{
+  CPPEnumType *enum_type = $3->as_enum_type();
+  if (enum_type == NULL) {
+    yyerror("an enumeration type is required", @3);
+    $$ = $3;
+  } else {
+    $$ = enum_type->get_underlying_type();
+  }
 }
         | KW_AUTO
 {
@@ -2100,6 +2285,17 @@ type:
 }
         ;
 
+type_pack:
+        TYPEPACK_IDENTIFIER
+{
+  $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
+  assert($$ != NULL);
+}
+        ;
+
 type_decl:
         simple_type
 {
@@ -2187,6 +2383,16 @@ type_decl:
     str << *$3;
     yyerror("could not determine type of " + str.str(), @3);
   }
+}
+        | KW_UNDERLYING_TYPE '(' full_type ')'
+{
+  CPPEnumType *enum_type = $3->as_enum_type();
+  if (enum_type == NULL) {
+    yyerror("an enumeration type is required", @3);
+    $$ = $3;
+  } else {
+    $$ = enum_type->get_underlying_type();
+  }
 }
         | KW_AUTO
 {
@@ -2251,6 +2457,16 @@ predefined_type:
     str << *$3;
     yyerror("could not determine type of " + str.str(), @3);
   }
+}
+        | KW_UNDERLYING_TYPE '(' full_type ')'
+{
+  CPPEnumType *enum_type = $3->as_enum_type();
+  if (enum_type == NULL) {
+    yyerror("an enumeration type is required", @3);
+    $$ = $3;
+  } else {
+    $$ = enum_type->get_underlying_type();
+  }
 }
         | KW_AUTO
 {
@@ -2279,6 +2495,15 @@ full_type:
 {
   $3->add_modifier(IIT_const);
   $$ = $3->unroll_type($2);
+}
+        | type_pack empty_instance_identifier
+{
+  $$ = $2->unroll_type($1);
+}
+        | KW_CONST type_pack empty_instance_identifier
+{
+  $3->add_modifier(IIT_const);
+  $$ = $3->unroll_type($2);
 }
         ;
 
@@ -2693,6 +2918,7 @@ element:
         | CHAR_TOK
         | IDENTIFIER
         | TYPENAME_IDENTIFIER
+        | TYPEPACK_IDENTIFIER
         | SCOPING
         | SIMPLE_IDENTIFIER
         | ELLIPSIS | OROR | ANDAND
@@ -2711,10 +2937,11 @@ element:
         | KW_OPERATOR | KW_OVERRIDE | KW_PRIVATE | KW_PROTECTED
         | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_REINTERPRET_CAST
         | KW_RETURN | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC
-        | KW_STATIC_ASSERT | KW_STATIC_CAST | KW_STRUCT
+        | KW_STATIC_ASSERT | KW_STATIC_CAST | KW_STRUCT | KW_TEMPLATE
         | KW_THREAD_LOCAL | KW_THROW | KW_TRUE | KW_TRY | KW_TYPEDEF
-        | KW_TYPEID | KW_TYPENAME | KW_UNION | KW_UNSIGNED | KW_USING
-        | KW_VIRTUAL | KW_VOID | KW_VOLATILE | KW_WCHAR_T | KW_WHILE
+        | KW_TYPEID | KW_TYPENAME | KW_UNDERLYING_TYPE | KW_UNION
+        | KW_UNSIGNED | KW_USING | KW_VIRTUAL | KW_VOID | KW_VOLATILE
+        | KW_WCHAR_T | KW_WHILE
 {
 }
         | '+' | '-' | '*' | '/' | '&' | '|' | '^' | '!' | '~' | '=' | '%'
@@ -2783,6 +3010,10 @@ no_angle_bracket_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::sizeof_ellipsis_func($4));
 }
         | KW_ALIGNOF '(' full_type ')' %prec UNARY
 {
@@ -3021,6 +3252,10 @@ const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::sizeof_ellipsis_func($4));
 }
         | KW_ALIGNOF '(' full_type ')' %prec UNARY
 {
@@ -3238,6 +3473,74 @@ const_operand:
         | '[' capture_list ']' '(' function_parameter_list ')' function_post maybe_trailing_return_type '{' code '}'
 {
   $$ = NULL;
+}
+        | KW_HAS_VIRTUAL_DESTRUCTOR '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_HAS_VIRTUAL_DESTRUCTOR, $3));
+}
+        | KW_IS_ABSTRACT '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_ABSTRACT, $3));
+}
+        | KW_IS_BASE_OF '(' full_type ',' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, $3, $5));
+}
+        | KW_IS_CLASS '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, $3));
+}
+        | KW_IS_CONSTRUCTIBLE '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, $3));
+}
+        | KW_IS_CONSTRUCTIBLE '(' full_type ',' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, $3, $5));
+}
+        | KW_IS_CONVERTIBLE_TO '(' full_type ',' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_CONVERTIBLE_TO, $3, $5));
+}
+        | KW_IS_DESTRUCTIBLE '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_DESTRUCTIBLE, $3));
+}
+        | KW_IS_EMPTY '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_EMPTY, $3));
+}
+        | KW_IS_ENUM '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_ENUM, $3));
+}
+        | KW_IS_FINAL '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_FINAL, $3));
+}
+        | KW_IS_FUNDAMENTAL '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_FUNDAMENTAL, $3));
+}
+        | KW_IS_POD '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_POD, $3));
+}
+        | KW_IS_POLYMORPHIC '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_POLYMORPHIC, $3));
+}
+        | KW_IS_STANDARD_LAYOUT '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_STANDARD_LAYOUT, $3));
+}
+        | KW_IS_TRIVIAL '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_TRIVIAL, $3));
+}
+        | KW_IS_UNION '(' full_type ')'
+{
+  $$ = new CPPExpression(CPPExpression::type_trait(KW_IS_UNION, $3));
 }
         ;
 
@@ -3276,6 +3579,10 @@ formal_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::sizeof_ellipsis_func($4));
 }
         | KW_ALIGNOF '(' full_type ')' %prec UNARY
 {
@@ -3518,6 +3825,12 @@ class_derivation_name:
         | KW_TYPENAME name
 {
   $$ = CPPType::new_type(new CPPTBDType($2));
+}
+        | name ELLIPSIS
+{
+  CPPClassTemplateParameter *ctp = new CPPClassTemplateParameter($1);
+  ctp->_packed = true;
+  $$ = CPPType::new_type(ctp);
 }
         ;
 
@@ -3550,6 +3863,10 @@ name:
         | TYPENAME_IDENTIFIER
 {
   $$ = $1;
+}
+        | TYPEPACK_IDENTIFIER
+{
+  $$ = $1;
 }
         | KW_FINAL
 {
@@ -3595,6 +3912,10 @@ name_no_final:
         | TYPENAME_IDENTIFIER
 {
   $$ = $1;
+}
+        | TYPEPACK_IDENTIFIER
+{
+  $$ = $1;
 }
         | KW_OVERRIDE
 {

+ 26 - 3
dtool/src/cppparser/cppClassTemplateParameter.cxx

@@ -21,7 +21,8 @@ CPPClassTemplateParameter::
 CPPClassTemplateParameter(CPPIdentifier *ident, CPPType *default_type) :
   CPPType(CPPFile()),
   _ident(ident),
-  _default_type(default_type)
+  _default_type(default_type),
+  _packed(false)
 {
 }
 
@@ -41,8 +42,14 @@ is_fully_specified() const {
 void CPPClassTemplateParameter::
 output(ostream &out, int indent_level, CPPScope *scope, bool complete) const {
   if (complete) {
-    out << "class ";
-    _ident->output(out, scope);
+    out << "class";
+    if (_packed) {
+      out << "...";
+    }
+    if (_ident != NULL) {
+      out << " ";
+      _ident->output(out, scope);
+    }
     if (_default_type) {
       out << " = ";
       _default_type->output(out, indent_level, scope, false);
@@ -82,6 +89,14 @@ is_equal(const CPPDeclaration *other) const {
     return false;
   }
 
+  if (_packed != ot->_packed) {
+    return false;
+  }
+
+  if (_ident == NULL || ot->_ident == NULL) {
+    return _ident == ot->_ident;
+  }
+
   return *_ident == *ot->_ident;
 }
 
@@ -99,5 +114,13 @@ is_less(const CPPDeclaration *other) const {
     return _default_type < ot->_default_type;
   }
 
+  if (_packed != ot->_packed) {
+    return _packed < ot->_packed;
+  }
+
+  if (_ident == NULL || ot->_ident == NULL) {
+    return _ident < ot->_ident;
+  }
+
   return *_ident < *ot->_ident;
 }

+ 1 - 0
dtool/src/cppparser/cppClassTemplateParameter.h

@@ -37,6 +37,7 @@ public:
 
   CPPIdentifier *_ident;
   CPPType *_default_type;
+  bool _packed;
 
 protected:
   virtual bool is_equal(const CPPDeclaration *other) const;

+ 42 - 1
dtool/src/cppparser/cppConstType.cxx

@@ -86,6 +86,22 @@ is_tbd() const {
   return _wrapped_around->is_tbd();
 }
 
+/**
+ * Returns true if the type is considered a fundamental type.
+ */
+bool CPPConstType::
+is_fundamental() const {
+  return _wrapped_around->is_fundamental();
+}
+
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPConstType::
+is_standard_layout() const {
+  return _wrapped_around->is_standard_layout();
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -94,12 +110,20 @@ is_trivial() const {
   return _wrapped_around->is_trivial();
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPConstType::
+is_constructible(const CPPType *given_type) const {
+  return _wrapped_around->is_constructible(given_type);
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
 bool CPPConstType::
 is_default_constructible() const {
-  return false;
+  return _wrapped_around->is_default_constructible();
 }
 
 /**
@@ -110,6 +134,23 @@ is_copy_constructible() const {
   return _wrapped_around->is_copy_constructible();
 }
 
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPConstType::
+is_destructible() const {
+  return _wrapped_around->is_destructible();
+}
+
+/**
+ * Returns true if variables of this type may be implicitly converted to
+ * the other type.
+ */
+bool CPPConstType::
+is_convertible_to(const CPPType *other) const {
+  return _wrapped_around->is_convertible_to(other);
+}
+
 /**
  * This is a little more forgiving than is_equal(): it returns true if the
  * types appear to be referring to the same thing, even if they may have

+ 5 - 0
dtool/src/cppparser/cppConstType.h

@@ -36,9 +36,14 @@ public:
                                 CPPScope *global_scope);
 
   virtual bool is_tbd() const;
+  virtual bool is_fundamental() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_destructible() const;
+  virtual bool is_convertible_to(const CPPType *other) const;
   virtual bool is_equivalent(const CPPType &other) const;
 
   virtual void output(ostream &out, int indent_level, CPPScope *scope,

+ 8 - 2
dtool/src/cppparser/cppDeclaration.cxx

@@ -41,8 +41,13 @@ CPPDeclaration(const CPPDeclaration &copy) :
 /**
  *
  */
-CPPDeclaration::
-~CPPDeclaration() {
+CPPDeclaration &CPPDeclaration::
+operator = (const CPPDeclaration &copy) {
+  _vis = copy._vis;
+  _template_scope = copy._template_scope;
+  _file = copy._file;
+  _leading_comment = copy._leading_comment;
+  return *this;
 }
 
 /**
@@ -124,6 +129,7 @@ CPPDeclaration *CPPDeclaration::
 substitute_decl(SubstDecl &subst, CPPScope *, CPPScope *) {
   SubstDecl::const_iterator si = subst.find(this);
   if (si != subst.end()) {
+    assert((*si).second != NULL);
     return (*si).second;
   }
   return this;

+ 70 - 1
dtool/src/cppparser/cppDeclaration.h

@@ -89,7 +89,9 @@ public:
 
   CPPDeclaration(const CPPFile &file);
   CPPDeclaration(const CPPDeclaration &copy);
-  virtual ~CPPDeclaration();
+  virtual ~CPPDeclaration() {};
+
+  CPPDeclaration &operator = (const CPPDeclaration &copy);
 
   bool operator == (const CPPDeclaration &other) const;
   bool operator != (const CPPDeclaration &other) const;
@@ -139,6 +141,73 @@ public:
   virtual CPPMakeProperty *as_make_property();
   virtual CPPMakeSeq *as_make_seq();
 
+  inline const CPPInstance *as_instance() const {
+    return ((CPPDeclaration *)this)->as_instance();
+  }
+  inline const CPPClassTemplateParameter *as_class_template_parameter() const {
+    return ((CPPDeclaration *)this)->as_class_template_parameter();
+  }
+  inline const CPPTypedefType *as_typedef_type() const {
+    return ((CPPDeclaration *)this)->as_typedef_type();
+  }
+  inline const CPPTypeDeclaration *as_type_declaration() const {
+    return ((CPPDeclaration *)this)->as_type_declaration();
+  }
+  inline const CPPExpression *as_expression() const {
+    return ((CPPDeclaration *)this)->as_expression();
+  }
+  inline const CPPType *as_type() const {
+    return ((CPPDeclaration *)this)->as_type();
+  }
+  inline const CPPNamespace *as_namespace() const {
+    return ((CPPDeclaration *)this)->as_namespace();
+  }
+  inline const CPPUsing *as_using() const {
+    return ((CPPDeclaration *)this)->as_using();
+  }
+  inline const CPPSimpleType *as_simple_type() const {
+    return ((CPPDeclaration *)this)->as_simple_type();
+  }
+  inline const CPPPointerType *as_pointer_type() const {
+    return ((CPPDeclaration *)this)->as_pointer_type();
+  }
+  inline const CPPReferenceType *as_reference_type() const {
+    return ((CPPDeclaration *)this)->as_reference_type();
+  }
+  inline const CPPArrayType *as_array_type() const {
+    return ((CPPDeclaration *)this)->as_array_type();
+  }
+  inline const CPPConstType *as_const_type() const {
+    return ((CPPDeclaration *)this)->as_const_type();
+  }
+  inline const CPPFunctionType *as_function_type() const {
+    return ((CPPDeclaration *)this)->as_function_type();
+  }
+  inline const CPPFunctionGroup *as_function_group() const {
+    return ((CPPDeclaration *)this)->as_function_group();
+  }
+  inline const CPPExtensionType *as_extension_type() const {
+    return ((CPPDeclaration *)this)->as_extension_type();
+  }
+  inline const CPPStructType *as_struct_type() const {
+    return ((CPPDeclaration *)this)->as_struct_type();
+  }
+  inline const CPPEnumType *as_enum_type() const {
+    return ((CPPDeclaration *)this)->as_enum_type();
+  }
+  inline const CPPTBDType *as_tbd_type() const {
+    return ((CPPDeclaration *)this)->as_tbd_type();
+  }
+  inline const CPPTypeProxy *as_type_proxy() const {
+    return ((CPPDeclaration *)this)->as_type_proxy();
+  }
+  inline const CPPMakeProperty *as_make_property() const {
+    return ((CPPDeclaration *)this)->as_make_property();
+  }
+  inline const CPPMakeSeq *as_make_seq() const {
+    return ((CPPDeclaration *)this)->as_make_seq();
+  }
+
   CPPVisibility _vis;
   CPPTemplateScope *_template_scope;
   CPPFile _file;

+ 212 - 7
dtool/src/cppparser/cppExpression.cxx

@@ -24,6 +24,7 @@
 #include "cppInstance.h"
 #include "cppFunctionGroup.h"
 #include "cppFunctionType.h"
+#include "cppStructType.h"
 #include "cppBison.h"
 #include "pdtoa.h"
 
@@ -389,6 +390,19 @@ typeid_op(CPPExpression *op1, CPPType *std_type_info) {
   return expr;
 }
 
+/**
+ * Creates an expression that returns a particular type trait.
+ */
+CPPExpression CPPExpression::
+type_trait(int trait, CPPType *type, CPPType *arg) {
+  CPPExpression expr(0);
+  expr._type = T_type_trait;
+  expr._u._type_trait._trait = trait;
+  expr._u._type_trait._type = type;
+  expr._u._type_trait._arg = arg;
+  return expr;
+}
+
 /**
  *
  */
@@ -401,6 +415,17 @@ sizeof_func(CPPType *type) {
   return expr;
 }
 
+/**
+ *
+ */
+CPPExpression CPPExpression::
+sizeof_ellipsis_func(CPPIdentifier *ident) {
+  CPPExpression expr(0);
+  expr._type = T_sizeof_ellipsis;
+  expr._u._ident = ident;
+  return expr;
+}
+
 /**
  *
  */
@@ -492,13 +517,6 @@ get_delete() {
   return expr;
 }
 
-/**
- *
- */
-CPPExpression::
-~CPPExpression() {
-}
-
 /**
  *
  */
@@ -579,6 +597,7 @@ evaluate() const {
   case T_new:
   case T_default_new:
   case T_sizeof:
+  case T_sizeof_ellipsis:
     return Result();
 
   case T_alignof:
@@ -801,6 +820,95 @@ evaluate() const {
   case T_typeid_expr:
     return Result();
 
+  case T_type_trait:
+    switch (_u._type_trait._trait) {
+    case KW_HAS_VIRTUAL_DESTRUCTOR:
+      {
+        CPPStructType *struct_type = _u._type_trait._type->as_struct_type();
+        return Result(struct_type != NULL && struct_type->has_virtual_destructor());
+      }
+
+    case KW_IS_ABSTRACT:
+      {
+        CPPStructType *struct_type = _u._type_trait._type->as_struct_type();
+        return Result(struct_type != NULL && struct_type->is_abstract());
+      }
+
+    case KW_IS_BASE_OF:
+      {
+        CPPStructType *struct_type1 = _u._type_trait._type->as_struct_type();
+        CPPStructType *struct_type2 = _u._type_trait._arg->as_struct_type();
+        return Result(struct_type1 != NULL && struct_type2 != NULL && struct_type1->is_base_of(struct_type2));
+      }
+
+    case KW_IS_CLASS:
+      {
+        CPPExtensionType *ext_type = _u._type_trait._type->as_extension_type();
+        return Result(ext_type != NULL && (
+          ext_type->_type == CPPExtensionType::T_class ||
+          ext_type->_type == CPPExtensionType::T_struct));
+      }
+
+    case KW_IS_CONSTRUCTIBLE:
+      if (_u._type_trait._arg == NULL) {
+        return Result(_u._type_trait._type->is_default_constructible());
+      } else {
+        return Result(_u._type_trait._type->is_constructible(_u._type_trait._arg));
+      }
+
+    case KW_IS_CONVERTIBLE_TO:
+      assert(_u._type_trait._arg != NULL);
+      return Result(_u._type_trait._type->is_convertible_to(_u._type_trait._arg));
+
+    case KW_IS_DESTRUCTIBLE:
+      return Result(_u._type_trait._type->is_destructible());
+
+    case KW_IS_EMPTY:
+      {
+        CPPStructType *struct_type = _u._type_trait._type->as_struct_type();
+        return Result(struct_type != NULL && struct_type->is_empty());
+      }
+
+    case KW_IS_ENUM:
+      return Result(_u._type_trait._type->is_enum());
+
+    case KW_IS_FINAL:
+      {
+        CPPStructType *struct_type = _u._type_trait._type->as_struct_type();
+        return Result(struct_type != NULL && struct_type->is_final());
+      }
+
+    case KW_IS_FUNDAMENTAL:
+      return Result(_u._type_trait._type->is_fundamental());
+
+    case KW_IS_POD:
+      return Result(_u._type_trait._type->is_trivial() &&
+                    _u._type_trait._type->is_standard_layout());
+
+    case KW_IS_POLYMORPHIC:
+      {
+        CPPStructType *struct_type = _u._type_trait._type->as_struct_type();
+        return Result(struct_type != NULL && struct_type->is_polymorphic());
+      }
+
+    case KW_IS_STANDARD_LAYOUT:
+      return Result(_u._type_trait._type->is_standard_layout());
+
+    case KW_IS_TRIVIAL:
+      return Result(_u._type_trait._type->is_trivial());
+
+    case KW_IS_UNION:
+      {
+        CPPExtensionType *ext_type = _u._type_trait._type->as_extension_type();
+        return Result(ext_type != NULL &&
+          ext_type->_type == CPPExtensionType::T_union);
+      }
+
+    default:
+      cerr << "**unexpected type trait**\n";
+      abort();
+    }
+
   default:
     cerr << "**invalid operand**\n";
     abort();
@@ -916,6 +1024,7 @@ determine_type() const {
     return CPPType::new_type(new CPPPointerType(_u._typecast._to));
 
   case T_sizeof:
+  case T_sizeof_ellipsis:
   case T_alignof:
     // Note: this should actually be size_t, but that is defined as a typedef
     // in parser-inc.  We could try to resolve it, but that's hacky.  Eh, it's
@@ -1057,6 +1166,9 @@ determine_type() const {
   case T_typeid_expr:
     return _u._typeid._std_type_info;
 
+  case T_type_trait:
+    return bool_type;
+
   default:
     cerr << "**invalid operand**\n";
     abort();
@@ -1113,6 +1225,9 @@ is_fully_specified() const {
   case T_alignof:
     return _u._typecast._to->is_fully_specified();
 
+  case T_sizeof_ellipsis:
+    return _u._ident->is_fully_specified();
+
   case T_trinary_operation:
     if (!_u._op._op3->is_fully_specified()) {
       return false;
@@ -1141,6 +1256,9 @@ is_fully_specified() const {
   case T_typeid_expr:
     return _u._typeid._expr->is_fully_specified();
 
+  case T_type_trait:
+    return _u._type_trait._type->is_fully_specified();
+
   default:
     return true;
   }
@@ -1276,6 +1394,13 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
     any_changed = any_changed || (rep->_u._typeid._expr != _u._typeid._expr);
     break;
 
+  case T_type_trait:
+    rep->_u._type_trait._type =
+      _u._type_trait._type->substitute_decl(subst, current_scope, global_scope)
+      ->as_type();
+    any_changed = any_changed || (rep->_u._type_trait._type != _u._type_trait._type);
+    break;
+
   default:
     break;
   }
@@ -1350,6 +1475,9 @@ is_tbd() const {
   case T_typeid_expr:
     return _u._typeid._expr->is_tbd();
 
+  case T_type_trait:
+    return _u._type_trait._type->is_tbd();
+
   default:
     return false;
   }
@@ -1541,6 +1669,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << ")";
     break;
 
+  case T_sizeof_ellipsis:
+    out << "sizeof...(";
+    _u._ident->output(out, scope);
+    out << ")";
+    break;
+
   case T_alignof:
     out << "alignof(";
     _u._typecast._to->output(out, indent_level, scope, false);
@@ -1753,6 +1887,65 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << "delete";
     break;
 
+  case T_type_trait:
+    switch (_u._type_trait._trait) {
+    case KW_HAS_VIRTUAL_DESTRUCTOR:
+      out << "__has_virtual_destructor";
+      break;
+    case KW_IS_ABSTRACT:
+      out << "__is_abstract";
+      break;
+    case KW_IS_BASE_OF:
+      out << "__is_base_of";
+      break;
+    case KW_IS_CLASS:
+      out << "__is_class";
+      break;
+    case KW_IS_CONSTRUCTIBLE:
+      out << "__is_constructible";
+      break;
+    case KW_IS_CONVERTIBLE_TO:
+      out << "__is_convertible_to";
+      break;
+    case KW_IS_DESTRUCTIBLE:
+      out << "__is_destructible";
+      break;
+    case KW_IS_EMPTY:
+      out << "__is_empty";
+      break;
+    case KW_IS_ENUM:
+      out << "__is_enum";
+      break;
+    case KW_IS_FINAL:
+      out << "__is_final";
+      break;
+    case KW_IS_FUNDAMENTAL:
+      out << "__is_fundamental";
+      break;
+    case KW_IS_POD:
+      out << "__is_pod";
+      break;
+    case KW_IS_POLYMORPHIC:
+      out << "__is_polymorphic";
+      break;
+    case KW_IS_STANDARD_LAYOUT:
+      out << "__is_standard_layout";
+      break;
+    case KW_IS_TRIVIAL:
+      out << "__is_trivial";
+      break;
+    case KW_IS_UNION:
+      out << "__is_union";
+      break;
+    default:
+      out << (evaluate().as_boolean() ? "true" : "false");
+      return;
+    }
+    out << '(';
+    _u._type_trait._type->output(out, indent_level, scope, false);
+    out << ')';
+    break;
+
   default:
     out << "(** invalid operand type " << (int)_type << " **)";
   }
@@ -1864,6 +2057,7 @@ is_equal(const CPPDeclaration *other) const {
     return _u._fgroup == ot->_u._fgroup;
 
   case T_unknown_ident:
+  case T_sizeof_ellipsis:
     return *_u._ident == *ot->_u._ident;
 
   case T_typecast:
@@ -1907,6 +2101,10 @@ is_equal(const CPPDeclaration *other) const {
   case T_typeid_expr:
     return _u._typeid._expr == ot->_u._typeid._expr;
 
+  case T_type_trait:
+    return _u._type_trait._trait == ot->_u._type_trait._trait &&
+           _u._type_trait._type == ot->_u._type_trait._type;
+
   default:
     cerr << "(** invalid operand type " << (int)_type << " **)";
   }
@@ -1954,6 +2152,7 @@ is_less(const CPPDeclaration *other) const {
     return *_u._fgroup < *ot->_u._fgroup;
 
   case T_unknown_ident:
+  case T_sizeof_ellipsis:
     return *_u._ident < *ot->_u._ident;
 
   case T_typecast:
@@ -2007,6 +2206,12 @@ is_less(const CPPDeclaration *other) const {
   case T_typeid_expr:
     return *_u._typeid._expr < *ot->_u._typeid._expr;
 
+  case T_type_trait:
+    if (_u._type_trait._trait != ot->_u._type_trait._trait) {
+      return _u._type_trait._trait < ot->_u._type_trait._trait;
+    }
+    return *_u._type_trait._type < *ot->_u._type_trait._type;
+
   default:
     cerr << "(** invalid operand type " << (int)_type << " **)";
   }

+ 9 - 2
dtool/src/cppparser/cppExpression.h

@@ -51,6 +51,7 @@ public:
     T_new,
     T_default_new,
     T_sizeof,
+    T_sizeof_ellipsis,
     T_alignof,
     T_unary_operation,
     T_binary_operation,
@@ -59,6 +60,7 @@ public:
     T_raw_literal,
     T_typeid_type,
     T_typeid_expr,
+    T_type_trait,
 
     // These are used when parsing =default and =delete methods.
     T_default,
@@ -81,7 +83,9 @@ public:
   static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL);
   static CPPExpression typeid_op(CPPType *type, CPPType *std_type_info);
   static CPPExpression typeid_op(CPPExpression *op1, CPPType *std_type_info);
+  static CPPExpression type_trait(int trait, CPPType *type, CPPType *arg = NULL);
   static CPPExpression sizeof_func(CPPType *type);
+  static CPPExpression sizeof_ellipsis_func(CPPIdentifier *ident);
   static CPPExpression alignof_func(CPPType *type);
 
   static CPPExpression literal(unsigned long long value, CPPInstance *lit_op);
@@ -93,8 +97,6 @@ public:
   static const CPPExpression &get_default();
   static const CPPExpression &get_delete();
 
-  ~CPPExpression();
-
   enum ResultType {
     RT_integer,
     RT_real,
@@ -170,6 +172,11 @@ public:
       CPPInstance *_operator;
       CPPExpression *_value;
     } _literal;
+    struct {
+      int _trait;
+      CPPType *_type;
+      CPPType *_arg;
+    } _type_trait;
   } _u;
 
 protected:

+ 23 - 3
dtool/src/cppparser/cppExtensionType.cxx

@@ -87,12 +87,32 @@ is_tbd() const {
   return false;
 }
 
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPExtensionType::
+is_standard_layout() const {
+  return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
 bool CPPExtensionType::
 is_trivial() const {
-  return (_type == T_enum);
+  return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
+}
+
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPExtensionType::
+is_constructible(const CPPType *given_type) const {
+  if (_type == T_enum || _type == T_enum_class || _type == T_enum_struct) {
+    const CPPExtensionType *other = ((CPPType *)given_type)->remove_reference()->remove_const()->as_extension_type();
+    return other != NULL && is_equal(other);
+  }
+  return false;
 }
 
 /**
@@ -100,7 +120,7 @@ is_trivial() const {
  */
 bool CPPExtensionType::
 is_default_constructible() const {
-  return (_type == T_enum);
+  return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
 }
 
 /**
@@ -108,7 +128,7 @@ is_default_constructible() const {
  */
 bool CPPExtensionType::
 is_copy_constructible() const {
-  return (_type == T_enum);
+  return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
 }
 
 /**

+ 2 - 0
dtool/src/cppparser/cppExtensionType.h

@@ -47,7 +47,9 @@ public:
 
   virtual bool is_incomplete() const;
   virtual bool is_tbd() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
 

+ 1 - 1
dtool/src/cppparser/cppFile.h

@@ -55,7 +55,7 @@ public:
 
   Filename _filename;
   Filename _filename_as_referenced;
-  Source _source;
+  mutable Source _source;
   mutable bool _pragma_once;
 };
 

+ 14 - 6
dtool/src/cppparser/cppFunctionType.cxx

@@ -89,9 +89,11 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
   }
 
   CPPFunctionType *rep = new CPPFunctionType(*this);
-  rep->_return_type =
-    _return_type->substitute_decl(subst, current_scope, global_scope)
-    ->as_type();
+  if (_return_type != NULL) {
+    rep->_return_type =
+      _return_type->substitute_decl(subst, current_scope, global_scope)
+      ->as_type();
+  }
 
   rep->_parameters =
     _parameters->substitute_decl(subst, current_scope, global_scope);
@@ -265,6 +267,9 @@ output_instance(ostream &out, int indent_level, CPPScope *scope,
   if (_flags & F_const_method) {
     out << " const";
   }
+  if (_flags & F_volatile_method) {
+    out << " volatile";
+  }
   if (_flags & F_noexcept) {
     out << " noexcept";
   }
@@ -321,14 +326,17 @@ as_function_type() {
  * This is similar to is_equal(), except it is more forgiving: it considers
  * the functions to be equivalent only if the return type and the types of all
  * parameters match.
+ *
+ * Note that this isn't symmetric to account for covariant return types.
  */
 bool CPPFunctionType::
-is_equivalent_function(const CPPFunctionType &other) const {
-  if (!_return_type->is_equivalent(*other._return_type)) {
+match_virtual_override(const CPPFunctionType &other) const {
+  if (!_return_type->is_equivalent(*other._return_type) &&
+      !_return_type->is_convertible_to(other._return_type)) {
     return false;
   }
 
-  if (_flags != other._flags) {
+  if (((_flags ^ other._flags) & ~(F_override | F_final)) != 0) {
     return false;
   }
 

+ 4 - 1
dtool/src/cppparser/cppFunctionType.h

@@ -40,6 +40,9 @@ public:
     F_trailing_return_type = 0x800,
     F_final             = 0x1000,
     F_override          = 0x2000,
+    F_volatile_method   = 0x4000,
+    F_lvalue_method     = 0x8000,
+    F_rvalue_method     = 0x10000,
   };
 
   CPPFunctionType(CPPType *return_type, CPPParameterList *parameters,
@@ -81,7 +84,7 @@ public:
 
   virtual CPPFunctionType *as_function_type();
 
-  bool is_equivalent_function(const CPPFunctionType &other) const;
+  bool match_virtual_override(const CPPFunctionType &other) const;
 
   CPPIdentifier *_class_owner;
 

+ 8 - 5
dtool/src/cppparser/cppInstance.cxx

@@ -82,6 +82,10 @@ CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class,
     // anyway.
   }
 
+  if (ii->_packed) {
+    _storage_class |= SC_parameter_pack;
+  }
+
   delete ii;
 }
 
@@ -353,11 +357,7 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
           CPPReferenceType *ref_type = param_type->as_reference_type();
 
           if (ref_type != NULL) {
-            param_type = ref_type->_pointing_at;
-
-            if (param_type->get_subtype() == CPPDeclaration::ST_const) {
-              param_type = param_type->as_const_type()->_wrapped_around;
-            }
+            param_type = ref_type->_pointing_at->remove_cv();
 
             if (class_name == param_type->get_simple_name()) {
               if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
@@ -563,6 +563,9 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete,
   if (_ident != NULL) {
     name = _ident->get_local_name(scope);
   }
+  if (_storage_class & SC_parameter_pack) {
+    name = "..." + name;
+  }
 
   if (_type->as_function_type()) {
     _type->as_function_type()->

+ 3 - 0
dtool/src/cppparser/cppInstance.h

@@ -68,6 +68,9 @@ public:
     // This isn't really a storage class.  It's only used temporarily by the
     // parser, to make parsing specifier sequences a bit easier.
     SC_const        = 0x20000,
+
+    // Used to indicate that this is a parameter pack.
+    SC_parameter_pack = 0x40000,
   };
 
   CPPInstance(CPPType *type, const string &name, int storage_class = 0);

+ 7 - 7
dtool/src/cppparser/cppInstanceIdentifier.cxx

@@ -26,12 +26,11 @@
  */
 CPPInstanceIdentifier::Modifier::
 Modifier(CPPInstanceIdentifierType type) :
-  _type(type)
-{
-  _func_params = NULL;
-  _func_flags = 0;
-  _scoping = NULL;
-  _expr = NULL;
+  _type(type),
+  _func_params(NULL),
+  _func_flags(0),
+  _scoping(NULL),
+  _expr(NULL) {
 }
 
 /**
@@ -83,7 +82,8 @@ initializer_type(CPPParameterList *params) {
 CPPInstanceIdentifier::
 CPPInstanceIdentifier(CPPIdentifier *ident) :
   _ident(ident),
-  _bit_width(-1) {
+  _bit_width(-1),
+  _packed(false) {
 }
 
 /**

+ 4 - 0
dtool/src/cppparser/cppInstanceIdentifier.h

@@ -88,8 +88,12 @@ public:
   typedef vector<Modifier> Modifiers;
   Modifiers _modifiers;
 
+  // If not -1, indicates a bitfield
   int _bit_width;
 
+  // Indicates a parameter pack
+  bool _packed;
+
 private:
   CPPType *
   r_unroll_type(CPPType *start_type, Modifiers::const_iterator mi);

+ 14 - 1
dtool/src/cppparser/cppParser.cxx

@@ -45,7 +45,20 @@ is_fully_specified() const {
  */
 bool CPPParser::
 parse_file(const Filename &filename) {
-  if (!init_cpp(CPPFile(filename, filename, CPPFile::S_local))) {
+  Filename canonical(filename);
+  canonical.make_canonical();
+
+  CPPFile file(canonical, filename, CPPFile::S_local);
+
+  // Don't read it if we included it before and it had #pragma once.
+  ParsedFiles::iterator it = _parsed_files.find(file);
+  if (it != _parsed_files.end() && it->_pragma_once) {
+    // But mark it as local.
+    it->_source = CPPFile::S_local;
+    return true;
+  }
+
+  if (!init_cpp(file)) {
     cerr << "Unable to read " << filename << "\n";
     return false;
   }

+ 65 - 0
dtool/src/cppparser/cppPointerType.cxx

@@ -14,6 +14,9 @@
 #include "cppPointerType.h"
 #include "cppFunctionType.h"
 #include "cppIdentifier.h"
+#include "cppArrayType.h"
+#include "cppStructType.h"
+#include "cppSimpleType.h"
 
 /**
  *
@@ -88,6 +91,14 @@ is_tbd() const {
   return _pointing_at->is_tbd();
 }
 
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPPointerType::
+is_standard_layout() const {
+  return true;
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -96,6 +107,60 @@ is_trivial() const {
   return true;
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPPointerType::
+is_constructible(const CPPType *given_type) const {
+  given_type = ((CPPType *)given_type)->remove_reference()->remove_cv();
+
+  // Can convert from compatible pointer or array type.
+  CPPType *other_target;
+  switch (given_type->get_subtype()) {
+  case ST_array:
+    other_target = given_type->as_array_type()->_element_type;
+    break;
+
+  case ST_pointer:
+    other_target = given_type->as_pointer_type()->_pointing_at;
+    break;
+
+  case ST_simple:
+    // Can initialize from nullptr.
+    return given_type->as_simple_type()->_type == CPPSimpleType::T_nullptr;
+
+  default:
+    return false;
+  }
+
+  // Can't convert const to non-const pointer.
+  if (other_target->is_const() && !_pointing_at->is_const()) {
+    return false;
+  }
+
+  // Are we pointing to the same type?  That's always OK.
+  const CPPType *a = _pointing_at->remove_cv();
+  const CPPType *b = other_target->remove_cv();
+  if (a == b || *a == *b) {
+    return true;
+  }
+
+  // Can initialize void pointer with any pointer.
+  const CPPSimpleType *simple_type = a->as_simple_type();
+  if (simple_type != NULL) {
+    return simple_type->_type == CPPSimpleType::T_void;
+  }
+
+  // Can initialize from derived class pointer.
+  const CPPStructType *a_struct = a->as_struct_type();
+  const CPPStructType *b_struct = b->as_struct_type();
+  if (a_struct != NULL && b_struct != NULL) {
+    return a_struct->is_base_of(b_struct);
+  }
+
+  return false;
+}
+
 /**
  * Returns true if the type is default-constructible.
  */

+ 2 - 0
dtool/src/cppparser/cppPointerType.h

@@ -36,7 +36,9 @@ public:
                                 CPPScope *global_scope);
 
   virtual bool is_tbd() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *other) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_equivalent(const CPPType &other) const;

+ 257 - 83
dtool/src/cppparser/cppPreprocessor.cxx

@@ -18,6 +18,7 @@
 #include "cppIdentifier.h"
 #include "cppTemplateScope.h"
 #include "cppTemplateParameterList.h"
+#include "cppClassTemplateParameter.h"
 #include "cppConstType.h"
 #include "cppFunctionGroup.h"
 #include "cppFunctionType.h"
@@ -416,7 +417,14 @@ get_next_token0() {
     int token_type = IDENTIFIER;
     CPPDeclaration *decl = ident->find_symbol(current_scope, global_scope);
     if (decl != NULL && decl->as_type() != NULL) {
-      token_type = TYPENAME_IDENTIFIER;
+      // We need to see type pack template parameters as a different type of
+      // identifier to resolve a parser ambiguity.
+      CPPClassTemplateParameter *ctp = decl->as_class_template_parameter();
+      if (ctp && ctp->_packed) {
+        token_type = TYPEPACK_IDENTIFIER;
+      } else {
+        token_type = TYPENAME_IDENTIFIER;
+      }
     }
 
     _last_token_loc = loc;
@@ -801,6 +809,8 @@ expand_manifests(const string &input_expr, bool expand_undefined,
         // Here's an identifier.  Is it "defined"?
         if (ident == "defined") {
           expand_defined_function(expr, q, p);
+        } else if (expand_undefined && ident == "__has_include") {
+          expand_has_include_function(expr, q, p, loc);
         } else {
           // Is it a manifest?
           Manifests::const_iterator mi = _manifests.find(ident);
@@ -809,6 +819,20 @@ expand_manifests(const string &input_expr, bool expand_undefined,
             expand_manifest_inline(expr, q, p, manifest);
             manifest_found = true;
 
+          } else if (ident == "__FILE__") {
+            // Special case: this is a dynamic definition.
+            string file = string("\"") + loc.file._filename_as_referenced.get_fullpath() + "\"";
+            expr = expr.substr(0, q) + file + expr.substr(p);
+            p = q + file.size();
+            manifest_found = true;
+
+          } else if (ident == "__LINE__") {
+            // So is this.
+            string line = format_string(loc.first_line);
+            expr = expr.substr(0, q) + line + expr.substr(p);
+            p = q + line.size();
+            manifest_found = true;
+
           } else if (expand_undefined && ident != "true" && ident != "false") {
             // It is not found.  Expand it to 0, but only if we are currently
             // parsing an #if expression.
@@ -951,7 +975,7 @@ internal_get_next_token() {
     case ',':
       if (_paren_nesting <= 0) {
         _state = S_end_nested;
-        return CPPToken::eof();
+        return CPPToken(0, loc);
       }
       break;
 
@@ -959,7 +983,7 @@ internal_get_next_token() {
       if (_paren_nesting <= 0) {
         _parsing_template_params = false;
         _state = S_end_nested;
-        return CPPToken::eof();
+        return CPPToken(0, loc);
       }
     }
   }
@@ -1453,7 +1477,6 @@ handle_define_directive(const string &args, const YYLTYPE &loc) {
       CPPManifest *other = result.first->second;
       warning("redefinition of macro '" + manifest->_name + "'", loc);
       warning("previous definition is here", other->_loc);
-      delete other;
       result.first->second = manifest;
     }
   }
@@ -1479,14 +1502,10 @@ handle_undef_directive(const string &args, const YYLTYPE &loc) {
  */
 void CPPPreprocessor::
 handle_ifdef_directive(const string &args, const YYLTYPE &loc) {
-  Manifests::const_iterator mi = _manifests.find(args);
-  if (mi != _manifests.end()) {
-    // The macro is defined.  We continue.
-    return;
+  if (!is_manifest_defined(args)) {
+    // The macro is undefined.  Skip stuff.
+    skip_false_if_block(true);
   }
-
-  // The macro is undefined.  Skip stuff.
-  skip_false_if_block(true);
 }
 
 /**
@@ -1494,17 +1513,12 @@ handle_ifdef_directive(const string &args, const YYLTYPE &loc) {
  */
 void CPPPreprocessor::
 handle_ifndef_directive(const string &args, const YYLTYPE &loc) {
-  Manifests::const_iterator mi = _manifests.find(args);
-  if (mi == _manifests.end()) {
-    // The macro is undefined.  We continue.
-    return;
+  if (is_manifest_defined(args)) {
+    // The macro is defined.  Skip stuff.
+    skip_false_if_block(true);
   }
-
-  // The macro is defined.  Skip stuff.
-  skip_false_if_block(true);
 }
 
-
 /**
  *
  */
@@ -1586,6 +1600,8 @@ handle_include_directive(const string &args, const YYLTYPE &loc) {
         _angle_includes.insert(filename);
       }
     }
+  } else {
+    warning("Ignoring invalid #include directive", loc);
   }
 
   filename.set_text();
@@ -1593,71 +1609,30 @@ handle_include_directive(const string &args, const YYLTYPE &loc) {
 
   // Now look for the filename.  If we didn't use angle quotes, look first in
   // the current directory.
-  bool found_file = false;
   CPPFile::Source source = CPPFile::S_none;
 
-  if (okflag) {
-    found_file = false;
+  if (find_include(filename, angle_quotes, source)) {
+    _last_c = '\0';
 
-    // Search the current directory.
-    if (!angle_quotes && !found_file && filename.exists()) {
-      found_file = true;
+    // If it was explicitly named on the command-line, mark it S_local.
+    filename.make_canonical();
+    if (_explicit_files.count(filename)) {
       source = CPPFile::S_local;
     }
 
-    // Search the same directory as the includer.
-    if (!angle_quotes && !found_file) {
-      Filename match(get_file()._filename.get_dirname(), filename);
-      if (match.exists()) {
-        filename = match;
-        found_file = true;
-        source = CPPFile::S_alternate;
-      }
-    }
+    CPPFile file(filename, filename_as_referenced, source);
 
-    // Now search the angle-include-path
-    if (angle_quotes && !found_file && filename.resolve_filename(_angle_include_path)) {
-      found_file = true;
-      source = CPPFile::S_system;
+    // Don't include it if we included it before and it had #pragma once.
+    ParsedFiles::const_iterator it = _parsed_files.find(file);
+    if (it != _parsed_files.end() && it->_pragma_once) {
+      return;
     }
 
-    // Now search the quote-include-path
-    if (!angle_quotes && !found_file) {
-      for (size_t dir=0; dir<_quote_include_path.get_num_directories(); dir++) {
-        Filename match(_quote_include_path.get_directory(dir), filename);
-        if (match.exists()) {
-          filename = match;
-          found_file = true;
-          source = _quote_include_kind[dir];
-        }
-      }
-    }
-
-    if (!found_file) {
-      warning("Cannot find " + filename.get_fullpath(), loc);
-    } else {
-      _last_c = '\0';
-
-      // If it was explicitly named on the command-line, mark it S_local.
-      filename.make_absolute();
-      if (_explicit_files.count(filename)) {
-        source = CPPFile::S_local;
-      }
-
-      CPPFile file(filename, filename_as_referenced, source);
-
-      // Don't include it if we included it before and it had #pragma once.
-      ParsedFiles::const_iterator it = _parsed_files.find(file);
-      if (it != _parsed_files.end() && it->_pragma_once) {
-        return;
-      }
-
-      if (!push_file(file)) {
-        warning("Unable to read " + filename.get_fullpath(), loc);
-      }
+    if (!push_file(file)) {
+      warning("Unable to read " + filename.get_fullpath(), loc);
     }
   } else {
-    warning("Ignoring invalid #include directive", loc);
+    warning("Cannot find " + filename.get_fullpath(), loc);
   }
 }
 
@@ -1671,6 +1646,38 @@ handle_pragma_directive(const string &args, const YYLTYPE &loc) {
     assert(it != _parsed_files.end());
     it->_pragma_once = true;
   }
+
+  char macro[64];
+  if (sscanf(args.c_str(), "push_macro ( \"%63[^\"]\" )", macro) == 1) {
+    // We just mark it as pushed for now, so that the next time someone tries
+    // to override it, we save the old value.
+    Manifests::iterator mi = _manifests.find(macro);
+    if (mi != _manifests.end()) {
+      _manifest_stack[macro].push_back(mi->second);
+    } else {
+      _manifest_stack[macro].push_back(NULL);
+    }
+
+  } else if (sscanf(args.c_str(), "pop_macro ( \"%63[^\"]\" )", macro) == 1) {
+    ManifestStack &stack = _manifest_stack[macro];
+    if (stack.size() > 0) {
+      CPPManifest *manifest = stack.back();
+      stack.pop_back();
+      Manifests::iterator mi = _manifests.find(macro);
+      if (manifest == NULL) {
+        // It was undefined when it was pushed, so make it undefined again.
+        if (mi != _manifests.end()) {
+          _manifests.erase(mi);
+        }
+      } else if (mi != _manifests.end()) {
+        mi->second = manifest;
+      } else {
+        _manifests.insert(Manifests::value_type(macro, manifest));
+      }
+    } else {
+      warning("pop_macro without matching push_macro", loc);
+    }
+  }
 }
 
 /**
@@ -1740,6 +1747,69 @@ skip_false_if_block(bool consider_elifs) {
   _save_comments = true;
 }
 
+/**
+ * Returns true if the given manifest is defined.
+ */
+bool CPPPreprocessor::
+is_manifest_defined(const string &manifest_name) {
+  Manifests::const_iterator mi = _manifests.find(manifest_name);
+  if (mi != _manifests.end()) {
+    return true;
+  }
+
+  if (manifest_name == "__has_include" ||
+      manifest_name == "__FILE__" ||
+      manifest_name == "__LINE__") {
+    // Special built-in directives that are considered "defined".
+    return true;
+  }
+
+  return false;
+}
+
+/**
+ * Locates the given filename.  Changes the first argument to the full path.
+ */
+bool CPPPreprocessor::
+find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source) {
+  // Now look for the filename.  If we didn't use angle quotes, look first in
+  // the current directory.
+  if (!angle_quotes && filename.exists()) {
+    source = CPPFile::S_local;
+    return true;
+  }
+
+  // Search the same directory as the includer.
+  if (!angle_quotes) {
+    Filename match(get_file()._filename.get_dirname(), filename);
+    if (match.exists()) {
+      filename = match;
+      source = CPPFile::S_alternate;
+      return true;
+    }
+  }
+
+  // Now search the angle-include-path
+  if (angle_quotes && filename.resolve_filename(_angle_include_path)) {
+    source = CPPFile::S_system;
+    return true;
+  }
+
+  // Now search the quote-include-path
+  if (!angle_quotes) {
+    for (size_t dir = 0; dir < _quote_include_path.get_num_directories(); ++dir) {
+      Filename match(_quote_include_path.get_directory(dir), filename);
+      if (match.exists()) {
+        filename = match;
+        source = _quote_include_kind[dir];
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 /**
  *
  */
@@ -1846,6 +1916,14 @@ get_identifier(int c) {
   if (mi != _manifests.end() && !should_ignore_manifest((*mi).second)) {
     return expand_manifest((*mi).second);
   }
+  if (name == "__FILE__") {
+    return get_literal(SIMPLE_STRING, loc, loc.file._filename_as_referenced);
+  }
+  if (name == "__LINE__") {
+    YYSTYPE result;
+    result.u.integer = loc.first_line;
+    return CPPToken(INTEGER, loc, "", result);
+  }
 
   // Check for keywords.
   int kw = check_keyword(name);
@@ -2188,9 +2266,7 @@ expand_defined_function(string &expr, size_t q, size_t &p) {
   vector_string args;
   extract_manifest_args_inline("defined", 1, -1, args, expr, p);
   if (args.size() >= 1) {
-    const string &manifest_name = args[0];
-    Manifests::const_iterator mi = _manifests.find(manifest_name);
-    if (mi != _manifests.end()) {
+    if (is_manifest_defined(args[0])) {
       // The macro is defined; the result is "1".
       result = "1";
     } else {
@@ -2203,6 +2279,70 @@ expand_defined_function(string &expr, size_t q, size_t &p) {
   p = q + result.size();
 }
 
+/**
+ * Expands the __has_include(manifest) function to either 1 or 0, depending on
+ * whether the include file exists.
+ */
+void CPPPreprocessor::
+expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc) {
+  bool found_file = false;
+
+  // Skip whitespace till paren.
+  while (p < expr.size() && isspace(expr[p])) {
+    p++;
+  }
+  size_t args_begin = p + 1;
+
+  vector_string args;
+  extract_manifest_args_inline("__has_include", 1, -1, args, expr, p);
+
+  if (!args.empty() && args[0].size() >= 2) {
+    Filename filename;
+    bool angle_quotes = false;
+
+    string inc = args[0];
+
+    // Just to play things safe, since our manifest-expansion logic might not
+    // filter out quotes and angle brackets properly, we'll only expand
+    // manifests if we don't begin with a quote or bracket.
+    if (!inc.empty() && (inc[0] != '"' && inc[0] != '<')) {
+      inc = expand_manifests(inc, false, loc);
+    }
+
+    if (inc[0] == '"' && inc[inc.size() - 1] == '"') {
+      filename = inc.substr(1, inc.size() - 2);
+    } else if (inc[0] == '<' && inc[inc.size() - 1] == '>') {
+      filename = inc.substr(1, inc.size() - 2);
+      if (!_noangles) {
+        // If _noangles is true, we don't make a distinction between angle
+        // brackets and quote marks--all #inc statements are treated the
+        // same, as if they used quote marks.
+        angle_quotes = true;
+      }
+    } else {
+      loc.last_column += loc.first_column + p - 2;
+      loc.first_column += args_begin;
+      warning("invalid argument for __has_include() directive", loc);
+      expr = expr.substr(0, q) + "0" + expr.substr(p);
+      p = q + 1;
+      return;
+    }
+
+    filename.set_text();
+
+    CPPFile::Source source = CPPFile::S_none;
+    found_file = find_include(filename, angle_quotes, source);
+  } else {
+    loc.last_column += loc.first_column + p - 2;
+    loc.first_column += args_begin;
+    warning("invalid argument for __has_include() directive", loc);
+  }
+
+  string result = found_file ? "1" : "0";
+  expr = expr.substr(0, q) + result + expr.substr(p);
+  p = q + result.size();
+}
+
 /**
  *
  */
@@ -2451,11 +2591,27 @@ check_keyword(const string &name) {
   if (name == "friend") return KW_FRIEND;
   if (name == "for") return KW_FOR;
   if (name == "goto") return KW_GOTO;
+  if (name == "__has_virtual_destructor") return KW_HAS_VIRTUAL_DESTRUCTOR;
   if (name == "if") return KW_IF;
   if (name == "inline") return KW_INLINE;
   if (name == "__inline") return KW_INLINE;
   if (name == "__inline__") return KW_INLINE;
   if (name == "int") return KW_INT;
+  if (name == "__is_abstract") return KW_IS_ABSTRACT;
+  if (name == "__is_base_of") return KW_IS_BASE_OF;
+  if (name == "__is_class") return KW_IS_CLASS;
+  if (name == "__is_constructible") return KW_IS_CONSTRUCTIBLE;
+  if (name == "__is_convertible_to") return KW_IS_CONVERTIBLE_TO;
+  if (name == "__is_destructible") return KW_IS_DESTRUCTIBLE;
+  if (name == "__is_empty") return KW_IS_EMPTY;
+  if (name == "__is_enum") return KW_IS_ENUM;
+  if (name == "__is_final") return KW_IS_FINAL;
+  if (name == "__is_fundamental") return KW_IS_FUNDAMENTAL;
+  if (name == "__is_pod") return KW_IS_POD;
+  if (name == "__is_polymorphic") return KW_IS_POLYMORPHIC;
+  if (name == "__is_standard_layout") return KW_IS_STANDARD_LAYOUT;
+  if (name == "__is_trivial") return KW_IS_TRIVIAL;
+  if (name == "__is_union") return KW_IS_UNION;
   if (name == "long") return KW_LONG;
   if (name == "__make_map_property") return KW_MAKE_MAP_PROPERTY;
   if (name == "__make_property") return KW_MAKE_PROPERTY;
@@ -2490,6 +2646,7 @@ check_keyword(const string &name) {
   if (name == "typedef") return KW_TYPEDEF;
   if (name == "typeid") return KW_TYPEID;
   if (name == "typename") return KW_TYPENAME;
+  if (name == "__underlying_type") return KW_UNDERLYING_TYPE;
   if (name == "union") return KW_UNION;
   if (name == "unsigned") return KW_UNSIGNED;
   if (name == "using") return KW_USING;
@@ -2751,7 +2908,7 @@ nested_parse_template_instantiation(CPPTemplateScope *scope) {
   _parsing_template_params = true;
 
   CPPToken token = internal_get_next_token();
-  if (token._token == '>') {
+  if (token._token == '>' || token._token == 0) {
     _parsing_template_params = false;
   } else {
     _saved_tokens.push_back(token);
@@ -2760,36 +2917,53 @@ nested_parse_template_instantiation(CPPTemplateScope *scope) {
   CPPTemplateParameterList *actual_params = new CPPTemplateParameterList;
 
   for (pi = formal_params._parameters.begin();
-       pi != formal_params._parameters.end() && _parsing_template_params;
-       ++pi) {
+       pi != formal_params._parameters.end() && _parsing_template_params;) {
     CPPToken token = peek_next_token();
     YYLTYPE loc = token._lloc;
 
     CPPDeclaration *decl = (*pi);
-    if (decl->as_type()) {
+    CPPClassTemplateParameter *param = decl->as_class_template_parameter();
+    CPPInstance *inst = decl->as_instance();
+    if (param) {
       // Parse a typename template parameter.
       _saved_tokens.push_back(CPPToken(START_TYPE));
       CPPType *type = ::parse_type(this, current_scope, global_scope);
       if (type == NULL) {
         loc.last_line = get_line_number();
         loc.last_column = get_col_number() - 1;
-        warning("Invalid type", loc);
+        warning("invalid type", loc);
         skip_to_end_nested();
         type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown));
       }
       actual_params->_parameters.push_back(type);
-    } else {
+
+      // If this is a variadic template, keep reading using this parameter.
+      if (!param->_packed) {
+        ++pi;
+      }
+    } else if (inst) {
       // Parse a constant expression template parameter.
       _saved_tokens.push_back(CPPToken(START_CONST_EXPR));
       CPPExpression *expr = parse_const_expr(this, current_scope, global_scope);
       if (expr == NULL) {
         loc.last_line = get_line_number();
         loc.last_column = get_col_number() - 1;
-        warning("Invalid expression", loc);
+        warning("invalid expression", loc);
         skip_to_end_nested();
         expr = new CPPExpression(0);
       }
       actual_params->_parameters.push_back(expr);
+
+      // If this is a variadic template, keep reading using this parameter.
+      if ((inst->_storage_class & CPPInstance::SC_parameter_pack) == 0) {
+        ++pi;
+      }
+    } else {
+      loc.last_line = get_line_number();
+      loc.last_column = get_col_number() - 1;
+      warning("invalid template parameter", loc);
+      skip_to_end_nested();
+      ++pi;
     }
 
     _state = S_nested;

+ 6 - 0
dtool/src/cppparser/cppPreprocessor.h

@@ -72,6 +72,9 @@ public:
   typedef map<string, CPPManifest *> Manifests;
   Manifests _manifests;
 
+  typedef pvector<CPPManifest *> ManifestStack;
+  map<string, ManifestStack> _manifest_stack;
+
   pvector<CPPFile::Source> _quote_include_kind;
   DSearchPath _quote_include_path;
   DSearchPath _angle_include_path;
@@ -140,6 +143,8 @@ private:
   void handle_error_directive(const string &args, const YYLTYPE &loc);
 
   void skip_false_if_block(bool consider_elifs);
+  bool is_manifest_defined(const string &manifest_name);
+  bool find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source);
 
   CPPToken get_quoted_char(int c);
   CPPToken get_quoted_string(int c);
@@ -150,6 +155,7 @@ private:
   void extract_manifest_args(const string &name, int num_args,
                              int va_arg, vector_string &args);
   void expand_defined_function(string &expr, size_t q, size_t &p);
+  void expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc);
   void expand_manifest_inline(string &expr, size_t q, size_t &p,
                               const CPPManifest *manifest);
   void extract_manifest_args_inline(const string &name, int num_args,

+ 79 - 0
dtool/src/cppparser/cppReferenceType.cxx

@@ -12,6 +12,8 @@
  */
 
 #include "cppReferenceType.h"
+#include "cppTypedefType.h"
+#include "cppStructType.h"
 
 /**
  *
@@ -87,6 +89,14 @@ is_tbd() const {
   return _pointing_at->is_tbd();
 }
 
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPReferenceType::
+is_standard_layout() const {
+  return false;
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -95,6 +105,67 @@ is_trivial() const {
   return false;
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPReferenceType::
+is_constructible(const CPPType *given_type) const {
+  const CPPType *a;
+  const CPPType *b;
+
+  CPPReferenceType *ref_type = ((CPPType *)given_type)->as_reference_type();
+  if (ref_type != NULL) {
+    if (ref_type->_value_category == VC_rvalue) {
+      return is_constructible(ref_type->_pointing_at);
+    }
+
+    if (_value_category == VC_rvalue) {
+      // Can never initialize an rvalue ref from an lvalue ref.
+      return false;
+    }
+
+    if (!_pointing_at->is_const()) {
+      // Cannot initialize a non-const reference using a const one.
+      if (ref_type->_pointing_at->is_const()) {
+        return false;
+      }
+    }
+
+    a = _pointing_at->remove_cv();
+    b = ref_type->_pointing_at->remove_cv();
+
+  } else {
+    // Initializing using an rvalue.
+    if (!_pointing_at->is_const()) {
+      // Cannot initialize a non-const reference using a const one.
+      if (given_type->is_const()) {
+        return false;
+      }
+
+      // Cannot initalise a non-const lvalue reference with an rvalue ref.
+      if (_value_category == VC_lvalue) {
+        return false;
+      }
+    }
+
+    a = _pointing_at->remove_cv();
+    b = ((CPPType *)given_type)->remove_cv();
+  }
+
+  if (a == b || *a == *b) {
+    return true;
+  }
+
+  // Can initialize from derived class pointer.
+  const CPPStructType *a_struct = a->as_struct_type();
+  const CPPStructType *b_struct = b->as_struct_type();
+  if (a_struct != NULL && b_struct != NULL) {
+    return a_struct->is_base_of(b_struct);
+  }
+
+  return false;
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
@@ -111,6 +182,14 @@ is_copy_constructible() const {
   return (_value_category == VC_lvalue);
 }
 
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPReferenceType::
+is_destructible() const {
+  return false;
+}
+
 /**
  * This is a little more forgiving than is_equal(): it returns true if the
  * types appear to be referring to the same thing, even if they may have

+ 3 - 0
dtool/src/cppparser/cppReferenceType.h

@@ -42,9 +42,12 @@ public:
                                 CPPScope *global_scope);
 
   virtual bool is_tbd() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_destructible() const;
   virtual bool is_equivalent(const CPPType &other) const;
 
   virtual void output(ostream &out, int indent_level, CPPScope *scope,

+ 1 - 1
dtool/src/cppparser/cppScope.cxx

@@ -27,8 +27,8 @@
 #include "cppPreprocessor.h"
 #include "cppTemplateScope.h"
 #include "cppClassTemplateParameter.h"
-#include "cppConstType.h"
 #include "cppFunctionType.h"
+#include "cppConstType.h"
 #include "cppUsing.h"
 #include "cppBisonDefs.h"
 #include "indent.h"

+ 3 - 2
dtool/src/cppparser/cppScope.h

@@ -139,11 +139,12 @@ public:
   Templates _templates;
   CPPNameComponent _name;
 
+  typedef set<CPPScope *> Using;
+  Using _using;
+
 protected:
   CPPScope *_parent_scope;
   CPPStructType *_struct_type;
-  typedef set<CPPScope *> Using;
-  Using _using;
   CPPVisibility _current_vis;
 
 private:

+ 53 - 0
dtool/src/cppparser/cppSimpleType.cxx

@@ -34,6 +34,30 @@ is_tbd() const {
   return (_type == T_unknown);
 }
 
+/**
+ * Returns true if the type is a boolean, floating point or integral type.
+ */
+bool CPPSimpleType::
+is_arithmetic() const {
+  return (_type > T_unknown && _type < T_void);
+}
+
+/**
+ * Returns true if the type is considered a fundamental type.
+ */
+bool CPPSimpleType::
+is_fundamental() const {
+  return (_type != T_unknown && _type != T_parameter && _type != T_auto);
+}
+
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPSimpleType::
+is_standard_layout() const {
+  return (_type != T_unknown && _type != T_parameter && _type != T_auto);
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -42,6 +66,27 @@ is_trivial() const {
   return true;
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPSimpleType::
+is_constructible(const CPPType *given_type) const {
+  given_type = ((CPPType *)given_type)->remove_reference()->remove_cv();
+
+  const CPPSimpleType *simple_type = given_type->as_simple_type();
+  if (simple_type == NULL) {
+    return given_type->is_enum() && is_arithmetic();
+  } else if (_type == T_nullptr) {
+    return simple_type->_type == T_nullptr;
+  } else if (_type == T_bool) {
+    return simple_type->is_arithmetic() || simple_type->_type == T_nullptr;
+  } else if (is_arithmetic()) {
+    return simple_type->is_arithmetic();
+  } else {
+    return false;
+  }
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
@@ -58,6 +103,14 @@ is_copy_constructible() const {
   return (_type != T_void);
 }
 
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPSimpleType::
+is_destructible() const {
+  return (_type != T_void);
+}
+
 /**
  * Returns true if the type is a special parameter expression type.
  *

+ 11 - 7
dtool/src/cppparser/cppSimpleType.h

@@ -19,7 +19,7 @@
 #include "cppType.h"
 
 /**
- *
+ * Represents a C++ fundamental type.
  */
 class CPPSimpleType : public CPPType {
 public:
@@ -40,12 +40,11 @@ public:
     // nullptr_t, which is a typedef of decltype(nullptr).
     T_nullptr,
 
-/*
- * T_parameter is a special type which is assigned to expressions that are
- * discovered where a formal parameter was expected.  This is a special case
- * for handling cases like this: int foo(0); which really means the same thing
- * as: int foo = 0; but it initially looks like a function prototype.
- */
+    // T_parameter is a special type which is assigned to expressions that are
+    // discovered where a formal parameter was expected.  This is a special
+    // case for handling cases like this: int foo(0); which really means the
+    // same thing as: int foo = 0; but it initially looks like a function
+    // prototype.
     T_parameter,
 
     // T_auto is also a special type that corresponds to the "auto" keyword
@@ -69,9 +68,14 @@ public:
   int _flags;
 
   virtual bool is_tbd() const;
+  bool is_arithmetic() const;
+  virtual bool is_fundamental() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_destructible() const;
   virtual bool is_parameter_expr() const;
 
   virtual string get_preferred_name() const;

+ 379 - 39
dtool/src/cppparser/cppStructType.cxx

@@ -13,6 +13,7 @@
 
 #include "cppStructType.h"
 #include "cppTypedefType.h"
+#include "cppReferenceType.h"
 #include "cppScope.h"
 #include "cppTypeProxy.h"
 #include "cppTemplateScope.h"
@@ -126,16 +127,154 @@ is_abstract() const {
   return !funcs.empty();
 }
 
+/**
+ * Returns true if this struct declaration is a base class of the other given
+ * class, or the same class.
+ */
+bool CPPStructType::
+is_base_of(const CPPStructType *other) const {
+  if (this == other) {
+    return true;
+  }
+  Derivation::const_iterator di;
+  for (di = other->_derivation.begin(); di != other->_derivation.end(); ++di) {
+    const CPPStructType *base = (*di)._base->as_struct_type();
+    if (base != NULL && is_base_of(base)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Returns true if this struct declaration defines no non-static data members
+ * other than bit-fields of size 0, no virtual functions, no virtual base
+ * classes, and no non-empty base classes, and is not a union.
+ */
+bool CPPStructType::
+is_empty() const {
+  if (_type == T_union) {
+    return false;
+  }
+
+  if (check_virtual()) {
+    return false;
+  }
+
+  // Make sure all base classes are empty and non-virtual.
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if ((*di)._is_virtual || (base != NULL && !base->is_empty())) {
+      return false;
+    }
+  }
+
+  // Make sure there are no non-static data members.
+  CPPScope::Variables::const_iterator vi;
+  for (vi = _scope->_variables.begin(); vi != _scope->_variables.end(); ++vi) {
+    CPPInstance *instance = (*vi).second;
+    assert(instance != NULL);
+
+    if (instance->_storage_class & CPPInstance::SC_static) {
+      // Static members don't count.
+      continue;
+    }
+
+    // Only members with a bit width of 0 are okay.
+    if (instance->_bit_width != 0) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+/**
+ * Returns true if this class or any of its base classes have virtual methods.
+ */
+bool CPPStructType::
+is_polymorphic() const {
+  if (_type == T_union) {
+    return false;
+  }
+  return check_virtual();
+}
+
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPStructType::
+is_standard_layout() const {
+  assert(_scope != NULL);
+
+  CPPVisibility member_vis = V_unknown;
+
+  // Make sure all data members have the same vis and are standard layout.
+  CPPScope::Variables::const_iterator vi;
+  for (vi = _scope->_variables.begin(); vi != _scope->_variables.end(); ++vi) {
+    CPPInstance *instance = (*vi).second;
+    assert(instance != NULL);
+
+    if (instance->_storage_class & CPPInstance::SC_static) {
+      // Static members don't count.
+      continue;
+    }
+
+    // Finally, check if the data member itself is standard layout.
+    assert(instance->_type != NULL);
+    if (!instance->_type->is_standard_layout()) {
+      return false;
+    }
+
+    if (member_vis == V_unknown) {
+      // The first non-static data member may not be a base class.
+      CPPStructType *struct_type = instance->_type->remove_cv()->as_struct_type();
+      if (struct_type != NULL && struct_type->is_base_of(this)) {
+        return false;
+      }
+      member_vis = instance->_vis;
+
+    } else if (member_vis != instance->_vis) {
+      // All members need to have the same access control.
+      return false;
+    }
+  }
+
+  // Make sure all base classes are standard-layout and non-virtual.
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if ((*di)._is_virtual) {
+      return false;
+    }
+
+    // If this class had instance members, all base classes need to be empty.
+    if (member_vis != V_unknown) {
+      if (!base->is_empty()) {
+        return false;
+      }
+    } else {
+      if (!base->is_standard_layout()) {
+        return false;
+      }
+    }
+  }
+
+  // Make sure we have no virtual functions.
+  return !check_virtual();
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
 bool CPPStructType::
 is_trivial() const {
-  // Make sure all base classes are trivial.
+  // Make sure all base classes are trivial and non-virtual.
   Derivation::const_iterator di;
   for (di = _derivation.begin(); di != _derivation.end(); ++di) {
     CPPStructType *base = (*di)._base->as_struct_type();
-    if (base != NULL && !base->is_trivial()) {
+    if ((*di)._is_virtual || (base != NULL && !base->is_trivial())) {
       return false;
     }
   }
@@ -166,7 +305,7 @@ is_trivial() const {
     }
   }
 
-  // Now look for functions that are virtual or condestructors.
+  // Now look for functions that are virtual or con/destructors.
   bool is_default_constructible = true;
   CPPScope::Functions::const_iterator fi;
   for (fi = _scope->_functions.begin(); fi != _scope->_functions.end(); ++fi) {
@@ -193,8 +332,8 @@ is_trivial() const {
       if (ftype->_flags & (CPPFunctionType::F_destructor |
                            CPPFunctionType::F_move_constructor |
                            CPPFunctionType::F_copy_constructor)) {
-        // User-provided destructors and copymove constructors are not trivial
-        // unless they are defaulted (and not virtual).
+        // User-provided destructors and copy/move constructors are not
+        // trivial unless they are defaulted (and not virtual).
         return false;
       }
 
@@ -220,6 +359,66 @@ is_trivial() const {
   return is_default_constructible;
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ * This implementation is rudimentary, as it does not attempt to follow all of
+ * the implicit type conversion rules, but it is still useful.
+ */
+bool CPPStructType::
+is_constructible(const CPPType *given_type) const {
+  // Does the type match the copy constructor or move constructor?
+  CPPType *base_type = ((CPPType *)given_type)->remove_reference();
+  if (is_equivalent(*base_type->remove_cv())) {
+    const CPPReferenceType *ref_type = given_type->as_reference_type();
+    if (ref_type == NULL ||
+        ref_type->_value_category == CPPReferenceType::VC_rvalue) {
+      return is_move_constructible(V_public);
+    } else {
+      return is_copy_constructible(V_public);
+    }
+  }
+
+  if (is_abstract()) {
+    return false;
+  }
+
+  // Check for a different constructor.
+  CPPFunctionGroup *fgroup = get_constructor();
+  if (fgroup != (CPPFunctionGroup *)NULL) {
+    CPPFunctionGroup::Instances::const_iterator ii;
+    for (ii = fgroup->_instances.begin();
+        ii != fgroup->_instances.end();
+        ++ii) {
+      CPPInstance *inst = (*ii);
+      assert(inst->_type != (CPPType *)NULL);
+
+      CPPFunctionType *ftype = inst->_type->as_function_type();
+      assert(ftype != (CPPFunctionType *)NULL);
+
+      CPPParameterList *params = ftype->_parameters;
+      if (params->_parameters.size() == 1 && !params->_includes_ellipsis) {
+        CPPType *param_type = params->_parameters[0]->_type->remove_reference();
+
+        if (!param_type->is_const() && base_type->is_const()) {
+          // Can't pass a const object to a function taking a non-const.
+          continue;
+        }
+
+        // It's deleted, anyhow.
+        if ((inst->_storage_class & CPPInstance::SC_deleted) != 0) {
+          continue;
+        }
+
+        if (param_type->is_equivalent(*base_type)) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
@@ -236,11 +435,23 @@ is_copy_constructible() const {
   return is_copy_constructible(V_public);
 }
 
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPStructType::
+is_destructible() const {
+  return is_destructible(V_public);
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
 bool CPPStructType::
 is_default_constructible(CPPVisibility min_vis) const {
+  if (is_abstract()) {
+    return false;
+  }
+
   CPPInstance *constructor = get_default_constructor();
   if (constructor != (CPPInstance *)NULL) {
     // It has a default constructor.
@@ -295,24 +506,6 @@ is_default_constructible(CPPVisibility min_vis) const {
     }
   }
 
-  // Check that we don't have pure virtual methods.
-  CPPScope::Functions::const_iterator fi;
-  for (fi = _scope->_functions.begin();
-       fi != _scope->_functions.end();
-       ++fi) {
-    CPPFunctionGroup *fgroup = (*fi).second;
-    CPPFunctionGroup::Instances::const_iterator ii;
-    for (ii = fgroup->_instances.begin();
-         ii != fgroup->_instances.end();
-         ++ii) {
-      CPPInstance *inst = (*ii);
-      if (inst->_storage_class & CPPInstance::SC_pure_virtual) {
-        // Here's a pure virtual function.
-        return false;
-      }
-    }
-  }
-
   return true;
 }
 
@@ -321,6 +514,10 @@ is_default_constructible(CPPVisibility min_vis) const {
  */
 bool CPPStructType::
 is_copy_constructible(CPPVisibility min_vis) const {
+  if (is_abstract()) {
+    return false;
+  }
+
   CPPInstance *constructor = get_copy_constructor();
   if (constructor != (CPPInstance *)NULL) {
     // It has a copy constructor.
@@ -378,25 +575,144 @@ is_copy_constructible(CPPVisibility min_vis) const {
     }
   }
 
-  // Check that we don't have pure virtual methods.
+  return true;
+}
+
+/**
+ * Returns true if the type is move-constructible.
+ */
+bool CPPStructType::
+is_move_constructible(CPPVisibility min_vis) const {
+  CPPInstance *constructor = get_move_constructor();
+  if (constructor != (CPPInstance *)NULL) {
+    // It has a user-declared move constructor.
+    if (constructor->_vis > min_vis) {
+      // Inaccessible move constructor.
+      return false;
+    }
+
+    if (constructor->_storage_class & CPPInstance::SC_deleted) {
+      // It is deleted.
+      return false;
+    }
+
+    if (is_abstract()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  return is_copy_constructible(min_vis);
+}
+
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPStructType::
+is_destructible(CPPVisibility min_vis) const {
+  // Do we have an explicit destructor?
+  CPPInstance *destructor = get_destructor();
+  if (destructor != (CPPInstance *)NULL) {
+    if (destructor->_vis > min_vis) {
+      // Yes, but it's inaccessible.
+      return false;
+    }
+
+    if (destructor->_storage_class & CPPInstance::SC_deleted) {
+      // Yes, but it's explicitly been deleted.
+      return false;
+    }
+
+    return true;
+  }
+
+  // Make sure all base classes are destructible.
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if (base != NULL && !base->is_destructible(V_protected)) {
+      return false;
+    }
+  }
+
+  assert(_scope != NULL);
+
+  // Make sure all members are destructible.
+  CPPScope::Variables::const_iterator vi;
+  for (vi = _scope->_variables.begin(); vi != _scope->_variables.end(); ++vi) {
+    CPPInstance *instance = (*vi).second;
+    assert(instance != NULL);
+
+    if (instance->_storage_class & CPPInstance::SC_static) {
+      // Static members don't count.
+      continue;
+    }
+
+    // If the data member is not destructible, no go.
+    assert(instance->_type != NULL);
+    if (!instance->_type->is_destructible()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+/**
+ * Returns true if variables of this type may be implicitly converted to
+ * the other type.
+ */
+bool CPPStructType::
+is_convertible_to(const CPPType *other) const {
+  if (CPPType::is_convertible_to(other)) {
+    return true;
+  }
+
+  // Check all typecast operators to see whether we can cast to a type that is
+  // convertible to the other type.
   CPPScope::Functions::const_iterator fi;
-  for (fi = _scope->_functions.begin();
-       fi != _scope->_functions.end();
-       ++fi) {
+  for (fi = _scope->_functions.begin(); fi != _scope->_functions.end(); ++fi) {
     CPPFunctionGroup *fgroup = (*fi).second;
+
     CPPFunctionGroup::Instances::const_iterator ii;
-    for (ii = fgroup->_instances.begin();
-         ii != fgroup->_instances.end();
-         ++ii) {
+    for (ii = fgroup->_instances.begin(); ii != fgroup->_instances.end(); ++ii) {
       CPPInstance *inst = (*ii);
-      if (inst->_storage_class & CPPInstance::SC_pure_virtual) {
-        // Here's a pure virtual function.
-        return false;
+
+      if (inst->_storage_class & (CPPInstance::SC_deleted | CPPInstance::SC_static | CPPInstance::SC_explicit)) {
+        // Exclude static/deleted/explicit methods.
+        continue;
+      }
+
+      // Also, the instance needs to be publicly visible.
+      if (inst->_vis > V_public) {
+        continue;
+      }
+
+      assert(inst->_type != (CPPType *)NULL);
+      CPPFunctionType *ftype = inst->_type->as_function_type();
+      assert(ftype != (CPPFunctionType *)NULL);
+
+      if (ftype->_return_type != NULL &&
+          (ftype->_flags & CPPFunctionType::F_operator_typecast) != 0) {
+        // Yes, this is a typecast operator.  Test using the return type.
+        if (ftype->_return_type->is_convertible_to(other)) {
+          return true;
+        }
       }
     }
   }
 
-  return true;
+  // Check whether any of the base classes are convertible.
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if (base != NULL && (*di)._vis <= V_public && !base->is_convertible_to(other)) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 /**
@@ -420,6 +736,30 @@ check_virtual() const {
   return !funcs.empty();
 }
 
+/**
+ * Returns true if this class, or any of its base classes, has a virtual
+ * destructor.
+ */
+bool CPPStructType::
+has_virtual_destructor() const {
+  CPPInstance *destructor = get_destructor();
+  if (destructor != NULL) {
+    if (destructor->_storage_class & CPPInstance::SC_virtual) {
+      return true;
+    }
+  }
+
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if (base != NULL && base->has_virtual_destructor()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /**
  * Returns true if this declaration is an actual, factual declaration, or
  * false if some part of the declaration depends on a template parameter which
@@ -459,7 +799,7 @@ get_constructor() const {
 
 /**
  * Returns the default constructor defined for the struct type, or NULL if
- * there is none.
+ * there is no user-declared constructor that takes 0 arguments.
  */
 CPPInstance *CPPStructType::
 get_default_constructor() const {
@@ -490,7 +830,7 @@ get_default_constructor() const {
 
 /**
  * Returns the copy constructor defined for the struct type, or NULL if no
- * copy constructor exists.
+ * user-declared copy constructor exists.
  */
 CPPInstance *CPPStructType::
 get_copy_constructor() const {
@@ -519,7 +859,7 @@ get_copy_constructor() const {
 
 /**
  * Returns the move constructor defined for the struct type, or NULL if no
- * move constructor exists.
+ * user-declared move constructor exists.
  */
 CPPInstance *CPPStructType::
 get_move_constructor() const {
@@ -548,7 +888,7 @@ get_move_constructor() const {
 
 /**
  * Returns the destructor defined for the struct type, if any, or NULL if no
- * destructor is found.
+ * user-declared destructor is found.
  */
 CPPInstance *CPPStructType::
 get_destructor() const {
@@ -854,7 +1194,7 @@ get_virtual_funcs(VFunctions &funcs) const {
           CPPFunctionType *new_ftype = new_inst->_type->as_function_type();
           assert(new_ftype != (CPPFunctionType *)NULL);
 
-          if (new_ftype->is_equivalent_function(*base_ftype)) {
+          if (new_ftype->match_virtual_override(*base_ftype)) {
             // It's a match!  We now know it's virtual.  Erase this function
             // from the list, so we can add it back in below.
             funcs.erase(vfi);

+ 12 - 0
dtool/src/cppparser/cppStructType.h

@@ -44,14 +44,26 @@ public:
   CPPScope *get_scope() const;
 
   bool is_abstract() const;
+  bool is_base_of(const CPPStructType *other) const;
+  bool is_empty() const;
+  bool is_polymorphic() const;
   bool check_virtual() const;
+  bool has_virtual_destructor() const;
   virtual bool is_fully_specified() const;
   virtual bool is_incomplete() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *arg_type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_destructible() const;
   bool is_default_constructible(CPPVisibility min_vis) const;
   bool is_copy_constructible(CPPVisibility min_vis) const;
+  bool is_move_constructible(CPPVisibility min_vis) const;
+  bool is_destructible(CPPVisibility min_vis) const;
+  virtual bool is_convertible_to(const CPPType *other) const;
+
+  inline bool is_final() const { return _final; }
 
   CPPFunctionGroup *get_constructor() const;
   CPPInstance *get_default_constructor() const;

+ 4 - 2
dtool/src/cppparser/cppTemplateScope.cxx

@@ -97,8 +97,10 @@ add_template_parameter(CPPDeclaration *param) {
   CPPClassTemplateParameter *cl = param->as_class_template_parameter();
   if (cl != NULL) {
     // Create an implicit typedef for this class parameter.
-    string name = cl->_ident->get_local_name();
-    _types[name] = cl;
+    if (cl->_ident != NULL) {
+      string name = cl->_ident->get_local_name();
+      _types[name] = cl;
+    }
   }
 
   CPPInstance *inst = param->as_instance();

+ 175 - 0
dtool/src/cppparser/cppType.cxx

@@ -12,7 +12,12 @@
  */
 
 #include "cppType.h"
+#include "cppConstType.h"
+#include "cppPointerType.h"
+#include "cppReferenceType.h"
+#include "cppStructType.h"
 #include "cppTypedefType.h"
+#include "cppExtensionType.h"
 #include <algorithm>
 
 CPPType::Types CPPType::_types;
@@ -57,6 +62,22 @@ is_tbd() const {
   return false;
 }
 
+/**
+ * Returns true if the type is considered a fundamental type.
+ */
+bool CPPType::
+is_fundamental() const {
+  return false;
+}
+
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPType::
+is_standard_layout() const {
+  return false;
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -65,6 +86,14 @@ is_trivial() const {
   return false;
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPType::
+is_constructible(const CPPType *given_type) const {
+  return false;
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
@@ -81,6 +110,14 @@ is_copy_constructible() const {
   return false;
 }
 
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPType::
+is_destructible() const {
+  return !is_incomplete();
+}
+
 /**
  * Returns true if the type is a special parameter expression type.
  *
@@ -92,6 +129,136 @@ is_parameter_expr() const {
   return false;
 }
 
+/**
+ * Returns true if this is an enum type, or a typedef to an enum type.
+ */
+bool CPPType::
+is_enum() const {
+  const CPPTypedefType *td_type = as_typedef_type();
+  if (td_type != NULL) {
+    return td_type->_type->is_enum();
+  }
+  const CPPExtensionType *ext_type = as_extension_type();
+  if (ext_type != NULL) {
+    return ext_type->_type == CPPExtensionType::T_enum ||
+           ext_type->_type == CPPExtensionType::T_enum_struct ||
+           ext_type->_type == CPPExtensionType::T_enum_class;
+  }
+  return false;
+}
+
+/**
+ * Returns true if this is a const type, or a typedef to a const type.
+ */
+bool CPPType::
+is_const() const {
+  const CPPTypedefType *td_type = as_typedef_type();
+  if (td_type != NULL) {
+    return td_type->_type->is_const();
+  }
+  return get_subtype() == ST_const;
+}
+
+/**
+ * Returns true if this is a reference type, or a typedef to a reference type.
+ */
+bool CPPType::
+is_reference() const {
+  const CPPTypedefType *td_type = as_typedef_type();
+  if (td_type != NULL) {
+    return td_type->_type->is_reference();
+  }
+  return get_subtype() == ST_reference;
+}
+
+/**
+ * Returns true if this is an unqualified or cv-qualified pointer type, or a
+ * typedef to one.
+ */
+bool CPPType::
+is_pointer() const {
+  const CPPTypedefType *td_type = as_typedef_type();
+  if (td_type != NULL) {
+    return td_type->_type->is_pointer();
+  }
+  const CPPConstType *const_type = as_const_type();
+  if (const_type != NULL) {
+    return const_type->_wrapped_around->is_pointer();
+  }
+  return get_subtype() == ST_pointer;
+}
+
+/**
+ * Returns the type with any const qualifier stripped off.  Will follow
+ * typedefs, but only if necessary.
+ */
+CPPType *CPPType::
+remove_const() {
+  const CPPTypedefType *td_type = as_typedef_type();
+  if (td_type != NULL) {
+    CPPType *unwrapped = td_type->_type->remove_const();
+    if (unwrapped != td_type->_type) {
+      return unwrapped;
+    } else {
+      return this;
+    }
+  }
+  const CPPConstType *const_type = as_const_type();
+  if (const_type != NULL) {
+    return const_type->_wrapped_around->remove_const();
+  }
+  return this;
+}
+
+/**
+ * Returns the type with any reference stripped off.
+ */
+CPPType *CPPType::
+remove_reference() {
+  const CPPTypedefType *td_type = as_typedef_type();
+  if (td_type != NULL) {
+    CPPType *unwrapped = td_type->_type->remove_reference();
+    if (unwrapped != td_type->_type) {
+      return unwrapped;
+    } else {
+      return this;
+    }
+  }
+  const CPPReferenceType *ref_type = as_reference_type();
+  if (ref_type != NULL) {
+    return ref_type->_pointing_at;
+  }
+  return this;
+}
+
+/**
+ * Returns the type with any pointer and cv-qualifiers stripped off.
+ */
+CPPType *CPPType::
+remove_pointer() {
+  switch (get_subtype()) {
+  case ST_typedef:
+    {
+      const CPPTypedefType *td_type = as_typedef_type();
+      CPPType *unwrapped = td_type->_type->remove_pointer();
+      if (unwrapped != td_type->_type) {
+        return unwrapped;
+      } else {
+        return this;
+      }
+    }
+
+  case ST_pointer:
+    return ((const CPPPointerType *)this)->_pointing_at;
+
+  case ST_const:
+    return ((const CPPConstType *)this)->_wrapped_around->remove_pointer();
+
+  default:
+    return this;
+  }
+}
+
 /**
  * Returns true if the type has even been typedef'ed and therefore has a
  * simple name available to stand for it.  Extension types are all implicitly
@@ -229,6 +396,14 @@ is_equivalent(const CPPType &other) const {
   return is_equal(&other);
 }
 
+/**
+ * Returns true if variables of this type may be implicitly converted to
+ * the other type.
+ */
+bool CPPType::
+is_convertible_to(const CPPType *other) const {
+  return other->is_constructible(this);
+}
 
 /**
  * Formats a C++-looking line that defines an instance of the given type, with

+ 17 - 0
dtool/src/cppparser/cppType.h

@@ -45,11 +45,27 @@ public:
                                 CPPScope *global_scope);
 
   virtual bool is_tbd() const;
+  virtual bool is_fundamental() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_destructible() const;
   virtual bool is_parameter_expr() const;
 
+  // Convenience methods.
+  bool is_enum() const;
+  bool is_const() const;
+  bool is_reference() const;
+  bool is_pointer() const;
+
+  CPPType *remove_const();
+  inline CPPType *remove_volatile() { return this; }
+  inline CPPType *remove_cv() { return remove_const(); };
+  CPPType *remove_reference();
+  CPPType *remove_pointer();
+
   bool has_typedef_name() const;
   string get_typedef_name(CPPScope *scope = NULL) const;
 
@@ -61,6 +77,7 @@ public:
   string get_alt_name(int n) const;
 
   virtual bool is_incomplete() const;
+  virtual bool is_convertible_to(const CPPType *other) const;
   virtual bool is_equivalent(const CPPType &other) const;
 
   void output_instance(ostream &out, const string &name,

+ 41 - 0
dtool/src/cppparser/cppTypedefType.cxx

@@ -157,6 +157,22 @@ is_tbd() const {
   return _type->is_tbd();
 }
 
+/**
+ * Returns true if the type is considered a fundamental type.
+ */
+bool CPPTypedefType::
+is_fundamental() const {
+  return _type->is_fundamental();
+}
+
+/**
+ * Returns true if the type is considered a standard layout type.
+ */
+bool CPPTypedefType::
+is_standard_layout() const {
+  return _type->is_standard_layout();
+}
+
 /**
  * Returns true if the type is considered a Plain Old Data (POD) type.
  */
@@ -165,6 +181,14 @@ is_trivial() const {
   return _type->is_trivial();
 }
 
+/**
+ * Returns true if the type can be constructed using the given argument.
+ */
+bool CPPTypedefType::
+is_constructible(const CPPType *given_type) const {
+  return _type->is_constructible(given_type);
+}
+
 /**
  * Returns true if the type is default-constructible.
  */
@@ -181,6 +205,14 @@ is_copy_constructible() const {
   return _type->is_copy_constructible();
 }
 
+/**
+ * Returns true if the type is destructible.
+ */
+bool CPPTypedefType::
+is_destructible() const {
+  return _type->is_destructible();
+}
+
 /**
  * Returns true if this declaration is an actual, factual declaration, or
  * false if some part of the declaration depends on a template parameter which
@@ -300,6 +332,15 @@ resolve_type(CPPScope *current_scope, CPPScope *global_scope) {
   return this;
 }
 
+/**
+ * Returns true if variables of this type may be implicitly converted to
+ * the other type.
+ */
+bool CPPTypedefType::
+is_convertible_to(const CPPType *other) const {
+  return _type->is_convertible_to(other);
+}
+
 /**
  * This is a little more forgiving than is_equal(): it returns true if the
  * types appear to be referring to the same thing, even if they may have

+ 5 - 0
dtool/src/cppparser/cppTypedefType.h

@@ -42,9 +42,13 @@ public:
 
   virtual bool is_incomplete() const;
   virtual bool is_tbd() const;
+  virtual bool is_fundamental() const;
+  virtual bool is_standard_layout() const;
   virtual bool is_trivial() const;
+  virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_destructible() const;
 
   virtual bool is_fully_specified() const;
 
@@ -60,6 +64,7 @@ public:
   virtual CPPType *resolve_type(CPPScope *current_scope,
                                 CPPScope *global_scope);
 
+  virtual bool is_convertible_to(const CPPType *other) const;
   virtual bool is_equivalent(const CPPType &other) const;
 
   virtual void output(ostream &out, int indent_level, CPPScope *scope,

+ 9 - 2
dtool/src/dtoolbase/deletedBufferChain.cxx

@@ -39,7 +39,7 @@ allocate(size_t size, TypeHandle type_handle) {
   assert(size <= _buffer_size);
 
   // Determine how much space to allocate.
-  const size_t alloc_size = _buffer_size + flag_reserved_bytes;
+  const size_t alloc_size = _buffer_size + flag_reserved_bytes + MemoryHook::get_memory_alignment() - 1;
 
   ObjectNode *obj;
 
@@ -69,7 +69,10 @@ allocate(size_t size, TypeHandle type_handle) {
   // If we get here, the deleted_chain is empty; we have to allocate a new
   // object from the system pool.
 
-  obj = (ObjectNode *)NeverFreeMemory::alloc(alloc_size);
+  // Allocate memory, and make sure the object starts at the proper alignment.
+  void *mem = NeverFreeMemory::alloc(alloc_size);
+  intptr_t pad = (-(intptr_t)flag_reserved_bytes - (intptr_t)mem) % MemoryHook::get_memory_alignment();
+  obj = (ObjectNode *)((uintptr_t)mem + pad);
 
 #ifdef USE_DELETEDCHAINFLAG
   obj->_flag = DCF_alive;
@@ -77,6 +80,10 @@ allocate(size_t size, TypeHandle type_handle) {
 
   void *ptr = node_to_buffer(obj);
 
+#ifndef NDEBUG
+  assert(((uintptr_t)ptr % MemoryHook::get_memory_alignment()) == 0);
+#endif
+
 #ifdef DO_MEMORY_USAGE
   type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
 #endif  // DO_MEMORY_USAGE

+ 1 - 5
dtool/src/dtoolbase/deletedBufferChain.h

@@ -95,12 +95,8 @@ private:
   // Without DELETEDCHAINFLAG, we don't even store the _flag member at all.
   static const size_t flag_reserved_bytes = 0;
 
-#elif defined(LINMATH_ALIGN)
-  // With SSE2 alignment, we need all 16 bytes to preserve alignment.
-  static const size_t flag_reserved_bytes = 16;
-
 #else
-  // Otherwise, we only need enough space for the Integer itself.
+  // Otherwise, we need space for the integer.
   static const size_t flag_reserved_bytes = sizeof(AtomicAdjust::Integer);
 #endif  // USE_DELETEDCHAINFLAG
 

+ 6 - 1
dtool/src/dtoolbase/dtoolbase.h

@@ -339,21 +339,26 @@ typedef struct _object PyObject;
 #define ALIGN_4BYTE
 #define ALIGN_8BYTE
 #define ALIGN_16BYTE
+#define ALIGN_32BYTE
 #define ALIGN_64BYTE
 #elif defined(_MSC_VER)
 #define ALIGN_4BYTE __declspec(align(4))
 #define ALIGN_8BYTE __declspec(align(8))
 #define ALIGN_16BYTE __declspec(align(16))
+#define ALIGN_32BYTE __declspec(align(32))
 #define ALIGN_64BYTE __declspec(align(64))
 #elif defined(__GNUC__)
 #define ALIGN_4BYTE __attribute__ ((aligned (4)))
 #define ALIGN_8BYTE __attribute__ ((aligned (8)))
 #define ALIGN_16BYTE __attribute__ ((aligned (16)))
+#define ALIGN_32BYTE __attribute__ ((aligned (32)))
 #define ALIGN_64BYTE __attribute__ ((aligned (64)))
 #else
 #define ALIGN_4BYTE
 #define ALIGN_8BYTE
 #define ALIGN_16BYTE
+#define ALIGN_32BYTE
+#define ALIGN_64BYTE
 #endif
 
 // Do we need to implement memory-alignment enforcement within the MemoryHook
@@ -374,7 +379,7 @@ typedef struct _object PyObject;
 // externally.
 #define MEMORY_HOOK_DO_ALIGN 1
 
-#elif defined(IS_OSX) || defined(_WIN64)
+#elif (defined(IS_OSX) || defined(_WIN64)) && !defined(__AVX__)
 // The OS-provided malloc implementation will do the required alignment.
 #undef MEMORY_HOOK_DO_ALIGN
 

+ 16 - 4
dtool/src/dtoolbase/memoryHook.I

@@ -43,10 +43,16 @@ get_memory_alignment() {
 #ifdef LINMATH_ALIGN
   // We require 16-byte alignment of certain structures, to support SSE2.  We
   // don't strictly have to align *everything*, but it's just easier to do so.
+#ifdef __AVX__
+  // Eigen requires 32-byte alignment when using AVX instructions.
+  const size_t alignment_size = 32;
+#else
   const size_t alignment_size = 16;
+#endif
 #else
-  // Otherwise, use word alignment.
-  const size_t alignment_size = sizeof(void *);
+  // Otherwise, align to two words.  This seems to be pretty standard to the
+  // point where some code may rely on this being the case.
+  const size_t alignment_size = sizeof(void *) * 2;
 #endif
   return alignment_size;
 }
@@ -64,7 +70,12 @@ get_header_reserved_bytes() {
 #ifdef LINMATH_ALIGN
   // If we're doing SSE2 alignment, we must reserve a full 16-byte block,
   // since anything less than that will spoil the alignment.
+#ifdef __AVX__
+  // Eigen requires 32-byte alignment when using AVX instructions.
+  static const size_t header_reserved_bytes = 32;
+#else
   static const size_t header_reserved_bytes = 16;
+#endif
 
 #elif defined(MEMORY_HOOK_DO_ALIGN)
   // If we're just aligning to words, we reserve a block as big as two words,
@@ -72,8 +83,9 @@ get_header_reserved_bytes() {
   static const size_t header_reserved_bytes = sizeof(size_t) + sizeof(size_t);
 
 #else
-  // If we're not aligning, we just need space for the word itself.
-  static const size_t header_reserved_bytes = sizeof(size_t);
+  // Virtually all allocators align to two words, so we make sure we preserve
+  // that alignment for the benefit of anyone who relies upon that.
+  static const size_t header_reserved_bytes = sizeof(void *) * 2;
 #endif
 
   return header_reserved_bytes;

+ 5 - 0
dtool/src/dtoolbase/memoryHook.cxx

@@ -53,8 +53,13 @@
 // drose: We require 16-byte alignment of certain structures, to
 // support SSE2.  We don't strictly have to align *everything*, but
 // it's just easier to do so.
+#ifdef __AVX__
+// Eigen requires 32-byte alignment when using AVX instructions.
+#define MALLOC_ALIGNMENT ((size_t)32U)
+#else
 #define MALLOC_ALIGNMENT ((size_t)16U)
 #endif
+#endif
 
 #include "dlmalloc_src.cxx"
 

+ 2 - 0
dtool/src/dtoolbase/neverFreeMemory.I

@@ -14,6 +14,8 @@
 /**
  * Returns a pointer to a newly-allocated block of memory of the indicated
  * size.
+ *
+ * Please note that the resulting pointer is not aligned to any boundary.
  */
 INLINE void *NeverFreeMemory::
 alloc(size_t size) {

+ 3 - 7
dtool/src/dtoolbase/neverFreeMemory.cxx

@@ -39,13 +39,9 @@ void *NeverFreeMemory::
 ns_alloc(size_t size) {
   _lock.acquire();
 
-  // We always allocate integer multiples of this many bytes, to guarantee
-  // this minimum alignment.
-  static const size_t alignment_size = MemoryHook::get_memory_alignment();
-
-  // Round up to the next alignment_size.
-  size = ((size + alignment_size - 1) / alignment_size) * alignment_size;
-
+  //NB: we no longer do alignment here.  The only class that uses this is
+  // DeletedBufferChain, and we can do the alignment potentially more
+  // efficiently there since we don't end up overallocating as much.
   _total_used += size;
 
   // Look for a page that has sufficient space remaining.

+ 0 - 3
dtool/src/dtoolutil/.gitignore

@@ -1,3 +0,0 @@
-/checkPandaVersion.cxx
-/checkPandaVersion.h
-/pandaVersion.h

+ 15 - 10
dtool/src/dtoolutil/filename.I

@@ -38,7 +38,6 @@ Filename(const char *filename) {
   (*this) = filename;
 }
 
-
 /**
  *
  */
@@ -84,6 +83,20 @@ Filename(Filename &&from) NOEXCEPT :
 }
 #endif  // USE_MOVE_SEMANTICS
 
+/**
+ * Creates an empty Filename.
+ */
+INLINE Filename::
+Filename() :
+  _dirname_end(0),
+  _basename_start(0),
+  _basename_end(string::npos),
+  _extension_start(string::npos),
+  _hash_start(string::npos),
+  _hash_end(string::npos),
+  _flags(0) {
+}
+
 /**
  *
  */
@@ -155,14 +168,6 @@ pattern_filename(const string &filename) {
   return result;
 }
 
-/**
- *
- */
-INLINE Filename::
-~Filename() {
-}
-
-
 /**
  *
  */
@@ -233,7 +238,7 @@ operator = (string &&filename) NOEXCEPT {
  */
 INLINE Filename &Filename::
 operator = (Filename &&from) NOEXCEPT {
-  _filename = MOVE(from._filename);
+  _filename = move(from._filename);
   _dirname_end = from._dirname_end;
   _basename_start = from._basename_start;
   _basename_end = from._basename_end;

+ 8 - 5
dtool/src/dtoolutil/filename.h

@@ -55,20 +55,22 @@ public:
   };
 
   INLINE Filename(const char *filename);
-
-PUBLISHED:
-  INLINE Filename(const string &filename = "");
+  INLINE Filename(const string &filename);
   INLINE Filename(const wstring &filename);
   INLINE Filename(const Filename &copy);
-  Filename(const Filename &dirname, const Filename &basename);
-  INLINE ~Filename();
 
 #ifdef USE_MOVE_SEMANTICS
   INLINE Filename(string &&filename) NOEXCEPT;
   INLINE Filename(Filename &&from) NOEXCEPT;
 #endif
 
+PUBLISHED:
+  INLINE Filename();
+  Filename(const Filename &dirname, const Filename &basename);
+
 #ifdef HAVE_PYTHON
+  EXTENSION(Filename(PyObject *path));
+
   EXTENSION(PyObject *__reduce__(PyObject *self) const);
 #endif
 
@@ -118,6 +120,7 @@ PUBLISHED:
   INLINE char operator [] (size_t n) const;
 
   EXTENSION(PyObject *__repr__() const);
+  EXTENSION(PyObject *__fspath__() const);
 
   INLINE string substr(size_t begin) const;
   INLINE string substr(size_t begin, size_t end) const;

+ 4 - 2
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2344,7 +2344,7 @@ write_module_class(ostream &out, Object *obj) {
 
           string expected_params;
 
-          out << "  if (arg2 != (PyObject *)NULL) {\n";
+          out << "  if (arg2 != (PyObject *)NULL && arg2 != Py_None) {\n";
           out << "    PyObject *args = PyTuple_Pack(2, arg, arg2);\n";
           write_function_forset(out, two_param_remaps, 2, 2, expected_params, 4,
                                 true, true, AT_varargs, RF_pyobject | RF_err_null | RF_decref_args, true);
@@ -6142,7 +6142,7 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap,
 
         write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const);
       }
-    } else if (TypeManager::is_struct(orig_type->as_pointer_type()->_pointing_at)) {
+    } else if (TypeManager::is_struct(orig_type->remove_pointer())) {
       TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false);
       const InterrogateType &itype = idb->get_type(type_index);
 
@@ -6749,6 +6749,8 @@ is_cpp_type_legal(CPPType *in_ctype) {
     return true;
   } else if (TypeManager::is_pointer_to_simple(type)) {
     return true;
+  } else if (builder.in_forcetype(type->get_local_name(&parser))) {
+    return true;
   } else if (TypeManager::is_exported(type)) {
     return true;
   } else if (TypeManager::is_pointer_to_PyObject(in_ctype)) {

+ 7 - 3
dtool/src/interrogate/interrogateBuilder.cxx

@@ -2401,7 +2401,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
 
   cpptype = TypeManager::resolve_type(cpptype)->as_struct_type();
   assert(cpptype != (CPPStructType *)NULL);
-  bool has_virt_methods = cpptype->check_virtual();
+  bool has_virt_methods = cpptype->is_polymorphic();
 
   switch (cpptype->_type) {
   case CPPExtensionType::T_class:
@@ -2501,7 +2501,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
           // (For many compilers, this does not require a pointer change.)
           generate_casts = true;
 
-        } else if (has_virt_methods && (base_type->as_struct_type() == (CPPStructType *)NULL || !base_type->as_struct_type()->check_virtual())) {
+        } else if (has_virt_methods && (base_type->as_struct_type() == (CPPStructType *)NULL || !base_type->as_struct_type()->is_polymorphic())) {
           // Finally, if this class has virtual methods, but its parent
           // doesn't, then we have to upcast (because this class will require
           // space for a virtual function table pointer, while the parent
@@ -2651,7 +2651,11 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
     }
   }
 
-  if ((itype._flags & InterrogateType::F_inherited_destructor) != 0) {
+  if (!cpptype->is_destructible()) {
+    // There's no way to destruct the type.
+    itype._destructor = 0;
+
+  } else if ((itype._flags & InterrogateType::F_inherited_destructor) != 0) {
     // If we have inherited our virtual destructor from our base class, go
     // ahead and assign the same function index.
     assert(!itype._derivations.empty());

+ 153 - 0
dtool/src/parser-inc/functional

@@ -0,0 +1,153 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file functional
+ * @author tobspr
+ * @date 2016-11-01
+ */
+
+// This file, and all the other files in this directory, aren't
+// intended to be compiled--they're just parsed by CPPParser (and
+// interrogate) in lieu of the actual system headers, to generate the
+// interrogate database.
+
+#ifndef FUNCTIONAL_H
+#define FUNCTIONAL_H
+
+#include <stddef.h>
+
+namespace std {
+
+  // base (deprecated):
+  template <class Arg, class Result> struct unary_function;
+  template <class Arg1, class Arg2, class Result> struct binary_function;
+ 
+  // reference_wrapper:
+  template <class T> class reference_wrapper;
+
+  // arithmetic operations:
+  template <class T> struct plus;
+  template <class T> struct minus;
+  template <class T> struct multiplies;
+  template <class T> struct divides;
+  template <class T> struct modulus;
+  template <class T> struct negate;
+ 
+  // comparisons:
+  template <class T> struct equal_to;
+  template <class T> struct not_equal_to;
+  template <class T> struct greater;
+  template <class T> struct less;
+  template <class T> struct greater_equal;
+  template <class T> struct less_equal;
+ 
+  // logical operations:
+  template <class T> struct logical_and;
+  template <class T> struct logical_or;
+  template <class T> struct logical_not;
+ 
+  // bitwise operations:
+  template <class T> struct bit_and;
+  template <class T> struct bit_or;
+  template <class T> struct bit_xor;
+ 
+  // negators:
+  template <class Predicate> class unary_negate;
+  template <class Predicate>  class binary_negate;
+ 
+  // bind:
+  template<class T> struct is_bind_expression;
+  template<class T> struct is_placeholder;
+
+  namespace placeholders {
+    // M is the implementation-defined number of placeholders
+    // (8 should be enough for interrogate)
+    extern char _1;
+    extern char _2;
+    extern char _3;
+    extern char _4;
+    extern char _5;
+    extern char _6;
+    extern char _7;
+    extern char _8;
+  }
+ 
+  // binders (deprecated):
+  template <class Fn> class binder1st;
+  template <class Fn> class binder2nd;
+
+  // adaptors (deprecated):
+  template <class Arg, class Result> class pointer_to_unary_function;
+  template <class Arg1, class Arg2, class Result>
+    class pointer_to_binary_function;
+ 
+  // adaptors (deprecated):
+  template<class S, class T> class mem_fun_t;
+  template<class S, class T, class A> class mem_fun1_t;
+  template<class S, class T> class mem_fun_ref_t;
+  template<class S, class T, class A> class mem_fun1_ref_t;
+  template <class S, class T> class const_mem_fun_t;
+  template <class S, class T, class A> class const_mem_fun1_t;
+  template <class S, class T> class const_mem_fun_ref_t;
+  template <class S, class T, class A> class const_mem_fun1_ref_t;
+ 
+
+  // polymorphic function wrappers:
+  class bad_function_call;
+
+  // hash function base template:
+  template <class T> struct hash;
+ 
+  // Hash function specializations
+  template <> struct hash<bool>;
+  template <> struct hash<char>;
+  template <> struct hash<signed char>;
+  template <> struct hash<unsigned char>;
+  template <> struct hash<char16_t>;
+  template <> struct hash<char32_t>;
+  template <> struct hash<wchar_t>;
+  template <> struct hash<short>;
+  template <> struct hash<unsigned short>;
+  template <> struct hash<int>;
+  template <> struct hash<unsigned int>;
+  template <> struct hash<long>;
+  template <> struct hash<long long>;
+  template <> struct hash<unsigned long>;
+  template <> struct hash<unsigned long long>;
+  template <> struct hash<float>;
+  template <> struct hash<double>;
+  template <> struct hash<long double>;
+  template<class T> struct hash<T*>;
+   
+  template <class T> class reference_wrapper {
+  public :
+    // types
+    typedef T type;
+    typedef void   result_type;      // not always defined
+    typedef void argument_type;    // not always defined
+    typedef void  first_argument_type;  // not always defined
+    typedef void  second_argument_type; // not always defined
+  };
+
+  template<class T> struct is_bind_expression {};
+  // : integral_constant<bool, true> {};
+
+  class bad_function_call : public std::exception {};
+
+  // template<class> class function; // undefined
+   
+  template< class R, class... ArgTypes >
+  class function {
+  public:
+    typedef R result_type;
+  };
+
+}
+
+
+#endif

+ 34 - 0
dtool/src/parser-inc/initializer_list

@@ -0,0 +1,34 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file initializer_list
+ * @author tobspr
+ * @date 2016-11-01
+ */
+
+// This file, and all the other files in this directory, aren't
+// intended to be compiled--they're just parsed by CPPParser (and
+// interrogate) in lieu of the actual system headers, to generate the
+// interrogate database.
+
+#ifndef INITIALIZER_LIST_H
+#define INITIALIZER_LIST_H
+
+#include <stdtypedefs.h>
+
+namespace std {
+  template<class E> class initializer_list {
+  public:
+    typedef E value_type;
+    typedef const E& reference;
+    typedef const E& const_reference;
+    typedef size_t size_type;
+    typedef const E* iterator;
+    typedef const E* const_iterator;
+  };
+}

+ 163 - 23
dtool/src/parser-inc/memory

@@ -1,16 +1,15 @@
-// Filename: allocator
-// Created by:  drose (12May00)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) Carnegie Mellon University.  All rights reserved.
-//
-// All use of this software is subject to the terms of the revised BSD
-// license.  You should have received a copy of this license along
-// with this source code in a file named "LICENSE."
-//
-////////////////////////////////////////////////////////////////////
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file memory
+ * @author tobspr
+ * @date 2016-11-01
+ */
 
 // This file, and all the other files in this directory, aren't
 // intended to be compiled--they're just parsed by CPPParser (and
@@ -21,6 +20,8 @@
 #define ALLOCATOR_H
 
 #include <stdtypedefs.h>
+#include <iterator>
+
 
 #ifdef GCC_STYLE_ALLOCATOR
 
@@ -33,16 +34,155 @@ public:
 #else  // GCC_STYLE_ALLOCATOR
 
 namespace std {
-  template<class Type>
-  class allocator {
-  public:
-    typedef Type *pointer;
-    typedef const Type *const_pointer;
-    typedef size_t size_type;
-
-    pointer allocate(size_type n, allocator<void>::const_pointer hint = 0);
-    void deallocate(pointer p, size_type n);
-  };
+
+    // pointer traits
+    template <class Ptr> struct pointer_traits;
+    template <class T> struct pointer_traits<T*>;
+ 
+    // pointer safety
+    enum class pointer_safety { relaxed, preferred, strict };
+    
+    // allocator argument tag
+    struct allocator_arg_t { };
+    constexpr allocator_arg_t allocator_arg = allocator_arg_t();
+ 
+    // uses_allocator
+    template <class T, class Alloc> struct uses_allocator;
+ 
+    // allocator traits
+    template <class Alloc> struct allocator_traits;
+ 
+    // the default allocator:
+    template <class T> class allocator;
+    template <> class allocator<void>;
+    
+    // raw storage iterator:
+    template <class OutputIterator, class T> class raw_storage_iterator;
+ 
+    // class template unique_ptr:
+    template <class T> class default_delete;
+    template <class T> class default_delete<T[]>;
+    template <class T, class D = default_delete<T>> class unique_ptr;
+    template <class T, class D> class unique_ptr<T[], D>;
+    // class bad_weak_ptr:
+    class bad_weak_ptr;
+ 
+    // class template shared_ptr:
+    template<class T> class shared_ptr;
+    // class template weak_ptr:
+    template<class T> class weak_ptr;
+ 
+    // class template owner_less:
+    template<class T> class owner_less;
+ 
+    // class template enable_shared_from_this:
+    template<class T> class enable_shared_from_this;
+ 
+    //  hash support
+    template <class T> struct hash;
+    template <class T, class D> struct hash<unique_ptr<T, D> >;
+    template <class T> struct hash<shared_ptr<T> >;
+ 
+    // auto_ptr (deprecated)
+    template <class X> class auto_ptr;
+
+    template <class Ptr> struct pointer_traits {
+        typedef Ptr         pointer;
+        typedef Ptr         element_type;
+        typedef ptrdiff_t   difference_type;
+ 
+        template <class U> using rebind = U; 
+    };
+
+    template <class T> struct pointer_traits<T*> {
+        typedef T*          pointer;
+        typedef T           element_type;
+        typedef ptrdiff_t   difference_type;
+ 
+        template <class U> using rebind = U*;
+    };
+
+
+    template <class Alloc> struct allocator_traits {
+        typedef Alloc allocator_type;
+ 
+        typedef typename Alloc::value_type value_type;
+ 
+        typedef void* pointer;
+        typedef const void* const_pointer;
+        typedef void* void_pointer;
+        typedef const void* const_void_pointer;
+ 
+        typedef std::ptrdiff_t difference_type;
+        typedef size_t size_type;
+ 
+        struct propagate_on_container_copy_assignment;
+        struct propagate_on_container_move_assignment;
+        struct propagate_on_container_swap;
+        struct is_always_equal;
+ 
+        template <class T> using rebind_alloc = size_t;
+        template <class T> using rebind_traits = allocator_traits<rebind_alloc<T> >;
+    };
+
+    template <class T> class allocator;
+    // specialize for void:
+    template <> class allocator<void> {
+    public:
+        typedef void*           pointer;
+        typedef const void*     const_pointer;
+        // reference-to-void members are impossible.
+        typedef void            value_type;
+        template <class U> struct rebind { typedef allocator<U> other; };
+    };
+    template <class T> class allocator {
+    public:
+        struct true_type_; // do not depend on type_traits header
+        typedef size_t          size_type;
+        typedef ptrdiff_t       difference_type;
+        typedef T*              pointer;
+        typedef const T*        const_pointer;
+        typedef T&              reference;
+        typedef const T&        const_reference;
+        typedef T               value_type;
+        template <class U> struct rebind { typedef allocator<U> other; };
+        typedef true_type_       propagate_on_container_move_assignment;
+        typedef true_type_       is_always_equal;
+ 
+    };
+
+    template <class OutputIterator, class T>
+    class raw_storage_iterator
+        : public iterator<output_iterator_tag,void,void,void,void> {
+    public:
+    };
+
+
+    template <class T> struct default_delete {};
+ 
+    template <class T> struct default_delete<T[]> {};
+
+    //  unique_ptr for single objects
+    template <class T, class D = default_delete<T>> class unique_ptr {
+    public:
+        typedef T* pointer;
+        typedef T  element_type;
+        typedef D  deleter_type;
+    };
+ 
+    class bad_weak_ptr: public std::exception {};
+
+    template<class T> class shared_ptr {
+    public:
+        typedef T element_type;
+    };
+
+    template<class T> class weak_ptr {
+    public:
+        typedef T element_type;
+    };
+
+    template<class T> class enable_shared_from_this {};
 }
 
 #endif  // GCC_STYLE_ALLOCATOR

+ 34 - 14
dtool/src/parser-inc/stdint.h

@@ -20,25 +20,45 @@
 #define __WORDSIZE 32
 #endif
 
-typedef signed char int8_t;
-typedef short int int16_t;
-typedef int int32_t;
-typedef long long int int64_t;
+using intmax_t = long long int;
+using uintmax_t = unsigned long long int;
 
-typedef unsigned char uint8_t;
-typedef unsigned short int uint16_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long int uint64_t;
+using int8_t = signed char;
+using int16_t = short int;
+using int32_t = int;
+using int64_t = long long int;
 
-typedef long long int intmax_t;
-typedef unsigned long long int uintmax_t;
+using uint8_t = unsigned char;
+using uint16_t = unsigned short int;
+using uint32_t = unsigned int;
+using uint64_t = unsigned long long int;
+
+using int_least8_t = int8_t;
+using int_least16_t = int16_t;
+using int_least32_t = int32_t;
+using int_least64_t = int64_t;
+
+using uint_least8_t = uint8_t;
+using uint_least16_t = uint16_t;
+using uint_least32_t = uint32_t;
+using uint_least64_t = uint64_t;
+
+using int_fast8_t = int8_t;
+using int_fast16_t = int16_t;
+using int_fast32_t = int32_t;
+using int_fast64_t = int64_t;
+
+using uint_fast8_t = uint8_t;
+using uint_fast16_t = uint16_t;
+using uint_fast32_t = uint32_t;
+using uint_fast64_t = uint64_t;
 
 #if __WORDSIZE == 64
-typedef int64_t intptr_t;
-typedef uint64_t uintptr_t;
+using intptr_t = int64_t;
+using uintptr_t = uint64_t;
 #else
-typedef int32_t intptr_t;
-typedef uint32_t uintptr_t;
+using intptr_t = int32_t;
+using uintptr_t = uint32_t;
 #endif
 
 #endif

+ 2 - 0
dtool/src/parser-inc/stdtypedefs.h

@@ -39,6 +39,8 @@ inline namespace std {
 #endif
 }
 
+struct timeval;
+
 #ifdef __cplusplus
 #define NULL 0L
 #else

+ 96 - 0
dtool/src/parser-inc/type_traits

@@ -0,0 +1,96 @@
+#pragma once
+
+#include <stdtypedefs.h>
+
+namespace std {
+  template<class T, T v>
+  struct integral_constant {
+    static constexpr T value = v;
+    typedef T value_type;
+    constexpr operator value_type() const noexcept { return value; }
+    constexpr value_type operator()() const noexcept { return value; }
+  };
+
+  typedef integral_constant<bool, true> true_type;
+  typedef integral_constant<bool, false> false_type;
+
+  template<class T, class U>
+  struct is_same : std::false_type {};
+
+  template<class T>
+  struct is_same<T, T> : std::true_type {};
+
+  template<class T>
+  struct is_void : std::false_type {};
+
+  template<>
+  struct is_void<void> : std::true_type {};
+
+  template<class T>
+  struct is_enum : integral_constant<bool, __is_enum(T)> {};
+
+  template<class T>
+  struct is_union : integral_constant<bool, __is_union(T)> {};
+
+  template<class T>
+  struct is_class : integral_constant<bool, __is_class(T)> {};
+
+  template<class T>
+  struct is_fundamental : integral_constant<bool, __is_fundamental(T)> {};
+
+  template<class T>
+  struct is_compound : integral_constant<bool, !__is_fundamental(T)> {};
+
+  template<class T>
+  struct is_trivial : integral_constant<bool, __is_trivial(T)> {};
+
+  template<class T>
+  struct is_standard_layout : integral_constant<bool, __is_standard_layout(T)> {};
+
+  template<class T>
+  struct is_pod : integral_constant<bool, __is_pod(T)> {};
+
+  template<class T>
+  struct is_empty : integral_constant<bool, __is_empty(T)> {};
+
+  template<class T>
+  struct is_polymorphic : integral_constant<bool, __is_polymorphic(T)> {};
+
+  template<class T>
+  struct is_abstract : integral_constant<bool, __is_abstract(T)> {};
+
+  template<class T>
+  struct is_final : integral_constant<bool, __is_final(T)> {};
+
+  template<class T>
+  struct is_default_constructible : integral_constant<bool, __is_constructible(T)> {};
+
+  template<class T>
+  struct is_copy_constructible : integral_constant<bool, __is_constructible(T, const T&)> {};
+
+  template<class T>
+  struct is_move_constructible : integral_constant<bool, __is_constructible(T, const T&&)> {};
+
+  template<class T>
+  struct is_destructible : integral_constant<bool, __is_destructible(T)> {};
+
+  template<class T>
+  struct has_virtual_destructor : integral_constant<bool, __has_virtual_destructor(T)> {};
+
+  template<class T >
+  struct alignment_of : std::integral_constant<std::size_t, alignof(T)> {};
+
+  template<class T, class U>
+  struct is_base_of : integral_constant<bool, __is_base_of(T, U)> {};
+
+  template<class T, class U>
+  struct is_convertible : integral_constant<bool, __is_convertible_to(T, U)> {};
+
+  template<class T> struct add_cv { typedef const volatile T type; };
+  template<class T> struct add_const { typedef const T type; };
+  template<class T> struct add_volatile { typedef volatile T type; };
+
+  template<class T> struct add_lvalue_reference { typedef T &type; };
+  template<class T> struct add_rvalue_reference { typedef T &&type; };
+  template<class T> struct add_pointer { typedef T *type; };
+}

+ 84 - 0
dtool/src/parser-inc/unordered_map

@@ -0,0 +1,84 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file unordered_map
+ * @author tobspr
+ * @date 2016-11-01
+ */
+
+// This file, and all the other files in this directory, aren't
+// intended to be compiled--they're just parsed by CPPParser (and
+// interrogate) in lieu of the actual system headers, to generate the
+// interrogate database.
+
+#ifndef UNORDERED_MAP_H
+#define UNORDERED_MAP_H
+
+#include <stdtypedefs.h>
+#include <stdcompare.h>
+#include <pair>
+#include <initializer_list>
+#include <functional>
+
+namespace std {
+ 
+  template <class Key,
+    class T,
+    class Hash = hash<Key>,
+    class Pred = std::equal_to<Key>,
+    class Allocator = std::allocator<std::pair<const Key, T> > >
+  class unordered_map
+  {
+  public:
+    // types
+    typedef Key key_type;
+    typedef Key value_type;
+    typedef Hash hasher;
+    typedef Pred key_equal;
+    typedef Allocator allocator_type;
+    typedef typename allocator_type::pointer pointer;
+    typedef typename allocator_type::const_pointer const_pointer;
+    typedef typename allocator_type::reference reference;
+    typedef typename allocator_type::const_reference const_reference;
+    typedef size_t size_type;
+    typedef std::ptrdiff_t difference_type;
+    class iterator;
+    class const_iterator;
+    class local_iterator;
+    class const_local_iterator;
+  };
+
+  template <class Key,
+  class T,
+  class Hash = hash<Key>,
+  class Pred = std::equal_to<Key>,
+  class Allocator = std::allocator<std::pair<const Key, T> > >
+  class unordered_multimap
+  {
+  public:
+    // types
+    typedef Key key_type;
+    typedef Key value_type;
+    typedef Hash hasher;
+    typedef Pred key_equal;
+    typedef Allocator allocator_type;
+    typedef typename allocator_type::pointer pointer;
+    typedef typename allocator_type::const_pointer const_pointer;
+    typedef typename allocator_type::reference reference;
+    typedef typename allocator_type::const_reference const_reference;
+    typedef size_t size_type;
+    typedef std::ptrdiff_t difference_type;
+    class iterator;
+    class const_iterator;
+    class local_iterator;
+    class const_local_iterator;
+  };
+
+} // namespace std
+
+#endif

+ 83 - 0
dtool/src/parser-inc/unordered_set

@@ -0,0 +1,83 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file unordered_set
+ * @author tobspr
+ * @date 2016-11-01
+ */
+
+// This file, and all the other files in this directory, aren't
+// intended to be compiled--they're just parsed by CPPParser (and
+// interrogate) in lieu of the actual system headers, to generate the
+// interrogate database.
+
+#ifndef UNORDERED_SET_H
+#define UNORDERED_SET_H
+
+#include <stdtypedefs.h>
+#include <stdcompare.h>
+#include <pair>
+#include <initializer_list>
+#include <functional>
+
+namespace std {
+
+  template <class Key,
+    class Hash = hash<Key>,
+    class Pred = std::equal_to<Key>,
+    class Allocator = std::allocator<Key> >
+  class unordered_set
+  {
+  public:
+    // types
+    typedef Key key_type;
+    typedef Key value_type;
+    typedef Hash hasher;
+    typedef Pred key_equal;
+    typedef Allocator allocator_type;
+    typedef typename allocator_type::pointer pointer;
+    typedef typename allocator_type::const_pointer const_pointer;
+    typedef typename allocator_type::reference reference;
+    typedef typename allocator_type::const_reference  const_reference;
+    typedef size_t size_type;
+    typedef std::ptrdiff_t difference_type;
+   
+    class iterator;
+    class const_iterator;
+    class local_iterator;
+    class const_local_iterator;
+  };
+
+  template <class Key,
+    class Hash = hash<Key>,
+    class Pred = std::equal_to<Key>,
+    class Allocator = std::allocator<Key> >
+  class unordered_multiset
+  {
+  public:
+    // types
+    typedef Key key_type;
+    typedef Key value_type;
+    typedef Hash hasher;
+    typedef Pred key_equal;
+    typedef Allocator allocator_type;
+    typedef typename allocator_type::pointer pointer;
+    typedef typename allocator_type::const_pointer    const_pointer;
+    typedef typename allocator_type::reference reference;
+    typedef typename allocator_type::const_reference  const_reference;
+    typedef size_t size_type;
+    typedef std::ptrdiff_t difference_type;
+    class iterator;
+    class const_iterator;
+    class local_iterator;
+    class const_local_iterator;
+  };
+
+} // namespace std
+
+#endif

+ 1 - 0
dtool/src/parser-inc/xmmintrin.h

@@ -0,0 +1 @@
+struct __m128;

+ 0 - 1
dtool/src/prc/.gitignore

@@ -1 +0,0 @@
-/prc_parameters.h

+ 42 - 33
dtool/src/prc/encryptStreamBuf.cxx

@@ -73,8 +73,8 @@ EncryptStreamBuf() {
   _key_length = encryption_key_length;
   _iteration_count = encryption_iteration_count;
 
-  _read_valid = false;
-  _write_valid = false;
+  _read_ctx = NULL;
+  _write_ctx = NULL;
 
   _read_overflow_buffer = NULL;
   _in_read_overflow_buffer = 0;
@@ -110,7 +110,11 @@ open_read(istream *source, bool owns_source, const string &password) {
 
   _source = source;
   _owns_source = owns_source;
-  _read_valid = false;
+
+  if (_read_ctx != NULL) {
+    EVP_CIPHER_CTX_free(_read_ctx);
+    _read_ctx = NULL;
+  }
 
   // Now read the header information.
   StreamReader sr(_source, false);
@@ -143,17 +147,21 @@ open_read(istream *source, bool owns_source, const string &password) {
 
   string iv = sr.extract_bytes(iv_length);
 
+  _read_ctx = EVP_CIPHER_CTX_new();
+  nassertv(_read_ctx != NULL);
+
   // Initialize the context
   int result;
-  result = EVP_DecryptInit(&_read_ctx, cipher, NULL, (unsigned char *)iv.data());
+  result = EVP_DecryptInit(_read_ctx, cipher, NULL, (unsigned char *)iv.data());
   nassertv(result > 0);
 
-  result = EVP_CIPHER_CTX_set_key_length(&_read_ctx, key_length);
+  result = EVP_CIPHER_CTX_set_key_length(_read_ctx, key_length);
   if (result <= 0) {
     prc_cat.error()
       << "Invalid key length " << key_length * 8 << " bits for algorithm "
       << OBJ_nid2sn(nid) << "\n";
-    EVP_CIPHER_CTX_cleanup(&_read_ctx);
+    EVP_CIPHER_CTX_free(_read_ctx);
+    _read_ctx = NULL;
     return;
   }
 
@@ -167,11 +175,9 @@ open_read(istream *source, bool owns_source, const string &password) {
   nassertv(result > 0);
 
   // Store the key within the context.
-  result = EVP_DecryptInit(&_read_ctx, NULL, key, NULL);
+  result = EVP_DecryptInit(_read_ctx, NULL, key, NULL);
   nassertv(result > 0);
 
-  _read_valid = true;
-
   _read_overflow_buffer = new unsigned char[_read_block_size];
   _in_read_overflow_buffer = 0;
   thread_consider_yield();
@@ -182,9 +188,9 @@ open_read(istream *source, bool owns_source, const string &password) {
  */
 void EncryptStreamBuf::
 close_read() {
-  if (_read_valid) {
-    EVP_CIPHER_CTX_cleanup(&_read_ctx);
-    _read_valid = false;
+  if (_read_ctx != NULL) {
+    EVP_CIPHER_CTX_free(_read_ctx);
+    _read_ctx = NULL;
   }
 
   if (_read_overflow_buffer != (unsigned char *)NULL) {
@@ -211,7 +217,6 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   close_write();
   _dest = dest;
   _owns_dest = owns_dest;
-  _write_valid = false;
 
   const EVP_CIPHER *cipher =
     EVP_get_cipherbyname(_algorithm.c_str());
@@ -220,21 +225,23 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
     prc_cat.error()
       << "Unknown encryption algorithm: " << _algorithm << "\n";
     return;
-  };
+  }
 
   int nid = EVP_CIPHER_nid(cipher);
 
   int iv_length = EVP_CIPHER_iv_length(cipher);
   _write_block_size = EVP_CIPHER_block_size(cipher);
 
-  unsigned char *iv = (unsigned char *)alloca(iv_length);
-
   // Generate a random IV.  It doesn't need to be cryptographically secure,
   // just unique.
+  unsigned char *iv = (unsigned char *)alloca(iv_length);
   RAND_pseudo_bytes(iv, iv_length);
 
+  _write_ctx = EVP_CIPHER_CTX_new();
+  nassertv(_write_ctx != NULL);
+
   int result;
-  result = EVP_EncryptInit(&_write_ctx, cipher, NULL, iv);
+  result = EVP_EncryptInit(_write_ctx, cipher, NULL, iv);
   nassertv(result > 0);
 
   // Store the appropriate key length in the context.
@@ -242,12 +249,13 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   if (key_length == 0) {
     key_length = EVP_CIPHER_key_length(cipher);
   }
-  result = EVP_CIPHER_CTX_set_key_length(&_write_ctx, key_length);
+  result = EVP_CIPHER_CTX_set_key_length(_write_ctx, key_length);
   if (result <= 0) {
     prc_cat.error()
       << "Invalid key length " << key_length * 8 << " bits for algorithm "
       << OBJ_nid2sn(nid) << "\n";
-    EVP_CIPHER_CTX_cleanup(&_write_ctx);
+    EVP_CIPHER_CTX_free(_write_ctx);
+    _write_ctx = NULL;
     return;
   }
 
@@ -271,7 +279,7 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   nassertv(result > 0);
 
   // Store the key in the context.
-  result = EVP_EncryptInit(&_write_ctx, NULL, key, NULL);
+  result = EVP_EncryptInit(_write_ctx, NULL, key, NULL);
   nassertv(result > 0);
 
   // Now write the header information to the stream.
@@ -284,7 +292,6 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   sw.add_uint16((uint16_t)count);
   sw.append_data(iv, iv_length);
 
-  _write_valid = true;
   thread_consider_yield();
 }
 
@@ -298,15 +305,16 @@ close_write() {
     write_chars(pbase(), n);
     pbump(-(int)n);
 
-    if (_write_valid) {
+    if (_write_ctx != NULL) {
       unsigned char *write_buffer = (unsigned char *)alloca(_write_block_size);
       int bytes_written = 0;
-      EVP_EncryptFinal(&_write_ctx, write_buffer, &bytes_written);
+      EVP_EncryptFinal(_write_ctx, write_buffer, &bytes_written);
       thread_consider_yield();
 
       _dest->write((const char *)write_buffer, bytes_written);
 
-      _write_valid = false;
+      EVP_CIPHER_CTX_free(_write_ctx);
+      _write_ctx = NULL;
     }
 
     if (_owns_dest) {
@@ -418,7 +426,7 @@ read_chars(char *start, size_t length) {
 
   do {
     // Get more bytes from the stream.
-    if (!_read_valid) {
+    if (_read_ctx == NULL) {
       return 0;
     }
 
@@ -429,20 +437,21 @@ read_chars(char *start, size_t length) {
     int result;
     if (source_length != 0) {
       result =
-        EVP_DecryptUpdate(&_read_ctx, read_buffer, &bytes_read,
+        EVP_DecryptUpdate(_read_ctx, read_buffer, &bytes_read,
                           source_buffer, source_length);
     } else {
       result =
-        EVP_DecryptFinal(&_read_ctx, read_buffer, &bytes_read);
-      _read_valid = false;
+        EVP_DecryptFinal(_read_ctx, read_buffer, &bytes_read);
+      EVP_CIPHER_CTX_free(_read_ctx);
+      _read_ctx = NULL;
     }
 
     if (result <= 0) {
       prc_cat.error()
         << "Error decrypting stream.\n";
-      if (_read_valid) {
-        EVP_CIPHER_CTX_cleanup(&_read_ctx);
-        _read_valid = false;
+      if (_read_ctx != NULL) {
+        EVP_CIPHER_CTX_free(_read_ctx);
+        _read_ctx = NULL;
       }
     }
     thread_consider_yield();
@@ -472,13 +481,13 @@ read_chars(char *start, size_t length) {
  */
 void EncryptStreamBuf::
 write_chars(const char *start, size_t length) {
-  if (_write_valid && length != 0) {
+  if (_write_ctx != NULL && length != 0) {
     size_t max_write_buffer = length + _write_block_size;
     unsigned char *write_buffer = (unsigned char *)alloca(max_write_buffer);
 
     int bytes_written = 0;
     int result =
-      EVP_EncryptUpdate(&_write_ctx, write_buffer, &bytes_written,
+      EVP_EncryptUpdate(_write_ctx, write_buffer, &bytes_written,
                         (unsigned char *)start, length);
     if (result <= 0) {
       prc_cat.error()

+ 2 - 4
dtool/src/prc/encryptStreamBuf.h

@@ -64,14 +64,12 @@ private:
   int _key_length;
   int _iteration_count;
 
-  bool _read_valid;
-  EVP_CIPHER_CTX _read_ctx;
+  EVP_CIPHER_CTX *_read_ctx;
   size_t _read_block_size;
   unsigned char *_read_overflow_buffer;
   size_t _in_read_overflow_buffer;
 
-  bool _write_valid;
-  EVP_CIPHER_CTX _write_ctx;
+  EVP_CIPHER_CTX *_write_ctx;
   size_t _write_block_size;
 };
 

+ 3 - 0
dtool/src/prc/notify.cxx

@@ -343,6 +343,9 @@ assert_failure(const char *expression, int line,
   // guarantee it has already been constructed.
   ALIGN_16BYTE ConfigVariableBool assert_abort("assert-abort", false);
   if (assert_abort) {
+    // Make sure the error message has been flushed to the output.
+    nout.flush();
+
 #ifdef WIN32
     // How to trigger an exception in VC++ that offers to take us into the
     // debugger?  abort() doesn't do it.  We used to be able to assert(false),

+ 4 - 0
dtool/src/pystub/pystub.cxx

@@ -149,12 +149,14 @@ extern "C" {
   EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...);
+  EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...);
+  EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...);
   EXPCL_PYSTUB int PyUnicode_AsUTF8(...);
   EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...);
@@ -349,12 +351,14 @@ int PyUnicodeUCS2_FromString(...) { return 0; }
 int PyUnicodeUCS2_FromStringAndSize(...) { return 0; }
 int PyUnicodeUCS2_FromWideChar(...) { return 0; }
 int PyUnicodeUCS2_AsWideChar(...) { return 0; }
+int PyUnicodeUCS2_AsWideCharString(...) { return 0; }
 int PyUnicodeUCS2_GetSize(...) { return 0; }
 int PyUnicodeUCS4_FromFormat(...) { return 0; }
 int PyUnicodeUCS4_FromString(...) { return 0; }
 int PyUnicodeUCS4_FromStringAndSize(...) { return 0; }
 int PyUnicodeUCS4_FromWideChar(...) { return 0; }
 int PyUnicodeUCS4_AsWideChar(...) { return 0; }
+int PyUnicodeUCS4_AsWideCharString(...) { return 0; }
 int PyUnicodeUCS4_GetSize(...) { return 0; }
 int PyUnicode_AsUTF8(...) { return 0; }
 int PyUnicode_AsUTF8AndSize(...) { return 0; }

+ 0 - 2
makepanda/.gitignore

@@ -1,2 +0,0 @@
-*.pyc
-*.pyo

+ 29 - 3
makepanda/installer.nsi

@@ -31,6 +31,7 @@ SetCompressor ${COMPRESSOR}
 !include "Sections.nsh"
 !include "WinMessages.nsh"
 !include "WordFunc.nsh"
+!include "x64.nsh"
 
 !define MUI_WELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
 !define MUI_UNWELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
@@ -120,6 +121,14 @@ Function runFunction
     ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk"
 FunctionEnd
 
+Function .onInit
+    ${If} ${REGVIEW} = 64
+    ${AndIfNot} ${RunningX64}
+        MessageBox MB_OK|MB_ICONEXCLAMATION "You are attempting to install the 64-bit version of Panda3D on a 32-bit version of Windows.  Please download and install the 32-bit version of Panda3D instead."
+        Abort
+    ${EndIf}
+FunctionEnd
+
 SectionGroup "Panda3D Libraries"
     Section "Core Libraries" SecCore
         SectionIn 1 2 RO
@@ -134,12 +143,22 @@ SectionGroup "Panda3D Libraries"
         SetOutPath "$INSTDIR"
         File "${BUILT}\LICENSE"
         File /r /x CVS "${BUILT}\ReleaseNotes"
-        SetOutPath $INSTDIR\bin
-        File /r /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x NxCharacter*.dll /x cudart*.dll /x PhysX*.dll /x libpandaphysx.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll"
-        File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest"
+
         SetOutPath $INSTDIR\etc
         File /r "${BUILT}\etc\*"
 
+        SetOutPath $INSTDIR\bin
+        File /r /x api-ms-win-*.dll /x ucrtbase.dll /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x NxCharacter*.dll /x cudart*.dll /x PhysX*.dll /x libpandaphysx.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll"
+        File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest"
+
+        ; Before Windows 10, we need these stubs for the UCRT as well.
+        ReadRegDWORD $0 HKLM "Software\Microsoft\Windows NT\CurrentVersion" "CurrentMajorVersionNumber"
+        ${If} $0 < 10
+            ClearErrors
+            File /nonfatal /r "${BUILT}\bin\api-ms-win-*.dll"
+            File /nonfatal "${BUILT}\bin\ucrtbase.dll"
+        ${Endif}
+
         SetDetailsPrint both
         DetailPrint "Installing models..."
         SetDetailsPrint listonly
@@ -147,6 +166,10 @@ SectionGroup "Panda3D Libraries"
         SetOutPath $INSTDIR\models
         File /r /x CVS "${BUILT}\models\*"
 
+        SetDetailsPrint both
+        DetailPrint "Installing optional components..."
+        SetDetailsPrint listonly
+
         RMDir /r "$SMPROGRAMS\${TITLE}"
         CreateDirectory "$SMPROGRAMS\${TITLE}"
     SectionEnd
@@ -634,6 +657,9 @@ Section -post
     WriteRegStr HKCU "Software\Classes\.pz" "PerceivedType" "compressed"
     WriteRegStr HKCU "Software\Classes\.mf" "" "Panda3D.Multifile"
     WriteRegStr HKCU "Software\Classes\.mf" "PerceivedType" "compressed"
+    WriteRegStr HKCU "Software\Classes\.prc" "" "inifile"
+    WriteRegStr HKCU "Software\Classes\.prc" "Content Type" "text/plain"
+    WriteRegStr HKCU "Software\Classes\.prc" "PerceivedType" "text"
 
     ; For convenience, if nobody registered .pyd, we will.
     ReadRegStr $0 HKCR "Software\Classes\.pyd" ""

+ 196 - 115
makepanda/makepanda.py

@@ -39,6 +39,7 @@ import sys
 
 COMPILER=0
 INSTALLER=0
+WHEEL=0
 GENMAN=0
 COMPRESSOR="zlib"
 THREADCOUNT=0
@@ -51,6 +52,7 @@ RUNTIME=0
 DISTRIBUTOR=""
 VERSION=None
 DEBVERSION=None
+WHLVERSION=None
 RPMRELEASE="1"
 GIT_COMMIT=None
 P3DSUFFIX=None
@@ -87,14 +89,13 @@ PkgListSet(["PYTHON", "DIRECT",                        # Python support
   "MFC", "WX", "FLTK",                                 # Used for web plug-in only
   "ROCKET", "AWESOMIUM",                               # GUI libraries
   "CARBON", "COCOA",                                   # Mac OS X toolkits
-  "X11", "XF86DGA", "XRANDR", "XCURSOR",               # Unix platform support
-  "PANDATOOL", "PVIEW", "DEPLOYTOOLS",                 # Toolchain
+  "X11",                                               # Unix platform support
+  "PANDATOOL", "PVIEW", "DEPLOYTOOLS", "DIRECTSCRIPTS",# Toolchain
   "SKEL",                                              # Example SKEL project
   "PANDAFX",                                           # Some distortion special lenses
   "PANDAPARTICLESYSTEM",                               # Built in particle system
   "CONTRIB",                                           # Experimental
   "SSE2", "NEON",                                      # Compiler features
-  "TOUCHINPUT",                                        # Touchinput interface (requires Windows 7)
 ])
 
 CheckPandaSourceTree()
@@ -125,6 +126,7 @@ def usage(problem):
     print("  --verbose         (print out more information)")
     print("  --runtime         (build a runtime build instead of an SDK build)")
     print("  --installer       (build an installer)")
+    print("  --wheel           (build a pip-installable .whl)")
     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)")
@@ -160,17 +162,18 @@ def usage(problem):
     os._exit(1)
 
 def parseopts(args):
-    global INSTALLER,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
+    global INSTALLER,WHEEL,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
     global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
-    global DEBVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
+    global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     longopts = [
         "help","distributor=","verbose","runtime","osxtarget=",
-        "optimize=","everything","nothing","installer","rtdist","nocolor",
+        "optimize=","everything","nothing","installer","wheel","rtdist","nocolor",
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
-        "universal", "target=", "arch=", "git-commit="]
+        "universal", "target=", "arch=", "git-commit=",
+        "use-touchinput", "no-touchinput"]
     anything = 0
     optimize = ""
     target = None
@@ -188,6 +191,7 @@ def parseopts(args):
             if (option=="--help"): raise Exception
             elif (option=="--optimize"): optimize=value
             elif (option=="--installer"): INSTALLER=1
+            elif (option=="--wheel"): WHEEL=1
             elif (option=="--verbose"): SetVerbose(True)
             elif (option=="--distributor"): DISTRIBUTOR=value
             elif (option=="--rtdist"): RTDIST=1
@@ -203,8 +207,11 @@ def parseopts(args):
             elif (option=="--arch"): target_arch = value.strip()
             elif (option=="--nocolor"): DisableColors()
             elif (option=="--version"):
-                VERSION=value
-                if (len(VERSION.split(".")) != 3): raise Exception
+                match = re.match(r'^\d+\.\d+\.\d+', value)
+                if not match:
+                    usage("version requires three digits")
+                WHLVERSION = value
+                VERSION = match.group()
             elif (option=="--lzma"): COMPRESSOR="lzma"
             elif (option=="--override"): AddOverride(value.strip())
             elif (option=="--static"): SetLinkAllStatic(True)
@@ -316,18 +323,6 @@ def parseopts(args):
             print("No Windows SDK version specified. Defaulting to '7.1'.")
             WINDOWS_SDK = '7.1'
 
-        is_win7 = False
-        if sys.platform == 'win32':
-            # Note: not available in cygwin.
-            winver = sys.getwindowsversion()
-            if winver[0] >= 6 and winver[1] >= 1:
-                is_win7 = True
-
-        if RUNTIME or not is_win7:
-            PkgDisable("TOUCHINPUT")
-    else:
-        PkgDisable("TOUCHINPUT")
-
     if clean_build and os.path.isdir(GetOutputDir()):
         print("Deleting %s" % (GetOutputDir()))
         shutil.rmtree(GetOutputDir())
@@ -428,9 +423,18 @@ if (RUNTIME):
 if (INSTALLER and RTDIST):
     exit("Cannot build an installer for the rtdist build!")
 
+if (WHEEL and RUNTIME):
+    exit("Cannot build a wheel for the runtime build!")
+
+if (WHEEL and RTDIST):
+    exit("Cannot build a wheel for the rtdist build!")
+
 if (INSTALLER) and (PkgSkip("PYTHON")) and (not RUNTIME) and GetTarget() == 'windows':
     exit("Cannot build installer on Windows without python")
 
+if WHEEL and PkgSkip("PYTHON"):
+    exit("Cannot build wheel without Python")
+
 if (RTDIST) and (PkgSkip("WX") and PkgSkip("FLTK")):
     exit("Cannot build rtdist without wx or fltk")
 
@@ -516,9 +520,6 @@ IncDirectory("ALWAYS", GetOutputDir()+"/include")
 
 if (COMPILER == "MSVC"):
     PkgDisable("X11")
-    PkgDisable("XRANDR")
-    PkgDisable("XF86DGA")
-    PkgDisable("XCURSOR")
     PkgDisable("GLES")
     PkgDisable("GLES2")
     PkgDisable("EGL")
@@ -808,9 +809,13 @@ if (COMPILER=="GCC"):
         SmartPkgEnable("JPEG",      "",          ("jpeg"), "jpeglib.h")
         SmartPkgEnable("PNG",       "libpng",    ("png"), "png.h", tool = "libpng-config")
 
-        if GetTarget() == "darwin" and not PkgSkip("FFMPEG"):
-            LibName("FFMPEG", "-Wl,-read_only_relocs,suppress")
-            LibName("FFMPEG", "-framework VideoDecodeAcceleration")
+        if not PkgSkip("FFMPEG"):
+            if GetTarget() == "darwin":
+                LibName("FFMPEG", "-Wl,-read_only_relocs,suppress")
+                LibName("FFMPEG", "-framework VideoDecodeAcceleration")
+            elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"):
+                # Needed when linking ffmpeg statically on Linux.
+                LibName("FFMPEG", "-Wl,-Bsymbolic")
 
         cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV")
         if cv_lib == "opencv_core":
@@ -840,12 +845,9 @@ if (COMPILER=="GCC"):
     if GetTarget() != 'darwin':
         # CgGL is covered by the Cg framework, and we don't need X11 components on OSX
         if not PkgSkip("NVIDIACG") and not RUNTIME:
-            SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h")
+            SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h", thirdparty_dir = "nvidiacg")
         if not RUNTIME:
             SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h"))
-            SmartPkgEnable("XRANDR", "xrandr", "Xrandr", "X11/extensions/Xrandr.h")
-            SmartPkgEnable("XF86DGA", "xxf86dga", "Xxf86dga", "X11/extensions/xf86dga.h")
-            SmartPkgEnable("XCURSOR", "xcursor", "Xcursor", "X11/Xcursor/Xcursor.h")
 
     if GetHost() != "darwin":
         # Workaround for an issue where pkg-config does not include this path
@@ -1055,11 +1057,12 @@ def CompileCxx(obj,src,opts):
                 cmd += "/favor:blend "
             cmd += "/wd4996 /wd4275 /wd4273 "
 
-            # Enable Windows 7 interfaces if we need Touchinput.
-            if PkgSkip("TOUCHINPUT") == 0:
-                cmd += "/DWINVER=0x601 "
-            else:
-                cmd += "/DWINVER=0x501 "
+            # We still target Windows XP.
+            cmd += "/DWINVER=0x501 "
+            # Work around a WinXP/2003 bug when using VS 2015+.
+            if SDK.get("VISUALSTUDIO_VERSION") == '14.0':
+                cmd += "/Zc:threadSafeInit- "
+
             cmd += "/Fo" + obj + " /nologo /c"
             if GetTargetArch() != 'x64' and (not PkgSkip("SSE2") or 'SSE2' in opts):
                 cmd += " /arch:SSE2"
@@ -1109,12 +1112,7 @@ def CompileCxx(obj,src,opts):
             if GetTargetArch() == 'x64':
                 cmd += "/favor:blend "
             cmd += "/wd4996 /wd4275 /wd4267 /wd4101 /wd4273 "
-
-            # Enable Windows 7 interfaces if we need Touchinput.
-            if PkgSkip("TOUCHINPUT") == 0:
-                cmd += "/DWINVER=0x601 "
-            else:
-                cmd += "/DWINVER=0x501 "
+            cmd += "/DWINVER=0x501 "
             cmd += "/Fo" + obj + " /c"
             for x in ipath: cmd += " /I" + x
             for (opt,dir) in INCDIRECTORIES:
@@ -1196,7 +1194,7 @@ def CompileCxx(obj,src,opts):
 
     if (COMPILER=="GCC"):
         if (src.endswith(".c")): cmd = GetCC() +' -fPIC -c -o ' + obj
-        else:                    cmd = GetCXX()+' -ftemplate-depth-70 -fPIC -c -o ' + obj
+        else:                    cmd = GetCXX()+' -std=gnu++0x -ftemplate-depth-70 -fPIC -c -o ' + obj
         for (opt, dir) in INCDIRECTORIES:
             if (opt=="ALWAYS") or (opt in opts): cmd += ' -I' + BracketNameWithQuotes(dir)
         for (opt, dir) in FRAMEWORKDIRECTORIES:
@@ -1682,11 +1680,7 @@ def CompileLink(dll, obj, opts):
                 if 'NOARCH:' + arch.upper() not in opts:
                     cmd += " -arch %s" % arch
 
-        if "SYSROOT" in SDK:
-            cmd += " --sysroot=%s -no-canonical-prefixes" % (SDK["SYSROOT"])
-
-        # Android-specific flags.
-        if GetTarget() == 'android':
+        elif GetTarget() == 'android':
             cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
             if GetTargetArch() == 'armv7a':
                 cmd += " -march=armv7-a -Wl,--fix-cortex-a8"
@@ -1694,9 +1688,17 @@ def CompileLink(dll, obj, opts):
         else:
             cmd += " -pthread"
 
+        if "SYSROOT" in SDK:
+            cmd += " --sysroot=%s -no-canonical-prefixes" % (SDK["SYSROOT"])
+
         if LDFLAGS != "":
             cmd += " " + LDFLAGS
 
+        # Don't link libraries with Python.
+        if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST:
+            opts = opts[:]
+            opts.remove("PYTHON")
+
         for (opt, dir) in LIBDIRECTORIES:
             if (opt=="ALWAYS") or (opt in opts):
                 cmd += ' -L' + BracketNameWithQuotes(dir)
@@ -2122,17 +2124,13 @@ DTOOL_CONFIG=[
     ("COMPILE_IN_DEFAULT_FONT",        '1',                      '1'),
     ("STDFLOAT_DOUBLE",                'UNDEF',                  'UNDEF'),
     ("HAVE_MAYA",                      '1',                      '1'),
-    ("MAYA_PRE_5_0",                   'UNDEF',                  'UNDEF'),
     ("HAVE_SOFTIMAGE",                 'UNDEF',                  'UNDEF'),
-    ("SSL_097",                        'UNDEF',                  'UNDEF'),
     ("REPORT_OPENSSL_ERRORS",          '1',                      '1'),
     ("USE_PANDAFILESTREAM",            '1',                      '1'),
     ("USE_DELETED_CHAIN",              '1',                      '1'),
-    ("HAVE_WIN_TOUCHINPUT",            'UNDEF',                  'UNDEF'),
     ("HAVE_GLX",                       'UNDEF',                  '1'),
     ("HAVE_WGL",                       '1',                      'UNDEF'),
     ("HAVE_DX9",                       'UNDEF',                  'UNDEF'),
-    ("HAVE_CHROMIUM",                  'UNDEF',                  'UNDEF'),
     ("HAVE_THREADS",                   '1',                      '1'),
     ("SIMPLE_THREADS",                 'UNDEF',                  'UNDEF'),
     ("OS_SIMPLE_THREADS",              '1',                      '1'),
@@ -2145,12 +2143,9 @@ DTOOL_CONFIG=[
     ("DO_COLLISION_RECORDING",         'UNDEF',                  'UNDEF'),
     ("SUPPORT_IMMEDIATE_MODE",         'UNDEF',                  'UNDEF'),
     ("SUPPORT_FIXED_FUNCTION",         '1',                      '1'),
-    ("TRACK_IN_INTERPRETER",           'UNDEF',                  'UNDEF'),
     ("DO_MEMORY_USAGE",                'UNDEF',                  'UNDEF'),
     ("DO_PIPELINING",                  '1',                      '1'),
     ("EXPORT_TEMPLATES",               'yes',                    'yes'),
-    ("LINK_IN_GL",                     'UNDEF',                  'UNDEF'),
-    ("LINK_IN_PHYSICS",                'UNDEF',                  'UNDEF'),
     ("DEFAULT_PATHSEP",                '";"',                    '":"'),
     ("WORDS_BIGENDIAN",                'UNDEF',                  'UNDEF'),
     ("HAVE_NAMESPACE",                 '1',                      '1'),
@@ -2206,13 +2201,9 @@ DTOOL_CONFIG=[
     ("PHAVE_STDINT_H",                 '1',                      '1'),
     ("HAVE_RTTI",                      '1',                      '1'),
     ("HAVE_X11",                       'UNDEF',                  '1'),
-    ("HAVE_XRANDR",                    'UNDEF',                  '1'),
-    ("HAVE_XF86DGA",                   'UNDEF',                  '1'),
-    ("HAVE_XCURSOR",                   'UNDEF',                  '1'),
     ("IS_LINUX",                       'UNDEF',                  '1'),
     ("IS_OSX",                         'UNDEF',                  'UNDEF'),
     ("IS_FREEBSD",                     'UNDEF',                  'UNDEF'),
-    ("GLOBAL_OPERATOR_NEW_EXCEPTIONS", 'UNDEF',                  '1'),
     ("HAVE_EIGEN",                     'UNDEF',                  'UNDEF'),
     ("LINMATH_ALIGN",                  '1',                      '1'),
     ("HAVE_ZLIB",                      'UNDEF',                  'UNDEF'),
@@ -2230,28 +2221,25 @@ DTOOL_CONFIG=[
     ("HAVE_PNM",                       '1',                      '1'),
     ("HAVE_STB_IMAGE",                 '1',                      '1'),
     ("HAVE_VORBIS",                    'UNDEF',                  'UNDEF'),
-    ("HAVE_NVIDIACG",                  'UNDEF',                  'UNDEF'),
     ("HAVE_FREETYPE",                  'UNDEF',                  'UNDEF'),
     ("HAVE_FFTW",                      'UNDEF',                  'UNDEF'),
     ("HAVE_OPENSSL",                   'UNDEF',                  'UNDEF'),
     ("HAVE_NET",                       'UNDEF',                  'UNDEF'),
+    ("WANT_NATIVE_NET",                '1',                      '1'),
+    ("SIMULATE_NETWORK_DELAY",         'UNDEF',                  'UNDEF'),
     ("HAVE_EGG",                       '1',                      '1'),
     ("HAVE_CG",                        'UNDEF',                  'UNDEF'),
     ("HAVE_CGGL",                      'UNDEF',                  'UNDEF'),
     ("HAVE_CGDX9",                     'UNDEF',                  'UNDEF'),
-    ("HAVE_FFMPEG",                    'UNDEF',                  'UNDEF'),
-    ("HAVE_SWSCALE",                   'UNDEF',                  'UNDEF'),
-    ("HAVE_SWRESAMPLE",                'UNDEF',                  'UNDEF'),
     ("HAVE_ARTOOLKIT",                 'UNDEF',                  'UNDEF'),
-    ("HAVE_OPENCV",                    'UNDEF',                  'UNDEF'),
     ("HAVE_DIRECTCAM",                 'UNDEF',                  'UNDEF'),
     ("HAVE_SQUISH",                    'UNDEF',                  'UNDEF'),
-    ("HAVE_FCOLLADA",                  'UNDEF',                  'UNDEF'),
     ("HAVE_CARBON",                    'UNDEF',                  'UNDEF'),
     ("HAVE_COCOA",                     'UNDEF',                  'UNDEF'),
     ("HAVE_OPENAL_FRAMEWORK",          'UNDEF',                  'UNDEF'),
     ("HAVE_ROCKET_PYTHON",             '1',                      '1'),
     ("HAVE_ROCKET_DEBUGGER",           'UNDEF',                  'UNDEF'),
+    ("USE_TAU",                        'UNDEF',                  'UNDEF'),
     ("PRC_SAVE_DESCRIPTIONS",          '1',                      '1'),
 #    ("_SECURE_SCL",                    '0',                      'UNDEF'),
 #    ("_SECURE_SCL_THROWS",             '0',                      'UNDEF'),
@@ -2298,9 +2286,6 @@ def WriteConfigSettings():
             else:
                 dtool_config["HAVE_"+x] = 'UNDEF'
 
-    if not PkgSkip("OPENCV"):
-        dtool_config["OPENCV_VER_23"] = '1' if OPENCV_VER_23 else 'UNDEF'
-
     dtool_config["HAVE_NET"] = '1'
 
     if (PkgSkip("NVIDIACG")==0):
@@ -2320,9 +2305,6 @@ def WriteConfigSettings():
         dtool_config["PHAVE_SYS_MALLOC_H"] = '1'
         dtool_config["HAVE_OPENAL_FRAMEWORK"] = '1'
         dtool_config["HAVE_X11"] = 'UNDEF'  # We might have X11, but we don't need it.
-        dtool_config["HAVE_XRANDR"] = 'UNDEF'
-        dtool_config["HAVE_XF86DGA"] = 'UNDEF'
-        dtool_config["HAVE_XCURSOR"] = 'UNDEF'
         dtool_config["HAVE_GLX"] = 'UNDEF'
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
@@ -2357,9 +2339,6 @@ def WriteConfigSettings():
     if (PkgSkip("PYTHON") != 0):
         dtool_config["HAVE_ROCKET_PYTHON"] = 'UNDEF'
 
-    if (PkgSkip("TOUCHINPUT") == 0 and GetTarget() == "windows"):
-        dtool_config["HAVE_WIN_TOUCHINPUT"] = '1'
-
     if (GetOptimize() <= 3):
         dtool_config["HAVE_ROCKET_DEBUGGER"] = '1'
 
@@ -2373,9 +2352,6 @@ def WriteConfigSettings():
     if (GetOptimize() <= 3):
         dtool_config["DO_COLLISION_RECORDING"] = '1'
 
-    #if (GetOptimize() <= 2):
-    #    dtool_config["TRACK_IN_INTERPRETER"] = '1'
-
     if (GetOptimize() <= 3):
         dtool_config["DO_MEMORY_USAGE"] = '1'
 
@@ -2790,8 +2766,13 @@ if (GetTarget() == 'darwin'):
     # OpenAL is not yet working well on OSX for us, so let's do this for now.
     configprc = configprc.replace("p3openal_audio", "p3fmod_audio")
 
-ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc)
-ConditionalWriteFile(GetOutputDir()+"/etc/Confauto.prc", confautoprc)
+if GetTarget() == 'windows':
+    # Convert to Windows newlines.
+    ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc, newline='\r\n')
+    ConditionalWriteFile(GetOutputDir()+"/etc/Confauto.prc", confautoprc, newline='\r\n')
+else:
+    ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc)
+    ConditionalWriteFile(GetOutputDir()+"/etc/Confauto.prc", confautoprc)
 
 ##########################################################################################
 #
@@ -2888,22 +2869,75 @@ if tp_dir is not None:
 
     if GetTarget() == 'windows':
         CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/")
-        if not PkgSkip("PYTHON"):
-            pydll = "/" + SDK["PYTHONVERSION"].replace(".", "")
-            if (GetOptimize() <= 2): pydll += "_d.dll"
-            else: pydll += ".dll"
-            CopyFile(GetOutputDir() + "/bin" + pydll, SDK["PYTHON"] + pydll)
-
-            for fn in glob.glob(SDK["PYTHON"] + "/vcruntime*.dll"):
-                CopyFile(GetOutputDir() + "/bin/", fn)
-
-            if not RTDIST:
-                CopyTree(GetOutputDir() + "/python", SDK["PYTHON"])
-                if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"):
-                    CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe")
-                if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"):
-                    CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe")
-                ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n")
+
+        if not PkgSkip("PYTHON") and not RTDIST:
+            #XXX rdb I don't think we need to copy over the Python DLL, do we?
+            #pydll = "/" + SDK["PYTHONVERSION"].replace(".", "")
+            #if (GetOptimize() <= 2): pydll += "_d.dll"
+            #else: pydll += ".dll"
+            #CopyFile(GetOutputDir() + "/bin" + pydll, SDK["PYTHON"] + pydll)
+
+            #for fn in glob.glob(SDK["PYTHON"] + "/vcruntime*.dll"):
+            #    CopyFile(GetOutputDir() + "/bin/", fn)
+
+            # Copy the whole Python directory.
+            CopyTree(GetOutputDir() + "/python", SDK["PYTHON"])
+
+            # NB: Python does not always ship with the correct manifest/dll.
+            # Figure out the correct one to ship, and grab it from WinSxS dir.
+            manifest = GetOutputDir() + '/tmp/python.manifest'
+            if os.path.isfile(manifest):
+                os.unlink(manifest)
+            oscmd('mt -inputresource:"%s\\python.exe";#1 -out:"%s" -nologo' % (SDK["PYTHON"], manifest), True)
+
+            if os.path.isfile(manifest):
+                import xml.etree.ElementTree as ET
+                tree = ET.parse(manifest)
+                idents = tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity')
+            else:
+                idents = ()
+
+            for ident in tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity'):
+                sxs_name = '_'.join([
+                    ident.get('processorArchitecture'),
+                    ident.get('name').lower(),
+                    ident.get('publicKeyToken'),
+                    ident.get('version'),
+                ])
+
+                # Find the manifest matching these parameters.
+                pattern = os.path.join('C:' + os.sep, 'Windows', 'WinSxS', 'Manifests', sxs_name + '_*.manifest')
+                manifests = glob.glob(pattern)
+                if not manifests:
+                    print("%sWARNING:%s Could not locate manifest %s.  You may need to reinstall the Visual C++ Redistributable." % (GetColor("red"), GetColor(), pattern))
+                    continue
+
+                CopyFile(GetOutputDir() + "/python/" + ident.get('name') + ".manifest", manifests[0])
+
+                # Also copy the corresponding msvcr dll.
+                pattern = os.path.join('C:' + os.sep, 'Windows', 'WinSxS', sxs_name + '_*', 'msvcr*.dll')
+                for file in glob.glob(pattern):
+                    CopyFile(GetOutputDir() + "/python/", file)
+
+            # Copy python.exe to ppython.exe.
+            if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"):
+                CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe")
+            if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"):
+                CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe")
+            ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n")
+
+# Copy over the MSVC runtime.
+if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
+    vcver = SDK["VISUALSTUDIO_VERSION"].replace('.', '')
+    crtname = "Microsoft.VC%s.CRT" % (vcver)
+    dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "redist", GetTargetArch(), crtname)
+
+    if os.path.isfile(os.path.join(dir, "msvcr" + vcver + ".dll")):
+        CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "msvcr" + vcver + ".dll"))
+    if os.path.isfile(os.path.join(dir, "msvcp" + vcver + ".dll")):
+        CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "msvcp" + vcver + ".dll"))
+    if os.path.isfile(os.path.join(dir, "vcruntime" + vcver + ".dll")):
+        CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "vcruntime" + vcver + ".dll"))
 
 ########################################################################
 ##
@@ -2911,8 +2945,14 @@ if tp_dir is not None:
 ##
 ########################################################################
 
-CopyFile(GetOutputDir()+"/", "doc/LICENSE")
-CopyFile(GetOutputDir()+"/", "doc/ReleaseNotes")
+if GetTarget() == 'windows':
+    # Convert to Windows newlines so they can be opened by notepad.
+    WriteFile(GetOutputDir() + "/LICENSE", ReadFile("doc/LICENSE"), newline='\r\n')
+    WriteFile(GetOutputDir() + "/ReleaseNotes", ReadFile("doc/ReleaseNotes"), newline='\r\n')
+else:
+    CopyFile(GetOutputDir()+"/", "doc/LICENSE")
+    CopyFile(GetOutputDir()+"/", "doc/ReleaseNotes")
+
 if (PkgSkip("PANDATOOL")==0):
     CopyAllFiles(GetOutputDir()+"/plugins/",  "pandatool/src/scripts/", ".mel")
     CopyAllFiles(GetOutputDir()+"/plugins/",  "pandatool/src/scripts/", ".ms")
@@ -3301,6 +3341,7 @@ if (not RUNTIME):
   TargetAdd('interrogate.exe', input='libp3cppParser.ilb')
   TargetAdd('interrogate.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('interrogate.exe', input='libp3interrogatedb.dll')
+  TargetAdd('interrogate.exe', input='libp3pystub.lib')
   TargetAdd('interrogate.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
   TargetAdd('interrogate_module_interrogate_module.obj', opts=OPTS, input='interrogate_module.cxx')
@@ -3308,6 +3349,7 @@ if (not RUNTIME):
   TargetAdd('interrogate_module.exe', input='libp3cppParser.ilb')
   TargetAdd('interrogate_module.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('interrogate_module.exe', input='libp3interrogatedb.dll')
+  TargetAdd('interrogate_module.exe', input='libp3pystub.lib')
   TargetAdd('interrogate_module.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
   if (not RTDIST):
@@ -3316,6 +3358,7 @@ if (not RUNTIME):
     TargetAdd('parse_file.exe', input='libp3cppParser.ilb')
     TargetAdd('parse_file.exe', input=COMMON_DTOOL_LIBS)
     TargetAdd('parse_file.exe', input='libp3interrogatedb.dll')
+    TargetAdd('parse_file.exe', input='libp3pystub.lib')
     TargetAdd('parse_file.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
 #
@@ -3339,6 +3382,7 @@ if (not RTDIST and not RUNTIME):
   TargetAdd('test_interrogate.exe', input='test_interrogate_test_interrogate.obj')
   TargetAdd('test_interrogate.exe', input='libp3interrogatedb.dll')
   TargetAdd('test_interrogate.exe', input=COMMON_DTOOL_LIBS)
+  TargetAdd('test_interrogate.exe', input='libp3pystub.lib')
   TargetAdd('test_interrogate.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
 #
@@ -4070,8 +4114,20 @@ if (not RUNTIME):
 #
 
 if (PkgSkip("VISION") == 0) and (not RUNTIME):
+  # We want to know whether we have ffmpeg so that we can override the .avi association.
+  if not PkgSkip("FFMPEG"):
+    DefSymbol("OPENCV", "HAVE_FFMPEG")
+  if not PkgSkip("OPENCV"):
+    DefSymbol("OPENCV", "HAVE_OPENCV")
+    if OPENCV_VER_23:
+        DefSymbol("OPENCV", "OPENCV_VER_23")
+
   OPTS=['DIR:panda/src/vision', 'BUILDING:VISION', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG', 'EXCEPTIONS']
-  TargetAdd('p3vision_composite1.obj', opts=OPTS, input='p3vision_composite1.cxx')
+  TargetAdd('p3vision_composite1.obj', opts=OPTS, input='p3vision_composite1.cxx', dep=[
+    'dtool_have_ffmpeg.dat',
+    'dtool_have_opencv.dat',
+    'dtool_have_directcam.dat',
+  ])
 
   TargetAdd('libp3vision.dll', input='p3vision_composite1.obj')
   TargetAdd('libp3vision.dll', input=COMMON_PANDA_LIBS)
@@ -4260,8 +4316,15 @@ if (PkgSkip("VRPN")==0 and not RUNTIME):
 # DIRECTORY: panda/src/ffmpeg
 #
 if PkgSkip("FFMPEG") == 0 and not RUNTIME:
+  if not PkgSkip("SWSCALE"):
+    DefSymbol("FFMPEG", "HAVE_SWSCALE")
+  if not PkgSkip("SWRESAMPLE"):
+    DefSymbol("FFMPEG", "HAVE_SWRESAMPLE")
+
   OPTS=['DIR:panda/src/ffmpeg', 'BUILDING:FFMPEG', 'FFMPEG', 'SWSCALE', 'SWRESAMPLE']
-  TargetAdd('p3ffmpeg_composite1.obj', opts=OPTS, input='p3ffmpeg_composite1.cxx')
+  TargetAdd('p3ffmpeg_composite1.obj', opts=OPTS, input='p3ffmpeg_composite1.cxx', dep=[
+    'dtool_have_swscale.dat', 'dtool_have_swresample.dat'])
+
   TargetAdd('libp3ffmpeg.dll', input='p3ffmpeg_composite1.obj')
   TargetAdd('libp3ffmpeg.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libp3ffmpeg.dll', opts=OPTS)
@@ -4425,8 +4488,17 @@ if (not RUNTIME):
 #
 
 if (not RUNTIME):
-  OPTS=['DIR:panda/src/framework', 'BUILDING:FRAMEWORK']
-  TargetAdd('p3framework_composite1.obj', opts=OPTS, input='p3framework_composite1.cxx')
+  deps = []
+  # Framework wants to link in a renderer when building statically, so tell it what is available.
+  if GetLinkAllStatic():
+    deps = ['dtool_have_gl.dat', 'dtool_have_tinydisplay.dat']
+    if not PkgSkip("GL"):
+      DefSymbol("FRAMEWORK", "HAVE_GL")
+    if not PkgSkip("TINYDISPLAY"):
+      DefSymbol("FRAMEWORK", "HAVE_TINYDISPLAY")
+
+  OPTS=['DIR:panda/src/framework', 'BUILDING:FRAMEWORK', 'FRAMEWORK']
+  TargetAdd('p3framework_composite1.obj', opts=OPTS, input='p3framework_composite1.cxx', dep=deps)
   TargetAdd('libp3framework.dll', input='p3framework_composite1.obj')
   TargetAdd('libp3framework.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libp3framework.dll', opts=['ADVAPI'])
@@ -4514,7 +4586,7 @@ if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("GL")==0 and PkgSkip("X
   TargetAdd('libpandagl.dll', input='p3glgsg_glgsg.obj')
   TargetAdd('libpandagl.dll', input='p3glxdisplay_composite1.obj')
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'X11'])
 
 #
 # DIRECTORY: panda/src/cocoadisplay/
@@ -4532,7 +4604,7 @@ if (GetTarget() == 'darwin' and PkgSkip("COCOA")==0 and PkgSkip("GL")==0 and not
   if (PkgSkip('PANDAFX')==0):
     TargetAdd('libpandagl.dll', input='libpandafx.dll')
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA'])
+  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA', 'CARBON'])
 
 #
 # DIRECTORY: panda/src/osxdisplay/
@@ -4589,7 +4661,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0 and not RUNTI
   TargetAdd('libpandagles.dll', input='p3glesgsg_glesgsg.obj')
   TargetAdd('libpandagles.dll', input='pandagles_egldisplay_composite1.obj')
   TargetAdd('libpandagles.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagles.dll', opts=['MODULE', 'GLES', 'EGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+  TargetAdd('libpandagles.dll', opts=['MODULE', 'GLES', 'EGL', 'X11'])
 
 #
 # DIRECTORY: panda/src/egldisplay/
@@ -4607,7 +4679,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0 and not RUNT
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='pandagles2_egldisplay_composite1.obj')
   TargetAdd('libpandagles2.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagles2.dll', opts=['MODULE', 'GLES2', 'EGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+  TargetAdd('libpandagles2.dll', opts=['MODULE', 'GLES2', 'EGL', 'X11'])
 
 #
 # DIRECTORY: panda/src/ode/
@@ -4903,7 +4975,7 @@ if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0)
     TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
   else:
     TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
-    TargetAdd('libp3tinydisplay.dll', opts=['X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+    TargetAdd('libp3tinydisplay.dll', opts=['X11'])
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite2.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_ztriangle_1.obj')
@@ -4921,7 +4993,7 @@ if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/directbase', 'PYTHON']
   TargetAdd('p3directbase_directbase.obj', opts=OPTS+['BUILDING:DIRECT'], input='directbase.cxx')
 
-  if (PkgSkip("PYTHON")==0 and not RTDIST and not RUNTIME):
+  if not PkgSkip("PYTHON") and not RTDIST and not RUNTIME and not PkgSkip("DIRECTSCRIPTS"):
     DefSymbol("BUILDING:PACKPANDA", "IMPORT_MODULE", "direct.directscripts.packpanda")
     TargetAdd('packpanda.obj', opts=OPTS+['BUILDING:PACKPANDA'], input='ppython.cxx')
     TargetAdd('packpanda.exe', input='packpanda.obj')
@@ -5080,7 +5152,7 @@ if (PkgSkip("DIRECT")==0):
   TargetAdd('direct.pyd', input='libp3direct.dll')
   TargetAdd('direct.pyd', input='libp3interrogatedb.dll')
   TargetAdd('direct.pyd', input=COMMON_PANDA_LIBS)
-  TargetAdd('direct.pyd', opts=['PYTHON', 'OPENSSL', 'WINUSER', 'WINGDI'])
+  TargetAdd('direct.pyd', opts=['PYTHON', 'OPENSSL', 'WINUSER', 'WINGDI', 'WINSOCK2'])
 
 #
 # DIRECTORY: direct/src/dcparse/
@@ -5096,6 +5168,7 @@ if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME
   TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
   TargetAdd('p3dcparse.exe', input='libp3direct.dll')
   TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
+  TargetAdd('p3dcparse.exe', input='libp3pystub.lib')
   TargetAdd('p3dcparse.exe', opts=['ADVAPI', 'PYTHON'])
 
 #
@@ -6005,8 +6078,11 @@ if (PkgSkip("PANDATOOL")==0):
 #
 
 if (PkgSkip("PANDATOOL")==0):
+    if not PkgSkip("FCOLLADA"):
+        DefSymbol("FCOLLADA", "HAVE_FCOLLADA")
+
     OPTS=['DIR:pandatool/src/ptloader', 'DIR:pandatool/src/flt', 'DIR:pandatool/src/lwo', 'DIR:pandatool/src/xfile', 'DIR:pandatool/src/xfileegg', 'DIR:pandatool/src/daeegg', 'BUILDING:PTLOADER', 'FCOLLADA']
-    TargetAdd('p3ptloader_config_ptloader.obj', opts=OPTS, input='config_ptloader.cxx')
+    TargetAdd('p3ptloader_config_ptloader.obj', opts=OPTS, input='config_ptloader.cxx', dep='dtool_have_fcollada.dat')
     TargetAdd('p3ptloader_loaderFileTypePandatool.obj', opts=OPTS, input='loaderFileTypePandatool.cxx')
     TargetAdd('libp3ptloader.dll', input='p3ptloader_config_ptloader.obj')
     TargetAdd('libp3ptloader.dll', input='p3ptloader_loaderFileTypePandatool.obj')
@@ -6776,9 +6852,9 @@ def MakeInstallerLinux():
         txt = txt.replace("VERSION", DEBVERSION).replace("ARCH", pkg_arch).replace("PV", PV).replace("MAJOR", MAJOR_VERSION)
         txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024))
         oscmd("mkdir --mode=0755 -p targetroot/DEBIAN")
-        oscmd("cd targetroot ; (find usr -type f -exec md5sum {} \;) >  DEBIAN/md5sums")
+        oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums")
         if (not RUNTIME):
-          oscmd("cd targetroot ; (find etc -type f -exec md5sum {} \;) >> DEBIAN/md5sums")
+          oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums")
           WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n")
         WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n")
         oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm")
@@ -6806,7 +6882,7 @@ def MakeInstallerLinux():
 
         if RUNTIME:
             # The runtime doesn't export any useful symbols, so just query the dependencies.
-            oscmd("cd targetroot; %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
+            oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
             depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip()
             recommends = ""
         else:
@@ -6814,12 +6890,12 @@ def MakeInstallerLinux():
             pkg_dir = "debian/panda3d" + MAJOR_VERSION
 
             # Generate a symbols file so that other packages can know which symbols we export.
-            oscmd("cd targetroot; dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals())
+            oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals())
 
             # Library dependencies are required, binary dependencies are recommended.
             # We explicitly exclude libphysx-extras since we don't want to depend on PhysX.
-            oscmd("cd targetroot; LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals())
-            oscmd("cd targetroot; LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals())
+            oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals())
+            oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals())
 
             # Parse the substvars files generated by dpkg-shlibdeps.
             depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
@@ -7181,6 +7257,11 @@ try:
             MakeInstallerFreeBSD()
         else:
             exit("Do not know how to make an installer for this platform")
+
+    if WHEEL:
+        ProgressOutput(100.0, "Building wheel")
+        from makewheel import makewheel
+        makewheel(WHLVERSION, GetOutputDir())
 finally:
     SaveDependencyCache()
 

+ 51 - 6
makepanda/makepandacore.py

@@ -396,10 +396,16 @@ def CrossCompiling():
     return GetTarget() != GetHost()
 
 def GetCC():
-    return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc')
+    if TARGET == 'darwin':
+        return os.environ.get('CC', TOOLCHAIN_PREFIX + 'clang')
+    else:
+        return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc')
 
 def GetCXX():
-    return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++')
+    if TARGET == 'darwin':
+        return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'clang++')
+    else:
+        return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++')
 
 def GetStrip():
     # Hack
@@ -512,6 +518,7 @@ def oscmd(cmd, ignoreError = False):
             exit("Cannot find "+exe+" on search path")
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
     else:
+        cmd = cmd.replace(';', '\\;')
         res = subprocess.call(cmd, shell=True)
         sig = res & 0x7F
         if (GetVerbose() and res != 0):
@@ -966,7 +973,12 @@ def ReadBinaryFile(wfile):
         ex = sys.exc_info()[1]
         exit("Cannot read %s: %s" % (wfile, ex))
 
-def WriteFile(wfile, data):
+def WriteFile(wfile, data, newline=None):
+    if newline is not None:
+        data = data.replace('\r\n', '\n')
+        data = data.replace('\r', '\n')
+        data = data.replace('\n', newline)
+
     try:
         dsthandle = open(wfile, "w")
         dsthandle.write(data)
@@ -984,18 +996,24 @@ def WriteBinaryFile(wfile, data):
         ex = sys.exc_info()[1]
         exit("Cannot write to %s: %s" % (wfile, ex))
 
-def ConditionalWriteFile(dest, desiredcontents):
+def ConditionalWriteFile(dest, data, newline=None):
+    if newline is not None:
+        data = data.replace('\r\n', '\n')
+        data = data.replace('\r', '\n')
+        data = data.replace('\n', newline)
+
     try:
         rfile = open(dest, 'r')
         contents = rfile.read(-1)
         rfile.close()
     except:
         contents = 0
-    if contents != desiredcontents:
+
+    if contents != data:
         if VERBOSE:
             print("Writing %s" % (dest))
         sys.stdout.flush()
-        WriteFile(dest, desiredcontents)
+        WriteFile(dest, data)
 
 def DeleteVCS(dir):
     if dir == "": dir = "."
@@ -1984,6 +2002,11 @@ def SdkLocatePython(prefer_thirdparty_python=False):
          SDK["PYTHONVERSION"] = "python" + ver
          SDK["PYTHONEXEC"] = "/System/Library/Frameworks/Python.framework/Versions/" + ver + "/bin/python" + ver
 
+         # Avoid choosing the one in the thirdparty package dir.
+         PkgSetCustomLocation("PYTHON")
+         IncDirectory("PYTHON", py_fwx + "/include")
+         LibDirectory("PYTHON", "%s/usr/lib" % (SDK.get("MACOSX", "")))
+
          if sys.version[:3] != ver:
              print("Warning: building with Python %s instead of %s since you targeted a specific Mac OS X version." % (ver, sys.version[:3]))
 
@@ -2057,6 +2080,10 @@ def SdkLocateWindows(version = '7.1'):
         # Choose the latest version of the Windows 10 SDK.
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
+        # Fallback in case we can't read the registry.
+        if not platsdk or not os.path.isdir(platsdk):
+            platsdk = "C:\\Program Files (x86)\\Windows Kits\\10\\"
+
         if platsdk and os.path.isdir(platsdk):
             incdirs = glob.glob(os.path.join(platsdk, 'Include', version + '.*.*'))
             max_version = ()
@@ -2089,6 +2116,10 @@ def SdkLocateWindows(version = '7.1'):
         # We chose a specific version of the Windows 10 SDK.  Verify it exists.
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
+        # Fallback in case we can't read the registry.
+        if not platsdk or not os.path.isdir(platsdk):
+            platsdk = "C:\\Program Files (x86)\\Windows Kits\\10\\"
+
         if version.count('.') == 2:
             version += '.0'
 
@@ -2098,6 +2129,10 @@ def SdkLocateWindows(version = '7.1'):
     elif version == '8.1':
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot81")
 
+        # Fallback in case we can't read the registry.
+        if not platsdk or not os.path.isdir(platsdk):
+            platsdk = "C:\\Program Files (x86)\\Windows Kits\\8.1\\"
+
     elif version == '8.0':
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot")
 
@@ -2403,9 +2438,19 @@ def SetupVisualStudioEnviron():
     # with Visual Studio 2015 requires use of the Universal CRT.
     if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] == '14.0':
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
+
+        # Fallback in case we can't read the registry.
+        if not win_kit or not os.path.isdir(win_kit):
+            win_kit = "C:\\Program Files (x86)\\Windows Kits\\10\\"
+        elif not win_kit.endswith('\\'):
+            win_kit += '\\'
+
         AddToPathEnv("LIB", win_kit + "Lib\\10.0.10150.0\\ucrt\\" + arch)
         AddToPathEnv("INCLUDE", win_kit + "Include\\10.0.10150.0\\ucrt")
 
+        # Copy the DLLs to the bin directory.
+        CopyAllFiles(GetOutputDir() + "/bin/", win_kit + "Redist\\ucrt\\DLLs\\" + arch + "\\")
+
 ########################################################################
 #
 # Include and Lib directories.

+ 598 - 0
makepanda/makewheel.py

@@ -0,0 +1,598 @@
+"""
+Generates a wheel (.whl) file from the output of makepanda.
+
+Since the wheel requires special linking, this will only work if compiled with
+the `--wheel` parameter.
+
+Please keep this file work with Panda3D 1.9 until that reaches EOL.
+"""
+from __future__ import print_function, unicode_literals
+from distutils.util import get_platform
+import json
+
+import sys
+import os
+from os.path import join
+import shutil
+import zipfile
+import hashlib
+import tempfile
+import subprocess
+from distutils.sysconfig import get_config_var
+from optparse import OptionParser
+from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose
+from base64 import urlsafe_b64encode
+
+
+default_platform = get_platform()
+
+if default_platform.startswith("linux-"):
+    # Is this manylinux1?
+    if os.path.isfile("/lib/libc-2.5.so") and os.path.isdir("/opt/python"):
+        default_platform = default_platform.replace("linux", "manylinux1")
+
+
+def get_abi_tag():
+    if sys.version_info >= (3, 0):
+        soabi = get_config_var('SOABI')
+        if soabi and soabi.startswith('cpython-'):
+            return 'cp' + soabi.split('-')[1]
+        elif soabi:
+            return soabi.replace('.', '_').replace('-', '_')
+
+    soabi = 'cp%d%d' % (sys.version_info[:2])
+
+    debug_flag = get_config_var('Py_DEBUG')
+    if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
+        soabi += 'd'
+
+    malloc_flag = get_config_var('WITH_PYMALLOC')
+    if malloc_flag is None or malloc_flag:
+        soabi += 'm'
+
+    if sys.version_info < (3, 3):
+        usize = get_config_var('Py_UNICODE_SIZE')
+        if (usize is None and sys.maxunicode == 0x10ffff) or usize == 4:
+            soabi += 'u'
+
+    return soabi
+
+
+def is_exe_file(path):
+    return os.path.isfile(path) and path.lower().endswith('.exe')
+
+
+def is_elf_file(path):
+    base = os.path.basename(path)
+    return os.path.isfile(path) and '.' not in base and \
+           open(path, 'rb').read(4) == b'\x7FELF'
+
+
+def is_mach_o_file(path):
+    base = os.path.basename(path)
+    return os.path.isfile(path) and '.' not in base and \
+           open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA',
+                                        b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
+                                        b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE')
+
+def is_fat_file(path):
+    return os.path.isfile(path) and \
+           open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA')
+
+
+if sys.platform in ('win32', 'cygwin'):
+    is_executable = is_exe_file
+elif sys.platform == 'darwin':
+    is_executable = is_mach_o_file
+else:
+    is_executable = is_elf_file
+
+
+# Other global parameters
+PY_VERSION = "cp{0}{1}".format(*sys.version_info)
+ABI_TAG = get_abi_tag()
+EXCLUDE_EXT = [".pyc", ".pyo", ".N", ".prebuilt", ".xcf", ".plist", ".vcproj", ".sln"]
+
+# Plug-ins to install.
+PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "pandadx9", "p3tinydisplay", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio"]
+
+WHEEL_DATA = """Wheel-Version: 1.0
+Generator: makepanda
+Root-Is-Purelib: false
+Tag: {0}-{1}-{2}
+"""
+
+METADATA = {
+    "license": "BSD",
+    "name": "Panda3D",
+    "metadata_version": "2.0",
+    "generator": "makepanda",
+    "summary": "Panda3D is a game engine, a framework for 3D rendering and "
+               "game development for Python and C++ programs.",
+    "extensions": {
+        "python.details": {
+            "project_urls": {
+                "Home": "https://www.panda3d.org/"
+            },
+            "document_names": {
+                "license": "LICENSE.txt"
+            },
+            "contacts": [
+                {
+                    "role": "author",
+                    "email": "[email protected]",
+                    "name": "Panda3D Team"
+                }
+            ]
+        }
+    },
+    "classifiers": [
+        "Development Status :: 5 - Production/Stable",
+        "Intended Audience :: Developers",
+        "Intended Audience :: End Users/Desktop",
+        "License :: OSI Approved :: BSD License",
+        "Operating System :: OS Independent",
+        "Programming Language :: C++",
+        "Programming Language :: Python",
+        "Topic :: Games/Entertainment",
+        "Topic :: Multimedia",
+        "Topic :: Multimedia :: Graphics",
+        "Topic :: Multimedia :: Graphics :: 3D Rendering"
+    ]
+}
+
+PANDA3D_TOOLS_INIT = """import os, sys
+import panda3d
+
+if sys.platform in ('win32', 'cygwin'):
+    path_var = 'PATH'
+elif sys.platform == 'darwin':
+    path_var = 'DYLD_LIBRARY_PATH'
+else:
+    path_var = 'LD_LIBRARY_PATH'
+
+dir = os.path.dirname(panda3d.__file__)
+del panda3d
+if not os.environ.get(path_var):
+    os.environ[path_var] = dir
+else:
+    os.environ[path_var] = dir + os.pathsep + os.environ[path_var]
+
+del os, sys, path_var, dir
+
+
+def _exec_tool(tool):
+    import os, sys
+    from subprocess import Popen
+    tools_dir = os.path.dirname(__file__)
+    handle = Popen(sys.argv, executable=os.path.join(tools_dir, tool))
+    try:
+        try:
+            return handle.wait()
+        except KeyboardInterrupt:
+            # Give the program a chance to handle the signal gracefully.
+            return handle.wait()
+    except:
+        handle.kill()
+        handle.wait()
+        raise
+
+# Register all the executables in this directory as global functions.
+{0}
+"""
+
+
+def parse_dependencies_windows(data):
+    """ Parses the given output from dumpbin /dependents to determine the list
+    of dll's this executable file depends on. """
+
+    lines = data.splitlines()
+    li = 0
+    while li < len(lines):
+        line = lines[li]
+        li += 1
+        if line.find(' has the following dependencies') != -1:
+            break
+
+    if li < len(lines):
+        line = lines[li]
+        if line.strip() == '':
+            # Skip a blank line.
+            li += 1
+
+    # Now we're finding filenames, until the next blank line.
+    filenames = []
+    while li < len(lines):
+        line = lines[li]
+        li += 1
+        line = line.strip()
+        if line == '':
+            # We're done.
+            return filenames
+        filenames.append(line)
+
+    # At least we got some data.
+    return filenames
+
+
+def parse_dependencies_unix(data):
+    """ Parses the given output from otool -XL or ldd to determine the list of
+    libraries this executable file depends on. """
+
+    lines = data.splitlines()
+    filenames = []
+    for l in lines:
+        l = l.strip()
+        if l != "statically linked":
+            filenames.append(l.split(' ', 1)[0])
+    return filenames
+
+
+def scan_dependencies(pathname):
+    """ Checks the named file for DLL dependencies, and adds any appropriate
+    dependencies found into pluginDependencies and dependentFiles. """
+
+    if sys.platform == "darwin":
+        command = ['otool', '-XL', pathname]
+    elif sys.platform in ("win32", "cygwin"):
+        command = ['dumpbin', '/dependents', pathname]
+    else:
+        command = ['ldd', pathname]
+
+    process = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode:
+        raise subprocess.CalledProcessError(retcode, command[0], output=output)
+    filenames = None
+
+    if sys.platform in ("win32", "cygwin"):
+        filenames = parse_dependencies_windows(output)
+    else:
+        filenames = parse_dependencies_unix(output)
+
+    if filenames is None:
+        sys.exit("Unable to determine dependencies from %s" % (pathname))
+
+    if sys.platform == "darwin" and len(filenames) > 0:
+        # Filter out the library ID.
+        if os.path.basename(filenames[0]).split('.', 1)[0] == os.path.basename(pathname).split('.', 1)[0]:
+            del filenames[0]
+
+    return filenames
+
+
+class WheelFile(object):
+    def __init__(self, name, version, platform):
+        self.name = name
+        self.version = version
+        self.platform = platform
+
+        wheel_name = "{0}-{1}-{2}-{3}-{4}.whl".format(
+            name, version, PY_VERSION, ABI_TAG, platform)
+
+        print("Writing %s" % (wheel_name))
+        self.zip_file = zipfile.ZipFile(wheel_name, 'w', zipfile.ZIP_DEFLATED)
+        self.records = []
+
+        # Used to locate dependency libraries.
+        self.lib_path = []
+        self.dep_paths = {}
+
+    def consider_add_dependency(self, target_path, dep, search_path=None):
+        """Considers adding a dependency library.
+        Returns the target_path if it was added, which may be different from
+        target_path if it was already added earlier, or None if it wasn't."""
+
+        if dep in self.dep_paths:
+            # Already considered this.
+            return self.dep_paths[dep]
+
+        self.dep_paths[dep] = None
+
+        if dep.lower().startswith("python") or os.path.basename(dep).startswith("libpython"):
+            # Don't include the Python library.
+            return
+
+        if sys.platform == "darwin" and dep.endswith(".so"):
+            # Temporary hack for 1.9, which had link deps on modules.
+            return
+
+        source_path = None
+
+        if search_path is None:
+            search_path = self.lib_path
+
+        for lib_dir in search_path:
+            # Ignore static stuff.
+            path = os.path.join(lib_dir, dep)
+            if os.path.isfile(path):
+                source_path = os.path.normpath(path)
+                break
+
+        if not source_path:
+            # Couldn't find library in the panda3d lib dir.
+            #print("Ignoring %s" % (dep))
+            return
+
+        self.dep_paths[dep] = target_path
+        self.write_file(target_path, source_path)
+        return target_path
+
+    def write_file(self, target_path, source_path):
+        """Adds the given file to the .whl file."""
+
+        # If this is a .so file, we should set the rpath appropriately.
+        temp = None
+        ext = os.path.splitext(source_path)[1]
+        if ext in ('.so', '.dylib') or '.so.' in os.path.basename(source_path) or \
+            (not ext and is_executable(source_path)):
+            # Scan and add Unix dependencies.
+            deps = scan_dependencies(source_path)
+            for dep in deps:
+                # Only include dependencies with relative path.  Otherwise we
+                # end up overwriting system files like /lib/ld-linux.so.2!
+                # Yes, it happened to me.
+                if '/' not in dep:
+                    target_dep = os.path.dirname(target_path) + '/' + dep
+                    self.consider_add_dependency(target_dep, dep)
+
+            suffix = ''
+            if '.so' in os.path.basename(source_path):
+                suffix = '.so'
+            elif ext == '.dylib':
+                suffix = '.dylib'
+
+            temp = tempfile.NamedTemporaryFile(suffix=suffix, prefix='whl', delete=False)
+
+            # On macOS, if no fat wheel was requested, extract the right architecture.
+            if sys.platform == "darwin" and is_fat_file(source_path) and not self.platform.endswith("_intel"):
+                if self.platform.endswith("_x86_64"):
+                    arch = 'x86_64'
+                else:
+                    arch = self.platform.split('_')[-1]
+                subprocess.call(['lipo', source_path, '-extract', arch, '-output', temp.name])
+            else:
+                # Otherwise, just copy it over.
+                temp.write(open(source_path, 'rb').read())
+
+            temp.write(open(source_path, 'rb').read())
+            os.fchmod(temp.fileno(), os.fstat(temp.fileno()).st_mode | 0o111)
+            temp.close()
+
+            # Fix things like @loader_path/../lib references
+            if sys.platform == "darwin":
+                loader_path = [os.path.dirname(source_path)]
+                for dep in deps:
+                    if '@loader_path' not in dep:
+                        continue
+
+                    dep_path = dep.replace('@loader_path', '.')
+                    target_dep = os.path.dirname(target_path) + '/' + os.path.basename(dep)
+                    target_dep = self.consider_add_dependency(target_dep, dep_path, loader_path)
+                    if not target_dep:
+                        # It won't be included, so no use adjusting the path.
+                        continue
+
+                    new_dep = os.path.join('@loader_path', os.path.relpath(target_dep, os.path.dirname(target_path)))
+                    subprocess.call(["install_name_tool", "-change", dep, new_dep, temp.name])
+            else:
+                subprocess.call(["strip", "-s", temp.name])
+                subprocess.call(["patchelf", "--set-rpath", "$ORIGIN", temp.name])
+
+            source_path = temp.name
+
+        ext = ext.lower()
+        if ext in ('.dll', '.pyd', '.exe'):
+            # Scan and add Win32 dependencies.
+            for dep in scan_dependencies(source_path):
+                target_dep = os.path.dirname(target_path) + '/' + dep
+                self.consider_add_dependency(target_dep, dep)
+
+        # Calculate the SHA-256 hash and size.
+        sha = hashlib.sha256()
+        fp = open(source_path, 'rb')
+        size = 0
+        data = fp.read(1024 * 1024)
+        while data:
+            size += len(data)
+            sha.update(data)
+            data = fp.read(1024 * 1024)
+        fp.close()
+
+        # Save it in PEP-0376 format for writing out later.
+        digest = str(urlsafe_b64encode(sha.digest()))
+        digest = digest.rstrip('=')
+        self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size))
+
+        if GetVerbose():
+            print("Adding %s from %s" % (target_path, source_path))
+        self.zip_file.write(source_path, target_path)
+
+        #if temp:
+        #    os.unlink(temp.name)
+
+    def write_file_data(self, target_path, source_data):
+        """Adds the given file from a string."""
+
+        sha = hashlib.sha256()
+        sha.update(source_data.encode())
+        digest = str(urlsafe_b64encode(sha.digest()))
+        digest = digest.rstrip('=')
+        self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, len(source_data)))
+
+        if GetVerbose():
+            print("Adding %s from data" % target_path)
+        self.zip_file.writestr(target_path, source_data)
+
+    def write_directory(self, target_dir, source_dir):
+        """Adds the given directory recursively to the .whl file."""
+
+        for root, dirs, files in os.walk(source_dir):
+            for file in files:
+                if os.path.splitext(file)[1] in EXCLUDE_EXT:
+                    continue
+
+                source_path = os.path.join(root, file)
+                target_path = os.path.join(target_dir, os.path.relpath(source_path, source_dir))
+                target_path = target_path.replace('\\', '/')
+                self.write_file(target_path, source_path)
+
+    def close(self):
+        # Write the RECORD file.
+        record_file = "{0}-{1}.dist-info/RECORD".format(self.name, self.version)
+        self.records.append(record_file + ",,\n")
+
+        self.zip_file.writestr(record_file, "".join(self.records))
+        self.zip_file.close()
+
+
+def makewheel(version, output_dir, platform=default_platform):
+    if sys.platform not in ("win32", "darwin") and not sys.platform.startswith("cygwin"):
+        if not LocateBinary("patchelf"):
+            raise Exception("patchelf is required when building a Linux wheel.")
+
+    platform = platform.replace('-', '_').replace('.', '_')
+
+    # Global filepaths
+    panda3d_dir = join(output_dir, "panda3d")
+    pandac_dir = join(output_dir, "pandac")
+    direct_dir = join(output_dir, "direct")
+    models_dir = join(output_dir, "models")
+    etc_dir = join(output_dir, "etc")
+    bin_dir = join(output_dir, "bin")
+    if sys.platform == "win32":
+        libs_dir = join(output_dir, "bin")
+    else:
+        libs_dir = join(output_dir, "lib")
+    license_src = "LICENSE"
+    readme_src = "README.md"
+
+    # Update relevant METADATA entries
+    METADATA['version'] = version
+    version_classifiers = [
+        "Programming Language :: Python :: {0}".format(*sys.version_info),
+        "Programming Language :: Python :: {0}.{1}".format(*sys.version_info),
+    ]
+    METADATA['classifiers'].extend(version_classifiers)
+
+    # Build out the metadata
+    details = METADATA["extensions"]["python.details"]
+    homepage = details["project_urls"]["Home"]
+    author = details["contacts"][0]["name"]
+    email = details["contacts"][0]["email"]
+    metadata = ''.join([
+        "Metadata-Version: {metadata_version}\n" \
+        "Name: {name}\n" \
+        "Version: {version}\n" \
+        "Summary: {summary}\n" \
+        "License: {license}\n".format(**METADATA),
+        "Home-page: {0}\n".format(homepage),
+        "Author: {0}\n".format(author),
+        "Author-email: {0}\n".format(email),
+        "Platform: {0}\n".format(platform),
+    ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']])
+
+    # Zip it up and name it the right thing
+    whl = WheelFile('panda3d', version, platform)
+    whl.lib_path = [libs_dir]
+
+    # Add the trees with Python modules.
+    whl.write_directory('direct', direct_dir)
+
+    # Write the panda3d tree.  We use a custom empty __init__ since the
+    # default one adds the bin directory to the PATH, which we don't have.
+    whl.write_file_data('panda3d/__init__.py', '')
+
+    ext_suffix = GetExtensionSuffix()
+
+    for file in os.listdir(panda3d_dir):
+        if file == '__init__.py':
+            pass
+        elif file.endswith(ext_suffix) or file.endswith('.py'):
+            source_path = os.path.join(panda3d_dir, file)
+
+            if file.endswith('.pyd') and platform.startswith('cygwin'):
+                # Rename it to .dll for cygwin Python to be able to load it.
+                target_path = 'panda3d/' + os.path.splitext(file)[0] + '.dll'
+            else:
+                target_path = 'panda3d/' + file
+
+            whl.write_file(target_path, source_path)
+
+    # Add plug-ins.
+    for lib in PLUGIN_LIBS:
+        plugin_name = 'lib' + lib
+        if sys.platform in ('win32', 'cygwin'):
+            plugin_name += '.dll'
+        elif sys.platform == 'darwin':
+            plugin_name += '.dylib'
+        else:
+            plugin_name += '.so'
+        plugin_path = os.path.join(libs_dir, plugin_name)
+        if os.path.isfile(plugin_path):
+            whl.write_file('panda3d/' + plugin_name, plugin_path)
+
+    # Add the .data directory, containing additional files.
+    data_dir = 'panda3d-{0}.data'.format(version)
+    #whl.write_directory(data_dir + '/data/etc', etc_dir)
+    #whl.write_directory(data_dir + '/data/models', models_dir)
+
+    # Actually, let's not.  That seems to install the files to the strangest
+    # places in the user's filesystem.  Let's instead put them in panda3d.
+    whl.write_directory('panda3d/etc', etc_dir)
+    whl.write_directory('panda3d/models', models_dir)
+
+    # Add the pandac tree for backward compatibility.
+    for file in os.listdir(pandac_dir):
+        if file.endswith('.py'):
+            whl.write_file('pandac/' + file, os.path.join(pandac_dir, file))
+
+    # Add a panda3d-tools directory containing the executables.
+    entry_points = '[console_scripts]\n'
+    entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n'
+    entry_points += 'packpanda = direct.directscripts.packpanda:main\n'
+    tools_init = ''
+    for file in os.listdir(bin_dir):
+        basename = os.path.splitext(file)[0]
+        if basename in ('eggcacher', 'packpanda'):
+            continue
+
+        source_path = os.path.join(bin_dir, file)
+
+        if is_executable(source_path):
+            # Put the .exe files inside the panda3d-tools directory.
+            whl.write_file('panda3d_tools/' + file, source_path)
+
+            # Tell pip to create a wrapper script.
+            funcname = basename.replace('-', '_')
+            entry_points += '{0} = panda3d_tools:{1}\n'.format(basename, funcname)
+            tools_init += '{0} = lambda: _exec_tool({1!r})\n'.format(funcname, file)
+
+    whl.write_file_data('panda3d_tools/__init__.py', PANDA3D_TOOLS_INIT.format(tools_init))
+
+    # Add the dist-info directory last.
+    info_dir = 'panda3d-{0}.dist-info'.format(version)
+    whl.write_file_data(info_dir + '/entry_points.txt', entry_points)
+    whl.write_file_data(info_dir + '/metadata.json', json.dumps(METADATA, indent=4, separators=(',', ': ')))
+    whl.write_file_data(info_dir + '/METADATA', metadata)
+    whl.write_file_data(info_dir + '/WHEEL', WHEEL_DATA.format(PY_VERSION, ABI_TAG, platform))
+    whl.write_file(info_dir + '/LICENSE.txt', license_src)
+    whl.write_file(info_dir + '/README.md', readme_src)
+    whl.write_file_data(info_dir + '/top_level.txt', 'direct\npanda3d\npandac\npanda3d_tools\n')
+
+    whl.close()
+
+
+if __name__ == "__main__":
+    version = ParsePandaVersion("dtool/PandaVersion.pp")
+
+    parser = OptionParser()
+    parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version)
+    parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built')
+    parser.add_option('', '--verbose', dest = 'verbose', help = 'Enable verbose output', action = 'store_true', default = False)
+    parser.add_option('', '--platform', dest = 'platform', help = 'Override platform tag (default: %s)' % (default_platform), default = get_platform())
+    (options, args) = parser.parse_args()
+
+    SetVerbose(options.verbose)
+    makewheel(options.version, options.outputdir, options.platform)

+ 0 - 8
panda/.gitignore

@@ -1,8 +0,0 @@
-*.pyc
-*.pyo
-# These are files that are generated within the source tree by the
-# ppremake system.
-Makefile
-pp.dep
-/built/
-Opt?-*

+ 1 - 1
panda/src/audiotraits/openalAudioManager.h

@@ -23,7 +23,7 @@
 #include "reMutex.h"
 
 // OSX uses the OpenAL framework
-#ifdef IS_OSX
+#ifdef HAVE_OPENAL_FRAMEWORK
   #include <OpenAL/al.h>
   #include <OpenAL/alc.h>
 #else

+ 1 - 1
panda/src/audiotraits/openalAudioSound.h

@@ -21,7 +21,7 @@
 #include "openalAudioManager.h"
 
 // OSX uses the OpenAL framework
-#ifdef IS_OSX
+#ifdef HAVE_OPENAL_FRAMEWORK
   #include <OpenAL/al.h>
   #include <OpenAL/alc.h>
 #else

+ 28 - 0
panda/src/bullet/bulletBodyNode.cxx

@@ -36,6 +36,34 @@ BulletBodyNode(const char *name) : PandaNode(name) {
   set_into_collide_mask(CollideMask::all_on());
 }
 
+/**
+ *
+ */
+BulletBodyNode::
+BulletBodyNode(const BulletBodyNode &copy) :
+  PandaNode(copy),
+  _shapes(copy._shapes)
+{
+  if (copy._shape && copy._shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) {
+    // btCompoundShape does not define a copy constructor.  Manually copy.
+    btCompoundShape *shape = new btCompoundShape;
+    _shape = shape;
+
+    btCompoundShape *copy_shape = (btCompoundShape *)copy._shape;
+    int num_children = copy_shape->getNumChildShapes();
+    for (int i = 0; i < num_children; ++i) {
+      shape->addChildShape(copy_shape->getChildTransform(i),
+                           copy_shape->getChildShape(i));
+    }
+  }
+  else if (copy._shape && copy._shape->getShapeType() == EMPTY_SHAPE_PROXYTYPE) {
+    _shape = new btEmptyShape();
+  }
+  else {
+    _shape = copy._shape;
+  }
+}
+
 /**
  * Returns the subset of CollideMask bits that may be set for this particular
  * type of PandaNode.  For BodyNodes this returns all bits on.

Some files were not shown because too many files changed in this diff