1# Copyright 2002, 2003 Dave Abrahams 2# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 3# Distributed under the Boost Software License, Version 1.0. 4# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 5 6# Deals with target type declaration and defines target class which supports 7# typed targets. 8 9import "class" : new ; 10import feature ; 11import generators : * ; 12import os ; 13import param ; 14import project ; 15import property ; 16import scanner ; 17 18# The following import would create a circular dependency: 19# project -> project-root -> builtin -> type -> targets -> project 20# import targets ; 21 22# The feature is optional so it would never get added implicitly. It is used 23# only for internal purposes and in all cases we want to use it explicitly. 24feature.feature target-type : : composite optional ; 25 26feature.feature main-target-type : : optional incidental ; 27feature.feature base-target-type : : composite optional free ; 28 29 30# Registers a target type, possible derived from a 'base-type'. Providing a list 31# of 'suffixes' here is a shortcut for separately calling the register-suffixes 32# rule with the given suffixes and the set-generated-target-suffix rule with the 33# first given suffix. 34# 35rule register ( type : suffixes * : base-type ? ) 36{ 37 # Type names cannot contain hyphens, because when used as feature-values 38 # they would be interpreted as composite features which need to be 39 # decomposed. 40 switch $(type) 41 { 42 case *-* : 43 import errors ; 44 errors.error "type name \"$(type)\" contains a hyphen" ; 45 } 46 47 if $(type) in $(.types) 48 { 49 import errors ; 50 errors.error "Type $(type) is already registered." ; 51 } 52 53 { 54 .types += $(type) ; 55 .base.$(type) = $(base-type) ; 56 .derived.$(base-type) += $(type) ; 57 .bases.$(type) = $(type) $(.bases.$(base-type)) ; 58 59 # Store suffixes for generated targets. 60 .suffixes.$(type) = [ new property-map ] ; 61 62 # Store prefixes for generated targets (e.g. "lib" for library). 63 .prefixes.$(type) = [ new property-map ] ; 64 65 if $(suffixes)-is-defined 66 { 67 # Specify mapping from suffixes to type. 68 register-suffixes $(suffixes) : $(type) ; 69 # By default generated targets of 'type' will use the first of 70 #'suffixes'. This may be overridden. 71 set-generated-target-suffix $(type) : : $(suffixes[1]) ; 72 } 73 74 feature.extend target-type : $(type) ; 75 feature.extend main-target-type : $(type) ; 76 feature.extend base-target-type : $(type) ; 77 78 feature.compose <target-type>$(type) : $(base-type:G=<base-target-type>) ; 79 feature.compose <base-target-type>$(type) : <base-target-type>$(base-type) ; 80 81 # We used to declare the main target rule only when a 'main' parameter 82 # has been specified. However, it is hard to decide that a type will 83 # *never* need a main target rule and so from time to time we needed to 84 # make yet another type 'main'. So now a main target rule is defined for 85 # each type. 86 main-rule-name = [ type-to-rule-name $(type) ] ; 87 .main-target-type.$(main-rule-name) = $(type) ; 88 IMPORT $(__name__) : main-target-rule : : $(main-rule-name) ; 89 90 # Adding a new derived type affects generator selection so we need to 91 # make the generator selection module update any of its cached 92 # information related to a new derived type being defined. 93 generators.update-cached-information-with-a-new-type $(type) ; 94 } 95} 96 97 98# Given a type, returns the name of the main target rule which creates targets 99# of that type. 100# 101rule type-to-rule-name ( type ) 102{ 103 # Lowercase everything. Convert underscores to dashes. 104 import regex ; 105 local n = [ regex.split $(type:L) "_" ] ; 106 return $(n:J=-) ; 107} 108 109 110# Given a main target rule name, returns the type for which it creates targets. 111# 112rule type-from-rule-name ( rule-name ) 113{ 114 return $(.main-target-type.$(rule-name)) ; 115} 116 117 118# Specifies that files with suffix from 'suffixes' be recognized as targets of 119# type 'type'. Issues an error if a different type is already specified for any 120# of the suffixes. 121# 122rule register-suffixes ( suffixes + : type ) 123{ 124 for local s in $(suffixes) 125 { 126 if ! $(.type.$(s)) 127 { 128 .type.$(s) = $(type) ; 129 } 130 else if $(.type.$(s)) != $(type) 131 { 132 import errors ; 133 errors.error Attempting to specify multiple types for suffix 134 \"$(s)\" : "Old type $(.type.$(s)), New type $(type)" ; 135 } 136 } 137} 138 139 140# Returns true iff type has been registered. 141# 142rule registered ( type ) 143{ 144 if $(type) in $(.types) 145 { 146 return true ; 147 } 148} 149 150 151# Issues an error if 'type' is unknown. 152# 153rule validate ( type ) 154{ 155 if ! [ registered $(type) ] 156 { 157 import errors ; 158 errors.error "Unknown target type $(type)" ; 159 } 160} 161 162 163# Sets a scanner class that will be used for this 'type'. 164# 165rule set-scanner ( type : scanner ) 166{ 167 validate $(type) ; 168 .scanner.$(type) = $(scanner) ; 169} 170 171 172# Returns a scanner instance appropriate to 'type' and 'properties'. 173# 174rule get-scanner ( type : property-set ) 175{ 176 if $(.scanner.$(type)) 177 { 178 return [ scanner.get $(.scanner.$(type)) : $(property-set) ] ; 179 } 180} 181 182 183# Returns a base type for the given type or nothing in case the given type is 184# not derived. 185# 186rule base ( type ) 187{ 188 return $(.base.$(type)) ; 189} 190 191 192# Returns the given type and all of its base types in order of their distance 193# from type. 194# 195rule all-bases ( type ) 196{ 197 return $(.bases.$(type)) ; 198} 199 200 201# Returns the given type and all of its derived types in order of their distance 202# from type. 203# 204rule all-derived ( type ) 205{ 206 local result = $(type) ; 207 for local d in $(.derived.$(type)) 208 { 209 result += [ all-derived $(d) ] ; 210 } 211 return $(result) ; 212} 213 214 215# Returns true if 'type' is equal to 'base' or has 'base' as its direct or 216# indirect base. 217# 218rule is-derived ( type base ) 219{ 220 if $(base) in $(.bases.$(type)) 221 { 222 return true ; 223 } 224} 225 226# Returns true if 'type' is either derived from or is equal to 'base'. 227# 228# TODO: It might be that is-derived and is-subtype were meant to be different 229# rules - one returning true for type = base and one not, but as currently 230# implemented they are actually the same. Clean this up. 231# 232rule is-subtype ( type base ) 233{ 234 return [ is-derived $(type) $(base) ] ; 235} 236 237 238 239 240# Sets a file suffix to be used when generating a target of 'type' with the 241# specified properties. Can be called with no properties if no suffix has 242# already been specified for the 'type'. The 'suffix' parameter can be an empty 243# string ("") to indicate that no suffix should be used. 244# 245# Note that this does not cause files with 'suffix' to be automatically 246# recognized as being of 'type'. Two different types can use the same suffix for 247# their generated files but only one type can be auto-detected for a file with 248# that suffix. User should explicitly specify which one using the 249# register-suffixes rule. 250# 251rule set-generated-target-suffix ( type : properties * : suffix ) 252{ 253 set-generated-target-ps suffix : $(type) : $(properties) : $(suffix) ; 254} 255 256 257# Change the suffix previously registered for this type/properties combination. 258# If suffix is not yet specified, sets it. 259# 260rule change-generated-target-suffix ( type : properties * : suffix ) 261{ 262 change-generated-target-ps suffix : $(type) : $(properties) : $(suffix) ; 263} 264 265 266# Returns the suffix used when generating a file of 'type' with the given 267# properties. 268# 269rule generated-target-suffix ( type : property-set ) 270{ 271 return [ generated-target-ps suffix : $(type) : $(property-set) ] ; 272} 273 274 275# Sets a target prefix that should be used when generating targets of 'type' 276# with the specified properties. Can be called with empty properties if no 277# prefix for 'type' has been specified yet. 278# 279# The 'prefix' parameter can be empty string ("") to indicate that no prefix 280# should be used. 281# 282# Usage example: library names use the "lib" prefix on unix. 283# 284rule set-generated-target-prefix ( type : properties * : prefix ) 285{ 286 set-generated-target-ps prefix : $(type) : $(properties) : $(prefix) ; 287} 288 289 290# Change the prefix previously registered for this type/properties combination. 291# If prefix is not yet specified, sets it. 292# 293rule change-generated-target-prefix ( type : properties * : prefix ) 294{ 295 change-generated-target-ps prefix : $(type) : $(properties) : $(prefix) ; 296} 297 298 299rule generated-target-prefix ( type : property-set ) 300{ 301 return [ generated-target-ps prefix : $(type) : $(property-set) ] ; 302} 303 304 305# Common rules for prefix/suffix provisioning follow. 306 307local rule set-generated-target-ps ( ps : type : properties * : psval ) 308{ 309 $(.$(ps)es.$(type)).insert $(properties) : $(psval) ; 310} 311 312 313local rule change-generated-target-ps ( ps : type : properties * : psval ) 314{ 315 local prev = [ $(.$(ps)es.$(type)).find-replace $(properties) : $(psval) ] ; 316 if ! $(prev) 317 { 318 set-generated-target-ps $(ps) : $(type) : $(properties) : $(psval) ; 319 } 320} 321 322 323# Returns either prefix or suffix (as indicated by 'ps') that should be used 324# when generating a target of 'type' with the specified properties. Parameter 325# 'ps' can be either "prefix" or "suffix". If no prefix/suffix is specified for 326# 'type', returns prefix/suffix for base type, if any. 327# 328local rule generated-target-ps ( ps : type : property-set ) 329{ 330 local result ; 331 local found ; 332 while $(type) && ! $(found) 333 { 334 result = [ $(.$(ps)es.$(type)).find $(property-set) ] ; 335 # If the prefix/suffix is explicitly set to an empty string, we consider 336 # prefix/suffix to be found. If we were not to compare with "", there 337 # would be no way to specify an empty prefix/suffix. 338 if $(result)-is-defined 339 { 340 found = true ; 341 } 342 type = $(.base.$(type)) ; 343 } 344 if $(result) = "" 345 { 346 result = ; 347 } 348 return $(result) ; 349} 350 351 352# Returns file type given its name. If there are several dots in filename, tries 353# each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and "so" will 354# be tried. 355# 356rule type ( filename ) 357{ 358 if [ os.name ] in NT CYGWIN 359 { 360 filename = $(filename:L) ; 361 } 362 local type ; 363 while ! $(type) && $(filename:S) 364 { 365 local suffix = $(filename:S) ; 366 type = $(.type$(suffix)) ; 367 filename = $(filename:S=) ; 368 } 369 return $(type) ; 370} 371 372 373# Rule used to construct all main targets. Note that this rule gets imported 374# into the global namespace under different alias names and the exact target 375# type to construct is selected based on the alias used to actually invoke this 376# rule. 377# 378rule main-target-rule ( name : sources * : requirements * : default-build * : 379 usage-requirements * ) 380{ 381 param.handle-named-params 382 sources requirements default-build usage-requirements ; 383 # First discover the required target type based on the exact alias used to 384 # invoke this rule. 385 local bt = [ BACKTRACE 1 ] ; 386 local rulename = $(bt[4]) ; 387 local target-type = [ type-from-rule-name $(rulename) ] ; 388 389 # This is a circular module dependency and so must be imported here. 390 import targets ; 391 392 return [ targets.create-typed-target $(target-type) : [ project.current ] : 393 $(name) : $(sources) : $(requirements) : $(default-build) : 394 $(usage-requirements) ] ; 395} 396 397 398rule __test__ ( ) 399{ 400 import assert ; 401 402 # TODO: Add tests for all the is-derived, is-base & related type relation 403 # checking rules. 404} 405