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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
|
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
# distribute this software is granted provided this copyright notice appears in
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
# Deals with target type declaration and defines target class which supports
# typed targets.
import feature ;
import generators : * ;
import "class" : new ;
import errors ;
import property ;
import scanner ;
import project ;
# This creates a circular dependency
# project-test1 -> project -> project-root -> builtin -> type -> targets -> project
# import targets ;
# The feature is optional so that it never implicitly added.
# It's used only for internal purposes, and in all cases we
# want to explicitly use it.
feature.feature target-type : : composite optional ;
# feature.feature base-target-type : : composite optional ;
feature.feature main-target-type : : optional incidental ;
feature.feature base-target-type : : composite optional free ;
# feature.feature main-target-type : : composite optional incidental ;
# Registers a target type, possible derived from a 'base-type'.
# If 'suffixes' are provided, they given all the suffixes that mean a file is of 'type'.
# Also, the first element gives the suffix to be used when constructing and object of
# 'type'.
rule register ( type : suffixes * : base-type ? )
{
# Type names cannot contain hyphens, because when used as
# feature-values they will be interpreted as composite features
# which need to be decomposed.
switch $(type)
{
case *-* : errors.error "type name \"$(type)\" contains a hyphen" ;
}
if $(type) in $(.types)
{
errors.error "Type $(type) is already registered." ;
}
else
{
.types += $(type) ;
.bases.$(type) = $(base-type) ;
.derived.$(base-type) += $(type) ;
if $(suffixes)-not-empty
{
# Generated targets of 'type' will use the first of 'suffixes'
# (this may be overriden)
$(.suffixes).insert <target-type>$(type) : $(suffixes[1]) ;
# Specify mapping from suffixes to type
register-suffixes $(suffixes) : $(type) ;
}
feature.extend target-type : $(type) ;
feature.extend main-target-type : $(type) ;
feature.compose <target-type>$(type) : $(base-type:G=<base-target-type>) ;
feature.extend base-target-type : $(type) ;
# feature.compose <target-type>$(type) : <base-target-type>$(type) ;
feature.compose <base-target-type>$(type) : <base-target-type>$(base-type) ;
# We used to declare main target rule only when 'main' parameter
# is specified. However, it's hard to decide that a type *never*
# will need a main target rule and so from time to time we needed
# to make yet another type 'main'. So, now main target rule is defined
# for each type.
main-rule-name = [ type-to-rule-name $(type) ] ;
.main-target-type.$(main-rule-name) = $(type) ;
IMPORT $(__name__) : main-target-rule : : $(main-rule-name) ;
}
}
# Given type, returns name of main target rule which creates
# targets of that type.
rule type-to-rule-name ( type )
{
# Lowercase everything. Convert underscores to dashes.ame.
import regex ;
local n = [ regex.split $(type:L) "_" ] ;
n = $(n:J=-) ;
return $(n) ;
}
# Returns a type, given the name of a main rule.
rule type-from-rule-name ( main-target-name )
{
return $(.main-target-type.$(main-target-name)) ;
}
# Specifies that targets with suffix from 'suffixes' has the type 'type'.
# If different type is already specified for any of syffixes,
# issues an error.
rule register-suffixes ( suffixes + : type )
{
for local s in $(suffixes)
{
if ! $(.type.$(s))
{
.type.$(s) = $(type) ;
}
else if $(.type.$(s)) != type
{
errors.error Attempting to specify type for suffix \"$(s)\"
: "Old type $(.type.$(s)), New type $(type)" ;
}
}
}
# Returns true iff type has been registered.
rule registered ( type )
{
if $(type) in $(.types)
{
return true ;
}
}
# Issues an error if 'type' is unknown.
rule validate ( type )
{
if ! $(type) in $(.types)
{
errors.error "Unknown target type $(type)" ;
}
}
# Sets a scanner class that will be used for this 'type'.
rule set-scanner ( type : scanner )
{
if ! $(type) in $(.types)
{
error "Type" $(type) "is not declared" ;
}
.scanner.$(type) = $(scanner) ;
}
# Returns a scanner instance appropriate to 'type' and 'properties'.
rule get-scanner ( type : property-set )
{
if $(.scanner.$(type)) {
return [ scanner.get $(.scanner.$(type)) : $(property-set) ] ;
}
}
# returns type and all of its bases in order of their distance from type.
rule all-bases ( type )
{
local result = $(type) ;
while $(type)
{
type = $(.bases.$(type)) ;
result += $(type) ;
}
return $(result) ;
}
rule all-derived ( type )
{
local result = $(type) ;
for local d in $(.derived.$(type))
{
result += [ all-derived $(d) ] ;
}
return $(result) ;
}
# Returns true if 'type' has 'base' as its direct or
# indirect base.
rule is-derived ( type base )
{
if $(base) in [ all-bases $(type) ]
{
return true ;
}
}
# Returns true if 'type' is either derived from 'base',
# or 'type' is equal to 'base'.
rule is-subtype ( type base )
{
if $(type) = $(base)
{
return true ;
}
else
{
return [ is-derived $(type) $(base) ] ;
}
}
# Store suffixes for generated targets
.suffixes = [ new property-map ] ;
# Store prefixes for generated targets (e.g. "lib" for library)
.prefixes = [ new property-map ] ;
# Sets a target suffix that should be used when generating target
# of 'type' with the specified properties. Can be called with
# empty properties if no suffix for 'type' was specified yet.
# This does not automatically specify that files 'suffix' have
# 'type' --- two different types can use the same suffix for
# generating, but only one type should be auto-detected for
# a file with that suffix. User should explicitly specify which
# one.
#
# The 'suffix' parameter can be empty string ("") to indicate that
# no suffix should be used.
rule set-generated-target-suffix ( type : properties * : suffix )
{
set-generated-target-ps suffix : $(type) : $(properties) : $(suffix) ;
}
# Change the suffix previously registered for this type/properties
# combination. If suffix is not yet specified, sets it.
rule change-generated-target-suffix ( type : properties * : suffix )
{
change-generated-target-ps suffix : $(type) : $(properties) : $(suffix) ;
}
rule generated-target-suffix ( type : property-set )
{
return [ generated-target-ps suffix : $(type) : $(property-set) ] ;
}
# Sets a target prefix that should be used when generating target
# of 'type' with the specified properties. Can be called with
# empty properties if no prefix for 'type' was specified yet.
#
# The 'prefix' parameter can be empty string ("") to indicate that
# no prefix should be used.
#
# Example usage is for library names that have to have a "lib"
# prefix as in unix.
rule set-generated-target-prefix ( type : properties * : prefix )
{
set-generated-target-ps prefix : $(type) : $(properties) : $(prefix) ;
}
# Change the prefix previously registered for this type/properties
# combination. If prefix is not yet specified, sets it.
rule change-generated-target-prefix ( type : properties * : prefix )
{
change-generated-target-ps prefix : $(type) : $(properties) : $(prefix) ;
}
rule generated-target-prefix ( type : property-set )
{
return [ generated-target-ps prefix : $(type) : $(property-set) ] ;
}
# Common rules for prefix/suffix provisioning follow
rule set-generated-target-ps ( ps : type : properties * : psval )
{
properties = <target-type>$(type) $(properties) ;
$(.$(ps)es).insert $(properties) : $(psval) ;
}
rule change-generated-target-ps ( ps : type : properties * : psval )
{
properties = <target-type>$(type) $(properties) ;
local prev = [ $(.$(ps)es).find-replace $(properties) : $(psval) ] ;
if ! $(prev)
{
set-generated-target-ps $(ps) : $(type) : $(properties) : $(psval) ;
}
}
# Returns either prefix or suffix (as indicated by 'ps') that
# should be used when generating target of 'type' with the specified properties.
# Parameter 'ps' can be either "prefix" or "suffix". If no prefix/suffix is
# specified for 'type', returns prefix/suffix for base type, if any.
rule generated-target-ps-real ( ps : type : properties * )
{
local result ;
local found ;
while $(type) && ! $(found)
{
result = [ $(.$(ps)es).find <target-type>$(type) $(properties) ] ;
# If the prefix/suffix is explicitly set to empty string,
# we consider prefix/suffix to be found. If we did not compare with "",
# there would be no way for user to set empty prefix/suffix.
if $(result)-is-not-empty
{
found = true ;
}
type = $(.bases.$(type)) ;
}
if $(result) = ""
{
result = ;
}
return $(result) ;
}
rule generated-target-ps ( ps : type : property-set )
{
local key = .$(ps).$(type).$(property-set) ;
local v = $($(key)) ;
if ! $(v)
{
v = [ generated-target-ps-real $(ps) : $(type)
: [ $(property-set).raw ] ] ;
if ! $(v)
{
v = none ;
}
$(key) = $(v) ;
}
if $(v) != none
{
return $(v) ;
}
}
# Returns file type given it's name. If there are several dots in filename,
# tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and
# "so" will be tried.
rule type ( filename )
{
local type ;
while ! $(type) && $(filename:S)
{
local suffix = $(filename:S) ;
type = $(.type$(suffix)) ;
filename = $(filename:S=) ;
}
return $(type) ;
}
rule main-target-rule ( name : sources * : requirements * : default-build *
: usage-requirements * )
{
# First find required target type, which is equal to the name used
# to invoke us.
local bt = [ BACKTRACE 1 ] ;
local rulename = $(bt[4]) ;
# This rule may be only called from Jamfile, and therefore,
# CALLER_MODULE is Jamfile module, which is used to denote
# a project.
local project = [ project.current ] ;
# This is a circular module dependency, so it must be imported here
import targets ;
return [ targets.create-typed-target $(.main-target-type.$(rulename)) : $(project)
: $(name) : $(sources) : $(requirements)
: $(default-build) : $(usage-requirements) ] ;
}
|