Imported Upstream version 1.51.0
[platform/upstream/boost.git] / tools / build / v2 / build / type.py
1 # Status: ported.
2 # Base revision: 45462.
3
4 #  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
5 #  distribute this software is granted provided this copyright notice appears in
6 #  all copies. This software is provided "as is" without express or implied
7 #  warranty, and with no claim as to its suitability for any purpose.
8
9
10
11 import re
12 import os
13 import os.path
14 from b2.util.utility import replace_grist, os_name
15 from b2.exceptions import *
16 from b2.build import feature, property, scanner
17 from b2.util import bjam_signature
18
19
20 __re_hyphen = re.compile ('-')
21
22 def __register_features ():
23     """ Register features need by this module.
24     """
25     # The feature is optional so that it is never implicitly added.
26     # It's used only for internal purposes, and in all cases we
27     # want to explicitly use it.
28     feature.feature ('target-type', [], ['composite', 'optional'])
29     feature.feature ('main-target-type', [], ['optional', 'incidental'])
30     feature.feature ('base-target-type', [], ['composite', 'optional', 'free'])
31
32 def reset ():
33     """ Clear the module state. This is mainly for testing purposes.
34         Note that this must be called _after_ resetting the module 'feature'.
35     """    
36     global __prefixes_suffixes, __suffixes_to_types, __types, __rule_names_to_types, __target_suffixes_cache
37     
38     __register_features ()
39
40     # Stores suffixes for generated targets.
41     __prefixes_suffixes = [property.PropertyMap(), property.PropertyMap()]
42     
43     # Maps suffixes to types
44     __suffixes_to_types = {}
45     
46     # A map with all the registered types, indexed by the type name
47     # Each entry is a dictionary with following values:
48     # 'base': the name of base type or None if type has no base
49     # 'derived': a list of names of type which derive from this one
50     # 'scanner': the scanner class registered for this type, if any
51     __types = {}
52
53     # Caches suffixes for targets with certain properties.
54     __target_suffixes_cache = {}
55     
56 reset ()
57
58 @bjam_signature((["type"], ["suffixes", "*"], ["base_type", "?"]))
59 def register (type, suffixes = [], base_type = None):
60     """ Registers a target type, possibly derived from a 'base-type'. 
61         If 'suffixes' are provided, they list all the suffixes that mean a file is of 'type'.
62         Also, the first element gives the suffix to be used when constructing and object of
63         'type'.
64         type: a string
65         suffixes: None or a sequence of strings
66         base_type: None or a string
67     """
68     # Type names cannot contain hyphens, because when used as
69     # feature-values they will be interpreted as composite features
70     # which need to be decomposed.
71     if __re_hyphen.search (type):
72         raise BaseException ('type name "%s" contains a hyphen' % type)
73     
74     if __types.has_key (type):
75         raise BaseException ('Type "%s" is already registered.' % type)
76
77     entry = {}
78     entry ['base'] = base_type
79     entry ['derived'] = []
80     entry ['scanner'] = None
81     __types [type] = entry
82     
83     if base_type:
84         __types.setdefault(base_type, {}).setdefault('derived', []).append(type)
85
86     if len (suffixes) > 0:
87         # Generated targets of 'type' will use the first of 'suffixes'
88         # (this may be overriden)
89         set_generated_target_suffix (type, [], suffixes [0])
90         
91         # Specify mapping from suffixes to type
92         register_suffixes (suffixes, type)
93     
94     feature.extend('target-type', [type])
95     feature.extend('main-target-type', [type])
96     feature.extend('base-target-type', [type])
97
98     if base_type:
99         feature.compose ('<target-type>' + type, replace_grist (base_type, '<base-target-type>'))
100         feature.compose ('<base-target-type>' + type, '<base-target-type>' + base_type)
101
102     import b2.build.generators as generators
103     # Adding a new derived type affects generator selection so we need to
104     # make the generator selection module update any of its cached
105     # information related to a new derived type being defined.
106     generators.update_cached_information_with_a_new_type(type)
107
108     # FIXME: resolving recursive dependency.
109     from b2.manager import get_manager
110     get_manager().projects().project_rules().add_rule_for_type(type)
111
112 # FIXME: quick hack.
113 def type_from_rule_name(rule_name):
114     return rule_name.upper().replace("-", "_")
115
116
117 def register_suffixes (suffixes, type):
118     """ Specifies that targets with suffix from 'suffixes' have the type 'type'. 
119         If a different type is already specified for any of syffixes, issues an error.
120     """
121     for s in suffixes:
122         if __suffixes_to_types.has_key (s):
123             old_type = __suffixes_to_types [s]
124             if old_type != type:
125                 raise BaseException ('Attempting to specify type for suffix "%s"\nOld type: "%s", New type "%s"' % (s, old_type, type))
126         else:
127             __suffixes_to_types [s] = type
128
129 def registered (type):
130     """ Returns true iff type has been registered.
131     """
132     return __types.has_key (type)
133
134 def validate (type):
135     """ Issues an error if 'type' is unknown.
136     """
137     if not registered (type):
138         raise BaseException ("Unknown target type '%s'" % type)
139
140 def set_scanner (type, scanner):
141     """ Sets a scanner class that will be used for this 'type'.
142     """
143     validate (type)
144     __types [type]['scanner'] = scanner
145
146 def get_scanner (type, prop_set):
147     """ Returns a scanner instance appropriate to 'type' and 'property_set'.
148     """
149     if registered (type):
150         scanner_type = __types [type]['scanner']
151         if scanner_type:
152             return scanner.get (scanner_type, prop_set.raw ())
153             pass
154             
155     return None
156
157 def base(type):
158     """Returns a base type for the given type or nothing in case the given type is
159     not derived."""
160
161     return __types[type]['base']
162
163 def all_bases (type):
164     """ Returns type and all of its bases, in the order of their distance from type.
165     """
166     result = []
167     while type:
168         result.append (type)
169         type = __types [type]['base']
170
171     return result
172
173 def all_derived (type):
174     """ Returns type and all classes that derive from it, in the order of their distance from type.
175     """
176     result = [type]
177     for d in __types [type]['derived']:
178         result.extend (all_derived (d))
179
180     return result
181
182 def is_derived (type, base):
183     """ Returns true if 'type' is 'base' or has 'base' as its direct or indirect base.
184     """
185     # TODO: this isn't very efficient, especially for bases close to type
186     if base in all_bases (type):
187         return True
188     else: 
189         return False
190
191 def is_subtype (type, base):
192     """ Same as is_derived. Should be removed.
193     """
194     # TODO: remove this method
195     return is_derived (type, base)
196
197 @bjam_signature((["type"], ["properties", "*"], ["suffix"]))
198 def set_generated_target_suffix (type, properties, suffix):
199     """ Sets a target suffix that should be used when generating target 
200         of 'type' with the specified properties. Can be called with
201         empty properties if no suffix for 'type' was specified yet.
202         This does not automatically specify that files 'suffix' have
203         'type' --- two different types can use the same suffix for
204         generating, but only one type should be auto-detected for
205         a file with that suffix. User should explicitly specify which
206         one.
207
208         The 'suffix' parameter can be empty string ("") to indicate that
209         no suffix should be used.
210     """
211     set_generated_target_ps(1, type, properties, suffix)
212
213
214     
215 def change_generated_target_suffix (type, properties, suffix):
216     """ Change the suffix previously registered for this type/properties 
217         combination. If suffix is not yet specified, sets it.
218     """
219     change_generated_target_ps(1, type, properties, suffix)
220
221 def generated_target_suffix(type, properties):
222     return generated_target_ps(1, type, properties)
223
224 # Sets a target prefix that should be used when generating targets of 'type'
225 # with the specified properties. Can be called with empty properties if no
226 # prefix for 'type' has been specified yet.
227 #
228 # The 'prefix' parameter can be empty string ("") to indicate that no prefix
229 # should be used.
230 #
231 # Usage example: library names use the "lib" prefix on unix.
232 @bjam_signature((["type"], ["properties", "*"], ["suffix"]))
233 def set_generated_target_prefix(type, properties, prefix):
234     set_generated_target_ps(0, type, properties, prefix)
235
236 # Change the prefix previously registered for this type/properties combination.
237 # If prefix is not yet specified, sets it.
238 def change_generated_target_prefix(type, properties, prefix):
239     change_generated_target_ps(0, type, properties, prefix)
240
241 def generated_target_prefix(type, properties):
242     return generated_target_ps(0, type, properties)
243
244 def set_generated_target_ps(is_suffix, type, properties, val):
245     properties.append ('<target-type>' + type)
246     __prefixes_suffixes[is_suffix].insert (properties, val)
247
248 def change_generated_target_ps(is_suffix, type, properties, val):
249     properties.append ('<target-type>' + type)
250     prev = __prefixes_suffixes[is_suffix].find_replace(properties, val)
251     if not prev:
252         set_generated_target_ps(is_suffix, type, properties, val)
253
254 # Returns either prefix or suffix (as indicated by 'is_suffix') that should be used
255 # when generating a target of 'type' with the specified properties.
256 # If no prefix/suffix is specified for 'type', returns prefix/suffix for
257 # base type, if any.
258 def generated_target_ps_real(is_suffix, type, properties):
259
260     result = ''
261     found = False
262     while type and not found:
263         result = __prefixes_suffixes[is_suffix].find (['<target-type>' + type] + properties)
264
265         # Note that if the string is empty (""), but not null, we consider
266         # suffix found.  Setting prefix or suffix to empty string is fine.
267         if result is not None:
268             found = True
269
270         type = __types [type]['base']
271
272     if not result:
273         result = ''
274     return result
275
276 def generated_target_ps(is_suffix, type, prop_set):
277     """ Returns suffix that should be used when generating target of 'type',
278         with the specified properties. If not suffix were specified for
279         'type', returns suffix for base type, if any.
280     """
281     key = (is_suffix, type, prop_set)
282     v = __target_suffixes_cache.get(key, None)
283
284     if not v:
285         v = generated_target_ps_real(is_suffix, type, prop_set.raw())
286         __target_suffixes_cache [key] = v
287
288     return v
289
290 def type(filename):
291     """ Returns file type given it's name. If there are several dots in filename,
292         tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and 
293         "so"  will be tried.
294     """
295     while 1:
296         filename, suffix = os.path.splitext (filename)
297         if not suffix: return None
298         suffix = suffix[1:]
299         
300         if __suffixes_to_types.has_key(suffix):
301             return __suffixes_to_types[suffix]
302
303 # NOTE: moved from tools/types/register
304 def register_type (type, suffixes, base_type = None, os = []):
305     """ Register the given type on the specified OSes, or on remaining OSes
306         if os is not specified.  This rule is injected into each of the type
307         modules for the sake of convenience.
308     """
309     if registered (type):
310         return
311
312     if not os or os_name () in os:
313         register (type, suffixes, base_type)