1# Copyright 2002, 2005 Dave Abrahams 2# Copyright 2002, 2003, 2006 Rene Rivera 3# Copyright 2003 Vladimir Prus 4# Distributed under the Boost Software License, Version 1.0. 5# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 6 7# Documentation system, handles --help requests. 8# It defines rules that attach documentation to modules, rules, and variables. 9# Collects and generates documentation for the various parts of the build 10# system. The documentation is collected from comments integrated into the code. 11 12import modules ; 13import print ; 14import set ; 15import container ; 16import "class" ; 17import sequence ; 18import path ; 19 20 21# The type of output to generate. 22# "console" is formatted text echoed to the console (the default); 23# "text" is formatted text appended to the output file; 24# "html" is HTML output to the file. 25# 26help-output = console ; 27 28 29# The file to output documentation to when generating "text" or "html" help. 30# This is without extension as the extension is determined by the type of 31# output. 32# 33help-output-file = help ; 34 35# Whether to include local rules in help output. 36# 37.option.show-locals ?= ; 38 39# When showing documentation for a module, whether to also generate 40# automatically the detailed docs for each item in the module. 41# 42.option.detailed ?= ; 43 44# Generate debug output as the help is generated and modules are parsed. 45# 46.option.debug ?= ; 47 48# These are all the options available for enabling or disabling to control the 49# help system in various ways. Options can be enabled or disabled with 50# '--help-enable-<option>', and '--help-disable-<option>' respectively. 51# 52.option-description = Help Options ; 53 54# Enable or disable a documentation option. 55# 56local rule set-option ( 57 option # The option name. 58 : value ? # Enabled (non-empty), or disabled (empty) 59) 60{ 61 .option.$(option) = $(value) ; 62} 63 64 65# Set the type of output. 66# 67local rule set-output ( type ) 68{ 69 help-output = $(type) ; 70} 71 72 73# Set the output to a file. 74# 75local rule set-output-file ( file ) 76{ 77 help-output-file = $(file) ; 78} 79 80 81# Extracts the brief comment from a complete comment. The brief comment is the 82# first sentence. 83# 84local rule brief-comment ( 85 docs * # The comment documentation. 86) 87{ 88 local d = $(docs:J=" ") ; 89 local p = [ MATCH ".*([.])$" : $(d) ] ; 90 if ! $(p) { d = $(d)"." ; } 91 d = $(d)" " ; 92 local m = [ MATCH "^([^.]+[.])(.*)" : $(d) ] ; 93 local brief = $(m[1]) ; 94 while $(m[2]) && [ MATCH "^([^ ])" : $(m[2]) ] 95 { 96 m = [ MATCH "^([^.]+[.])(.*)" : $(m[2]) ] ; 97 brief += $(m[1]) ; 98 } 99 return $(brief:J="") ; 100} 101 102 103# Specifies the documentation for the current module. 104# 105local rule set-module-doc ( 106 module-name ? # The name of the module to document. 107 : docs * # The documentation for the module. 108) 109{ 110 module-name ?= * ; 111 112 $(module-name).brief = [ brief-comment $(docs) ] ; 113 $(module-name).docs = $(docs) ; 114 115 if ! $(module-name) in $(documented-modules) 116 { 117 documented-modules += $(module-name) ; 118 } 119} 120 121 122# Specifies the documentation for the current module. 123# 124local rule set-module-copyright ( 125 module-name ? # The name of the module to document. 126 : copyright * # The copyright for the module. 127) 128{ 129 module-name ?= * ; 130 131 $(module-name).copy-brief = [ brief-comment $(copyright) ] ; 132 $(module-name).copy-docs = $(docs) ; 133 134 if ! $(module-name) in $(documented-modules) 135 { 136 documented-modules += $(module-name) ; 137 } 138} 139 140 141# Specifies the documentation for a rule in the current module. If called in the 142# global module, this documents a global rule. 143# 144local rule set-rule-doc ( 145 name # The name of the rule. 146 module-name ? # The name of the module to document. 147 is-local ? # Whether the rule is local to the module. 148 : docs * # The documentation for the rule. 149) 150{ 151 module-name ?= * ; 152 153 $(module-name).$(name).brief = [ brief-comment $(docs) ] ; 154 $(module-name).$(name).docs = $(docs) ; 155 $(module-name).$(name).is-local = $(is-local) ; 156 157 if ! $(name) in $($(module-name).rules) 158 { 159 $(module-name).rules += $(name) ; 160 } 161} 162 163 164# Specify a class, will turn a rule into a class. 165# 166local rule set-class-doc ( 167 name # The name of the class. 168 module-name ? # The name of the module to document. 169 : super-name ? # The super class name. 170) 171{ 172 module-name ?= * ; 173 174 $(module-name).$(name).is-class = true ; 175 if $(super-name) 176 { 177 $(module-name).$(name).super-name = $(super-name) ; 178 } 179 $(module-name).$(name).class-rules = 180 [ MATCH "^($(name)[.].*)" : $($(module-name).rules) ] ; 181 $(module-name).$($(module-name).$(name).class-rules).is-class-rule = true ; 182 183 $(module-name).classes += $(name) ; 184 $(module-name).class-rules += $($(module-name).$(name).class-rules) ; 185 $(module-name).rules = 186 [ set.difference $($(module-name).rules) : 187 $(name) $($(module-name).$(name).class-rules) ] ; 188} 189 190 191# Set the argument call signature of a rule. 192# 193local rule set-rule-arguments-signature ( 194 name # The name of the rule. 195 module-name ? # The name of the module to document. 196 : signature * # The arguments signature. 197) 198{ 199 module-name ?= * ; 200 201 $(module-name).$(name).signature = $(signature) ; 202} 203 204 205# Specifies the documentation for an argument of a rule. 206# 207local rule set-argument-doc ( 208 name # The name of the argument. 209 qualifier # Argument syntax qualifier, "*", "+", etc. 210 rule-name # The name of the rule. 211 module-name ? # THe optional name of the module. 212 : docs * # The documentation. 213) 214{ 215 module-name ?= * ; 216 217 $(module-name).$(rule-name).args.$(name).qualifier = $(qualifier) ; 218 $(module-name).$(rule-name).args.$(name).docs = $(docs) ; 219 220 if ! $(name) in $($(module-name).$(rule-name).args) 221 { 222 $(module-name).$(rule-name).args += $(name) ; 223 } 224} 225 226 227# Specifies the documentation for a variable in the current module. If called in 228# the global module, the global variable is documented. 229# 230local rule set-variable-doc ( 231 name # The name of the variable. 232 default # The default value. 233 initial # The initial value. 234 module-name ? # The name of the module to document. 235 : docs * # The documentation for the variable. 236) 237{ 238 module-name ?= * ; 239 240 $(module-name).$(name).brief = [ brief-comment $(docs) ] ; 241 $(module-name).$(name).default = $(default) ; 242 $(module-name).$(name).initial = $(initial) ; 243 $(module-name).$(name).docs = $(docs) ; 244 245 if ! $(name) in $($(module-name).variables) 246 { 247 $(module-name).variables += $(name) ; 248 } 249} 250 251 252# Generates a general description of the documentation and help system. 253# 254local rule print-help-top ( ) 255{ 256 print.section "General command line usage" ; 257 258 print.text " b2 [options] [properties] [targets] 259 260 Options, properties and targets can be specified in any order. 261 " ; 262 263 print.section "Important Options" ; 264 265 print.list-start ; 266 print.list-item "--clean Remove targets instead of building" ; 267 print.list-item "-a Rebuild everything" ; 268 print.list-item "-n Don't execute the commands, only print them" ; 269 print.list-item "-d+2 Show commands as they are executed" ; 270 print.list-item "-d0 Suppress all informational messages" ; 271 print.list-item "-q Stop at first error" ; 272 print.list-item "--reconfigure Rerun all configuration checks" ; 273 print.list-item "--debug-configuration Diagnose configuration" ; 274 print.list-item "--debug-building Report which targets are built with what properties" ; 275 print.list-item "--debug-generator Diagnose generator search/execution" ; 276 print.list-end ; 277 278 print.section "Further Help" 279 The following options can be used to obtain additional documentation. 280 ; 281 282 print.list-start ; 283 print.list-item "--help-options Print more obscure command line options." ; 284 print.list-item "--help-internal B2 implementation details." ; 285 print.list-item "--help-doc-options Implementation details doc formatting." ; 286 print.list-end ; 287} 288 289 290# Generate Jam/Boost.Jam command usage information. 291# 292local rule print-help-usage ( ) 293{ 294 print.section "B2 Usage" 295 "b2 [ options... ] targets..." 296 ; 297 print.list-start ; 298 print.list-item -a\; 299 Build all targets, even if they are current. ; 300 print.list-item -fx\; 301 Read '"x"' as the Jamfile for building instead of searching for the 302 B2 system. ; 303 print.list-item -jx\; 304 Run up to '"x"' commands concurrently. ; 305 print.list-item -n\; 306 Do not execute build commands. Instead print out the commands as they 307 would be executed if building. ; 308 print.list-item -ox\; 309 Output the used build commands to file '"x"'. ; 310 print.list-item -q\; 311 Quit as soon as a build failure is encountered. Without this option 312 Boost.Jam will continue building as many targets as it can. ; 313 print.list-item -sx=y\; 314 Sets a Jam variable '"x"' to the value '"y"', overriding any value that 315 variable would have from the environment. ; 316 print.list-item -tx\; 317 Rebuild the target '"x"', even if it is up-to-date. ; 318 print.list-item -v\; 319 Display the version of b2. ; 320 print.list-item --x\; 321 Any option not explicitly handled by B2 remains available to 322 build scripts using the '"ARGV"' variable. ; 323 print.list-item --abbreviate-paths\; 324 Use abbreviated paths for targets. ; 325 print.list-item --hash\; 326 Shorten target paths by using an MD5 hash. ; 327 print.list-item -dconsole\; 328 Run the interactive debugger. Cannot be used with any other option. ; 329 print.list-item -dn\; 330 Enables output of diagnostic messages. The debug level '"n"' and all 331 below it are enabled by this option. ; 332 print.list-item -d+n\; 333 Enables output of diagnostic messages. Only the output for debug level 334 '"n"' is enabled. ; 335 print.list-end ; 336 print.section "Debug Levels" 337 Each debug level shows a different set of information. Usually with 338 higher levels producing more verbose information. The following levels 339 are supported\: ; 340 print.list-start ; 341 print.list-item 0\; 342 Turn off all diagnostic output. Only errors are reported. ; 343 print.list-item 1\; 344 Show the actions taken for building targets, as they are executed. ; 345 print.list-item 2\; 346 Show "quiet" actions and display all action text, as they are executed. ; 347 print.list-item 3\; 348 Show dependency analysis, and target/source timestamps/paths. ; 349 print.list-item 4\; 350 Show arguments of shell invocations. ; 351 print.list-item 5\; 352 Show rule invocations and variable expansions. ; 353 print.list-item 6\; 354 Show directory/header file/archive scans, and attempts at binding to targets. ; 355 print.list-item 7\; 356 Show variable settings. ; 357 print.list-item 8\; 358 Show variable fetches, variable expansions, and evaluation of '"if"' expressions. ; 359 print.list-item 9\; 360 Show variable manipulation, scanner tokens, and memory usage. ; 361 print.list-item 10\; 362 Show execution times for rules. ; 363 print.list-item 11\; 364 Show parsing progress of Jamfiles. ; 365 print.list-item 12\; 366 Show graph for target dependencies. ; 367 print.list-item 13\; 368 Show changes in target status (fate). ; 369 print.list-end ; 370} 371 372# Generates description of options controlling the help system. This 373# automatically reads the options as all variables in the module given 374# with the name `module-name` of the form ".option.*". 375# 376local rule print-help-options ( 377 module-name 378) 379{ 380 local options-to-list = [ MATCH "^[.]option[.](.*)" : $($(module-name).variables) ] ; 381 if $(options-to-list) 382 { 383 local option-title = $($(module-name)..option-description.initial) ; 384 if ! $(option-title) || $(option-title) = "(empty)" 385 { 386 option-title = "$(module-name) Options" ; 387 } 388 local option-description = $(option-title) 389 $($(module-name)..option-description.docs) ; 390 print.section $(option-description) ; 391 print.list-start ; 392 for local option in [ sequence.insertion-sort $(options-to-list) ] 393 { 394 local def = disabled ; 395 if $($(module-name)..option.$(option).default) != "(empty)" 396 { 397 def = $($(module-name)..option.$(option).default) ; 398 } 399 print.list-item $(option)\: $($(module-name)..option.$(option).docs) 400 Default is $(def). ; 401 } 402 print.list-end ; 403 } 404} 405 406 407# Generate brief documentation for all the known items in the section for a 408# module. Possible sections are: "rules", and "variables". 409# 410local rule print-help-module-section ( 411 module # The module name. 412 section # rules or variables. 413 : section-head # The title of the section. 414 section-description * # The detailed description of the section. 415) 416{ 417 if $($(module).$(section)) 418 { 419 print.section $(section-head) $(section-description) ; 420 print.list-start ; 421 for local item in [ sequence.insertion-sort $($(module).$(section)) ] 422 { 423 local show = ; 424 if ! $($(module).$(item).is-local) 425 { 426 show = yes ; 427 } 428 if $(.option.show-locals) 429 { 430 show = yes ; 431 } 432 if $(show) 433 { 434 print.list-item $(item)\: $($(module).$(item).brief) ; 435 } 436 } 437 print.list-end ; 438 } 439} 440 441 442# Generate documentation for all possible modules. We attempt to list all known 443# modules together with a brief description of each. 444# 445local rule print-help-all ( 446 ignored # Usually the module name, but is ignored here. 447) 448{ 449 print.section "Modules" 450 "These are all the known modules. Use --help <module> to get more" 451 "detailed information." 452 ; 453 if $(documented-modules) 454 { 455 print.list-start ; 456 for local module-name in [ sequence.insertion-sort $(documented-modules) ] 457 { 458 # The brief docs for each module. 459 print.list-item $(module-name)\: $($(module-name).brief) ; 460 } 461 print.list-end ; 462 } 463 # The documentation for each module when details are requested. 464 if $(documented-modules) && $(.option.detailed) 465 { 466 for local module-name in [ sequence.insertion-sort $(documented-modules) ] 467 { 468 # The brief docs for each module. 469 print-help-module $(module-name) ; 470 } 471 } 472} 473 474 475# Generate documentation for a module. Basic information about the module is 476# generated. 477# 478local rule print-help-module ( 479 module-name # The module to generate docs for. 480) 481{ 482 # Print the docs. 483 print.section "Module '$(module-name)'" $($(module-name).docs) ; 484 485 # Print out the documented classes. 486 print-help-module-section $(module-name) classes : "Module '$(module-name)' classes" 487 Use --help $(module-name).<class-name> to get more information. ; 488 489 # Print out the documented rules. 490 print-help-module-section $(module-name) rules : "Module '$(module-name)' rules" 491 Use --help $(module-name).<rule-name> to get more information. ; 492 493 # Print out the documented variables. 494 print-help-module-section $(module-name) variables : "Module '$(module-name)' variables" 495 Use --help $(module-name).<variable-name> to get more information. ; 496 497 # Print out all the same information but indetailed form. 498 if $(.option.detailed) 499 { 500 print-help-classes $(module-name) ; 501 print-help-rules $(module-name) ; 502 print-help-variables $(module-name) ; 503 } 504} 505 506 507# Generate documentation for a set of rules in a module. 508# 509local rule print-help-rules ( 510 module-name # Module of the rules. 511 : name * # Optional list of rules to describe. 512) 513{ 514 name ?= $($(module-name).rules) ; 515 if [ set.intersection $(name) : $($(module-name).rules) $($(module-name).class-rules) ] 516 { 517 # Print out the given rules. 518 for local rule-name in [ sequence.insertion-sort $(name) ] 519 { 520 if $(.option.show-locals) || ! $($(module-name).$(rule-name).is-local) 521 { 522 local signature = $($(module-name).$(rule-name).signature:J=" ") ; 523 signature ?= "" ; 524 print.section "Rule '$(module-name).$(rule-name) ( $(signature) )'" 525 $($(module-name).$(rule-name).docs) ; 526 if $($(module-name).$(rule-name).args) && 527 $($(module-name).$(rule-name).args.$($(module-name).$(rule-name).args).docs) 528 { 529 print.list-start ; 530 for local arg-name in $($(module-name).$(rule-name).args) 531 { 532 if $($(module-name).$(rule-name).args.$(arg-name).docs) 533 { 534 print.list-item $(arg-name)\: $($(module-name).$(rule-name).args.$(arg-name).docs) ; 535 } 536 } 537 print.list-end ; 538 } 539 } 540 } 541 } 542} 543 544 545# Generate documentation for a set of classes in a module. 546# 547local rule print-help-classes ( 548 module-name # Module of the classes. 549 : name * # Optional list of classes to describe. 550) 551{ 552 name ?= $($(module-name).classes) ; 553 if [ set.intersection $(name) : $($(module-name).classes) ] 554 { 555 # Print out the given classes. 556 for local class-name in [ sequence.insertion-sort $(name) ] 557 { 558 if $(.option.show-locals) || ! $($(module-name).$(class-name).is-local) 559 { 560 local signature = $($(module-name).$(class-name).signature:J=" ") ; 561 signature ?= "" ; 562 print.section "Class '$(module-name).$(class-name) ( $(signature) )'" 563 $($(module-name).$(class-name).docs) 564 "Inherits from '"$($(module-name).$(class-name).super-name)"'." ; 565 if $($(module-name).$(class-name).args) 566 { 567 print.list-start ; 568 for local arg-name in $($(module-name).$(class-name).args) 569 { 570 print.list-item $(arg-name)\: $($(module-name).$(class-name).args.$(arg-name).docs) ; 571 } 572 print.list-end ; 573 } 574 } 575 576 # Print out the documented rules of the class. 577 print-help-module-section $(module-name) $(class-name).class-rules : "Class '$(module-name).$(class-name)' rules" 578 Use --help $(module-name).<rule-name> to get more information. ; 579 580 # Print out all the rules if details are requested. 581 if $(.option.detailed) 582 { 583 print-help-rules $(module-name) : $($(module-name).$(class-name).class-rules) ; 584 } 585 } 586 } 587} 588 589 590# Generate documentation for a set of variables in a module. 591# 592local rule print-help-variables ( 593 module-name ? # Module of the variables. 594 : name * # Optional list of variables to describe. 595) 596{ 597 name ?= $($(module-name).variables) ; 598 if [ set.intersection $(name) : $($(module-name).variables) ] 599 { 600 # Print out the given variables. 601 for local variable-name in [ sequence.insertion-sort $(name) ] 602 { 603 print.section "Variable '$(module-name).$(variable-name)'" $($(module-name).$(variable-name).docs) ; 604 if $($(module-name).$(variable-name).default) || 605 $($(module-name).$(variable-name).initial) 606 { 607 print.list-start ; 608 if $($(module-name).$(variable-name).default) 609 { 610 print.list-item "default value:" '$($(module-name).$(variable-name).default:J=" ")' ; 611 } 612 if $($(module-name).$(variable-name).initial) 613 { 614 print.list-item "initial value:" '$($(module-name).$(variable-name).initial:J=" ")' ; 615 } 616 print.list-end ; 617 } 618 } 619 } 620} 621 622 623# Generate documentation for a project. 624# 625local rule print-help-project ( 626 unused ? 627 : jamfile * # The project Jamfile. 628) 629{ 630 if $(jamfile<$(jamfile)>.docs) 631 { 632 # Print the docs. 633 print.section "Project-specific help" 634 Project has jamfile at $(jamfile) ; 635 636 print.lines $(jamfile<$(jamfile)>.docs) "" ; 637 } 638} 639 640 641# Generate documentation for a config file. 642# 643local rule print-help-config ( 644 unused ? 645 : type # The type of configuration file user or site. 646 config-file # The configuration Jamfile. 647) 648{ 649 if $(jamfile<$(config-file)>.docs) 650 { 651 # Print the docs. 652 print.section "Configuration help" 653 Configuration file at $(config-file) ; 654 655 print.lines $(jamfile<$(config-file)>.docs) "" ; 656 } 657} 658 659 660ws = "\t " ; 661 662# Extract the text from a single comment 663# 664local rule extract-one-comment ( 665 var # The name of the variable to extract from 666 : start # The initial part after the leading '#' 667) 668{ 669 local m = [ MATCH ^(\\|)(.*) : $(start) ] ; 670 if $(m) 671 { 672 start = $(m[2]) ; 673 local comment ; 674 while true 675 { 676 local end = [ MATCH "(.*)(\\|#)(.*)" : $(start) ] ; 677 if $(end) 678 { 679 comment += $(end[1]) ; 680 $(var) = $(end[3]) $($(var)[2-]) ; 681 return $(comment) ; 682 } 683 else 684 { 685 comment += $(start) ; 686 $(var) = $($(var)[2-]) ; 687 } 688 start = $($(var)[1]) ; 689 } 690 } 691 else 692 { 693 $(var) = $($(var)[2-]) ; 694 if $(start) { return [ MATCH "^[$(ws)]?(.*)$" : $(start) ] ; } 695 else { return "" ; } 696 } 697} 698 699# Extract the text from a block of comments. 700# 701local rule extract-comment ( 702 var # The name of the variable to extract from. 703) 704{ 705 local comment = ; 706 local line = $($(var)[1]) ; 707 local l = [ MATCH "^[$(ws)]*(#)(.*)$" : $(line) ] ; 708 while $(l[1]) && $($(var)) 709 { 710 comment += [ extract-one-comment $(var) : $(l[2]) ] ; 711 line = $($(var)[1]) ; 712 l = [ MATCH "^[$(ws)]*(#)(.*)$" : $(line) ] ; 713 } 714 return $(comment) ; 715} 716 717 718# Extract s single line of Jam syntax, ignoring any comments. 719# 720local rule extract-syntax ( 721 var # The name of the variable to extract from. 722) 723{ 724 local syntax = ; 725 local line = $($(var)[1]) ; 726 while ! $(syntax) && ! [ MATCH "^[$(ws)]*(#)" : $(line) ] && $($(var)) 727 { 728 local m = [ MATCH "^[$(ws)]*(.*)$" : $(line) ] ; 729 if $(m) 730 { 731 syntax = $(m) ; 732 } 733 $(var) = $($(var)[2-]) ; 734 line = $($(var)[1]) ; 735 } 736 return $(syntax) ; 737} 738 739 740# Extract the next token, this is either a single Jam construct or a comment as 741# a single token. 742# 743local rule extract-token ( 744 var # The name of the variable to extract from. 745) 746{ 747 local parts = ; 748 while ! $(parts) 749 { 750 parts = [ MATCH "^[$(ws)]*([^$(ws)]+)[$(ws)]*(.*)" : $($(var)[1]) ] ; 751 if ! $(parts) 752 { 753 $(var) = $($(var)[2-]) ; 754 } 755 } 756 local token = ; 757 if [ MATCH "^(#)" : $(parts[1]) ] 758 { 759 token = $(parts:J=" ") ; 760 $(var) = $($(var)[2-]) ; 761 } 762 else 763 { 764 token = $(parts[1]) ; 765 $(var) = $(parts[2-]:J=" ") $($(var)[2-]) ; 766 } 767 return $(token) ; 768} 769 770 771# Scan for a rule declaration as the next item in the variable. 772# 773local rule scan-rule ( 774 syntax ? # The first part of the text which contains the rule declaration. 775 : var # The name of the variable to extract from. 776) 777{ 778 local rule-parts = 779 [ MATCH "^[$(ws)]*(rule|local[$(ws)]*rule)[$(ws)]+([^$(ws)]+)[$(ws)]*(.*)" : $(syntax:J=" ") ] ; 780 if $(rule-parts[1]) 781 { 782 # Mark as doc for rule. 783 local rule-name = $(rule-parts[2]) ; 784 if $(scope-name) 785 { 786 rule-name = $(scope-name).$(rule-name) ; 787 } 788 local is-local = [ MATCH "^(local).*" : $(rule-parts[1]) ] ; 789 if $(comment-block) 790 { 791 set-rule-doc $(rule-name) $(module-name) $(is-local) : $(comment-block) ; 792 } 793 # Parse args of rule. 794 $(var) = $(rule-parts[3-]) $($(var)) ; 795 set-rule-arguments-signature $(rule-name) $(module-name) : [ scan-rule-arguments $(var) ] ; 796 # Scan within this rules scope. 797 local scope-level = [ extract-token $(var) ] ; 798 local scope-name = $(rule-name) ; 799 while $(scope-level) && $($(var)) 800 { 801 local comment-block = [ extract-comment $(var) ] ; 802 local syntax-block = [ extract-syntax $(var) ] ; 803 if [ scan-rule $(syntax-block) : $(var) ] 804 { 805 } 806 else if [ MATCH "^(\\{)" : $(syntax-block) ] 807 { 808 scope-level += "{" ; 809 } 810 else if [ MATCH "^[^\\}]*([\\}])[$(ws)]*$" : $(syntax-block) ] 811 { 812 scope-level = $(scope-level[2-]) ; 813 } 814 } 815 816 return true ; 817 } 818} 819 820 821# Scan the arguments of a rule. 822# 823local rule scan-rule-arguments ( 824 var # The name of the variable to extract from. 825) 826{ 827 local arg-syntax = ; 828 local token = [ extract-token $(var) ] ; 829 while $(token) != "(" && $(token) != "{" 830 { 831 token = [ extract-token $(var) ] ; 832 } 833 if $(token) != "{" 834 { 835 token = [ extract-token $(var) ] ; 836 } 837 local arg-signature = ; 838 while $(token) != ")" && $(token) != "{" 839 { 840 local arg-name = ; 841 local arg-qualifier = " " ; 842 local arg-doc = ; 843 if $(token) = ":" 844 { 845 arg-signature += $(token) ; 846 token = [ extract-token $(var) ] ; 847 } 848 arg-name = $(token) ; 849 arg-signature += $(token) ; 850 token = [ extract-token $(var) ] ; 851 if [ MATCH "^([\\*\\+\\?])" : $(token) ] 852 { 853 arg-qualifier = $(token) ; 854 arg-signature += $(token) ; 855 token = [ extract-token $(var) ] ; 856 } 857 if $(token) = ":" 858 { 859 arg-signature += $(token) ; 860 token = [ extract-token $(var) ] ; 861 } 862 if [ MATCH "^(#)" : $(token) ] 863 { 864 $(var) = $(token) $($(var)) ; 865 arg-doc = [ extract-comment $(var) ] ; 866 token = [ extract-token $(var) ] ; 867 } 868 set-argument-doc $(arg-name) $(arg-qualifier) $(rule-name) $(module-name) : $(arg-doc) ; 869 } 870 while $(token) != "{" 871 { 872 token = [ extract-token $(var) ] ; 873 } 874 $(var) = "{" $($(var)) ; 875 arg-signature ?= "" ; 876 return $(arg-signature) ; 877} 878 879 880# Scan for a variable declaration. 881# 882local rule scan-variable ( 883 syntax ? # The first part of the text which contains the variable declaration. 884 : var # The name of the variable to extract from. 885) 886{ 887 # [1] = name, [2] = value(s) 888 local var-parts = 889 [ MATCH "^[$(ws)]*([^$(ws)]+)[$(ws)]+([\\?\\=]*)[$(ws)]+([^\\;]*)\\;" : $(syntax) ] ; 890 if $(var-parts) 891 { 892 local value = [ MATCH "^(.*)[ ]$" : $(var-parts[3-]:J=" ") ] ; 893 local default-value = "" ; 894 local initial-valie = "" ; 895 if $(var-parts[2]) = "?=" 896 { 897 default-value = $(value) ; 898 default-value ?= "(empty)" ; 899 } 900 else 901 { 902 initial-value = $(value) ; 903 initial-value ?= "(empty)" ; 904 } 905 if $(comment-block) 906 { 907 set-variable-doc $(var-parts[1]) $(default-value) $(initial-value) $(module-name) : $(comment-block) ; 908 } 909 return true ; 910 } 911} 912 913 914# Scan a class declaration. 915# 916local rule scan-class ( 917 syntax ? # The syntax text for the class declaration. 918 : var # The name of the variable to extract from. 919) 920{ 921 # [1] = class?, [2] = name, [3] = superclass 922 local class-parts = 923 [ MATCH "^[$(ws)]*([^$(ws)]+)[$(ws)]+([^$(ws)]+)[$(ws)]*:*[$(ws)]*([^$(ws);]*)" : $(syntax) ] ; 924 if $(class-parts[1]) = "class" || $(class-parts[1]) = "class.class" 925 { 926 # Scan within this class scope. 927 local scope-level = [ extract-token $(var) ] ; 928 local scope-name = $(class-parts[2]) ; 929 while $(scope-level) && $($(var)) 930 { 931 local comment-block = [ extract-comment $(var) ] ; 932 local syntax-block = [ extract-syntax $(var) ] ; 933 if [ scan-rule $(syntax-block) : $(var) ] 934 { 935 } 936 else if [ MATCH "^(\\{)" : $(syntax-block) ] 937 { 938 scope-level += "{" ; 939 } 940 else if [ MATCH "^[^\\}]*([\\}])[$(ws)]*$" : $(syntax-block) ] 941 { 942 scope-level = $(scope-level[2-]) ; 943 } 944 } 945 946 # This has to come after parsing the rules, because 947 # it looks up the rules for the class from the global list. 948 set-class-doc $(class-parts[2]) $(module-name) : $(class-parts[3]) ; 949 950 return true ; 951 } 952} 953 954 955# Scan a module file for documentation comments. This also invokes any actions 956# assigned to the module. The actions are the rules that do the actual output of 957# the documentation. This rule is invoked as the header scan rule for the module 958# file. 959# 960rule scan-module ( 961 target # The module file. 962 : text * # The text in the file, one item per line. 963 : action * # Rule to call to output docs for the module. 964) 965{ 966 if $(.option.debug) { ECHO "HELP:" scanning module target '$(target)' ; } 967 local module-name = $(target:B) ; 968 local module-documented = ; 969 local comment-block = ; 970 local syntax-block = ; 971 # This is a hack because we can not get the line of a file if it happens to 972 # not have a new-line termination. 973 text += "}" ; 974 while $(text) 975 { 976 comment-block = [ extract-comment text ] ; 977 syntax-block = [ extract-syntax text ] ; 978 if $(.option.debug) 979 { 980 ECHO "HELP:" comment block\; '$(comment-block)' ; 981 ECHO "HELP:" syntax block\; '$(syntax-block)' ; 982 } 983 if [ scan-rule $(syntax-block) : text ] { } 984 else if [ scan-variable $(syntax-block) : text ] { } 985 else if [ scan-class $(syntax-block) : text ] { } 986 else if [ MATCH ".*([cC]opyright).*" : $(comment-block:J=" ") ] 987 { 988 # mark as the copy for the module. 989 set-module-copyright $(module-name) : $(comment-block) ; 990 } 991 else if $(action[1]) in "print-help-project" "print-help-config" 992 && ! $(jamfile<$(target)>.docs) 993 { 994 # special module docs for the project jamfile. 995 jamfile<$(target)>.docs = $(comment-block) ; 996 } 997 else if ! $(module-documented) 998 { 999 # document the module. 1000 set-module-doc $(module-name) : $(comment-block) ; 1001 module-documented = true ; 1002 } 1003 } 1004 if $(action) 1005 { 1006 $(action[1]) $(module-name) : $(action[2-]) ; 1007 } 1008} 1009 1010 1011# Import scan-module to global scope, so that it is available during header 1012# scanning phase. 1013# 1014IMPORT $(__name__) : scan-module : : doc.scan-module ; 1015 1016 1017# Read in a file using the SHELL builtin and return the individual lines as 1018# would be done for header scanning. 1019# 1020local rule read-file ( 1021 file # The file to read in. 1022) 1023{ 1024 file = [ path.native [ path.root [ path.make $(file) ] [ path.pwd ] ] ] ; 1025 if ! $(.file<$(file)>.lines) 1026 { 1027 local content ; 1028 switch [ modules.peek : OS ] 1029 { 1030 case NT : 1031 content = [ SHELL "TYPE \"$(file)\"" ] ; 1032 1033 case * : 1034 content = [ SHELL "cat \"$(file)\"" ] ; 1035 } 1036 local lines ; 1037 local << = "([^\r\n]*)[\r]?[\n](.*)" ; 1038 local line+ = [ MATCH "$(<<)" : "$(content)" ] ; 1039 while $(line+) 1040 { 1041 lines += $(line+[1]) ; 1042 line+ = [ MATCH "$(<<)" : "$(line+[2])" ] ; 1043 } 1044 .file<$(file)>.lines = $(lines) ; 1045 } 1046 return $(.file<$(file)>.lines) ; 1047} 1048 1049 1050# Add a scan action to perform to generate the help documentation. The action 1051# rule is passed the name of the module as the first argument. The second 1052# argument(s) are optional and passed directly as specified here. 1053# 1054local rule do-scan ( 1055 modules + # The modules to scan and perform the action on. 1056 : action * # The action rule, plus the secondary arguments to pass to the action rule. 1057) 1058{ 1059 if $(help-output) = text 1060 { 1061 print.output $(help-output-file).txt plain ; 1062 ALWAYS $(help-output-file).txt ; 1063 DEPENDS all : $(help-output-file).txt ; 1064 } 1065 if $(help-output) = html 1066 { 1067 print.output $(help-output-file).html html ; 1068 ALWAYS $(help-output-file).html ; 1069 DEPENDS all : $(help-output-file).html ; 1070 } 1071 for local module-file in $(modules[1--2]) 1072 { 1073 scan-module $(module-file) : [ read-file $(module-file) ] ; 1074 } 1075 scan-module $(modules[-1]) : [ read-file $(modules[-1]) ] : $(action) ; 1076} 1077