1import feature ; 2 3# This module is imported by testing.py. The definitions here are 4# too tricky to do in Python 5 6# Causes the 'target' to exist after bjam invocation if and only if all the 7# dependencies were successfully built. 8# 9rule expect-success ( target : dependency + : requirements * ) 10{ 11 **passed** $(target) : $(sources) ; 12} 13IMPORT testing : expect-success : : testing.expect-success ; 14 15# Causes the 'target' to exist after bjam invocation if and only if all some of 16# the dependencies were not successfully built. 17# 18rule expect-failure ( target : dependency + : properties * ) 19{ 20 local grist = [ MATCH ^<(.*)> : $(dependency:G) ] ; 21 local marker = $(dependency:G=$(grist)*fail) ; 22 (failed-as-expected) $(marker) ; 23 FAIL_EXPECTED $(dependency) ; 24 LOCATE on $(marker) = [ on $(dependency) return $(LOCATE) ] ; 25 RMOLD $(marker) ; 26 DEPENDS $(marker) : $(dependency) ; 27 DEPENDS $(target) : $(marker) ; 28 **passed** $(target) : $(marker) ; 29} 30IMPORT testing : expect-failure : : testing.expect-failure ; 31 32# The rule/action combination used to report successful passing of a test. 33# 34rule **passed** 35{ 36 # Force deletion of the target, in case any dependencies failed to build. 37 RMOLD $(<) ; 38} 39 40 41# Used to create test files signifying passed tests. 42# 43actions **passed** 44{ 45 echo passed > "$(<)" 46} 47 48 49# Used to create replacement object files that do not get created during tests 50# that are expected to fail. 51# 52actions (failed-as-expected) 53{ 54 echo failed as expected > "$(<)" 55} 56 57 58if [ os.name ] = VMS 59{ 60 actions **passed** 61 { 62 PIPE WRITE SYS$OUTPUT "passed" > $(<:W) 63 } 64 65 actions (failed-as-expected) 66 { 67 PIPE WRITE SYS$OUTPUT "failed as expected" > $(<:W) 68 } 69} 70 71 72# Runs executable 'sources' and stores stdout in file 'target'. Unless 73# --preserve-test-targets command line option has been specified, removes the 74# executable. The 'target-to-remove' parameter controls what should be removed: 75# - if 'none', does not remove anything, ever 76# - if empty, removes 'source' 77# - if non-empty and not 'none', contains a list of sources to remove. 78# 79rule capture-output ( target : source : properties * : targets-to-remove * ) 80{ 81 output-file on $(target) = $(target:S=.output) ; 82 LOCATE on $(target:S=.output) = [ on $(target) return $(LOCATE) ] ; 83 84 # The INCLUDES kill a warning about independent target... 85 INCLUDES $(target) : $(target:S=.output) ; 86 # but it also puts .output into dependency graph, so we must tell jam it is 87 # OK if it cannot find the target or updating rule. 88 NOCARE $(target:S=.output) ; 89 90 # This has two-fold effect. First it adds input files to the dependency 91 # graph, preventing a warning. Second, it causes input files to be bound 92 # before target is created. Therefore, they are bound using SEARCH setting 93 # on them and not LOCATE setting of $(target), as in other case (due to jam 94 # bug). 95 DEPENDS $(target) : [ on $(target) return $(INPUT_FILES) ] ; 96 97 if $(targets-to-remove) = none 98 { 99 targets-to-remove = ; 100 } 101 else if ! $(targets-to-remove) 102 { 103 targets-to-remove = $(source) ; 104 } 105 106 if [ on $(target) return $(REMOVE_TEST_TARGETS) ] 107 { 108 TEMPORARY $(targets-to-remove) ; 109 # Set a second action on target that will be executed after capture 110 # output action. The 'RmTemps' rule has the 'ignore' modifier so it is 111 # always considered succeeded. This is needed for 'run-fail' test. For 112 # that test the target will be marked with FAIL_EXPECTED, and without 113 # 'ignore' successful execution will be negated and be reported as 114 # failure. With 'ignore' we do not detect a case where removing files 115 # fails, but it is not likely to happen. 116 RmTemps $(target) : $(targets-to-remove) ; 117 } 118 119 if ! [ feature.get-values testing.launcher : $(properties) ] 120 { 121 ## On VMS set default launcher to MCR 122 if [ os.name ] = VMS { LAUNCHER on $(target) = MCR ; } 123 } 124} 125 126 127if [ os.name ] = NT 128{ 129 .STATUS = %status% ; 130 .SET_STATUS = "set status=%ERRORLEVEL%" ; 131 .RUN_OUTPUT_NL = "echo." ; 132 .THEN = "(" ; 133 .EXIT_SUCCESS = "0" ; 134 .STATUS_0 = "%status% EQU 0 $(.THEN)" ; 135 .STATUS_NOT_0 = "%status% NEQ 0 $(.THEN)" ; 136 .VERBOSE = "%verbose% EQU 1 $(.THEN)" ; 137 .ENDIF = ")" ; 138 .SHELL_SET = "set " ; 139 .CATENATE = type ; 140 .CP = copy ; 141 .NULLIN = ; 142} 143else if [ os.name ] = VMS 144{ 145 local nl = " 146" ; 147 148 .STATUS = "''status'" ; 149 .SET_STATUS = "status=$STATUS" ; 150 .SAY = "pipe write sys$output" ; ## not really echo 151 .RUN_OUTPUT_NL = "$(.SAY) \"\"" ; 152 .THEN = "$(nl)then" ; 153 .EXIT_SUCCESS = "1" ; 154 .SUCCESS = "status .eq. $(.EXIT_SUCCESS) $(.THEN)" ; 155 .STATUS_0 = "status .eq. 0 $(.THEN)" ; 156 .STATUS_NOT_0 = "status .ne. 0 $(.THEN)" ; 157 .VERBOSE = "verbose .eq. 1 $(.THEN)" ; 158 .ENDIF = "endif" ; 159 .SHELL_SET = "" ; 160 .CATENATE = type ; 161 .CP = copy ; 162 .NULLIN = ; 163} 164else 165{ 166 .STATUS = "$status" ; 167 .SET_STATUS = "status=$?" ; 168 .RUN_OUTPUT_NL = "echo" ; 169 .THEN = "; then" ; 170 .EXIT_SUCCESS = "0" ; 171 .STATUS_0 = "test $status -eq 0 $(.THEN)" ; 172 .STATUS_NOT_0 = "test $status -ne 0 $(.THEN)" ; 173 .VERBOSE = "test $verbose -eq 1 $(.THEN)" ; 174 .ENDIF = "fi" ; 175 .SHELL_SET = "" ; 176 .CATENATE = cat ; 177 .CP = cp ; 178 .NULLIN = "<" "/dev/null" ; 179} 180 181 182.VERBOSE_TEST = 0 ; 183if --verbose-test in [ modules.peek : ARGV ] 184{ 185 .VERBOSE_TEST = 1 ; 186} 187 188 189.RM = [ common.rm-command ] ; 190 191 192actions capture-output bind INPUT_FILES output-file 193{ 194 $(PATH_SETUP) 195 $(LAUNCHER) "$(>)" $(ARGS) "$(INPUT_FILES)" > "$(output-file)" 2>&1 196 $(.SET_STATUS) 197 $(.RUN_OUTPUT_NL) >> "$(output-file)" 198 echo EXIT STATUS: $(.STATUS) >> "$(output-file)" 199 if $(.STATUS_0) 200 $(.CP) "$(output-file)" "$(<)" 201 $(.ENDIF) 202 $(.SHELL_SET)verbose=$(.VERBOSE_TEST) 203 if $(.STATUS_NOT_0) 204 $(.SHELL_SET)verbose=1 205 $(.ENDIF) 206 if $(.VERBOSE) 207 echo ====== BEGIN OUTPUT ====== 208 $(.CATENATE) "$(output-file)" 209 echo ====== END OUTPUT ====== 210 $(.ENDIF) 211 exit $(.STATUS) 212} 213 214IMPORT testing : capture-output : : testing.capture-output ; 215 216 217actions quietly updated ignore piecemeal together RmTemps 218{ 219 $(.RM) "$(>)" 220} 221 222 223if [ os.name ] = VMS 224{ 225 actions capture-output bind INPUT_FILES output-file 226 { 227 $(PATH_SETUP) 228 !! Execute twice - first for status, second for output 229 set noon 230 pipe $(LAUNCHER) $(>:W) $(ARGS) $(INPUT_FILES:W) 2>NL: >NL: 231 $(.SET_STATUS) 232 pipe $(LAUNCHER) $(>:W) $(ARGS) $(INPUT_FILES:W) | type sys$input /out=$(output-file:W) 233 set on 234 !! Harmonize VMS success status with POSIX 235 if $(.SUCCESS) 236 $(.SHELL_SET)status="0" 237 $(.ENDIF) 238 $(.RUN_OUTPUT_NL) | append /new sys$input $(output-file:W) 239 $(.SAY) "EXIT STATUS: $(.STATUS)" | append /new sys$input $(output-file:W) 240 if $(.STATUS_0) 241 $(.CP) $(output-file:W) $(<:W) 242 $(.ENDIF) 243 $(.SHELL_SET)verbose=$(.VERBOSE_TEST) 244 if $(.STATUS_NOT_0) 245 $(.SHELL_SET)verbose=1 246 $(.ENDIF) 247 if $(.VERBOSE) 248 $(.SAY) "====== BEGIN OUTPUT ======" 249 $(.CATENATE) $(output-file:W) 250 $(.SAY) "====== END OUTPUT ======" 251 $(.ENDIF) 252 !! Harmonize VMS success status with POSIX on exit 253 if $(.STATUS_0) 254 $(.SHELL_SET)status="$(.EXIT_SUCCESS)" 255 $(.ENDIF) 256 exit "$(.STATUS)" 257 } 258 259 actions quietly updated ignore piecemeal together RmTemps 260 { 261 $(.RM) $(>:WJ=;*,);* 262 } 263} 264 265 266.MAKE_FILE = [ common.file-creation-command ] ; 267 268 269rule unit-test ( target : source : properties * ) 270{ 271 if ! [ feature.get-values testing.launcher : $(properties) ] 272 { 273 ## On VMS set default launcher to MCR 274 if [ os.name ] = VMS { LAUNCHER on $(target) = MCR ; } 275 } 276} 277 278actions unit-test 279{ 280 $(PATH_SETUP) 281 $(LAUNCHER) "$(>)" $(ARGS) && $(.MAKE_FILE) "$(<)" 282} 283 284if [ os.name ] = VMS 285{ 286 actions unit-test 287 { 288 $(PATH_SETUP) 289 pipe $(LAUNCHER) $(>:W) $(ARGS) && $(.MAKE_FILE) $(<:W) 290 } 291} 292 293# Note that this rule may be called multiple times for a single target in case 294# there are multiple actions operating on the same target in sequence. One such 295# example are msvc exe targets first created by a linker action and then updated 296# with an embedded manifest file by a separate action. 297rule record-time ( target : source : start end user system ) 298{ 299 local src-string = [$(source:G=:J=",")"] " ; 300 USER_TIME on $(target) += $(src-string)$(user) ; 301 SYSTEM_TIME on $(target) += $(src-string)$(system) ; 302 303 # We need the following variables because attempting to perform such 304 # variable expansion in actions would not work due to quotes getting treated 305 # as regular characters. 306 USER_TIME_SECONDS on $(target) += $(src-string)$(user)" seconds" ; 307 SYSTEM_TIME_SECONDS on $(target) += $(src-string)$(system)" seconds" ; 308} 309 310# Calling this rule requests that Boost Build time how long it takes to build 311# the 'source' target and display the results both on the standard output and in 312# the 'target' file. 313# 314rule time ( target : sources + : properties * ) 315{ 316 # Set up rule for recording timing information. 317 __TIMING_RULE__ on $(sources) = testing.record-time $(target) ; 318 319 # Make sure the sources get rebuilt any time we need to retrieve that 320 # information. 321 REBUILDS $(target) : $(sources) ; 322} 323 324 325actions time 326{ 327 echo user: $(USER_TIME) 328 echo system: $(SYSTEM_TIME) 329 330 echo user: $(USER_TIME_SECONDS) > "$(<)" 331 echo system: $(SYSTEM_TIME_SECONDS) >> "$(<)" 332} 333 334if [ os.name ] = VMS 335{ 336 actions time 337 { 338 WRITE SYS$OUTPUT "user: ", "$(USER_TIME)" 339 WRITE SYS$OUTPUT "system: ", "(SYSTEM_TIME)" 340 341 PIPE WRITE SYS$OUTPUT "user: ", "$(USER_TIME_SECONDS)" | TYPE SYS$INPUT /OUT=$(<:W) 342 PIPE WRITE SYS$OUTPUT "system: ", "$(SYSTEM_TIME_SECONDS)" | APPEND /NEW SYS$INPUT $(<:W) 343 } 344} 345