• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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