1# Copyright 2003 Dave Abrahams
2# Copyright 2002, 2003, 2004, 2005 Vladimir Prus
3# Distributed under the Boost Software License, Version 1.0.
4# (See accompanying file LICENSE_1_0.txt or copy at
5# http://www.boost.org/LICENSE_1_0.txt)
6
7# Implements scanners: objects computing implicit dependencies for files, such
8# as includes in C++.
9#
10# A scanner has a regular expression used to find the dependencies, some data
11# needed to interpret those dependencies (e.g., include paths), and code which
12# establishing needed relationships between actual jam targets.
13#
14# Scanner objects are created by actions when they try to actualize virtual
15# targets, passed to the virtual-target.actualize() method and are then
16# associated with actual targets. It is possible to use several scanners for a
17# single virtual-target. For example, a single source file might be compiled
18# twice - each time using a different include path. In this case, two separate
19# actual targets will be created, each having a scanner of its own.
20#
21# Typically, scanners are created from target type and the action's properties,
22# using the rule 'get' in this module. Directly creating scanners is not
23# recommended, as it might create multiple equvivalent but different instances,
24# and lead to unnecessary actual target duplication. However, actions can also
25# create scanners in a special way, instead of relying on just the target type.
26
27import "class" : new ;
28import property ;
29import property-set ;
30import virtual-target ;
31
32# Base scanner class.
33#
34class scanner
35{
36    rule __init__ ( )
37    {
38    }
39
40    # Returns a pattern to use for scanning.
41    #
42    rule pattern ( )
43    {
44        import errors : error : errors.error ;
45        errors.error "method must be overriden" ;
46    }
47
48    # Establish necessary relationship between targets, given an actual target
49    # beeing scanned and a list of pattern matches in that file.
50    #
51    rule process ( target : matches * )
52    {
53        import errors : error : errors.error ;
54        errors.error "method must be overriden" ;
55    }
56}
57
58
59# Registers a new generator class, specifying a set of properties relevant to
60# this scanner. Constructor for that class should have one parameter: a list of
61# properties.
62#
63rule register ( scanner-class : relevant-properties * )
64{
65    .registered += $(scanner-class) ;
66    .relevant-properties.$(scanner-class) = $(relevant-properties) ;
67}
68
69
70# Common scanner class, usable when there is only one kind of includes (unlike
71# C, where "" and <> includes have different search paths).
72#
73class common-scanner : scanner
74{
75    import scanner ;
76
77    rule __init__ ( includes * )
78    {
79        scanner.__init__ ;
80        self.includes = $(includes) ;
81    }
82
83    rule process ( target : matches * : binding )
84    {
85        local target_path = [ NORMALIZE_PATH $(binding:D) ] ;
86
87        NOCARE $(matches) ;
88        INCLUDES $(target) : $(matches) ;
89        SEARCH on $(matches) = $(target_path) $(self.includes:G=) ;
90        ISFILE $(matches) ;
91
92        scanner.propagate $(__name__) : $(matches) : $(target) ;
93    }
94}
95
96
97# Returns an instance of a previously registered scanner, with the specified
98# properties.
99#
100rule get ( scanner-class : property-set )
101{
102    if ! $(scanner-class) in $(.registered)
103    {
104        import errors ;
105        errors.error "attempt to get an unregisted scanner" ;
106    }
107
108    local r = $(.rv-cache.$(property-set)) ;
109    if ! $(r)
110    {
111        r = [ property-set.create
112            [ property.select $(.relevant-properties.$(scanner-class)) :
113              [ $(property-set).raw ] ] ] ;
114        .rv-cache.$(property-set) = $(r) ;
115    }
116
117    if ! $(scanner.$(scanner-class).$(r:J=-))
118    {
119        local s = [ new $(scanner-class) [ $(r).raw ] ] ;
120        scanner.$(scanner-class).$(r:J=-) = $(s) ;
121    }
122    return $(scanner.$(scanner-class).$(r:J=-)) ;
123}
124
125
126# Installs the specified scanner on the actual target 'target'.
127#
128rule install ( scanner : target )
129{
130    HDRSCAN on $(target) = [ $(scanner).pattern ] ;
131    SCANNER on $(target) = $(scanner) ;
132    HDRRULE on $(target) = scanner.hdrrule ;
133
134    # Scanner reflects differences in properties affecting binding of 'target',
135    # which will be known when processing includes for it, and give information
136    # on how to interpret different include types (e.g. quoted vs. those in
137    # angle brackets in C files).
138    HDRGRIST on $(target) = $(scanner) ;
139}
140
141
142# Propagate scanner settings from 'including-target' to 'targets'.
143#
144rule propagate ( scanner : targets * : including-target )
145{
146    HDRSCAN on $(targets) = [ on $(including-target) return $(HDRSCAN) ] ;
147    SCANNER on $(targets) = $(scanner) ;
148    HDRRULE on $(targets) = scanner.hdrrule ;
149    HDRGRIST on $(targets) = [ on $(including-target) return $(HDRGRIST) ] ;
150}
151
152
153rule hdrrule ( target : matches * : binding )
154{
155    local scanner = [ on $(target) return $(SCANNER) ] ;
156    $(scanner).process $(target) : $(matches) : $(binding) ;
157}
158
159
160# hdrrule must be available at global scope so it can be invoked by header
161# scanning.
162#
163IMPORT scanner : hdrrule : : scanner.hdrrule ;
164