]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/tools/testing.jam
update sources to v12.2.3
[ceph.git] / ceph / src / boost / tools / build / src / tools / testing.jam
1 # Copyright 2005 Dave Abrahams
2 # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
3 # Copyright 2014-2015 Rene Rivera
4 # Copyright 2014 Microsoft Corporation
5 # Distributed under the Boost Software License, Version 1.0.
6 # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
7
8 # This module implements regression testing framework. It declares a number of
9 # main target rules which perform some action and, if the results are OK,
10 # creates an output file.
11 #
12 # The exact list of rules is:
13 # 'compile' -- creates .test file if compilation of sources was
14 # successful.
15 # 'compile-fail' -- creates .test file if compilation of sources failed.
16 # 'run' -- creates .test file is running of executable produced from
17 # sources was successful. Also leaves behind .output file
18 # with the output from program run.
19 # 'run-fail' -- same as above, but .test file is created if running fails.
20 #
21 # In all cases, presence of .test file is an indication that the test passed.
22 # For more convenient reporting, you might want to use C++ Boost regression
23 # testing utilities (see http://www.boost.org/more/regression.html).
24 #
25 # For historical reason, a 'unit-test' rule is available which has the same
26 # syntax as 'exe' and behaves just like 'run'.
27
28 # Things to do:
29 # - Teach compiler_status handle Jamfile.v2.
30 # Notes:
31 # - <no-warn> is not implemented, since it is Como-specific, and it is not
32 # clear how to implement it
33 # - std::locale-support is not implemented (it is used in one test).
34
35
36 import alias ;
37 import "class" ;
38 import common ;
39 import errors ;
40 import feature ;
41 import generators ;
42 import os ;
43 import path ;
44 import project ;
45 import property ;
46 import property-set ;
47 import regex ;
48 import sequence ;
49 import targets ;
50 import toolset ;
51 import type ;
52 import virtual-target ;
53
54
55 rule init ( )
56 {
57 }
58
59
60 # Feature controlling the command used to launch test programs.
61 feature.feature testing.launcher : : free optional ;
62
63 feature.feature test-info : : free incidental ;
64 feature.feature testing.arg : : free incidental ;
65 feature.feature testing.input-file : : free dependency ;
66
67 feature.feature preserve-test-targets : on off : incidental propagated ;
68
69 # Feature to control whether executable binaries are run as part of test.
70 # This can be used to just compile test cases in cross compilation situations.
71 feature.feature testing.execute : on off : incidental propagated ;
72 feature.set-default testing.execute : on ;
73
74 # Register target types.
75 type.register TEST : test ;
76 type.register COMPILE : : TEST ;
77 type.register COMPILE_FAIL : : TEST ;
78 type.register RUN_OUTPUT : run ;
79 type.register RUN : : TEST ;
80 type.register RUN_FAIL : : TEST ;
81 type.register LINK_FAIL : : TEST ;
82 type.register LINK : : TEST ;
83 type.register UNIT_TEST : passed : TEST ;
84
85
86 # Suffix to denote test target directory
87 #
88 .TEST-DIR-SUFFIX = ".test" ;
89 if [ os.name ] = VMS
90 {
91 .TEST-DIR-SUFFIX = "$test" ;
92 }
93
94 # Declare the rules which create main targets. While the 'type' module already
95 # creates rules with the same names for us, we need extra convenience: default
96 # name of main target, so write our own versions.
97
98 # Helper rule. Create a test target, using basename of first source if no target
99 # name is explicitly passed. Remembers the created target in a global variable.
100 #
101 rule make-test ( target-type : sources + : requirements * : target-name ? )
102 {
103 target-name ?= $(sources[1]:D=:S=) ;
104
105 # Having periods (".") in the target name is problematic because the typed
106 # generator will strip the suffix and use the bare name for the file
107 # targets. Even though the location-prefix averts problems most times it
108 # does not prevent ambiguity issues when referring to the test targets. For
109 # example when using the XML log output. So we rename the target to remove
110 # the periods, and provide an alias for users.
111 local real-name = [ regex.replace $(target-name) "[.]" "~" ] ;
112
113 local project = [ project.current ] ;
114 # The <location-prefix> forces the build system for generate paths in the
115 # form '$build_dir/array1$(.TEST-DIR-SUFFIX)/gcc/debug'. This is necessary
116 # to allow post-processing tools to work.
117 local t = [ targets.create-typed-target [ type.type-from-rule-name
118 $(target-type) ] : $(project) : $(real-name) : $(sources) :
119 $(requirements) <location-prefix>$(real-name)$(.TEST-DIR-SUFFIX) ] ;
120
121 # The alias to the real target, per period replacement above.
122 if $(real-name) != $(target-name)
123 {
124 alias $(target-name) : $(t) ;
125 }
126
127 # Remember the test (for --dump-tests). A good way would be to collect all
128 # given a project. This has some technical problems: e.g. we can not call
129 # this dump from a Jamfile since projects referred by 'build-project' are
130 # not available until the whole Jamfile has been loaded.
131 .all-tests += $(t) ;
132 return $(t) ;
133 }
134
135
136 # Note: passing more that one cpp file here is known to fail. Passing a cpp file
137 # and a library target works.
138 #
139 rule compile ( sources + : requirements * : target-name ? )
140 {
141 return [ make-test compile : $(sources) : $(requirements) : $(target-name) ]
142 ;
143 }
144
145
146 rule compile-fail ( sources + : requirements * : target-name ? )
147 {
148 return [ make-test compile-fail : $(sources) : $(requirements) :
149 $(target-name) ] ;
150 }
151
152
153 rule link ( sources + : requirements * : target-name ? )
154 {
155 return [ make-test link : $(sources) : $(requirements) : $(target-name) ] ;
156 }
157
158
159 rule link-fail ( sources + : requirements * : target-name ? )
160 {
161 return [ make-test link-fail : $(sources) : $(requirements) : $(target-name)
162 ] ;
163 }
164
165
166 rule handle-input-files ( input-files * )
167 {
168 if $(input-files[2])
169 {
170 # Check that sorting made when creating property-set instance will not
171 # change the ordering.
172 if [ sequence.insertion-sort $(input-files) ] != $(input-files)
173 {
174 errors.user-error "Names of input files must be sorted alphabetically"
175 : "due to internal limitations" ;
176 }
177 }
178 return <testing.input-file>$(input-files) ;
179 }
180
181
182 rule run ( sources + : args * : input-files * : requirements * : target-name ? :
183 default-build * )
184 {
185 requirements += <testing.arg>$(args:J=" ") ;
186 requirements += [ handle-input-files $(input-files) ] ;
187 return [ make-test run : $(sources) : $(requirements) : $(target-name) ] ;
188 }
189
190
191 rule run-fail ( sources + : args * : input-files * : requirements * :
192 target-name ? : default-build * )
193 {
194 requirements += <testing.arg>$(args:J=" ") ;
195 requirements += [ handle-input-files $(input-files) ] ;
196 return [ make-test run-fail : $(sources) : $(requirements) : $(target-name)
197 ] ;
198 }
199
200
201 # Use 'test-suite' as a synonym for 'alias', for backward compatibility.
202 IMPORT : alias : : test-suite ;
203
204
205 # For all main targets in 'project-module', which are typed targets with type
206 # derived from 'TEST', produce some interesting information.
207 #
208 rule dump-tests
209 {
210 for local t in $(.all-tests)
211 {
212 dump-test $(t) ;
213 }
214 }
215
216
217 # Given a project location in normalized form (slashes are forward), compute the
218 # name of the Boost library.
219 #
220 local rule get-library-name ( path )
221 {
222 # Path is in normalized form, so all slashes are forward.
223 local match1 = [ MATCH /(tools|libs)/(.*)/(test|example) : $(path) ] ;
224 local match2 = [ MATCH /(tools|libs)/(.*)$ : $(path) ] ;
225 local match3 = [ MATCH (/status$) : $(path) ] ;
226
227 if $(match1) { return $(match1[2]) ; }
228 else if $(match2) { return $(match2[2]) ; }
229 else if $(match3) { return "" ; }
230 else if --dump-tests in [ modules.peek : ARGV ]
231 {
232 # The 'run' rule and others might be used outside boost. In that case,
233 # just return the path, since the 'library name' makes no sense.
234 return $(path) ;
235 }
236 }
237
238
239 # Was an XML dump requested?
240 .out-xml = [ MATCH --out-xml=(.*) : [ modules.peek : ARGV ] ] ;
241
242
243 # Takes a target (instance of 'basic-target') and prints
244 # - its type
245 # - its name
246 # - comments specified via the <test-info> property
247 # - relative location of all source from the project root.
248 #
249 rule dump-test ( target )
250 {
251 local type = [ $(target).type ] ;
252 local name = [ $(target).name ] ;
253 local project = [ $(target).project ] ;
254
255 local project-root = [ $(project).get project-root ] ;
256 local library = [ get-library-name [ path.root [ $(project).get location ]
257 [ path.pwd ] ] ] ;
258 if $(library)
259 {
260 name = $(library)/$(name) ;
261 }
262
263 local sources = [ $(target).sources ] ;
264 local source-files ;
265 for local s in $(sources)
266 {
267 if [ class.is-a $(s) : file-reference ]
268 {
269 local location = [ path.root [ path.root [ $(s).name ]
270 [ $(s).location ] ] [ path.pwd ] ] ;
271
272 source-files += [ path.relative-to [ path.root $(project-root)
273 [ path.pwd ] ] $(location) ] ;
274 }
275 }
276
277 local target-name =
278 [ $(project).get location ] // [ $(target).name ] $(.TEST-DIR-SUFFIX) ;
279 target-name = $(target-name:J=) ;
280
281 local r = [ $(target).requirements ] ;
282 # Extract values of the <test-info> feature.
283 local test-info = [ $(r).get <test-info> ] ;
284
285 # If the user requested XML output on the command-line, add the test info to
286 # that XML file rather than dumping them to stdout.
287 if $(.out-xml)
288 {
289 local nl = "
290 " ;
291 .contents on $(.out-xml) +=
292 "$(nl) <test type=\"$(type)\" name=\"$(name)\">"
293 "$(nl) <target><![CDATA[$(target-name)]]></target>"
294 "$(nl) <info><![CDATA[$(test-info)]]></info>"
295 "$(nl) <source><![CDATA[$(source-files)]]></source>"
296 "$(nl) </test>"
297 ;
298 }
299 else
300 {
301 # Format them into a single string of quoted strings.
302 test-info = \"$(test-info:J=\"\ \")\" ;
303
304 ECHO boost-test($(type)) \"$(name)\" [$(test-info)] ":"
305 \"$(source-files)\" ;
306 }
307 }
308
309
310 # Register generators. Depending on target type, either 'expect-success' or
311 # 'expect-failure' rule will be used.
312 generators.register-standard testing.expect-success : OBJ : COMPILE ;
313 generators.register-standard testing.expect-failure : OBJ : COMPILE_FAIL ;
314 generators.register-standard testing.expect-success : RUN_OUTPUT : RUN ;
315 generators.register-standard testing.expect-failure : RUN_OUTPUT : RUN_FAIL ;
316 generators.register-standard testing.expect-failure : EXE : LINK_FAIL ;
317 generators.register-standard testing.expect-success : EXE : LINK ;
318
319 # Generator which runs an EXE and captures output.
320 generators.register-standard testing.capture-output : EXE : RUN_OUTPUT ;
321
322 # Generator which creates a target if sources run successfully. Differs from RUN
323 # in that run output is not captured. The reason why it exists is that the 'run'
324 # rule is much better for automated testing, but is not user-friendly (see
325 # http://article.gmane.org/gmane.comp.lib.boost.build/6353).
326 generators.register-standard testing.unit-test : EXE : UNIT_TEST ;
327
328
329 # The action rules called by generators.
330
331 # Causes the 'target' to exist after bjam invocation if and only if all the
332 # dependencies were successfully built.
333 #
334 rule expect-success ( target : dependency + : requirements * )
335 {
336 **passed** $(target) : $(dependency) ;
337 }
338
339
340 # Causes the 'target' to exist after bjam invocation if and only if all some of
341 # the dependencies were not successfully built.
342 #
343 rule expect-failure ( target : dependency + : properties * )
344 {
345 local grist = [ MATCH ^<(.*)> : $(dependency:G) ] ;
346 local marker = $(dependency:G=$(grist)*fail) ;
347 (failed-as-expected) $(marker) ;
348 FAIL_EXPECTED $(dependency) ;
349 LOCATE on $(marker) = [ on $(dependency) return $(LOCATE) ] ;
350 RMOLD $(marker) ;
351 DEPENDS $(marker) : $(dependency) ;
352 DEPENDS $(target) : $(marker) ;
353 **passed** $(target) : $(marker) ;
354 }
355
356
357 # The rule/action combination used to report successful passing of a test.
358 #
359 rule **passed**
360 {
361 remove-test-targets $(<) ;
362
363 # Dump all the tests, if needed. We do it here, since dump should happen
364 # only after all Jamfiles have been read, and there is no such place
365 # currently defined (but there should be).
366 if ! $(.dumped-tests) && ( --dump-tests in [ modules.peek : ARGV ] )
367 {
368 .dumped-tests = true ;
369 dump-tests ;
370 }
371
372 # Force deletion of the target, in case any dependencies failed to build.
373 RMOLD $(<) ;
374 }
375
376
377
378 # Used to create test files signifying passed tests.
379 #
380 actions **passed**
381 {
382 echo passed > "$(<)"
383 }
384
385 # Used to create replacement object files that do not get created during tests
386 # that are expected to fail.
387 #
388 actions (failed-as-expected)
389 {
390 echo failed as expected > "$(<)"
391 }
392
393
394 if [ os.name ] = VMS
395 {
396 actions **passed**
397 {
398 PIPE WRITE SYS$OUTPUT "passed" > $(<:W)
399 }
400
401 actions (failed-as-expected)
402 {
403 PIPE WRITE SYS$OUTPUT "failed as expected" > $(<:W)
404 }
405 }
406
407 rule run-path-setup ( target : source : properties * )
408 {
409 # For testing, we need to make sure that all dynamic libraries needed by the
410 # test are found. So, we collect all paths from dependency libraries (via
411 # xdll-path property) and add whatever explicit dll-path user has specified.
412 # The resulting paths are added to the environment on each test invocation.
413 local target-os = [ feature.get-values <target-os> : $(properties) ] ;
414 local dll-paths = [ feature.get-values <dll-path> : $(properties) ] ;
415 dll-paths += [ feature.get-values <xdll-path> : $(properties) ] ;
416 if $(target-os) != vxworks
417 {
418 dll-paths += [ on $(source) return $(RUN_PATH) ] ;
419 }
420 dll-paths = [ sequence.unique $(dll-paths) ] ;
421 if $(dll-paths)
422 {
423 translate-to-os = path.native ;
424 if [ os.name ] = VMS
425 {
426 translate-to-os = path.to-VMS ;
427 }
428 if $(target-os) = vxworks
429 {
430 # map <build-os> paths to <target-os> paths
431 local save-os = [ modules.peek os : .name ] ;
432 modules.poke os : .name : VXWORKS ;
433 local parent = [ os.environ PKG_SRC_BUILD_DIR ] ;
434 local prefix = [ os.environ LAYER_SRC_PATH ] ;
435 local target-dll-paths ;
436 for local e in $(dll-paths)
437 {
438 target-dll-paths += [ path.join $(prefix) [ path.relative $(e) $(parent) : noerror ] ] ;
439 }
440 PATH_SETUP on $(target) = [ common.prepend-path-variable-command
441 [ os.shared-library-path-variable ] : $(target-dll-paths) ] ;
442 modules.poke os : .name : $(save-os) ;
443 }
444 else
445 {
446 dll-paths = [ sequence.transform $(translate-to-os) : $(dll-paths) ] ;
447 PATH_SETUP on $(target) = [ common.prepend-path-variable-command
448 [ os.shared-library-path-variable ] : $(dll-paths) ] ;
449 }
450 }
451 }
452
453
454 local argv = [ modules.peek : ARGV ] ;
455
456 toolset.flags testing.capture-output ARGS <testing.arg> ;
457 toolset.flags testing.capture-output INPUT_FILES <testing.input-file> ;
458 toolset.flags testing.capture-output LAUNCHER <testing.launcher> ;
459
460 .preserve-test-targets = on ;
461 if --remove-test-targets in [ modules.peek : ARGV ]
462 {
463 .preserve-test-targets = off ;
464 }
465
466
467 # Runs executable 'sources' and stores stdout in file 'target'. Unless
468 # --preserve-test-targets command line option has been specified, removes the
469 # executable. The 'target-to-remove' parameter controls what should be removed:
470 # - if 'none', does not remove anything, ever
471 # - if empty, removes 'source'
472 # - if non-empty and not 'none', contains a list of sources to remove.
473 #
474 rule capture-output ( target : source : properties * : targets-to-remove * )
475 {
476 output-file on $(target) = $(target:S=.output) ;
477 LOCATE on $(target:S=.output) = [ on $(target) return $(LOCATE) ] ;
478
479 # The INCLUDES kill a warning about independent target...
480 INCLUDES $(target) : $(target:S=.output) ;
481 # but it also puts .output into dependency graph, so we must tell jam it is
482 # OK if it cannot find the target or updating rule.
483 NOCARE $(target:S=.output) ;
484
485 # This has two-fold effect. First it adds input files to the dependency
486 # graph, preventing a warning. Second, it causes input files to be bound
487 # before target is created. Therefore, they are bound using SEARCH setting
488 # on them and not LOCATE setting of $(target), as in other case (due to jam
489 # bug).
490 DEPENDS $(target) : [ on $(target) return $(INPUT_FILES) ] ;
491
492 if $(targets-to-remove) = none
493 {
494 targets-to-remove = ;
495 }
496 else if ! $(targets-to-remove)
497 {
498 targets-to-remove = $(source) ;
499 }
500
501 run-path-setup $(target) : $(source) : $(properties) ;
502
503 DISABLE_TEST_EXECUTION on $(target) = 0 ;
504 if [ feature.get-values testing.execute : $(properties) ] = off
505 {
506 DISABLE_TEST_EXECUTION on $(target) = 1 ;
507 }
508
509 if [ feature.get-values preserve-test-targets : $(properties) ] = off
510 || $(.preserve-test-targets) = off
511 {
512 rmtemp-sources $(target) : $(targets-to-remove) ;
513 for local to-remove in $(targets-to-remove)
514 {
515 rmtemp-all-sources $(to-remove) ;
516 }
517 }
518
519 if ! [ feature.get-values testing.launcher : $(properties) ]
520 {
521 ## On VMS set default launcher to MCR
522 if [ os.name ] = VMS { LAUNCHER on $(target) = MCR ; }
523 }
524 }
525
526 .types-to-remove = EXE OBJ ;
527
528 local rule remove-test-targets ( targets + )
529 {
530 if $(.preserve-test-targets) = off
531 {
532 rmtemp-all-sources $(target) ;
533 }
534 }
535
536 local rule rmtemp-all-sources ( target )
537 {
538 local sources ;
539 local action = [ on $(target) return $(.action) ] ;
540 if $(action)
541 {
542 local action-sources = [ $(action).sources ] ;
543 for local source in $(action-sources)
544 {
545 local source-type = [ $(source).type ] ;
546 if $(source-type) in $(.types-to-remove)
547 {
548 sources += [ $(source).actual-name ] ;
549 }
550 else
551 {
552 # ECHO IGNORED: $(source) :: $(source-type) ;
553 }
554 }
555 if $(sources)
556 {
557 rmtemp-sources $(target) : $(sources) ;
558 for local source in $(sources)
559 {
560 rmtemp-all-sources $(source) ;
561 }
562 }
563 }
564 }
565
566 local rule rmtemp-sources ( target : sources * )
567 {
568 if $(sources)
569 {
570 TEMPORARY $(sources) ;
571 # Set a second action on target that will be executed after capture
572 # output action. The 'RmTemps' rule has the 'ignore' modifier so it is
573 # always considered succeeded. This is needed for 'run-fail' test. For
574 # that test the target will be marked with FAIL_EXPECTED, and without
575 # 'ignore' successful execution will be negated and be reported as
576 # failure. With 'ignore' we do not detect a case where removing files
577 # fails, but it is not likely to happen.
578 RmTemps $(target) : $(sources) ;
579 }
580 }
581
582
583 if [ os.name ] = NT
584 {
585 .STATUS = %status% ;
586 .SET_STATUS = "set status=%ERRORLEVEL%" ;
587 .RUN_OUTPUT_NL = "echo." ;
588 .THEN = "(" ;
589 .EXIT_SUCCESS = "0" ;
590 .STATUS_0 = "%status% EQU 0 $(.THEN)" ;
591 .STATUS_NOT_0 = "%status% NEQ 0 $(.THEN)" ;
592 .VERBOSE = "%verbose% EQU 1 $(.THEN)" ;
593 .ENDIF = ")" ;
594 .SHELL_SET = "set " ;
595 .CATENATE = type ;
596 .CP = copy ;
597 .NULLIN = ;
598 }
599 else if [ os.name ] = VMS
600 {
601 local nl = "
602 " ;
603
604 .STATUS = "''status'" ;
605 .SET_STATUS = "status=$STATUS" ;
606 .SAY = "pipe write sys$output" ; ## not really echo
607 .RUN_OUTPUT_NL = "$(.SAY) \"\"" ;
608 .THEN = "$(nl)then" ;
609 .EXIT_SUCCESS = "1" ;
610 .SUCCESS = "status .eq. $(.EXIT_SUCCESS) $(.THEN)" ;
611 .STATUS_0 = "status .eq. 0 $(.THEN)" ;
612 .STATUS_NOT_0 = "status .ne. 0 $(.THEN)" ;
613 .VERBOSE = "verbose .eq. 1 $(.THEN)" ;
614 .ENDIF = "endif" ;
615 .SHELL_SET = "" ;
616 .CATENATE = type ;
617 .CP = copy ;
618 .NULLIN = ;
619 }
620 else
621 {
622 .STATUS = "$status" ;
623 .SET_STATUS = "status=$?" ;
624 .RUN_OUTPUT_NL = "echo" ;
625 .THEN = "; then" ;
626 .EXIT_SUCCESS = "0" ;
627 .STATUS_0 = "test $status -eq 0 $(.THEN)" ;
628 .STATUS_NOT_0 = "test $status -ne 0 $(.THEN)" ;
629 .VERBOSE = "test $verbose -eq 1 $(.THEN)" ;
630 .ENDIF = "fi" ;
631 .SHELL_SET = "" ;
632 .CATENATE = cat ;
633 .CP = cp ;
634 .NULLIN = "<" "/dev/null" ;
635 }
636
637
638 .VERBOSE_TEST = 0 ;
639 if --verbose-test in [ modules.peek : ARGV ]
640 {
641 .VERBOSE_TEST = 1 ;
642 }
643
644
645 .RM = [ common.rm-command ] ;
646
647
648 actions capture-output bind INPUT_FILES output-file
649 {
650 $(PATH_SETUP)
651 $(.SHELL_SET)status=$(DISABLE_TEST_EXECUTION)
652 if $(.STATUS_NOT_0)
653 echo Skipping test execution due to testing.execute=off
654 exit $(.EXIT_SUCCESS)
655 $(.ENDIF)
656 $(LAUNCHER) "$(>)" $(ARGS) "$(INPUT_FILES)" > "$(output-file)" 2>&1 $(.NULLIN)
657 $(.SET_STATUS)
658 $(.RUN_OUTPUT_NL) >> "$(output-file)"
659 echo EXIT STATUS: $(.STATUS) >> "$(output-file)"
660 if $(.STATUS_0)
661 $(.CP) "$(output-file)" "$(<)"
662 $(.ENDIF)
663 $(.SHELL_SET)verbose=$(.VERBOSE_TEST)
664 if $(.STATUS_NOT_0)
665 $(.SHELL_SET)verbose=1
666 $(.ENDIF)
667 if $(.VERBOSE)
668 echo ====== BEGIN OUTPUT ======
669 $(.CATENATE) "$(output-file)"
670 echo ====== END OUTPUT ======
671 $(.ENDIF)
672 exit $(.STATUS)
673 }
674
675
676 actions quietly updated ignore piecemeal together RmTemps
677 {
678 $(.RM) "$(>)"
679 }
680
681 if [ os.name ] = VMS
682 {
683 actions capture-output bind INPUT_FILES output-file
684 {
685 $(PATH_SETUP)
686 $(.SHELL_SET)status=$(DISABLE_TEST_EXECUTION)
687 if $(.STATUS_NOT_0)
688 $(.SAY) "Skipping test execution due to testing.execute=off"
689 exit "$(.EXIT_SUCCESS)"
690 $(.ENDIF)
691 !! Execute twice - first for status, second for output
692 set noon
693 pipe $(LAUNCHER) $(>:W) $(ARGS) $(INPUT_FILES:W) 2>NL: >NL:
694 $(.SET_STATUS)
695 pipe $(LAUNCHER) $(>:W) $(ARGS) $(INPUT_FILES:W) | type sys$input /out=$(output-file:W)
696 set on
697 !! Harmonize VMS success status with POSIX
698 if $(.SUCCESS)
699 $(.SHELL_SET)status="0"
700 $(.ENDIF)
701 $(.RUN_OUTPUT_NL) | append /new sys$input $(output-file:W)
702 $(.SAY) "EXIT STATUS: $(.STATUS)" | append /new sys$input $(output-file:W)
703 if $(.STATUS_0)
704 $(.CP) $(output-file:W) $(<:W)
705 $(.ENDIF)
706 $(.SHELL_SET)verbose=$(.VERBOSE_TEST)
707 if $(.STATUS_NOT_0)
708 $(.SHELL_SET)verbose=1
709 $(.ENDIF)
710 if $(.VERBOSE)
711 $(.SAY) "====== BEGIN OUTPUT ======"
712 $(.CATENATE) $(output-file:W)
713 $(.SAY) "====== END OUTPUT ======"
714 $(.ENDIF)
715 !! Harmonize VMS success status with POSIX on exit
716 if $(.STATUS_0)
717 $(.SHELL_SET)status="$(.EXIT_SUCCESS)"
718 $(.ENDIF)
719 exit "$(.STATUS)"
720 }
721
722 actions quietly updated ignore piecemeal together RmTemps
723 {
724 $(.RM) $(>:WJ=;*,);*
725 }
726 }
727
728 .MAKE_FILE = [ common.file-creation-command ] ;
729
730 toolset.flags testing.unit-test LAUNCHER <testing.launcher> ;
731 toolset.flags testing.unit-test ARGS <testing.arg> ;
732
733
734 rule unit-test ( target : source : properties * )
735 {
736 run-path-setup $(target) : $(source) : $(properties) ;
737
738 if ! [ feature.get-values testing.launcher : $(properties) ]
739 {
740 ## On VMS set default launcher to MCR
741 if [ os.name ] = VMS { LAUNCHER on $(target) = MCR ; }
742 }
743 }
744
745
746 actions unit-test
747 {
748 $(PATH_SETUP)
749 $(LAUNCHER) "$(>)" $(ARGS) && $(.MAKE_FILE) "$(<)"
750 }
751
752 if [ os.name ] = VMS
753 {
754 actions unit-test
755 {
756 $(PATH_SETUP)
757 pipe $(LAUNCHER) $(>:W) $(ARGS) && $(.MAKE_FILE) $(<:W)
758 }
759 }
760
761 IMPORT $(__name__) : compile compile-fail run run-fail link link-fail
762 : : compile compile-fail run run-fail link link-fail ;
763
764
765 # This is a composing generator to support cases where a generator for the
766 # specified target constructs other targets as well. One such example is msvc's
767 # exe generator that constructs both EXE and PDB targets.
768 type.register TIME : time ;
769 generators.register-composing testing.time : : TIME ;
770
771
772 # Note that this rule may be called multiple times for a single target in case
773 # there are multiple actions operating on the same target in sequence. One such
774 # example are msvc exe targets first created by a linker action and then updated
775 # with an embedded manifest file by a separate action.
776 rule record-time ( target : source : start end user system clock )
777 {
778 local src-string = [$(source:G=:J=",")"] " ;
779 USER_TIME on $(target) += $(src-string)$(user) ;
780 SYSTEM_TIME on $(target) += $(src-string)$(system) ;
781 CLOCK_TIME on $(target) += $(src-string)$(clock) ;
782
783 # We need the following variables because attempting to perform such
784 # variable expansion in actions would not work due to quotes getting treated
785 # as regular characters.
786 USER_TIME_SECONDS on $(target) += $(src-string)$(user)" seconds" ;
787 SYSTEM_TIME_SECONDS on $(target) += $(src-string)$(system)" seconds" ;
788 CLOCK_TIME_SECONDS on $(target) += $(src-string)$(clock)" seconds" ;
789 }
790
791
792 # Support for generating timing information for any main target. To use
793 # declare a custom make target that uses the testing.time generator rule
794 # specified here. For example:
795 #
796 # make main.cpp : main_cpp.pro : @do-something ;
797 # time main.time : main.cpp ;
798 # actions do-something
799 # {
800 # sleep 2 && echo "$(<)" > "$(<)"
801 # }
802 #
803 # The above will generate a "main.time", and echo to output, timing
804 # information for the action of source "main.cpp".
805
806
807 IMPORT testing : record-time : : testing.record-time ;
808
809
810 # Calling this rule requests that Boost Build time how long it takes to build
811 # the 'source' target and display the results both on the standard output and in
812 # the 'target' file.
813 #
814 rule time ( target : sources + : properties * )
815 {
816 # Set up rule for recording timing information.
817 local action = [ on $(target) return $(.action) ] ;
818 for local action.source in [ $(action).sources ]
819 {
820 # Yes, this uses the private "actual-name" of the target action.
821 # But it's the only way to get at the real name of the sources
822 # given the context of header scanners.
823 __TIMING_RULE__ on [ $(action.source).actual-name ] = testing.record-time $(target) ;
824 }
825
826 # Make sure the sources get rebuilt any time we need to retrieve that
827 # information.
828 REBUILDS $(target) : $(sources) ;
829 }
830
831
832 actions time
833 {
834 echo user: $(USER_TIME)
835 echo system: $(SYSTEM_TIME)
836 echo clock: $(CLOCK_TIME)
837
838 echo user: $(USER_TIME_SECONDS) > "$(<)"
839 echo system: $(SYSTEM_TIME_SECONDS) >> "$(<)"
840 echo clock: $(CLOCK_TIME_SECONDS) >> "$(<)"
841 }
842
843 if [ os.name ] = VMS
844 {
845 actions time
846 {
847 WRITE SYS$OUTPUT "user: ", "$(USER_TIME)"
848 WRITE SYS$OUTPUT "system: ", "(SYSTEM_TIME)"
849 WRITE SYS$OUTPUT "clock: ", "(CLOCK_TIME)"
850
851 PIPE WRITE SYS$OUTPUT "user: ", "$(USER_TIME_SECONDS)" | TYPE SYS$INPUT /OUT=$(<:W)
852 PIPE WRITE SYS$OUTPUT "system: ", "$(SYSTEM_TIME_SECONDS)" | APPEND /NEW SYS$INPUT $(<:W)
853 PIPE WRITE SYS$OUTPUT "clock: ", "$(CLOCK_TIME_SECONDS)" | APPEND /NEW SYS$INPUT $(<:W)
854 }
855 }