]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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. | |
31 | import property | |
32 | import bjam | |
33 | import os | |
34 | from b2.manager import get_manager | |
35 | from b2.util import is_iterable_typed | |
36 | ||
37 | ||
38 | def 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 | ||
51 | reset () | |
52 | ||
53 | ||
54 | def 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 | ||
63 | def registered(scanner_class): | |
64 | """ Returns true iff a scanner of that class is registered | |
65 | """ | |
66 | return str(scanner_class) in __scanners | |
67 | ||
68 | def 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): | |
92f5a8d4 | 77 | raise BaseException ("attempt to get unregistered scanner: %s" % scanner_name) |
7c673cae FG |
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 | ||
89 | class 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 | """ | |
92f5a8d4 | 98 | raise BaseException ("method must be overridden") |
7c673cae FG |
99 | |
100 | def process (self, target, matches, binding): | |
101 | """ Establish necessary relationship between targets, | |
92f5a8d4 | 102 | given actual target being scanned, and a list of |
7c673cae FG |
103 | pattern matches in that file. |
104 | """ | |
92f5a8d4 | 105 | raise BaseException ("method must be overridden") |
7c673cae FG |
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). | |
111 | class 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 | ||
126 | class 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 |