blob: d6042ea2c78dff3105a9cd8bdfdf84c5dc6de69f [file] [log] [blame]
# 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 ;