# This module is imported by testing.py. The definitions here are # too tricky to do in Python # Causes the 'target' to exist after bjam invocation if and only if all the # dependencies were successfully built. # rule expect-success ( target : dependency + : requirements * ) { **passed** $(target) : $(sources) ; } IMPORT testing : expect-success : : testing.expect-success ; # Causes the 'target' to exist after bjam invocation if and only if all some of # the dependencies were not successfully built. # rule expect-failure ( target : dependency + : properties * ) { local grist = [ MATCH ^<(.*)> : $(dependency:G) ] ; local marker = $(dependency:G=$(grist)*fail) ; (failed-as-expected) $(marker) ; FAIL_EXPECTED $(dependency) ; LOCATE on $(marker) = [ on $(dependency) return $(LOCATE) ] ; RMOLD $(marker) ; DEPENDS $(marker) : $(dependency) ; DEPENDS $(target) : $(marker) ; **passed** $(target) : $(marker) ; } IMPORT testing : expect-failure : : testing.expect-failure ; # The rule/action combination used to report successful passing of a test. # rule **passed** { # Force deletion of the target, in case any dependencies failed to build. RMOLD $(<) ; } # Used to create test files signifying passed tests. # actions **passed** { echo passed > "$(<)" } # Used to create replacement object files that do not get created during tests # that are expected to fail. # actions (failed-as-expected) { echo failed as expected > "$(<)" } if [ os.name ] = VMS { actions **passed** { PIPE WRITE SYS$OUTPUT "passed" > $(<:W) } actions (failed-as-expected) { PIPE WRITE SYS$OUTPUT "failed as expected" > $(<:W) } } # Runs executable 'sources' and stores stdout in file 'target'. Unless # --preserve-test-targets command line option has been specified, removes the # executable. The 'target-to-remove' parameter controls what should be removed: # - if 'none', does not remove anything, ever # - if empty, removes 'source' # - if non-empty and not 'none', contains a list of sources to remove. # rule capture-output ( target : source : properties * : targets-to-remove * ) { output-file on $(target) = $(target:S=.output) ; LOCATE on $(target:S=.output) = [ on $(target) return $(LOCATE) ] ; # The INCLUDES kill a warning about independent target... INCLUDES $(target) : $(target:S=.output) ; # but it also puts .output into dependency graph, so we must tell jam it is # OK if it cannot find the target or updating rule. NOCARE $(target:S=.output) ; # This has two-fold effect. First it adds input files to the dependency # graph, preventing a warning. Second, it causes input files to be bound # before target is created. Therefore, they are bound using SEARCH setting # on them and not LOCATE setting of $(target), as in other case (due to jam # bug). DEPENDS $(target) : [ on $(target) return $(INPUT_FILES) ] ; if $(targets-to-remove) = none { targets-to-remove = ; } else if ! $(targets-to-remove) { targets-to-remove = $(source) ; } if [ on $(target) return $(REMOVE_TEST_TARGETS) ] { TEMPORARY $(targets-to-remove) ; # Set a second action on target that will be executed after capture # output action. The 'RmTemps' rule has the 'ignore' modifier so it is # always considered succeeded. This is needed for 'run-fail' test. For # that test the target will be marked with FAIL_EXPECTED, and without # 'ignore' successful execution will be negated and be reported as # failure. With 'ignore' we do not detect a case where removing files # fails, but it is not likely to happen. RmTemps $(target) : $(targets-to-remove) ; } if ! [ feature.get-values testing.launcher : $(properties) ] { ## On VMS set default launcher to MCR if [ os.name ] = VMS { LAUNCHER on $(target) = MCR ; } } } if [ os.name ] = NT { .STATUS = %status% ; .SET_STATUS = "set status=%ERRORLEVEL%" ; .RUN_OUTPUT_NL = "echo." ; .THEN = "(" ; .EXIT_SUCCESS = "0" ; .STATUS_0 = "%status% EQU 0 $(.THEN)" ; .STATUS_NOT_0 = "%status% NEQ 0 $(.THEN)" ; .VERBOSE = "%verbose% EQU 1 $(.THEN)" ; .ENDIF = ")" ; .SHELL_SET = "set " ; .CATENATE = type ; .CP = copy ; .NULLIN = ; } else if [ os.name ] = VMS { local nl = " " ; .STATUS = "''status'" ; .SET_STATUS = "status=$STATUS" ; .SAY = "pipe write sys$output" ; ## not really echo .RUN_OUTPUT_NL = "$(.SAY) \"\"" ; .THEN = "$(nl)then" ; .EXIT_SUCCESS = "1" ; .SUCCESS = "status .eq. $(.EXIT_SUCCESS) $(.THEN)" ; .STATUS_0 = "status .eq. 0 $(.THEN)" ; .STATUS_NOT_0 = "status .ne. 0 $(.THEN)" ; .VERBOSE = "verbose .eq. 1 $(.THEN)" ; .ENDIF = "endif" ; .SHELL_SET = "" ; .CATENATE = type ; .CP = copy ; .NULLIN = ; } else { .STATUS = "$status" ; .SET_STATUS = "status=$?" ; .RUN_OUTPUT_NL = "echo" ; .THEN = "; then" ; .EXIT_SUCCESS = "0" ; .STATUS_0 = "test $status -eq 0 $(.THEN)" ; .STATUS_NOT_0 = "test $status -ne 0 $(.THEN)" ; .VERBOSE = "test $verbose -eq 1 $(.THEN)" ; .ENDIF = "fi" ; .SHELL_SET = "" ; .CATENATE = cat ; .CP = cp ; .NULLIN = "<" "/dev/null" ; } .VERBOSE_TEST = 0 ; if --verbose-test in [ modules.peek : ARGV ] { .VERBOSE_TEST = 1 ; } .RM = [ common.rm-command ] ; actions capture-output bind INPUT_FILES output-file { $(PATH_SETUP) $(LAUNCHER) "$(>)" $(ARGS) "$(INPUT_FILES)" > "$(output-file)" 2>&1 $(.SET_STATUS) $(.RUN_OUTPUT_NL) >> "$(output-file)" echo EXIT STATUS: $(.STATUS) >> "$(output-file)" if $(.STATUS_0) $(.CP) "$(output-file)" "$(<)" $(.ENDIF) $(.SHELL_SET)verbose=$(.VERBOSE_TEST) if $(.STATUS_NOT_0) $(.SHELL_SET)verbose=1 $(.ENDIF) if $(.VERBOSE) echo ====== BEGIN OUTPUT ====== $(.CATENATE) "$(output-file)" echo ====== END OUTPUT ====== $(.ENDIF) exit $(.STATUS) } IMPORT testing : capture-output : : testing.capture-output ; actions quietly updated ignore piecemeal together RmTemps { $(.RM) "$(>)" } if [ os.name ] = VMS { actions capture-output bind INPUT_FILES output-file { $(PATH_SETUP) !! Execute twice - first for status, second for output set noon pipe $(LAUNCHER) $(>:W) $(ARGS) $(INPUT_FILES:W) 2>NL: >NL: $(.SET_STATUS) pipe $(LAUNCHER) $(>:W) $(ARGS) $(INPUT_FILES:W) | type sys$input /out=$(output-file:W) set on !! Harmonize VMS success status with POSIX if $(.SUCCESS) $(.SHELL_SET)status="0" $(.ENDIF) $(.RUN_OUTPUT_NL) | append /new sys$input $(output-file:W) $(.SAY) "EXIT STATUS: $(.STATUS)" | append /new sys$input $(output-file:W) if $(.STATUS_0) $(.CP) $(output-file:W) $(<:W) $(.ENDIF) $(.SHELL_SET)verbose=$(.VERBOSE_TEST) if $(.STATUS_NOT_0) $(.SHELL_SET)verbose=1 $(.ENDIF) if $(.VERBOSE) $(.SAY) "====== BEGIN OUTPUT ======" $(.CATENATE) $(output-file:W) $(.SAY) "====== END OUTPUT ======" $(.ENDIF) !! Harmonize VMS success status with POSIX on exit if $(.STATUS_0) $(.SHELL_SET)status="$(.EXIT_SUCCESS)" $(.ENDIF) exit "$(.STATUS)" } actions quietly updated ignore piecemeal together RmTemps { $(.RM) $(>:WJ=;*,);* } } .MAKE_FILE = [ common.file-creation-command ] ; rule unit-test ( target : source : properties * ) { if ! [ feature.get-values testing.launcher : $(properties) ] { ## On VMS set default launcher to MCR if [ os.name ] = VMS { LAUNCHER on $(target) = MCR ; } } } actions unit-test { $(PATH_SETUP) $(LAUNCHER) "$(>)" $(ARGS) && $(.MAKE_FILE) "$(<)" } if [ os.name ] = VMS { actions unit-test { $(PATH_SETUP) pipe $(LAUNCHER) $(>:W) $(ARGS) && $(.MAKE_FILE) $(<:W) } } # Note that this rule may be called multiple times for a single target in case # there are multiple actions operating on the same target in sequence. One such # example are msvc exe targets first created by a linker action and then updated # with an embedded manifest file by a separate action. rule record-time ( target : source : start end user system ) { local src-string = [$(source:G=:J=",")"] " ; USER_TIME on $(target) += $(src-string)$(user) ; SYSTEM_TIME on $(target) += $(src-string)$(system) ; # We need the following variables because attempting to perform such # variable expansion in actions would not work due to quotes getting treated # as regular characters. USER_TIME_SECONDS on $(target) += $(src-string)$(user)" seconds" ; SYSTEM_TIME_SECONDS on $(target) += $(src-string)$(system)" seconds" ; } # Calling this rule requests that Boost Build time how long it takes to build # the 'source' target and display the results both on the standard output and in # the 'target' file. # rule time ( target : sources + : properties * ) { # Set up rule for recording timing information. __TIMING_RULE__ on $(sources) = testing.record-time $(target) ; # Make sure the sources get rebuilt any time we need to retrieve that # information. REBUILDS $(target) : $(sources) ; } actions time { echo user: $(USER_TIME) echo system: $(SYSTEM_TIME) echo user: $(USER_TIME_SECONDS) > "$(<)" echo system: $(SYSTEM_TIME_SECONDS) >> "$(<)" } if [ os.name ] = VMS { actions time { WRITE SYS$OUTPUT "user: ", "$(USER_TIME)" WRITE SYS$OUTPUT "system: ", "(SYSTEM_TIME)" PIPE WRITE SYS$OUTPUT "user: ", "$(USER_TIME_SECONDS)" | TYPE SYS$INPUT /OUT=$(<:W) PIPE WRITE SYS$OUTPUT "system: ", "$(SYSTEM_TIME_SECONDS)" | APPEND /NEW SYS$INPUT $(<:W) } }