1# Copyright 2004-2008 Roman Yakovenko.
2# Distributed under the Boost Software License, Version 1.0. (See
3# accompanying file LICENSE_1_0.txt or copy at
4# http://www.boost.org/LICENSE_1_0.txt)
5
6import os
7from . import custom
8from . import license
9from . import include
10from . import compound
11from . import namespace
12from . import algorithm
13from . import module_body
14from . import library_reference
15from . import declaration_based
16from . import include_directories
17from pygccxml.utils import utils
18
19
20
21class module_t(compound.compound_t):
22    """This class represents the source code for the entire extension module.
23
24    The root of the code creator tree is always a module_t object.
25    """
26    def __init__(self, global_ns, code_generator_type):
27        """Constructor.
28        """
29        compound.compound_t.__init__(self)
30        self.__global_ns = global_ns
31        self._code_generator = code_generator_type
32
33    @property
34    def global_ns(self):
35        "reference to global_ns ( namespace_t ) declaration"
36        return self.__global_ns
37
38    def _get_license( self ):
39        if isinstance( self.creators[0], license.license_t ):
40            return self.creators[0]
41        return None
42
43    def _set_license( self, license_text ):
44        if not isinstance( license_text, license.license_t ):
45            license_inst = license.license_t( license_text )
46        if isinstance( self.creators[0], license.license_t ):
47            self.remove_creator( self.creators[0] )
48        self.adopt_creator( license_inst, 0 )
49    license = property( _get_license, _set_license,
50                        doc="""License text.
51
52                        The license text will always be the first children node.
53                        @type: str or :class:`code_creators.license_t`""")
54
55    def _get_system_files_impl( self ):
56        return []
57
58    @utils.cached
59    def specially_exposed_decls(self):
60        """list of exposed declarations, which were not ``included``, but still
61        were exposed. For example, std containers.
62        """
63        decls = set()
64        #select all declaration based code creators
65        ccs = [cc for cc in algorithm.make_flatten_list( self ) if isinstance( cc, declaration_based.declaration_based_t )]
66        #leave only "ignored"
67        ccs = [cc for cc in ccs if cc.declaration.ignore == True]
68
69        decls = [cc.declaration for cc in ccs]
70
71        return set( decls )
72
73    def update_documentation( self, doc_extractor ):
74        if not doc_extractor:
75            return
76        visited = set()
77        for cc in algorithm.make_flatten( self ):
78            if not isinstance( cc, declaration_based.declaration_based_t ):
79                continue
80            if id( cc.declaration ) in visited:
81                continue
82            cc.declaration.documentation = doc_extractor( cc.declaration )
83            visited.add( id( cc.declaration ) )
84
85class bpmodule_t(module_t):
86    """This class represents the source code for the entire extension module.
87
88    The root of the code creator tree is always a module_t object.
89    """
90    def __init__(self, global_ns):
91        """Constructor.
92        """
93        module_t.__init__(self, global_ns, bpmodule_t.CODE_GENERATOR_TYPES.BOOST_PYTHON)
94        self.__body = None
95
96    def _get_include_dirs(self):
97        include_dirs = algorithm.creator_finder.find_by_class_instance(
98            what=include_directories.include_directories_t
99            , where=self.creators
100            , recursive=False)
101        if 0 == len( include_dirs ):
102            include_dirs = include_directories.include_directories_t()
103            if self.license:
104                self.adopt_creator( include_dirs, 1 )
105            else:
106                self.adopt_creator( include_dirs, 0 )
107            return include_dirs
108        elif 1 == len( include_dirs ):
109            return include_dirs[0]
110        else:
111            assert not "only single instance of include_directories_t should exist"
112
113    def _get_std_directories(self):
114        include_dirs = self._get_include_dirs()
115        return include_dirs.std
116    std_directories = property( _get_std_directories )
117
118    def _get_user_defined_directories(self):
119        include_dirs = self._get_include_dirs()
120        return include_dirs.user_defined
121    user_defined_directories = property( _get_user_defined_directories )
122
123    @property
124    def body(self):
125        """Return reference to :class:`code_creators.module_body_t` code creator"""
126        if None is self.__body:
127            found = algorithm.creator_finder.find_by_class_instance( what=module_body.module_body_t
128                                                                    , where=self.creators
129                                                                    , recursive=False )
130            if found:
131                self.__body = found[0]
132        return self.__body
133
134    def last_include_index(self):
135        """
136        return the children index of the last :class:`code_creators.include_t` object.
137
138        An exception is raised when there is no include_t object among
139        the children creators.
140
141        :rtype: int
142        """
143        for i in range( len(self.creators) - 1, -1, -1 ):
144            if isinstance( self.creators[i], include.include_t ):
145                return i
146        else:
147            return 0
148
149    def replace_included_headers( self, headers, leave_system_headers=True ):
150        to_be_removed = []
151        for creator in self.creators:
152            if isinstance( creator, include.include_t ):
153                to_be_removed.append( creator )
154            elif isinstance( creator, module_body.module_body_t ):
155                break
156
157        for creator in to_be_removed:
158            if creator.is_system:
159                if not leave_system_headers:
160                    self.remove_creator( creator )
161            elif creator.is_user_defined:
162                pass
163            else:
164                self.remove_creator( creator )
165        for header in headers:
166            self.adopt_include( include.include_t( header=header ) )
167
168    def adopt_include(self, include_creator):
169        """Insert an :class:`code_creators.include_t` object.
170
171        The include creator is inserted right after the last include file.
172
173        :param include_creator: Include creator object
174        :type include_creator: :class:`code_creators.include_t`
175        """
176        lii = self.last_include_index()
177        if lii == 0:
178            if not self.creators:
179                lii = -1
180            elif not isinstance( self.creators[0], include.include_t ):
181                lii = -1
182            else:
183                pass
184        self.adopt_creator( include_creator, lii + 1 )
185
186    def do_include_dirs_optimization(self):
187        include_dirs = self._get_include_dirs()
188        includes = [creator for creator in self.creators if isinstance( creator, include.include_t )]
189        for include_creator in includes:
190            include_creator.include_dirs_optimization = include_dirs
191
192    def _create_impl(self):
193        self.do_include_dirs_optimization()
194        index = 0
195        code = []
196        for index in range( len( self.creators ) ):
197            if not isinstance( self.creators[index], include.include_t ):
198                break
199            else:
200                code.append( self.creators[index].create() )
201        if code:
202            code.append( 2* os.linesep )
203        code.append( self.create_internal_code( self.creators[index:], indent_code=False ))
204        code.append( os.linesep )
205        return os.linesep.join( code )
206
207    def add_include( self, header, user_defined=True, system=False ):
208        creator = include.include_t( header=header, user_defined=user_defined, system=system )
209        self.adopt_include( creator )
210
211    def add_namespace_usage( self, namespace_name ):
212        self.adopt_creator( namespace.namespace_using_t( namespace_name )
213                            , self.last_include_index() + 1 )
214
215    def add_namespace_alias( self, alias, full_namespace_name ):
216        self.adopt_creator( namespace.namespace_alias_t(
217                                alias=alias
218                                , full_namespace_name=full_namespace_name )
219                            , self.last_include_index() + 1 )
220
221    def adopt_declaration_creator( self, creator ):
222        self.adopt_creator( creator, self.creators.index( self.body ) )
223
224    def add_declaration_code( self, code, position ):
225        self.adopt_declaration_creator( custom.custom_text_t( code ) )
226
227
228
229class ctypes_module_t(module_t):
230    """This class represents the source code for the entire extension module.
231
232    The root of the code creator tree is always a module_t object.
233    """
234    def __init__(self, global_ns):
235        """Constructor.
236        """
237        module_t.__init__(self, global_ns, ctypes_module_t.CODE_GENERATOR_TYPES.CTYPES)
238        self.treat_char_ptr_as_binary_data = False
239
240    def _create_impl(self):
241        return self.create_internal_code( self.creators, indent_code=False )
242
243    @utils.cached
244    def library_var_name(self):
245        for creator in self.creators:
246            if isinstance( creator, library_reference.library_reference_t ):
247                return creator.library_var_name
248        else:
249            raise RuntimeError( "Internal Error: library_reference_t creator was not created" )
250