# 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 ;