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