1# Status: ported. 2# Base revision: 45462 3# 4# Copyright 2003 Dave Abrahams 5# Copyright 2002, 2003, 2004, 2005 Vladimir Prus 6# Distributed under the Boost Software License, Version 1.0. 7# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 8 9# Implements scanners: objects that compute implicit dependencies for 10# files, such as includes in C++. 11# 12# Scanner has a regular expression used to find dependencies, some 13# data needed to interpret those dependencies (for example, include 14# paths), and a code which actually established needed relationship 15# between actual jam targets. 16# 17# Scanner objects are created by actions, when they try to actualize 18# virtual targets, passed to 'virtual-target.actualize' method and are 19# then associated with actual targets. It is possible to use 20# several scanners for a virtual-target. For example, a single source 21# might be used by to compile actions, with different include paths. 22# In this case, two different actual targets will be created, each 23# having scanner of its own. 24# 25# Typically, scanners are created from target type and action's 26# properties, using the rule 'get' in this module. Directly creating 27# scanners is not recommended, because it might create many equvivalent 28# but different instances, and lead in unneeded duplication of 29# actual targets. However, actions can also create scanners in a special 30# way, instead of relying on just target type. 31import property 32import bjam 33import os 34from b2.manager import get_manager 35from b2.util import is_iterable_typed 36 37 38def reset (): 39 """ Clear the module state. This is mainly for testing purposes. 40 """ 41 global __scanners, __rv_cache, __scanner_cache 42 43 # Maps registered scanner classes to relevant properties 44 __scanners = {} 45 46 # A cache of scanners. 47 # The key is: class_name.properties_tag, where properties_tag is the concatenation 48 # of all relevant properties, separated by '-' 49 __scanner_cache = {} 50 51reset () 52 53 54def register(scanner_class, relevant_properties): 55 """ Registers a new generator class, specifying a set of 56 properties relevant to this scanner. Ctor for that class 57 should have one parameter: list of properties. 58 """ 59 assert issubclass(scanner_class, Scanner) 60 assert isinstance(relevant_properties, basestring) 61 __scanners[str(scanner_class)] = relevant_properties 62 63def registered(scanner_class): 64 """ Returns true iff a scanner of that class is registered 65 """ 66 return str(scanner_class) in __scanners 67 68def get(scanner_class, properties): 69 """ Returns an instance of previously registered scanner 70 with the specified properties. 71 """ 72 assert issubclass(scanner_class, Scanner) 73 assert is_iterable_typed(properties, basestring) 74 scanner_name = str(scanner_class) 75 76 if not registered(scanner_name): 77 raise BaseException ("attempt to get unregistered scanner: %s" % scanner_name) 78 79 relevant_properties = __scanners[scanner_name] 80 r = property.select(relevant_properties, properties) 81 82 scanner_id = scanner_name + '.' + '-'.join(r) 83 84 if scanner_id not in __scanner_cache: 85 __scanner_cache[scanner_id] = scanner_class(r) 86 87 return __scanner_cache[scanner_id] 88 89class Scanner: 90 """ Base scanner class. 91 """ 92 def __init__ (self): 93 pass 94 95 def pattern (self): 96 """ Returns a pattern to use for scanning. 97 """ 98 raise BaseException ("method must be overridden") 99 100 def process (self, target, matches, binding): 101 """ Establish necessary relationship between targets, 102 given actual target being scanned, and a list of 103 pattern matches in that file. 104 """ 105 raise BaseException ("method must be overridden") 106 107 108# Common scanner class, which can be used when there's only one 109# kind of includes (unlike C, where "" and <> includes have different 110# search paths). 111class CommonScanner(Scanner): 112 113 def __init__ (self, includes): 114 Scanner.__init__(self) 115 self.includes = includes 116 117 def process(self, target, matches, binding): 118 119 target_path = os.path.normpath(os.path.dirname(binding[0])) 120 bjam.call("mark-included", target, matches) 121 122 get_manager().engine().set_target_variable(matches, "SEARCH", 123 [target_path] + self.includes) 124 get_manager().scanners().propagate(self, matches) 125 126class ScannerRegistry: 127 128 def __init__ (self, manager): 129 self.manager_ = manager 130 self.count_ = 0 131 self.exported_scanners_ = {} 132 133 def install (self, scanner, target, vtarget): 134 """ Installs the specified scanner on actual target 'target'. 135 vtarget: virtual target from which 'target' was actualized. 136 """ 137 assert isinstance(scanner, Scanner) 138 assert isinstance(target, basestring) 139 assert isinstance(vtarget, basestring) 140 engine = self.manager_.engine() 141 engine.set_target_variable(target, "HDRSCAN", scanner.pattern()) 142 if scanner not in self.exported_scanners_: 143 exported_name = "scanner_" + str(self.count_) 144 self.count_ = self.count_ + 1 145 self.exported_scanners_[scanner] = exported_name 146 bjam.import_rule("", exported_name, scanner.process) 147 else: 148 exported_name = self.exported_scanners_[scanner] 149 150 engine.set_target_variable(target, "HDRRULE", exported_name) 151 152 # scanner reflects difference in properties affecting 153 # binding of 'target', which will be known when processing 154 # includes for it, will give information on how to 155 # interpret quoted includes. 156 engine.set_target_variable(target, "HDRGRIST", str(id(scanner))) 157 pass 158 159 def propagate(self, scanner, targets): 160 assert isinstance(scanner, Scanner) 161 assert is_iterable_typed(targets, basestring) or isinstance(targets, basestring) 162 engine = self.manager_.engine() 163 engine.set_target_variable(targets, "HDRSCAN", scanner.pattern()) 164 engine.set_target_variable(targets, "HDRRULE", 165 self.exported_scanners_[scanner]) 166 engine.set_target_variable(targets, "HDRGRIST", str(id(scanner))) 167 168