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 11import re 12import os 13import os.path 14from b2.util.utility import replace_grist, os_name 15from b2.exceptions import * 16from b2.build import feature, property, scanner 17from b2.util import bjam_signature 18 19 20__re_hyphen = re.compile ('-') 21 22def __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 32def 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 56reset () 57 58@bjam_signature((["type"], ["suffixes", "*"], ["base_type", "?"])) 59def 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. 113def type_from_rule_name(rule_name): 114 return rule_name.upper().replace("-", "_") 115 116 117def 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 129def registered (type): 130 """ Returns true iff type has been registered. 131 """ 132 return __types.has_key (type) 133 134def 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 140def 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 146def 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 157def 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 163def 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 173def 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 182def 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 191def 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"])) 198def 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 215def 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 221def 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"])) 233def 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. 238def change_generated_target_prefix(type, properties, prefix): 239 change_generated_target_ps(0, type, properties, prefix) 240 241def generated_target_prefix(type, properties): 242 return generated_target_ps(0, type, properties) 243 244def set_generated_target_ps(is_suffix, type, properties, val): 245 properties.append ('<target-type>' + type) 246 __prefixes_suffixes[is_suffix].insert (properties, val) 247 248def 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. 258def 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 276def 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 290def 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 304def 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) 314