scons/swig: refactor some of the scons/SWIG code
authorSteve Reinhardt <steve.reinhardt@amd.com>
Thu, 20 Oct 2011 20:08:49 +0000 (13:08 -0700)
committerSteve Reinhardt <steve.reinhardt@amd.com>
Thu, 20 Oct 2011 20:08:49 +0000 (13:08 -0700)
- Move the random bits of SWIG code generation out of src/SConscript
  file and into methods on the objects being wrapped.
- Cleaned up some variable naming and added some comments to make
  the process a little clearer.
- Did a little generated file/module renaming:
   - vptype_Foo now Foo_vector
   - init_Foo is now Foo_init
  This makes it easier to see all the Foo-related files in a
  sorted directory listing.
- Made cxx_predecls and swig_predecls normal SimObject classmethods.
- Got rid of swig_objdecls hook, even though this breaks the System
  objects get/setMemoryMode method exports.  Will be fixing this in
  a future changeset.

src/SConscript
src/python/m5/SimObject.py
src/python/m5/params.py
src/sim/System.py

index b4bfb61fd54b468a79ddd568375ab3501d5c5135..7c6bcd8462f0270c7bf93378f7e97e3804aa163e 100755 (executable)
@@ -449,7 +449,13 @@ sys.meta_path.remove(importer)
 sim_objects = m5.SimObject.allClasses
 all_enums = m5.params.allEnums
 
-all_params = {}
+# Find param types that need to be explicitly wrapped with swig.
+# These will be recognized because the ParamDesc will have a
+# swig_decl() method.  Most param types are based on types that don't
+# need this, either because they're based on native types (like Int)
+# or because they're SimObjects (which get swigged independently).
+# For now the only things handled here are VectorParam types.
+params_to_swig = {}
 for name,obj in sorted(sim_objects.iteritems()):
     for param in obj._params.local.values():
         # load the ptype attribute now because it depends on the
@@ -461,8 +467,8 @@ for name,obj in sorted(sim_objects.iteritems()):
         if not hasattr(param, 'swig_decl'):
             continue
         pname = param.ptype_str
-        if pname not in all_params:
-            all_params[pname] = param
+        if pname not in params_to_swig:
+            params_to_swig[pname] = param
 
 ########################################################################
 #
@@ -523,24 +529,23 @@ PySource('m5', 'python/m5/info.py')
 # Create all of the SimObject param headers and enum headers
 #
 
-def createSimObjectParam(target, source, env):
+def createSimObjectParamStruct(target, source, env):
     assert len(target) == 1 and len(source) == 1
 
     name = str(source[0].get_contents())
     obj = sim_objects[name]
 
     code = code_formatter()
-    obj.cxx_decl(code)
+    obj.cxx_param_decl(code)
     code.write(target[0].abspath)
 
-def createSwigParam(target, source, env):
+def createParamSwigWrapper(target, source, env):
     assert len(target) == 1 and len(source) == 1
 
     name = str(source[0].get_contents())
-    param = all_params[name]
+    param = params_to_swig[name]
 
     code = code_formatter()
-    code('%module(package="m5.internal") $0_${name}', param.file_ext)
     param.swig_decl(code)
     code.write(target[0].abspath)
 
@@ -554,7 +559,7 @@ def createEnumStrings(target, source, env):
     obj.cxx_def(code)
     code.write(target[0].abspath)
 
-def createEnumParam(target, source, env):
+def createEnumDecls(target, source, env):
     assert len(target) == 1 and len(source) == 1
 
     name = str(source[0].get_contents())
@@ -564,25 +569,25 @@ def createEnumParam(target, source, env):
     obj.cxx_decl(code)
     code.write(target[0].abspath)
 
-def createEnumSwig(target, source, env):
+def createEnumSwigWrapper(target, source, env):
     assert len(target) == 1 and len(source) == 1
 
     name = str(source[0].get_contents())
     obj = all_enums[name]
 
     code = code_formatter()
