| # Copyright 2002, 2003 Dave Abrahams |
| # Copyright 2002, 2003, 2004, 2005, 2006 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) |
| |
| # Deals with target type declaration and defines target class which supports |
| # typed targets. |
| |
| import "class" : new ; |
| import errors ; |
| import feature ; |
| import generators : * ; |
| import project ; |
| import property ; |
| import scanner ; |
| import os ; |
| |
| # The following import would create a circular dependency: |
| # project -> project-root -> builtin -> type -> targets -> project |
| # import targets ; |
| |
| # The feature is optional so it would never get added implicitly. It is used |
| # only for internal purposes and in all cases we want to use it explicitly. |
| feature.feature target-type : : composite optional ; |
| |
| feature.feature main-target-type : : optional incidental ; |
| feature.feature base-target-type : : composite optional free ; |
| |
| |
| # Registers a target type, possible derived from a 'base-type'. Providing a list |
| # of 'suffixes' here is a shortcut for separately calling the register-suffixes |
| # rule with the given suffixes and the set-generated-target-suffix rule with the |
| # first given suffix. |
| # |
| rule register ( type : suffixes * : base-type ? ) |
| { |
| # Type names cannot contain hyphens, because when used as feature-values |
| # they would 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) ; |
| .base.$(type) = $(base-type) ; |
| .derived.$(base-type) += $(type) ; |
| |
| if $(suffixes)-is-not-empty |
| { |
| # Specify mapping from suffixes to type. |
| register-suffixes $(suffixes) : $(type) ; |
| # By default generated targets of 'type' will use the first of |
| #'suffixes'. This may be overriden. |
| set-generated-target-suffix $(type) : : $(suffixes[1]) ; |
| } |
| |
| feature.extend target-type : $(type) ; |
| feature.extend main-target-type : $(type) ; |
| feature.extend base-target-type : $(type) ; |
| |
| feature.compose <target-type>$(type) : $(base-type:G=<base-target-type>) ; |
| feature.compose <base-target-type>$(type) : <base-target-type>$(base-type) ; |
| |
| # We used to declare the main target rule only when a 'main' parameter |
| # has been specified. However, it is hard to decide that a type will |
| # *never* need a main target rule and so from time to time we needed to |
| # make yet another type 'main'. So now a 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) ; |
| |
| # Adding a new derived type affects generator selection so we need to |
| # make the generator selection module update any of its cached |
| # information related to a new derived type being defined. |
| generators.update-cached-information-with-a-new-type $(type) ; |
| } |
| } |
| |
| |
| # Given a type, returns the name of the main target rule which creates targets |
| # of that type. |
| # |
| rule type-to-rule-name ( type ) |
| { |
| # Lowercase everything. Convert underscores to dashes. |
| import regex ; |
| local n = [ regex.split $(type:L) "_" ] ; |
| return $(n:J=-) ; |
| } |
| |
| |
| # Given a main target rule name, returns the type for which it creates targets. |
| # |
| rule type-from-rule-name ( rule-name ) |
| { |
| return $(.main-target-type.$(rule-name)) ; |
| } |
| |
| |
| # Specifies that files with suffix from 'suffixes' be recognized as targets of |
| # type 'type'. Issues an error if a different type is already specified for any |
| # of the suffixes. |
| # |
| 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 multiple types 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 ! [ registered $(type) ] |
| { |
| errors.error "Unknown target type $(type)" ; |
| } |
| } |
| |
| |
| # Sets a scanner class that will be used for this 'type'. |
| # |
| rule set-scanner ( type : scanner ) |
| { |
| validate $(type) ; |
| .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 a base type for the given type or nothing in case the given type is |
| # not derived. |
| # |
| rule base ( type ) |
| { |
| return $(.base.$(type)) ; |
| } |
| |
| |
| # Returns the given type and all of its base types in order of their distance |
| # from type. |
| # |
| rule all-bases ( type ) |
| { |
| local result = $(type) ; |
| while $(type) |
| { |
| type = [ base $(type) ] ; |
| result += $(type) ; |
| } |
| return $(result) ; |
| } |
| |
| |
| # Returns the given type and all of its derived types in order of their distance |
| # from type. |
| # |
| rule all-derived ( type ) |
| { |
| local result = $(type) ; |
| for local d in $(.derived.$(type)) |
| { |
| result += [ all-derived $(d) ] ; |
| } |
| return $(result) ; |
| } |
| |
| |
| # Returns true if 'type' is equal to 'base' or 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 or is equal to 'base'. |
| # |
| # TODO: It might be that is-derived and is-subtype were meant to be different |
| # rules - one returning true for type = base and one not, but as currently |
| # implemented they are actually the same. Clean this up. |
| # |
| rule is-subtype ( type base ) |
| { |
| 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 file suffix to be used when generating a target of 'type' with the |
| # specified properties. Can be called with no properties if no suffix has |
| # already been specified for the 'type'. The 'suffix' parameter can be an empty |
| # string ("") to indicate that no suffix should be used. |
| # |
| # Note that this does not cause files with 'suffix' to be automatically |
| # recognized as being of 'type'. Two different types can use the same suffix for |
| # their generated files but only one type can be auto-detected for a file with |
| # that suffix. User should explicitly specify which one using the |
| # register-suffixes rule. |
| # |
| 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) ; |
| } |
| |
| |
| # Returns the suffix used when generating a file of 'type' with the given |
| # properties. |
| # |
| 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 targets of 'type' |
| # with the specified properties. Can be called with empty properties if no |
| # prefix for 'type' has been specified yet. |
| # |
| # The 'prefix' parameter can be empty string ("") to indicate that no prefix |
| # should be used. |
| # |
| # Usage example: library names use the "lib" prefix on 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. |
| |
| local rule set-generated-target-ps ( ps : type : properties * : psval ) |
| { |
| properties = <target-type>$(type) $(properties) ; |
| $(.$(ps)es).insert $(properties) : $(psval) ; |
| } |
| |
| |
| local 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 a 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. |
| # |
| local 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 an empty string, we consider |
| # prefix/suffix to be found. If we were not to compare with "", there |
| # would be no way to specify an empty prefix/suffix. |
| if $(result)-is-not-empty |
| { |
| found = true ; |
| } |
| type = $(.base.$(type)) ; |
| } |
| if $(result) = "" |
| { |
| result = ; |
| } |
| return $(result) ; |
| } |
| |
| |
| local 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 its 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 ) |
| { |
| if [ os.name ] in NT CYGWIN |
| { |
| filename = $(filename:L) ; |
| } |
| local type ; |
| while ! $(type) && $(filename:S) |
| { |
| local suffix = $(filename:S) ; |
| type = $(.type$(suffix)) ; |
| filename = $(filename:S=) ; |
| } |
| return $(type) ; |
| } |
| |
| |
| # Rule used to construct all main targets. Note that this rule gets imported |
| # into the global namespace under different alias names and the exact target |
| # type to construct is selected based on the alias used to actually invoke this |
| # rule. |
| # |
| rule main-target-rule ( name : sources * : requirements * : default-build * : |
| usage-requirements * ) |
| { |
| # First discover the required target type based on the exact alias used to |
| # invoke this rule. |
| local bt = [ BACKTRACE 1 ] ; |
| local rulename = $(bt[4]) ; |
| local target-type = [ type-from-rule-name $(rulename) ] ; |
| |
| # This is a circular module dependency and so must be imported here. |
| import targets ; |
| |
| return [ targets.create-typed-target $(target-type) : [ project.current ] : |
| $(name) : $(sources) : $(requirements) : $(default-build) : |
| $(usage-requirements) ] ; |
| } |
| |
| |
| rule __test__ ( ) |
| { |
| import assert ; |
| |
| # TODO: Add tests for all the is-derived, is-base & related type relation |
| # checking rules. |
| } |