# Copyright Rene Rivera 2015 # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # Defines rules that provide requirements based on checking # conditions using Boost Predef definitions and version numbers. import modules ; import project ; import feature ; import string ; import toolset ; import modules ; import path ; import "class" : new ; import regex ; # Create a project for our targets. project.extension predef check ; # Feature to pass check expressions to check programs. feature.feature predef-expression : : free ; # Checks the expressions and when used evaluates to the true-properties # if the expressions are all true. Otherwise evaluates to the # false-properties. rule check ( expressions + : language ? : true-properties * : false-properties * ) { # Default to C++ on the check context. language ?= cpp ; local project_target = [ project.target $(__name__) ] ; project.push-current $(project_target) ; local terms ; local result ; for expression in $(expressions) { if $(expression:L) in "and" "or" { terms += $(expression:L) ; } else { # Create the check run if we don't have one yet. local key = [ MD5 $(language)::$(expression) ] ; if ! ( $(key) in $(_checks_) ) { _checks_ += $(key) ; _message_(/check/predef//predef_check_cc_$(key)) = $(expression) ; check_target $(language) $(key) : [ change_term_to_def $(expression) ] ; } terms += /check/predef//predef_check_cc_$(key) ; } } local instance = [ new check-expression-evaluator $(terms) : $(true-properties) : $(false-properties) ] ; result = @$(instance).check ; project.pop-current ; return $(result) ; } # Checks the expressions and when used evaluates to no # if the expressions are all false. Otherwise evaluates to the # nothing. rule require ( expressions + : language ? ) { return [ check $(expressions) : $(language) : : no ] ; } ############################################################################# .c.ext = c ; .cpp.ext = cpp ; .objc.ext = m ; .objcpp.ext = mm ; # Check targets. Each needs to be compiled for different languages # even though they are all the same source code. local rule check_target ( language key : requirements * ) { # Need to use absolute paths because we don't know the # context of the invocation which affects where the paths # originate from. local predef_jam = [ modules.binding $(__name__) ] ; local source_path = $(predef_jam:D)/predef_check_cc_as_$(language).$(.$(language).ext) ; local include_path = $(predef_jam:D)/../../include ; obj predef_check_cc_$(key) : $(source_path) : $(include_path) $(requirements) ; explicit predef_check_cc_$(key) ; return predef_check_cc_$(key) ; } local rule change_term_to_def ( term ) { local parts = [ regex.split $(term) " " ] ; if $(parts[3]) { local version_number = [ regex.split $(parts[3]) "[.]" ] ; if ! $(version_number[3]) { version_number += "0" ; } if ! $(version_number[3]) { version_number += "0" ; } parts = $(parts[1-2]) BOOST_VERSION_NUMBER($(version_number:J=",")) ; } return CHECK=\"$(parts:J=" ")\" ; } class check-expression-evaluator { import configure ; rule __init__ ( expression + : true-properties * : false-properties * ) { self.expression = $(expression) ; self.true-properties = $(true-properties) ; self.false-properties = $(false-properties) ; } rule check ( properties * ) { local to-eval ; local tokens = "and" "or" ; # Go through the expression and: eval the target values, # and normalize to a full expression. for local term in $(self.expression) { if ! ( $(term:L) in $(tokens) ) { # A value is a target reference that will evan to "true" # or "false". if $(to-eval[-1]:L) && ! ( $(to-eval[-1]:L) in $(tokens) ) { # Default to "and" operation. to-eval += "and" ; } local message = [ modules.peek predef : _message_($(term)) ] ; if [ configure.builds $(term) : $(properties) : $(message) ] { to-eval += "true" ; } else { to-eval += "false" ; } } else { to-eval += $(term) ; } } # Eval full the expression. local eval-result = [ eval $(to-eval) ] ; # And resolve true/false properties. if $(eval-result) = "true" { return $(self.true-properties) ; } else { return $(self.false-properties) ; } } rule eval ( e * ) { local r ; if $(e[1]) && $(e[2]) && $(e[3]) { if $(e[2]) = "and" { if $(e[1]) = "true" && $(e[3]) = "true" { r = [ eval "true" $(e[4-]) ] ; } else { r = [ eval "false" $(e[4-]) ] ; } } else if $(e[2]) = "or" { if $(e[1]) = "true" || $(e[3]) = "true" { r = [ eval "true" $(e[4-]) ] ; } else { r = [ eval "false" $(e[4-]) ] ; } } } else { r = $(e[1]) ; } return $(r) ; } }