-    code('''\
-%module(package="m5.internal") enum_$name
+    obj.swig_decl(code)
+    code.write(target[0].abspath)
 
-%{
-#include "enums/$name.hh"
-%}
+def createSimObjectSwigWrapper(target, source, env):
+    name = source[0].get_contents()
+    obj = sim_objects[name]
 
-%include "enums/$name.hh"
-''')
+    code = code_formatter()
+    obj.swig_decl(code)
     code.write(target[0].abspath)
 
-# Generate all of the SimObject param struct header files
+# Generate all of the SimObject param C++ struct header files
 params_hh_files = []
 for name,simobj in sorted(sim_objects.iteritems()):
     py_source = PySource.modules[simobj.__module__]
@@ -591,16 +596,16 @@ for name,simobj in sorted(sim_objects.iteritems()):
     hh_file = File('params/%s.hh' % name)
     params_hh_files.append(hh_file)
     env.Command(hh_file, Value(name),
-                MakeAction(createSimObjectParam, Transform("SO PARAM")))
+                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
     env.Depends(hh_file, depends + extra_deps)
 
-# Generate any parameter header files needed
+# Generate any needed param SWIG wrapper files
 params_i_files = []
-for name,param in all_params.iteritems():
-    i_file = File('python/m5/internal/%s_%s.i' % (param.file_ext, name))
+for name,param in params_to_swig.iteritems():
+    i_file = File('python/m5/internal/%s.i' % (param.swig_module_name()))
     params_i_files.append(i_file)
     env.Command(i_file, Value(name),
-                MakeAction(createSwigParam, Transform("SW PARAM")))
+                MakeAction(createParamSwigWrapper, Transform("SW PARAM")))
     env.Depends(i_file, depends)
     SwigSource('m5.internal', i_file)
 
@@ -617,54 +622,22 @@ for name,enum in sorted(all_enums.iteritems()):
 
     hh_file = File('enums/%s.hh' % name)
     env.Command(hh_file, Value(name),
-                MakeAction(createEnumParam, Transform("EN PARAM")))
+                MakeAction(createEnumDecls, Transform("ENUMDECL")))
     env.Depends(hh_file, depends + extra_deps)
 
     i_file = File('python/m5/internal/enum_%s.i' % name)
     env.Command(i_file, Value(name),
-                MakeAction(createEnumSwig, Transform("ENUMSWIG")))
+                MakeAction(createEnumSwigWrapper, Transform("ENUMSWIG")))
     env.Depends(i_file, depends + extra_deps)
     SwigSource('m5.internal', i_file)
 
-def buildParam(target, source, env):
-    name = source[0].get_contents()
-    obj = sim_objects[name]
-    class_path = obj.cxx_class.split('::')
-    classname = class_path[-1]
-    namespaces = class_path[:-1]
-    params = obj._params.local.values()
-
-    code = code_formatter()
-
-    code('%module(package="m5.internal") param_$name')
-    code()
-    code('%{')
-    code('#include "params/$obj.hh"')
-    for param in params:
-        param.cxx_predecls(code)
-    code('%}')
-    code()
-
-    for param in params:
-        param.swig_predecls(code)
-
-    code()
-    if obj._base:
-        code('%import "python/m5/internal/param_${{obj._base}}.i"')
-    code()
-    obj.swig_objdecls(code)
-    code()
-
-    code('%include "params/$obj.hh"')
-
-    code.write(target[0].abspath)
-
+# Generate SimObject SWIG wrapper files
 for name in sim_objects.iterkeys():
-    params_file = File('python/m5/internal/param_%s.i' % name)
-    env.Command(params_file, Value(name),
-                MakeAction(buildParam, Transform("BLDPARAM")))
-    env.Depends(params_file, depends)
-    SwigSource('m5.internal', params_file)
+    i_file = File('python/m5/internal/param_%s.i' % name)
+    env.Command(i_file, Value(name),
+                MakeAction(createSimObjectSwigWrapper, Transform("SO SWIG")))
+    env.Depends(i_file, depends)
+    SwigSource('m5.internal', i_file)
 
 # Generate the main swig init file
 def makeEmbeddedSwigInit(target, source, env):
