Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Linker/Linker.py
    1| # $Id: Linker.py,v 1.56 2003/02/01 23:57:08 chalky Exp $
    2| #
    3| # This file is a part of Synopsis.
    4| # Copyright (C) 2000, 2001 Stefan Seefeld
    5| # Copyright (C) 2000, 2001 Stephen Davies
    6| #
    7| # Synopsis is free software; you can redistribute it and/or modify it
    8| # under the terms of the GNU General Public License as published by
    9| # the Free Software Foundation; either version 2 of the License, or
   10| # (at your option) any later version.
   11| #
   12| # This program is distributed in the hope that it will be useful,
   13| # but WITHOUT ANY WARRANTY; without even the implied warranty of
   14| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   15| # General Public License for more details.
   16| #
   17| # You should have received a copy of the GNU General Public License
   18| # along with this program; if not, write to the Free Software
   19| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   20| # 02111-1307, USA.
   21| #
   22| # $Log: Linker.py,v $
   23| # Revision 1.56  2003/02/01 23:57:08  chalky
   24| # Remove obsolete option that was blocking the real -p
   25| #
   26| # Revision 1.55  2003/02/01 05:40:48  chalky
   27| # Remove the old config_obj handlers since they're no longer used, but added
   28| # backward compat for old 'mappers' option (it's now called mapper_list).
   29| #
   30| # Revision 1.54  2003/01/20 07:00:43  chalky
   31| # Use summary comment_processor by default
   32| #
   33| # Revision 1.53  2002/10/29 06:56:52  chalky
   34| # Fixes to work on cygwin
   35| #
   36| # Revision 1.52  2002/10/28 16:30:05  chalky
   37| # Trying to fix some bugs in the unduplication/stripping stages. Needs more work
   38| #
   39| # Revision 1.51  2002/10/11 05:57:17  chalky
   40| # Support suspect comments
   41| #
   42| # Revision 1.50  2002/08/23 04:37:26  chalky
   43| # Huge refactoring of Linker to make it modular, and use a config system similar
   44| # to the HTML package
   45| #
   46| # Revision 1.49  2002/07/11 09:29:32  chalky
   47| # Huge speed improvement O(n^2) -> O(n) in unduplicator, eg: 230sec -> 9sec
   48| #
   49| # Revision 1.48  2002/01/09 11:43:41  chalky
   50| # Inheritance pics
   51| #
   52| # Revision 1.47  2001/07/26 08:21:58  chalky
   53| # Fixes bug caused by bad template support
   54| #
   55| # Revision 1.46  2001/07/19 08:11:38  chalky
   56| # Typo
   57| #
   58| # Revision 1.45  2001/07/19 07:57:52  chalky
   59| # Bug fixes
   60| #
   61| # Revision 1.44  2001/07/19 03:59:56  chalky
   62| # New .syn file format. Added EmptyNS to remove empty namespaces
   63| #
   64| # Revision 1.43  2001/06/15 18:27:08  stefan
   65| # some cleanup; debugged and refactored the various stages such as prefix stripping, unduplicating, prefixing; some renaming (we ought to put some naming conventions into place...
   66| #
   67| # Revision 1.42  2001/06/13 13:13:16  chalky
   68| # Realnames dont need mapping anymore since they are built from name()
   69| # dynamically
   70| #
   71| # Revision 1.41  2001/06/05 10:01:28  chalky
   72| # Can set (string)type of mapped decl names (eg: Package)
   73| #
   74| # Revision 1.40  2001/05/25 13:45:49  stefan
   75| # fix problem with getopt error reporting
   76| #
   77| # Revision 1.39  2001/04/17 15:47:26  chalky
   78| # Added declaration name mapper, and changed refmanual to use it instead of the
   79| # old language mapping
   80| #
   81| # Revision 1.38  2001/04/06 02:35:39  chalky
   82| # Parse config object
   83| #
   84| # Revision 1.37  2001/04/05 16:21:44  chalky
   85| # Check for type with no name, ie: dummy enumerator
   86| #
   87| # Revision 1.36  2001/03/28 12:53:32  chalky
   88| # Made module comments carry through into meta-module comments
   89| #
   90| # Revision 1.35  2001/02/13 06:55:23  chalky
   91| # Made synopsis -l work again
   92| #
   93| # Revision 1.34  2001/02/13 06:36:46  chalky
   94| # Added -a flag to strip decls with greater access
   95| #
   96| # Revision 1.33  2001/02/12 04:08:09  chalky
   97| # Added config options to HTML and Linker. Config demo has doxy and synopsis styles.
   98| #
   99| # Revision 1.32  2001/02/11 05:39:33  stefan
  100| # first try at a more powerful config framework for synopsis
  101| #
  102| # Revision 1.31  2001/02/06 17:37:20  chalky
  103| # Reorder languagize to before unduplicator to prevent module conflicts
  104| #
  105| # Revision 1.30  2001/02/06 06:54:27  chalky
  106| # Added comment processing to Linker..
  107| #
  108| 
  109| # THIS-IS-A-LINKER
  110| 
  111| import sys, getopt, os, os.path, string, types
  112| from Synopsis.Core import Util, Type, AST
  113| 
  114| from Synopsis.Core.Util import import_object
  115| 
  116| pyTypes = types
  117| del types
  118| strip = []
  119| mapperList = []
  120| verbose = 0
  121| max_access = None
  122| 
  123| def usage():
  124|     print \
  125| """
  126|   -s <scope>                           Select only the named scope.
  127|   -m <mapper>                          Use a mapper plugin to map unresolved types new names
  128|   -M <mapper>                          Use a std mapper (part of the Linker module) to map unresolved types new names.
  129|                                        Currently the C++toIDL mapper is supported
  130|   -a <access level>                   Removes all declarations 
  131|                                     access level than that given.
  132|                                     1==public only, 2==protected or public
  133|                """
  134| class Config:
  135|     """Central configuration repository for Linker."""
  136|     def __init__(self):
  137|         """Constructor - initialise objects to None."""
  138|         self.ast = None
  139|         self.strip = []
  140|         self.mapper_list = []
  141|         self.types = None # Filled in first thing in resolve()
  142|         self.max_access = None
  143|         self.map_declaration_names = None
  144|         self.map_declaration_type = 'Language'
  145|         self.operations = [
  146|             'Unduplicator', 'Stripper', 'NameMapper',
  147|             'Comments', 'EmptyNS', 'AccessRestrictor'
  148|         ] # Off by default: 'LanguageMapper', 
  149|         self.verbose = 0
  150|         self.comment_processors = ['summary']
  151|    
  152|     def use_config(self, obj):
  153|         """Extracts useful attributes from 'obj' and stores them. The object
  154|         itself is also stored as config.obj"""
  155|         # obj.pages is a list of module names
  156|         self.obj = obj
  157|         options = ('verbose', 'strip', 'mappers', 'mapper_list', 'max_access',
  158|             'operations', 'map_declaration_names', 'map_declaration_type',
  159|             'comment_processors')
  160|         for option in options:
  161|             if hasattr(obj, option):
  162|                getattr(self, '_config_'+option)(getattr(obj, option))
  163|             elif self.verbose: print "Option",option,"not found in config."
  164| 
  165|     def _config_verbose(self, verbose):
  166|         "Configures from the given verbose boolean"
  167|         self.verbose = verbose
  168|         
  169|     def _config_strip(self, strip):
  170|         "Configures from the given list of strip"
  171|         if type(strip) != pyTypes.ListType:
  172|             raise TypeError, "Linker.strip must be a list of strings."
  173|         if self.verbose: print "Using strip:", strip
  174|         self.strip = strip
  175| 
  176| 
  177|     def _config_mapper_list(self, mapper_list):
  178|         "Configures from the given list of mapper_list"
  179|         if type(mapper_list) != pyTypes.ListType:
  180|             raise TypeError, "Linker.mapper_list must be a list of strings."
  181|         if self.verbose: print "Using mapper_list:", mapper_list
  182|         for mapper in mapper_list:
  183|             if type(mapper) is pyTypes.StringType:
  184|                self.mapper_list.append(mappers[mapper]())
  185|          else:
  186|                self.mapper_list.append(getattr(Util._import(mapper[0]), mapper[1])())
  187|     
  188|     # mappers is the old config name
  189|     _config_mappers = _config_mapper_list
  190| 
  191|     def _config_operations(self, operations):
  192|         "Configures from the given list of operations"
  193|         if type(operations) != pyTypes.ListType:
  194|             raise TypeError, "Linker.operations must be a list."
  195|         if self.verbose: print "Using operations:", operations
  196|         self.operations = operations
  197| 
  198|     def _config_max_access(self, access):
  199|         if self.verbose: print "Using max_access:",access
  200|         self.max_access = access
  201| 
  202|     def _config_map_declaration_names(self, names):
  203|         if not names: return
  204|         if self.verbose: print "Using map_declaration_names:", names
  205|         self.map_declaration_names = tuple(string.split(names[0], '::'))
  206|         self.map_declaration_type = names[1]
  207| 
  208|     def _config_map_declaration_type(self, typename):
  209|         if self.verbose: print "Using map_declaration_type:", typename
  210|         self.max_declaration_type = typename
  211| 
  212|     def _config_comment_processors(self, processors):
  213|         if self.verbose: print "Using comment_processors:", processors
  214|         self.comment_processors = processors
  215| 
  216| # Instantiate a global config object in this module
  217| config = Config()
  218| 
  219| class Operation:
  220|     """The base class for Linker operations. The linker executes a number of
  221|     operations, depending on the config option 'operations'. Each operation
  222|     may use other config options"""
  223|     def execute(self):
  224|         """Executes this operation"""
  225|         pass
  226| 
  227| class CXX2IDL:
  228|     """this function maps a C++ external reference to an IDL interface if the name either
  229|     starts with 'POA_' or ends in '_ptr'"""
  230|     def __init__(self): pass
  231|     def map(self, unknown):
  232|         global verbose
  233|         import __builtin__
  234|         name = unknown.name()
  235|         language = unknown.language()
  236|         if language != "C++": return
  237|         if name[0][0:4] == "POA_":
  238|             interface = __builtin__.map(None, name)
  239|             interface[0] = interface[0][4:]
  240|             unknown.resolve("IDL", name, tuple(interface))
  241|             if verbose: print "mapping", string.join(name, "::"), "to", string.join(interface, "::")
  242|         elif name[-1][-4:] == "_ptr":
  243|             interface = __builtin__.map(None, name)
  244|             interface[-1] = interface[-1][:-4]
  245|             unknown.resolve("IDL", name, tuple(interface))
  246|             if verbose: print "mapping", string.join(name, "::"), "to", string.join(interface, "::")
  247| 
  248| mappers = {
  249|     'C++toIDL' : CXX2IDL,
  250|     }
  251| 
  252| def __parseArgs(args, config_obj):
  253|     global strip, mapperList, verbose, languagize, processor_args, max_access
  254|     global map_decl_names, map_decl_name_type
  255|     languagize = 0
  256|     processor_args = []
  257|     map_decl_names = None
  258|     map_decl_name_type = "scope"
  259| 
  260|     # Use config first
  261|     config.use_config(config_obj)
  262| 
  263|     # Now parse args
  264|     try:
  265|         opts,remainder = getopt.getopt(args, "s:m:M:vlp:a:d:")
  266|     except getopt.error, e:
  267|         sys.stderr.write("Error in arguments: " + str(e) + "\n")
  268|         sys.exit(1)
  269| 
  270|     for opt in opts:
  271|         o,a = opt
  272| 
  273|         if o == "-s": config.strip.append(a)
  274|         elif o == "-m":
  275|             config.mapper_list.append(Util._import(a))
  276|         elif o == "-M":
  277|             if mappers.has_key(a): config.mapper_list.append(mappers[a]())
  278|          else:
  279|                print "Error: Unknown mapper. Available mappers are:",string.join(mappers.keys(), ', ')
  280|                sys.exit(1)
  281|         elif o == "-v": config.verbose = 1
  282|         elif o == '-l': languagize = 1
  283|         elif o == '-a': max_access = int(a)
  284|         elif o == '-d': 
  285|             map_decl_names, map_decl_name_type = string.split(a, '=')
  286|             map_decl_names = string.split(map_decl_names, '::')
  287|         elif o == '-p':
  288|             if not hasattr(config, 'comment_processors'):
  289|                config.comment_processors = []
  290|             config.comment_processors.append(a)
  291| 
  292| class Mapper(Type.Visitor):
  293|     """allow user to supply a mapping functor that is applied to Unknown types.
  294|     This is useful for linking to externally defined types, such as when cross-
  295|     referencing modules written in different languages"""
  296|     def __init__(self, mappers):
  297|         self.__mappers = mappers
  298|     def visitUnknown(self, unknown):
  299|         for mapper in self.__mappers:
  300|             mapper.map(unknown)
  301| 
  302| 
  303| def mapTypes(m):
  304|     if len(m):
  305|         mapper = Mapper(m)
  306|         for type in types.values():
  307|             type.accept(mapper)
  308| 
  309| def resolve(args, ast, config_obj):
  310|     global types, mapperList
  311|     types = ast.types()
  312|     declarations = ast.declarations()
  313| 
  314|     config.types = ast.types()
  315| 
  316|     __parseArgs(args, config_obj)
  317| 
  318|     def_attr = 'linkerOperation'
  319|     base_path = 'Synopsis.Linker.'
  320|     for op_spec in config.operations:
  321|         oper_class = import_object(op_spec, def_attr, base_path)
  322|         oper = oper_class()
  323|         oper.execute(ast)
  324| 
  325|     # apply user supplied mapping of external types (Type.Unknown)
  326|     mapTypes(config.mapper_list)
  327|