2 # Base revision: 45462.
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.
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
20 __re_hyphen = re.compile ('-')
22 def __register_features ():
23 """ Register features need by this module.
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'])
33 """ Clear the module state. This is mainly for testing purposes.
34 Note that this must be called _after_ resetting the module 'feature'.
36 global __prefixes_suffixes, __suffixes_to_types, __types, __rule_names_to_types, __target_suffixes_cache
38 __register_features ()
40 # Stores suffixes for generated targets.
41 __prefixes_suffixes = [property.PropertyMap(), property.PropertyMap()]
43 # Maps suffixes to types
44 __suffixes_to_types = {}
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
53 # Caches suffixes for targets with certain properties.
54 __target_suffixes_cache = {}
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
65 suffixes: None or a sequence of strings
66 base_type: None or a string
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)
74 if __types.has_key (type):
75 raise BaseException ('Type "%s" is already registered.' % type)
78 entry ['base'] = base_type
79 entry ['derived'] = []
80 entry ['scanner'] = None
81 __types [type] = entry
84 __types.setdefault(base_type, {}).setdefault('derived', []).append(type)
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])
91 # Specify mapping from suffixes to type
92 register_suffixes (suffixes, type)
94 feature.extend('target-type', [type])
95 feature.extend('main-target-type', [type])
96 feature.extend('base-target-type', [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)
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)
108 # FIXME: resolving recursive dependency.
109 from b2.manager import get_manager
110 get_manager().projects().project_rules().add_rule_for_type(type)
113 def type_from_rule_name(rule_name):
114 return rule_name.upper().replace("-", "_")
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.
122 if __suffixes_to_types.has_key (s):
123 old_type = __suffixes_to_types [s]
125 raise BaseException ('Attempting to specify type for suffix "%s"\nOld type: "%s", New type "%s"' % (s, old_type, type))
127 __suffixes_to_types [s] = type
129 def registered (type):
130 """ Returns true iff type has been registered.
132 return __types.has_key (type)
135 """ Issues an error if 'type' is unknown.
137 if not registered (type):
138 raise BaseException ("Unknown target type '%s'" % type)
140 def set_scanner (type, scanner):
141 """ Sets a scanner class that will be used for this 'type'.
144 __types [type]['scanner'] = scanner
146 def get_scanner (type, prop_set):
147 """ Returns a scanner instance appropriate to 'type' and 'property_set'.
149 if registered (type):
150 scanner_type = __types [type]['scanner']
152 return scanner.get (scanner_type, prop_set.raw ())
158 """Returns a base type for the given type or nothing in case the given type is
161 return __types[type]['base']
163 def all_bases (type):
164 """ Returns type and all of its bases, in the order of their distance from type.
169 type = __types [type]['base']
173 def all_derived (type):
174 """ Returns type and all classes that derive from it, in the order of their distance from type.
177 for d in __types [type]['derived']:
178 result.extend (all_derived (d))
182 def is_derived (type, base):
183 """ Returns true if 'type' is 'base' or has 'base' as its direct or indirect base.
185 # TODO: this isn't very efficient, especially for bases close to type
186 if base in all_bases (type):
191 def is_subtype (type, base):
192 """ Same as is_derived. Should be removed.
194 # TODO: remove this method
195 return is_derived (type, base)
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
208 The 'suffix' parameter can be empty string ("") to indicate that
209 no suffix should be used.
211 set_generated_target_ps(1, type, properties, suffix)
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.
219 change_generated_target_ps(1, type, properties, suffix)
221 def generated_target_suffix(type, properties):
222 return generated_target_ps(1, type, properties)
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.
228 # The 'prefix' parameter can be empty string ("") to indicate that no prefix
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)
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)
241 def generated_target_prefix(type, properties):
242 return generated_target_ps(0, type, properties)
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)
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)
252 set_generated_target_ps(is_suffix, type, properties, val)
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
258 def generated_target_ps_real(is_suffix, type, properties):
262 while type and not found:
263 result = __prefixes_suffixes[is_suffix].find (['<target-type>' + type] + properties)
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:
270 type = __types [type]['base']
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.
281 key = (is_suffix, type, prop_set)
282 v = __target_suffixes_cache.get(key, None)
285 v = generated_target_ps_real(is_suffix, type, prop_set.raw())
286 __target_suffixes_cache [key] = v
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
296 filename, suffix = os.path.splitext (filename)
297 if not suffix: return None
300 if __suffixes_to_types.has_key(suffix):
301 return __suffixes_to_types[suffix]
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.
309 if registered (type):
312 if not os or os_name () in os:
313 register (type, suffixes, base_type)