summaryrefslogtreecommitdiff
path: root/tools/build/src/build/scanner.py
blob: 19f1431d47610d109b9b257da1cb163134bfe4cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Status: ported.
# Base revision: 45462
# 
# Copyright 2003 Dave Abrahams 
# Copyright 2002, 2003, 2004, 2005 Vladimir Prus 
# Distributed under the Boost Software License, Version 1.0. 
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 

#  Implements scanners: objects that compute implicit dependencies for
#  files, such as includes in C++.
#
#  Scanner has a regular expression used to find dependencies, some
#  data needed to interpret those dependencies (for example, include
#  paths), and a code which actually established needed relationship
#  between actual jam targets.
#
#  Scanner objects are created by actions, when they try to actualize
#  virtual targets, passed to 'virtual-target.actualize' method and are
#  then associated with actual targets. It is possible to use
#  several scanners for a virtual-target. For example, a single source
#  might be used by to compile actions, with different include paths.
#  In this case, two different actual targets will be created, each 
#  having scanner of its own.
#
#  Typically, scanners are created from target type and action's 
#  properties, using the rule 'get' in this module. Directly creating
#  scanners is not recommended, because it might create many equvivalent
#  but different instances, and lead in unneeded duplication of
#  actual targets. However, actions can also create scanners in a special
#  way, instead of relying on just target type.

import property
import bjam
import os
from b2.exceptions import *
from b2.manager import get_manager

def reset ():
    """ Clear the module state. This is mainly for testing purposes.
    """
    global __scanners, __rv_cache, __scanner_cache

    # Maps registered scanner classes to relevant properties
    __scanners = {}
    
    # A cache of scanners.
    # The key is: class_name.properties_tag, where properties_tag is the concatenation 
    # of all relevant properties, separated by '-'
    __scanner_cache = {}
    
reset ()


def register(scanner_class, relevant_properties):
    """ Registers a new generator class, specifying a set of 
        properties relevant to this scanner.  Ctor for that class
        should have one parameter: list of properties.
    """
    __scanners[str(scanner_class)] = relevant_properties

def registered(scanner_class):
    """ Returns true iff a scanner of that class is registered
    """
    return __scanners.has_key(str(scanner_class))
    
def get(scanner_class, properties):
    """ Returns an instance of previously registered scanner
        with the specified properties.
    """
    scanner_name = str(scanner_class)
    
    if not registered(scanner_name):
        raise BaseException ("attempt to get unregisted scanner: %s" % scanner_name)

    relevant_properties = __scanners[scanner_name]
    r = property.select(relevant_properties, properties)

    scanner_id = scanner_name + '.' + '-'.join(r)
    
    if not __scanner_cache.has_key(scanner_name):
        __scanner_cache[scanner_name] = scanner_class(r)

    return __scanner_cache[scanner_name]

class Scanner:
    """ Base scanner class.
    """
    def __init__ (self):
        pass
    
    def pattern (self):
        """ Returns a pattern to use for scanning.
        """
        raise BaseException ("method must be overriden")

    def process (self, target, matches):
        """ Establish necessary relationship between targets,
            given actual target beeing scanned, and a list of
            pattern matches in that file.
        """
        raise BaseException ("method must be overriden")


# Common scanner class, which can be used when there's only one
# kind of includes (unlike C, where "" and <> includes have different
# search paths).
class CommonScanner(Scanner):

    def __init__ (self, includes):
        Scanner.__init__(self)
        self.includes = includes

    def process(self, target, matches, binding):

        target_path = os.path.normpath(os.path.dirname(binding[0]))
        bjam.call("mark-included", target, matches)

        get_manager().engine().set_target_variable(matches, "SEARCH",
                                                   [target_path] + self.includes)
        get_manager().scanners().propagate(self, matches)

class ScannerRegistry:
    
    def __init__ (self, manager):
        self.manager_ = manager
        self.count_ = 0
        self.exported_scanners_ = {}

    def install (self, scanner, target, vtarget):
        """ Installs the specified scanner on actual target 'target'. 
            vtarget: virtual target from which 'target' was actualized.
        """
        engine = self.manager_.engine()
        engine.set_target_variable(target, "HDRSCAN", scanner.pattern())
        if not self.exported_scanners_.has_key(scanner):
            exported_name = "scanner_" + str(self.count_)
            self.count_ = self.count_ + 1
            self.exported_scanners_[scanner] = exported_name
            bjam.import_rule("", exported_name, scanner.process)
        else:
            exported_name = self.exported_scanners_[scanner]

        engine.set_target_variable(target, "HDRRULE", exported_name)
        
        # scanner reflects difference in properties affecting    
        # binding of 'target', which will be known when processing
        # includes for it, will give information on how to
        # interpret quoted includes.
        engine.set_target_variable(target, "HDRGRIST", str(id(scanner)))
        pass

    def propagate(self, scanner, targets):
        engine = self.manager_.engine()
        engine.set_target_variable(targets, "HDRSCAN", scanner.pattern())
        engine.set_target_variable(targets, "HDRRULE",
                                   self.exported_scanners_[scanner])
        engine.set_target_variable(targets, "HDRGRIST", str(id(scanner)))