@@ -687,7 +660,7 @@ for swig in SwigSource.all:
                 MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
                 '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
     cc_file = str(swig.tnode)
-    init_file = '%s/init_%s.cc' % (dirname(cc_file), basename(cc_file))
+    init_file = '%s/%s_init.cc' % (dirname(cc_file), basename(cc_file))
     env.Command(init_file, Value(swig.module),
                 MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
     Source(init_file, **swig.guards)
index 9729fd30fe4243e7668870cda76b587424864389..6d393273d578ab095933c256217692ad737bda2d 100644 (file)
@@ -97,37 +97,6 @@ allClasses = {}
 # dict to look up SimObjects based on path
 instanceDict = {}
 
-def default_cxx_predecls(cls, code):
-    code('#include "params/$cls.hh"')
-
-def default_swig_predecls(cls, code):
-    code('%import "python/m5/internal/param_$cls.i"')
-
-def default_swig_objdecls(cls, code):
-    class_path = cls.cxx_class.split('::')
-    classname = class_path[-1]
-    namespaces = class_path[:-1]
-
-    for ns in namespaces:
-        code('namespace $ns {')
-
-    if namespaces:
-        code('// avoid name conflicts')
-        sep_string = '_COLONS_'
-        flat_name = sep_string.join(class_path)
-        code('%rename($flat_name) $classname;')
-
-    code()
-    code('// stop swig from creating/wrapping default ctor/dtor')
-    code('%nodefault $classname;')
-    code('class $classname')
-    if cls._base:
-        code('    : public ${{cls._base.cxx_class}}')
-    code('{};')
-
-    for ns in reversed(namespaces):
-        code('} // namespace $ns')
-
 def public_value(key, value):
     return key.startswith('_') or \
                isinstance(value, (FunctionType, MethodType, ModuleType,
@@ -142,9 +111,6 @@ class MetaSimObject(type):
     init_keywords = { 'abstract' : bool,
                       'cxx_class' : str,
                       'cxx_type' : str,
-                      'cxx_predecls'  : MethodType,
-                      'swig_objdecls' : MethodType,
-                      'swig_predecls' : MethodType,
                       'type' : str }
     # Attributes that can be set any time
     keywords = { 'check' : FunctionType }
@@ -223,18 +189,6 @@ class MetaSimObject(type):
                 cls._value_dict['cxx_class'] = cls._value_dict['type']
 
             cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class']
-                
-            if 'cxx_predecls' not in cls.__dict__:
-                m = MethodType(default_cxx_predecls, cls, MetaSimObject)
-                setattr(cls, 'cxx_predecls', m)
-
-            if 'swig_predecls' not in cls.__dict__:
-                m = MethodType(default_swig_predecls, cls, MetaSimObject)
-                setattr(cls, 'swig_predecls', m)
-
-        if 'swig_objdecls' not in cls.__dict__:
-            m = MethodType(default_swig_objdecls, cls, MetaSimObject)
-            setattr(cls, 'swig_objdecls', m)
 
         # Now process the _value_dict items.  They could be defining
         # new (or overriding existing) parameters or ports, setting
@@ -378,8 +332,76 @@ class MetaSimObject(type):
     def __str__(cls):
         return cls.__name__
 
-    def cxx_decl(cls, code):
-        # The 'dict' attribute restricts us to the params declared in
+    # See ParamValue.cxx_predecls for description.
+    def cxx_predecls(cls, code):
+        code('#include "params/$cls.hh"')
+
+    # See ParamValue.swig_predecls for description.
+    def swig_predecls(cls, code):
+        code('%import "python/m5/internal/param_$cls.i"')
+
+    # Generate the declaration for this object for wrapping with SWIG.
+    # Generates code that goes into a SWIG .i file.  Called from
+    # src/SConscript.
+    def swig_decl(cls, code):
+        class_path = cls.cxx_class.split('::')
+        classname = class_path[-1]
+        namespaces = class_path[:-1]
+
+        # The 'local' attribute restricts us to the params declared in
+        # the object itself, not including inherited params (which
+        # will also be inherited from the base class's param struct
+        # here).
+        params = cls._params.local.values()
+
+        code('%module(package="m5.internal") param_$cls')
+        code()
+        code('%{')
+        code('#include "params/$cls.hh"')
+        for param in params:
+            param.cxx_predecls(code)
+        code('%}')
+        code()
+
+        for param in params:
+            param.swig_predecls(code)
+
+        code()
+        if cls._base:
+            code('%import "python/m5/internal/param_${{cls._base}}.i"')
+        code()
+
+        for ns in namespaces:
+            code('namespace $ns {')
+
+        if namespaces:
+            code('// avoid name conflicts')
+            sep_string = '_COLONS_'
+            flat_name = sep_string.join(class_path)
+            code('%rename($flat_name) $classname;')
+
+        if cls == SimObject:
+            code('%include "python/swig/sim_object.i"')
+        else:
+            code()
+            code('// stop swig from creating/wrapping default ctor/dtor')
+            code('%nodefault $classname;')
+            code('class $classname')
+            if cls._base:
+                code('    : public ${{cls._base.cxx_class}}')
+            code('{};')
+
+        for ns in reversed(namespaces):
+            code('} // namespace $ns')
+
+        code()
+        code('%include "params/$cls.hh"')
+
+
+    # Generate the C++ declaration (.hh file) for this SimObject's
+    # param struct.  Called from src/SConscript.
+    def cxx_param_decl(cls, code):
+        # The 'local' attribute restricts us to the params declared in
         # the object itself, not including inherited params (which
         # will also be inherited from the base class's param struct
         # here).
@@ -421,65 +443,29 @@ class MetaSimObject(type):
                 code('#include "enums/${{ptype.__name__}}.hh"')
                 code()
 
-        cls.cxx_struct(code, cls._base, params)
-
-        code()
-        code('#endif // __PARAMS__${cls}__')
-        return code
-
-    def cxx_struct(cls, code, base, params):
+        # now generate the actual param struct
         if cls == SimObject:
             code('#include "sim/sim_object_params.hh"')
-            return
-
-        # now generate the actual param struct
-        code("struct ${cls}Params")
-        if base:
-            code("    : public ${{base.type}}Params")
-        code("{")
-        if not hasattr(cls, 'abstract') or not cls.abstract:
-            if 'type' in cls.__dict__:
-                code("    ${{cls.cxx_type}} create();")
-
-        code.indent()
-        for param in params:
-            param.cxx_decl(code)
-        code.dedent()
-        code('};')
-
-    def swig_decl(cls, code):
-        code('''\
-%module $cls
-
-%{
-#include "params/$cls.hh"
-%}
-
-''')
-
-        # The 'dict' attribute restricts us to the params declared in
-        # the object itself, not including inherited params (which
-        # will also be inherited from the base class's param struct
-        # here).
-        params = cls._params.local.values()
-        ptypes = [p.ptype for p in params]
+        else:
+            code("struct ${cls}Params")
+            if cls._base:
+                code("    : public ${{cls._base.type}}Params")
+            code("{")
+            if not hasattr(cls, 'abstract') or not cls.abstract:
+                if 'type' in cls.__dict__:
+                    code("    ${{cls.cxx_type}} create();")
+
+            code.indent()
+            for param in params:
+                param.cxx_decl(code)
+            code.dedent()
+            code('};')
 
-        # get all predeclarations
-        for param in params:
-            param.swig_predecls(code)
         code()
+        code('#endif // __PARAMS__${cls}__')
+        return code
 
-        if cls._base:
-            code('%import "python/m5/internal/param_${{cls._base.type}}.i"')
-            code()
 
-        for ptype in ptypes:
-            if issubclass(ptype, Enum):
-                code('%import "enums/${{ptype.__name__}}.hh"')
-                code()
-
-        code('%import "params/${cls}_type.hh"')
-        code('%include "params/${cls}.hh"')
 
 # The SimObject class is the root of the special hierarchy.  Most of
 # the code in this class deals with the configuration hierarchy itself
@@ -491,10 +477,6 @@ class SimObject(object):
     type = 'SimObject'
     abstract = True
 
-    @classmethod
-    def swig_objdecls(cls, code):
-        code('%include "python/swig/sim_object.i"')
-
     # Initialize new instance.  For objects with SimObject-valued
     # children, we need to recursively clone the classes represented
     # by those param values as well in a consistent "deep copy"-style
index ee94ae004bc15d3b27cd6aed37d6aee63a13e959..87fc25131d1a8533fa79a438a3cb4d6603c95c23 100644 (file)
@@ -81,10 +81,17 @@ class MetaParamValue(type):
 class ParamValue(object):
     __metaclass__ = MetaParamValue
 
+
+    # Generate the code needed as a prerequisite for declaring a C++
+    # object of this type.  Typically generates one or more #include
+    # statements.  Used when declaring parameters of this type.
     @classmethod
     def cxx_predecls(cls, code):
         pass
 
+    # Generate the code needed as a prerequisite for including a
+    # reference to a C++ object of this type in a SWIG .i file.
+    # Typically generates one or more %import or %include statements.
     @classmethod
     def swig_predecls(cls, code):
         pass
@@ -101,8 +108,6 @@ class ParamValue(object):
 
 # Regular parameter description.
 class ParamDesc(object):
-    file_ext = 'ptype'
-
     def __init__(self, ptype_str, ptype, *args, **kwargs):
         self.ptype_str = ptype_str
         # remember ptype only if it is provided
@@ -223,8 +228,6 @@ class SimObjectVector(VectorParamValue):
                 yield obj
 
 class VectorParamDesc(ParamDesc):
-    file_ext = 'vptype'
-
     # Convert assigned value to appropriate type.  If the RHS is not a
     # list or tuple, it generates a single-element list.
     def convert(self, value):
@@ -240,10 +243,14 @@ class VectorParamDesc(ParamDesc):
         else:
             return VectorParamValue(tmp_list)
 
+    def swig_module_name(self):
+        return "%s_vector" % self.ptype_str
+
     def swig_predecls(self, code):
-        code('%import "vptype_${{self.ptype_str}}.i"')
+        code('%import "${{self.swig_module_name()}}.i"')
 
     def swig_decl(self, code):
+        code('%module(package="m5.internal") ${{self.swig_module_name()}}')
         code('%{')
         self.ptype.cxx_predecls(code)
         code('%}')
@@ -1043,6 +1050,19 @@ namespace Enums {
 } // namespace Enums
 ''')
 
+    def swig_decl(cls, code):
+        name = cls.__name__
+        code('''\
+%module(package="m5.internal") enum_$name
+
+%{
+#include "enums/$name.hh"
+%}
+
+%include "enums/$name.hh"
+''')
+
+
 # Base class for enum types.
 class Enum(ParamValue):
     __metaclass__ = MetaEnum
index a6897d8344b25ab58d6470cc0ed098c3bfd6b191..1a69db95ff971a1703f2481e735207657ad1a52c 100644 (file)
@@ -40,6 +40,8 @@ class MemoryMode(Enum): vals = ['invalid', 'atomic', 'timing']
 class System(SimObject):
     type = 'System'
 
+    # This method is temporarily obsolete.  Its functionality will be
+    # restored in a future changeset.
     @classmethod
     def swig_objdecls(cls, code):
         code('%include "python/swig/system.i"')