# 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 "class" : new ; | |
import property virtual-target property-set ; | |
import errors : error ; | |
# Base scanner class. | |
class scanner | |
{ | |
rule __init__ ( ) | |
{ | |
} | |
# Returns a pattern to use for scanning | |
rule pattern ( ) | |
{ | |
error "method must be overriden" ; | |
} | |
# Establish necessary relationship between targets, | |
# given actual target beeing scanned, and a list of | |
# pattern matches in that file. | |
rule process ( target : matches * ) | |
{ | |
error "method must be overriden" ; | |
} | |
} | |
# 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. | |
rule register ( scanner-class : relevant-properties * ) | |
{ | |
.registered += $(scanner-class) ; | |
.relevant-properties.$(scanner-class) = $(relevant-properties) ; | |
} | |
# 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 common-scanner : scanner | |
{ | |
import scanner ; | |
rule __init__ ( includes * ) | |
{ | |
scanner.__init__ ; | |
self.includes = $(includes) ; | |
} | |
rule process ( target : matches * : binding ) | |
{ | |
local target_path = [ NORMALIZE_PATH $(binding:D) ] ; | |
NOCARE $(matches) ; | |
INCLUDES $(target) : $(matches) ; | |
SEARCH on $(matches) = $(target_path) $(self.includes:G=) ; | |
ISFILE $(matches) ; | |
scanner.propagate $(__name__) : $(matches) : $(target) ; | |
} | |
} | |
# Returns an instance of previously registered scanner, | |
# with the specified properties. | |
rule get ( scanner-class : property-set ) | |
{ | |
if ! $(scanner-class) in $(.registered) | |
{ | |
error "attempt to get unregisted scanner" ; | |
} | |
local r = $(.rv-cache.$(property-set)) ; | |
if ! $(r) | |
{ | |
r = [ property-set.create | |
[ property.select $(.relevant-properties.$(scanner-class)) : | |
[ $(property-set).raw ] ] ] ; | |
.rv-cache.$(property-set) = $(r) ; | |
} | |
if ! $(scanner.$(scanner-class).$(r:J=-)) | |
{ | |
scanner.$(scanner-class).$(r:J=-) = [ new $(scanner-class) [ $(r).raw ] ] ; | |
} | |
return $(scanner.$(scanner-class).$(r:J=-)) ; | |
} | |
# Installs the specified scanner on actual target 'target'. | |
rule install ( scanner : target | |
vtarget # virtual target from which 'target' was actualized | |
) | |
{ | |
HDRSCAN on $(target) = [ $(scanner).pattern ] ; | |
SCANNER on $(target) = $(scanner) ; | |
HDRRULE on $(target) = scanner.hdrrule ; | |
# 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. | |
HDRGRIST on $(target) = $(scanner) ; | |
} | |
# Propagate scanner setting from 'including-target' to 'targets'. | |
rule propagate ( scanner : targets * : including-target ) | |
{ | |
HDRSCAN on $(targets) = [ on $(including-target) return $(HDRSCAN) ] ; | |
SCANNER on $(targets) = $(scanner) ; | |
HDRRULE on $(targets) = scanner.hdrrule ; | |
HDRGRIST on $(targets) = [ on $(including-target) return $(HDRGRIST) ] ; | |
} | |
rule hdrrule ( target : matches * : binding ) | |
{ | |
local scanner = [ on $(target) return $(SCANNER) ] ; | |
$(scanner).process $(target) : $(matches) : $(binding) ; | |
} | |
# hdrrule must be available at global scope so that it can be invoked | |
# by header scanning | |
IMPORT scanner : hdrrule : : scanner.hdrrule ; | |