1# Copyright Rene Rivera 2015 2# Distributed under the Boost Software License, Version 1.0. 3# (See accompanying file LICENSE_1_0.txt or copy at 4# http://www.boost.org/LICENSE_1_0.txt) 5 6# Defines rules that provide requirements based on checking 7# conditions using Boost Predef definitions and version numbers. 8 9import modules ; 10import project ; 11import feature ; 12import string ; 13import toolset ; 14import modules ; 15import path ; 16import "class" : new ; 17import regex ; 18 19# Create a project for our targets. 20project.extension predef check ; 21 22# Feature to pass check expressions to check programs. 23feature.feature predef-expression : : free ; 24 25# Checks the expressions and when used evaluates to the true-properties 26# if the expressions are all true. Otherwise evaluates to the 27# false-properties. 28rule check ( expressions + : language ? : true-properties * : false-properties * ) 29{ 30 # Default to C++ on the check context. 31 language ?= cpp ; 32 33 local project_target = [ project.target $(__name__) ] ; 34 project.push-current $(project_target) ; 35 local terms ; 36 local result ; 37 for expression in $(expressions) 38 { 39 if $(expression:L) in "and" "or" 40 { 41 terms += $(expression:L) ; 42 } 43 else 44 { 45 # Create the check run if we don't have one yet. 46 local key = [ MD5 "$(language)::$(expression)" ] ; 47 if ! ( $(key) in $(_checks_) ) 48 { 49 _checks_ += $(key) ; 50 _message_(/check/predef//predef_check_cc_$(key)) = $(expression) ; 51 check_target $(language) $(key) : [ change_term_to_def $(expression) ] ; 52 } 53 54 terms += /check/predef//predef_check_cc_$(key) ; 55 } 56 } 57 local instance = [ new check-expression-evaluator 58 $(terms) : $(true-properties) : $(false-properties) ] ; 59 result = <conditional>@$(instance).check ; 60 project.pop-current ; 61 return $(result) ; 62} 63 64# Checks the expressions and when used evaluates to <build>no 65# if the expressions are all false. Otherwise evaluates to the 66# nothing. 67rule require ( expressions + : language ? ) 68{ 69 return [ check $(expressions) : $(language) : : <build>no ] ; 70} 71 72############################################################################# 73 74.c.ext = c ; 75.cpp.ext = cpp ; 76.objc.ext = m ; 77.objcpp.ext = mm ; 78 79# Check targets. Each needs to be compiled for different languages 80# even though they are all the same source code. 81local rule check_target ( language key : requirements * ) 82{ 83 # Need to use absolute paths because we don't know the 84 # context of the invocation which affects where the paths 85 # originate from. 86 local predef_jam 87 = [ modules.binding $(__name__) ] ; 88 local source_path 89 = $(predef_jam:D)/predef_check_cc_as_$(language).$(.$(language).ext) ; 90 local include_path 91 = $(predef_jam:D)/../../include $(BOOST_ROOT) ; 92 obj predef_check_cc_$(key) 93 : $(source_path) 94 : <include>$(include_path) $(requirements) ; 95 explicit predef_check_cc_$(key) ; 96 return predef_check_cc_$(key) ; 97} 98 99local rule change_term_to_def ( term ) 100{ 101 local parts = [ regex.split $(term) " " ] ; 102 if $(parts[3]) 103 { 104 local version_number = [ regex.split $(parts[3]) "[.]" ] ; 105 if ! $(version_number[2]) { version_number += "0" ; } 106 if ! $(version_number[3]) { version_number += "0" ; } 107 parts = $(parts[1-2]) BOOST_VERSION_NUMBER($(version_number:J=",")) ; 108 } 109 return <define>CHECK=\"$(parts:J=" ")\" ; 110} 111 112class check-expression-evaluator 113{ 114 import configure ; 115 116 rule __init__ ( expression + : true-properties * : false-properties * ) 117 { 118 self.expression = $(expression) ; 119 self.true-properties = $(true-properties) ; 120 self.false-properties = $(false-properties) ; 121 } 122 123 rule check ( properties * ) 124 { 125 local to-eval ; 126 local tokens = "and" "or" ; 127 # Go through the expression and: eval the target values, 128 # and normalize to a full expression. 129 for local term in $(self.expression) 130 { 131 if ! ( $(term:L) in $(tokens) ) 132 { 133 # A value is a target reference that will evan to "true" 134 # or "false". 135 if $(to-eval[-1]:L) && ! ( $(to-eval[-1]:L) in $(tokens) ) 136 { 137 # Default to "and" operation. 138 to-eval += "and" ; 139 } 140 local message = [ modules.peek predef : _message_($(term)) ] ; 141 if [ configure.builds $(term) : $(properties) : $(message) ] 142 { 143 to-eval += "true" ; 144 } 145 else 146 { 147 to-eval += "false" ; 148 } 149 } 150 else 151 { 152 to-eval += $(term) ; 153 } 154 } 155 # Eval full the expression. 156 local eval-result = [ eval $(to-eval) ] ; 157 # And resolve true/false properties. 158 if $(eval-result) = "true" 159 { 160 return $(self.true-properties) ; 161 } 162 else 163 { 164 return $(self.false-properties) ; 165 } 166 } 167 168 rule eval ( e * ) 169 { 170 local r ; 171 if $(e[1]) && $(e[2]) && $(e[3]) 172 { 173 if $(e[2]) = "and" 174 { 175 if $(e[1]) = "true" && $(e[3]) = "true" 176 { 177 r = [ eval "true" $(e[4-]) ] ; 178 } 179 else 180 { 181 r = [ eval "false" $(e[4-]) ] ; 182 } 183 } 184 else if $(e[2]) = "or" 185 { 186 if $(e[1]) = "true" || $(e[3]) = "true" 187 { 188 r = [ eval "true" $(e[4-]) ] ; 189 } 190 else 191 { 192 r = [ eval "false" $(e[4-]) ] ; 193 } 194 } 195 } 196 else 197 { 198 r = $(e[1]) ; 199 } 200 return $(r) ; 201 } 202} 203