diff --git a/.ci/azp-linux-test.yml b/.ci/azp-linux-test.yml index 393a01b46..d9bbce2dd 100644 --- a/.ci/azp-linux-test.yml +++ b/.ci/azp-linux-test.yml @@ -1,46 +1,46 @@ parameters: -- name: b2_opts - type: string - default: '' + - name: b2_opts + type: string + default: "" steps: -- bash: | - set -e - uname -a - ./.ci/linux-cxx-install.sh - python3 --version - displayName: Install -- bash: | - set -e - cd src/engine - set PATH=${PATH};${CXX_PATH} - ./build.sh ${TOOLSET} - ./b2 -v - cd ../.. - echo "using ${TOOLSET%%-[0-9]*} : : ${CXX} ;" > ${HOME}/user-config.jam - displayName: Build -- bash: | - set -e - echo "Available system locales:" - locale -a - echo "Switch to C.UTF-8 locale." - export LC_ALL=C.UTF-8 - cd test - ./test_all.py ${TOOLSET} - cd .. - displayName: Test -- bash: | - set -e - ./src/engine/b2 b2 warnings-as-errors=on variant=debug,release ${TOOLSET:+toolset=$TOOLSET} ${{ parameters.b2_opts }} - displayName: "No Warnings" -- bash: | - set -e - ./bootstrap.sh ${TOOLSET} - ./b2 --prefix=$HOME/temp/.b2 install ${TOOLSET:+toolset=$TOOLSET} ${{ parameters.b2_opts }} - rm ./b2 - export PATH=$HOME/temp/.b2/bin:$PATH - cd $HOME - touch build.jam - b2 -v - b2 -n --debug-configuration ${TOOLSET:+toolset=$TOOLSET} ${{ parameters.b2_opts }} - displayName: Bootstrap + - bash: | + set -e + uname -a + ./.ci/linux-cxx-install.sh + python3 --version + displayName: Install + - bash: | + set -e + cd src/engine + set PATH=${PATH};${CXX_PATH} + ./build.sh ${TOOLSET} + ./b2 -v + cd ../.. + echo "using ${TOOLSET%%-[0-9]*} : : ${CXX} ;" > ${HOME}/user-config.jam + displayName: Build + - bash: | + set -e + echo "Available system locales:" + locale -a + echo "Switch to C.UTF-8 locale." + export LC_ALL=C.UTF-8 + cd test + ./test_all.py ${TOOLSET} + cd .. + displayName: Test + - bash: | + set -e + ./src/engine/b2 b2 warnings-as-errors=on variant=debug,release ${TOOLSET:+toolset=$TOOLSET} ${{ parameters.b2_opts }} + displayName: "No Warnings" + - bash: | + set -e + ./bootstrap.sh ${TOOLSET} + ./b2 --prefix=$HOME/temp/.b2 install ${TOOLSET:+toolset=$TOOLSET} ${{ parameters.b2_opts }} + rm ./b2 + export PATH=$HOME/temp/.b2/bin:$PATH + cd $HOME + touch build.jam + b2 -v + b2 -n --debug-configuration ${TOOLSET:+toolset=$TOOLSET} ${{ parameters.b2_opts }} + displayName: Bootstrap diff --git a/.clang-format b/.clang-format index 4f3b977b5..b3de370ba 100644 --- a/.clang-format +++ b/.clang-format @@ -7,11 +7,14 @@ UseTab: Always Language: Cpp AccessModifierOffset: 0 AlignAfterOpenBracket: DontAlign -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false +AlignArrayOfStructures: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: None AlignEscapedNewlines: DontAlign AlignOperands: DontAlign -AlignTrailingComments: false +AlignTrailingComments: { Kind: Never } AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true @@ -29,22 +32,22 @@ BinPackParameters: false BitFieldColonSpacing: Both BreakBeforeBraces: Custom BraceWrapping: - AfterCaseLabel: true - AfterClass: true - AfterControlStatement: Always - AfterEnum: true - AfterExternBlock: true - AfterFunction: true - AfterNamespace: false - AfterStruct: true - AfterUnion: true - BeforeCatch: true - BeforeElse: true - BeforeWhile: true - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyNamespace: true - SplitEmptyRecord: false + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterExternBlock: true + AfterFunction: true + AfterNamespace: false + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: true + SplitEmptyRecord: false BreakBeforeBinaryOperators: All BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma @@ -70,6 +73,7 @@ NamespaceIndentation: None PenaltyBreakBeforeFirstCallParameter: 0 PenaltyBreakFirstLessLess: 0 PenaltyReturnTypeOnItsOwnLine: 999999 +PenaltyIndentedWhitespace: 999999 PointerAlignment: Middle PPIndentWidth: 0 ReflowComments: true diff --git a/.editorconfig b/.editorconfig index 895e4fb50..dd3e9379f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,10 @@ trim_trailing_whitespace = true indent_size = 2 indent_style = space +[*.clang-format] +indent_size = 1 +indent_style = space + [*.jam] indent_style = space diff --git a/.vscode/launch.json b/.vscode/launch.json index a651c23ea..bcc092770 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "(gdb) Pipe Launch", "type": "cppdbg", "request": "launch", - "program": "/root/b2/.build/gcc-12/debug/cxxstd-11-iso/threading-multi/b2", + "program": "/root/b2/.build/gcc-14/debug/cxxstd-11-iso/threading-multi/b2", "args": [], "stopAtEntry": true, "cwd": "/root/b2/test/grep", @@ -68,11 +68,16 @@ "request": "launch", "program": "${workspaceFolder}\\.build\\msvc-14.3\\debug\\cxxstd-11-iso\\threading-multi\\b2.exe", "args": [ - "-v" + "-ftest.jam" ], "stopAtEntry": false, - "cwd": "${workspaceFolder}\\src\\engine", - "environment": [] + "cwd": "${workspaceFolder}\\test", + "environment": [ + { + "name": "BOOST_BUILD_PATH", + "value": "${workspaceFolder}" + } + ] }, { "name": "(msvc) Launch, Unit Tests", @@ -92,10 +97,12 @@ "name": "(gcc) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/.build/gcc-13/debug/cxxstd-11-iso/threading-multi/b2", - "args": ["-d1", "--command-database=json", "b2"], + "program": "${workspaceFolder}/.build/gcc-14/debug/cxxstd-11-iso/threading-multi/b2", + "args": [ + "-h" + ], "stopAtEntry": false, - "cwd": "${workspaceFolder}", + "cwd": "${workspaceFolder}/example/hello", "environment": [], "externalConsole": false, "MIMode": "gdb", @@ -111,10 +118,10 @@ "name": "(gcc) Launch: ASAN", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/.build/gcc-13/debug/address-sanitizer-on/cxxstd-11-iso/b2", + "program": "${workspaceFolder}/.build/gcc-14/debug/address-sanitizer-on/cxxstd-11-iso/threading-multi/b2", "args": [ - "-na", - "toolset=gcc" + "-n", + "-a" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -133,7 +140,7 @@ "name": "(gcc) Launch, Unit Tests", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/.build/gcc-13/debug/cxxstd-11-iso/threading-multi/b2", + "program": "${workspaceFolder}/.build/gcc-14/debug/cxxstd-11-iso/threading-multi/b2", "args": [ "--debug", "--build-system=test/test", @@ -158,7 +165,7 @@ "name": "(gcc) Launch, Unit Tests: ASAN", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/.build/gcc-13/debug/address-sanitizer-on/cxxstd-11-iso/b2", + "program": "${workspaceFolder}/.build/gcc-14/debug/address-sanitizer-on/cxxstd-11-iso/b2", "args": [ "--debug", "--build-system=test/test", @@ -187,7 +194,7 @@ "name": "(rr) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/.build/gcc-13/debug/cxxstd-11-iso/b2", + "program": "${workspaceFolder}/.build/gcc-14/debug/cxxstd-11-iso/b2", "args": [ "--grep" ], diff --git a/doc/src/history.adoc b/doc/src/history.adoc index fdc32eaab..bae2e1ccc 100644 --- a/doc/src/history.adoc +++ b/doc/src/history.adoc @@ -9,6 +9,9 @@ * *New*: Add generic `cc` toolset for simply, direct, configuration of unknown C compiler toolsets (for example `tcc`). -- _René Ferdinand Rivera Morell_ +* *New*: Add `args` module for common handling of both native and Jam side + command line arguments. + -- _René Ferdinand Rivera Morell_ * Fix command database output file path when an output dir is not given. -- _René Ferdinand Rivera Morell_ * Fix asciidoctor ignoring relevant features when building which caused invalid @@ -28,6 +31,10 @@ -- _Andrey Semashev_ * Fix Building on Haiku OS which doesn't have `wait4`. -- _Fas Xmut_ +* Rewrite the command like argument parsing to use the Lyra library. The result + is that arguments from Jam and native are integrated and available with the + built-in `-h` help. + -- _René Ferdinand Rivera Morell_ == Version 5.2.1 diff --git a/doc/src/reference.adoc b/doc/src/reference.adoc index 58d3eae1f..4ef51d478 100644 --- a/doc/src/reference.adoc +++ b/doc/src/reference.adoc @@ -780,6 +780,7 @@ include::../../src/engine/mod_set.h[tag=reference] include::../../src/engine/mod_string.h[tag=reference] include::../../src/engine/mod_version.h[tag=reference] include::../../src/engine/mod_db.h[tag=reference] +include::../../src/engine/mod_args.h[tag=reference] include::path.adoc[] diff --git a/src/build-system.jam b/src/build-system.jam index 7e23acbfc..7eb5d1423 100644 --- a/src/build-system.jam +++ b/src/build-system.jam @@ -8,6 +8,7 @@ # This file is part of Boost Build version 2. You can think of it as forming the # main() routine. It is invoked by the bootstrapping code in bootstrap.jam. +import args ; import build-request ; import builtin ; import "class" : new ; @@ -43,7 +44,7 @@ import virtual-target ; # Flag indicating we should display additional debugging information related to # locating and loading Boost Build configuration files. -.debug-config = [ MATCH ^(--debug-configuration)$ : $(.argv) ] ; +.debug-config = [ args.get-arg debug-configuration ] ; # Virtual targets obtained when building main targets references on the command # line. When running 'bjam --clean main_target' we want to clean only files @@ -52,7 +53,9 @@ import virtual-target ; .results-of-main-targets = ; # Was an XML dump requested? -.out-xml = [ MATCH ^--out-xml=(.*)$ : $(.argv) ] ; +args.add-arg out-xml : --out-xml + : "Output build information to an XML file." ; +.out-xml = [ args.get-arg out-xml ] ; # Default toolset & version to be used in case no other toolset has been used # explicitly by either the loaded configuration files, the loaded project build @@ -281,7 +284,7 @@ local rule load-config ( module-name : filename : path + : must-find ? ) # local rule handle-config-option ( name : env ? ) { - local result = [ MATCH ^--$(name)=(.*)$ : $(.argv) ] ; + local result = [ args.get-arg $(name) ] ; if ! $(result)-is-defined && $(env) { result = [ os.environ $(env) ] ; @@ -311,12 +314,16 @@ local rule handle-config-option ( name : env ? ) # If this configuration file is loaded, regular site and user configuration # files will not be. If a relative path is specified, file is searched for in # the current folder. +args.add-arg test-config : --test-config + : "Override all configuration files, without error if not found." ; # # -- all-config -- # Loaded only if specified on the command-line using the --config command # line option. If a file name is specified, it must exist and replaces all # other configuration files. If an empty file name is passed, no configuration # files will be loaded. +args.add-arg all-config : --config + : "Override all configuration files." ; # # -- site-config -- # Named site-config.jam by default or may be named explicitly using the @@ -327,6 +334,11 @@ local rule handle-config-option ( name : env ? ) # order. Not loaded in case the test-config configuration file is loaded, # the file is explicitly set to the empty string or the --ignore-site-config # command-line option is specified. +args.add-arg site-config : --site-config + : "Override the default site-config.jam." ; +args.add-arg ignore-site-config : --ignore-site-config + : "Do not load a site-config.jam." + : flag ; # # -- user-config -- # Named user-config.jam by default or may be named explicitly using the @@ -337,6 +349,8 @@ local rule handle-config-option ( name : env ? ) # case either the test-config configuration file is loaded or an empty file name # is explicitly specified. If the file name has been given explicitly then the # file must exist. +args.add-arg user-config : --user-config + : "Override the default user-config.jam." ; # # -- project-config -- # Named project-config.jam. Looked up in the current working folder and @@ -345,6 +359,8 @@ local rule handle-config-option ( name : env ? ) # is specified explicitly, it is found relative to the current working # directory and must exist. If an empty file name is passed, project-config # will not be loaded. +args.add-arg project-config : --project-config + : "Override the default project-config.jam." ; # # Test configurations have been added primarily for use by Boost Build's # internal unit testing system but may be used freely in other places as well. @@ -352,8 +368,7 @@ local rule handle-config-option ( name : env ? ) local rule load-configuration-files { # Flag indicating that site configuration should not be loaded. - local ignore-site-config = - [ MATCH ^(--ignore-site-config)$ : $(.argv) ] ; + local ignore-site-config = [ args.get-arg ignore-site-config ] ; local ignore-user-config ; local ignore-project-config ; @@ -377,7 +392,7 @@ local rule load-configuration-files } initialize-config-module all-config ; - local all-config = [ handle-config-option config ] ; + local all-config = [ handle-config-option all-config ] ; if $(all-config) { load-config all-config : $(all-config:D=) : $(all-config:D) : required ; @@ -405,7 +420,7 @@ local rule load-configuration-files site-path = [ modules.peek : SystemRoot ] $(user-path) ; } - if $(.debug-config) && $(ignore-site-config) = --ignore-site-config + if $(.debug-config) && [ args.get-arg ignore-site-config ] { ECHO "notice: Site configuration files will be ignored due to the" ; ECHO "notice: --ignore-site-config command-line option." ; @@ -501,12 +516,14 @@ local rule load-configuration-files # toolset=xx,yy,...zz in the command line. May return additional properties to # be processed as if they had been specified by the user. # +args.add-arg toolsets : --toolset + : "Autoconfigure toolsets (--toolset=xx,yy,...zz or toolset=xx,yy,...zz)." ; local rule process-explicit-toolset-requests { local extra-properties ; - local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*)$ : $(.argv) ] : "," ] ; - local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*)$ : $(.argv) ] : "," ] ; + local option-toolsets = [ regex.split-list [ args.get-arg toolsets ] : "," ] ; + local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*)$ : $(.argv) ] : "," ] ; for local t in $(option-toolsets) $(feature-toolsets) { @@ -596,10 +613,14 @@ local rule should-clean-project ( project ) ################################################################################ { - if --version in $(.argv) + args.add-arg version : --version + : "Display version, and other information. (same as -v)" + : flag ; + + if [ args.get-arg version ] { version.print ; - EXIT ; + EXIT : 0 ; } version.verify-engine-version ; @@ -685,8 +706,14 @@ local rule should-clean-project ( project ) # clean existing instead of create new targets. Note that these are not the # final flag values as they may get changed later on due to some special # targets being specified on the command line. - local clean ; if "--clean" in $(.argv) { clean = true ; } - local cleanall ; if "--clean-all" in $(.argv) { cleanall = true ; } + args.add-arg clean : --clean + : "Remove known build results instead of building them." + : flag ; + local clean = [ args.get-arg clean ] ; + args.add-arg clean-all : --clean-all + : "Remove known build results instead of building them." + : flag ; + local cleanall = [ args.get-arg clean-all ] ; # List of explicitly requested files to build. Any target references read @@ -745,7 +772,10 @@ local rule should-clean-project ( project ) targets += [ project.target [ project.module-name "." ] ] ; } - if [ option.get dump-generators : : true ] + args.add-arg dump-generators : --dump-generators + : "Display the active build generators." + ; + if [ args.get-arg dump-generators ] { generators.dump ; } @@ -811,7 +841,10 @@ local rule should-clean-project ( project ) } } - local durations = [ MATCH "(--durations(=([0-9]+))?)" : $(.argv) ] ; + args.add-arg durations : --durations + : "Record the timing of some number (default 10) of actions." + ; + local durations = [ args.get-arg durations ] ; if $(durations) { durations = $(durations[3]) ; durations ?= 10 ; @@ -1022,26 +1055,6 @@ local rule should-clean-project ( project ) ; } - local j = [ option.get jobs ] ; - if $(j) - { - modules.poke : PARALLELISM : $(j) ; - } - - local k = [ option.get keep-going : true : true ] ; - if $(k) in "on" "yes" "true" - { - modules.poke : KEEP_GOING : 1 ; - } - else if $(k) in "off" "no" "false" - { - modules.poke : KEEP_GOING : 0 ; - } - else - { - EXIT "error: Invalid value for the --keep-going option" ; - } - # The 'all' pseudo target is not strictly needed expect in the case when we # use it below but people often assume they always have this target # available and do not declare it themselves before use which may cause @@ -1049,6 +1062,14 @@ local rule should-clean-project ( project ) # 'all' target. NOTFILE all ; + # Skip building (i.e. updating) if the user is asking for help. We need all + # the declarations to get ad-hoc help arguments declared. But we don't + # want to update while also asking for help. + if [ args.get-arg help ] + { + return ; + } + # And now that all the actual raw Jam targets and all the dependencies # between them have been prepared all that is left is to tell Jam to update # those targets. diff --git a/src/build/config-cache.jam b/src/build/config-cache.jam index 33495194a..132d00a97 100644 --- a/src/build/config-cache.jam +++ b/src/build/config-cache.jam @@ -8,6 +8,7 @@ import regex ; import path ; import project ; import os ; +import args ; rule get ( name ) { @@ -68,7 +69,7 @@ rule load ( cache-file ) errors.error duplicate load of cache file ; } cache-file = [ path.native $(cache-file) ] ; - if [ path.exists $(cache-file) ] && ! ( --reconfigure in [ modules.peek : ARGV ] ) + if [ path.exists $(cache-file) ] && ! [ args.get-arg reconfigure ] { FILE_CONTENTS on $(cache-file) = "" ; config-cache.write $(cache-file) ; diff --git a/src/build/configure.jam b/src/build/configure.jam index 89bf8ba1e..3837a5162 100644 --- a/src/build/configure.jam +++ b/src/build/configure.jam @@ -11,6 +11,7 @@ # process. # - Reporting what is built, and how it is configured. +import args ; import "class" : new ; import common ; import indirect ; @@ -27,6 +28,13 @@ import utility ; import virtual-target ; +args.add-arg reconfigure : --reconfigure + : "Recreated shared toolset configuration files." + : flag ; + +.reconfigure = [ args.get-arg reconfigure ] ; + + rule log-summary ( ) { } @@ -161,11 +169,6 @@ rule print-configure-checks-summary ( ) } } -if --reconfigure in [ modules.peek : ARGV ] -{ - .reconfigure = true ; -} - # Handle the --reconfigure option rule maybe-force-rebuild ( targets * ) { diff --git a/src/build/generators.jam b/src/build/generators.jam index 14ca3c251..1c989355f 100644 --- a/src/build/generators.jam +++ b/src/build/generators.jam @@ -51,12 +51,12 @@ import regex ; import type ; import utility ; import virtual-target ; +import args ; -if "--debug-generators" in [ modules.peek : ARGV ] -{ - .debug = true ; -} +args.add-arg debug-generators : --debug-generators + : "Produces debug output from the generator search process." ; +.debug = [ args.get-arg debug-generators ] ; # Updated cached viable source target type information as needed after a new diff --git a/src/build/project.jam b/src/build/project.jam index 670c217bc..b19be1253 100644 --- a/src/build/project.jam +++ b/src/build/project.jam @@ -34,6 +34,7 @@ # rule. Standalone projects are not recorded and can only be referenced using # their project id. +import args ; import "class" : new ; import modules ; import os ; @@ -45,8 +46,23 @@ import sequence ; import command-db ; -.debug-loading = [ MATCH ^(--debug-loading)$ : [ modules.peek : ARGV ] ] ; -.debug-project-search = [ MATCH ^(--debug-project-search)$ : [ modules.peek : ARGV ] ] ; +args.add-arg project-search : --project-search + : "Defines search paths for resolving global, i.e. rooted, project references." ; + +args.add-arg debug-loading : --debug-loading + : "Display the progress of loading projects." + : flag ; + +args.add-arg debug-project-search : --debug-project-search + : "Display the progress of searching for projects." + : flag ; + +args.add-arg build-dir : --build-dir + : "Specify the directory to output build artifacts." ; + +.debug-loading = [ args.get-arg debug-loading ] ; +.debug-project-search = [ args.get-arg debug-project-search ] ; +.global-build-dir = [ args.get-arg build-dir ] ; # Loads the Jamfile at the given location. After loading, project global file @@ -213,7 +229,7 @@ rule find ( name : current-location ) B2_PROJECT_PATH = [ modules.peek : B2_PROJECT_PATH ] [ regex.split-list - [ MATCH ^--project-search=(.*)$ : [ modules.peek : ARGV ] ] + [ args.get-arg project-search ] : [ os.path-separator ] ] ; { while $(B2_PROJECT_PATH) @@ -445,7 +461,9 @@ PACKAGE_MANAGER_BUILD_INFO(CONAN) = "conanbuildinfo.jam" ; # 3. Environment variable "PACKAGE_MANAGER_BUILD_INFO". # 4. Conan, others. # -local .use-package-manager = [ MATCH "^--use-package-manager=(.*)$" : [ modules.peek : ARGV ] ] ; +args.add-arg use-package-manager : --use-package-manage + : "Load target definitions from the given package manager." ; +local .use-package-manager = [ args.get-arg use-package-manager ] ; PACKAGE_MANAGER_BUILD_INFO ?= $(PACKAGE_MANAGER_BUILD_INFO($(.use-package-manager:U))) ; PACKAGE_MANAGER_BUILD_INFO ?= [ modules.peek : PACKAGE_MANAGER_BUILD_INFO ] ; PACKAGE_MANAGER_BUILD_INFO ?= $(PACKAGE_MANAGER_BUILD_INFO(CONAN)) ; @@ -635,14 +653,6 @@ rule load-aux ( module-name : file ) } -.global-build-dir = [ MATCH ^--build-dir=(.*)$ : [ modules.peek : ARGV ] ] ; -if $(.global-build-dir) -{ - # If the option is specified several times, take the last value. - .global-build-dir = [ path.make $(.global-build-dir[-1]) ] ; -} - - # Initialize the module for a project. # rule initialize ( diff --git a/src/build/property-set.jam b/src/build/property-set.jam index ce348efd0..664de3ffc 100644 --- a/src/build/property-set.jam +++ b/src/build/property-set.jam @@ -13,6 +13,7 @@ import property ; import sequence ; import set ; import option ; +import args ; # Class for storing a set of properties. # @@ -113,11 +114,9 @@ class property-set local result ; local features = [ $(features-ps).get ] ; features = <$(features)> ; - local ignore-relevance = [ modules.peek - property-set : .ignore-relevance ] ; for local p in $(self.raw) { - if $(ignore-relevance) || $(p:G) in $(features) + if $(p:G) in $(features) { local att = [ feature.attributes $(p:G) ] ; if ! ( incidental in $(att) ) @@ -395,10 +394,9 @@ class property-set local relevant-features = [ get ] ; relevant-features = [ feature.expand-relevant $(relevant-features) ] ; relevant-features = <$(relevant-features)> ; - ignore-relevance = [ modules.peek property-set : .ignore-relevance ] ; for local p in $(self.raw) { - if $(ignore-relevance) || $(p:G) in $(relevant-features) + if $(p:G) in $(relevant-features) { local att = [ feature.attributes $(p:G) ] ; if ! ( incidental in $(att) ) @@ -451,14 +449,6 @@ class property-set } } -# This is a temporary measure to help users work around -# any problems. Remove it once we've verified that -# everything works. -if --ignore-relevance in [ modules.peek : ARGV ] -{ - .ignore-relevance = true ; -} - # Creates a new 'property-set' instance for the given raw properties or returns # an already existing ones. # @@ -578,8 +568,10 @@ rule empty ( ) return $(.empty) ; } - -if [ option.get hash : : yes ] = yes +args.add-arg hash : --hash + : "Compress target paths using an MD5 hash." + : flag ; +if [ args.get-arg hash ] { rule hash-maybe ( path ? ) { diff --git a/src/build/property.jam b/src/build/property.jam index b36586a3e..cbbde2a5f 100644 --- a/src/build/property.jam +++ b/src/build/property.jam @@ -14,6 +14,7 @@ import string ; import sequence ; import set ; import utility ; +import args ; # Refines 'properties' by overriding any non-free and non-conditional properties @@ -280,7 +281,9 @@ local rule identity ( string ) } -if --abbreviate-paths in [ modules.peek : ARGV ] +args.add-arg abbreviate-paths : --abbreviate-paths + : "Compresses target paths by abbreviating each component." ; +if [ args.get-arg abbreviate-paths ] { .abbrev = abbreviate-dashed ; } diff --git a/src/build/targets.jam b/src/build/targets.jam index 51cc8c3d2..769d81558 100644 --- a/src/build/targets.jam +++ b/src/build/targets.jam @@ -76,6 +76,7 @@ import property-set ; import sequence ; import set ; import toolset ; +import args ; # Base class for all abstract targets. @@ -147,10 +148,9 @@ class abstract-target } -if --debug-building in [ modules.peek : ARGV ] -{ - modules.poke : .debug-building : true ; -} +args.add-arg debug-building : --debug-building + : "Prints what targets are being built and with what properties." ; +modules.poke : .debug-building : [ args.get-arg debug-building ] ; rule indent ( ) diff --git a/src/build/toolset.jam b/src/build/toolset.jam index 0180f1db9..2f88100d7 100644 --- a/src/build/toolset.jam +++ b/src/build/toolset.jam @@ -7,6 +7,7 @@ # Support for toolset definition. +import args ; import errors ; import feature ; import generators ; @@ -28,10 +29,9 @@ import utility ; # This is used only for testing, to make sure we do not get random extra # elements in paths. -if --ignore-toolset-requirements in [ modules.peek : ARGV ] -{ - .ignore-requirements = 1 ; -} +args.add-arg ignore-toolset-requirements : --ignore-toolset-requirements + : "?" ; +.ignore-requirements = [ args.get-arg ignore-toolset-requirements ] ; # Initializes an additional toolset-like module. First load the 'toolset-module' diff --git a/src/contrib/boost.jam b/src/contrib/boost.jam index 01241c300..3694906fe 100644 --- a/src/contrib/boost.jam +++ b/src/contrib/boost.jam @@ -48,6 +48,7 @@ # find the correct pre-built library. # +import args ; import common ; import modules ; import numbers ; @@ -58,10 +59,7 @@ import toolset ; .boost.auto_config = [ property-set.create system ] ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Configuration of the boost library to use. # diff --git a/src/contrib/modular.jam b/src/contrib/modular.jam index 31d78d6ba..791937f2f 100644 --- a/src/contrib/modular.jam +++ b/src/contrib/modular.jam @@ -3,6 +3,7 @@ # (See accompanying file LICENSE.txt or copy at # https://www.bfgroup.xyz/b2/LICENSE.txt) +import args ; import path ; import project ; import modules ; @@ -190,13 +191,16 @@ local rule define-library ( name caller-module ? : root ) } } +args.add-arg modular-search-dir : --modular-search-dir + : "" ; + local rule process-args ( ) { if ! $(.did-process-args) { .did-process-args = yes ; local argv = [ modules.peek : ARGV ] ; - local dirs = [ MATCH ^--modular-search-dir=(.*)$ : $(argv) ] ; + local dirs = [ args.get-arg modular-search-dir ] ; for local dir in $(dirs) { add-location $(dir) : [ path.pwd ] ; diff --git a/src/contrib/wxFormBuilder.jam b/src/contrib/wxFormBuilder.jam index d3d6544c4..d4a15ac58 100644 --- a/src/contrib/wxFormBuilder.jam +++ b/src/contrib/wxFormBuilder.jam @@ -53,6 +53,7 @@ # ################################################################################ +import args ; import generators ; import os ; import path ; @@ -189,7 +190,4 @@ rule is-initialized ( ) .executable = ; .initialized = ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; diff --git a/src/engine/bindjam.cpp b/src/engine/bindjam.cpp index 711ae6c96..6abca25bc 100644 --- a/src/engine/bindjam.cpp +++ b/src/engine/bindjam.cpp @@ -21,6 +21,7 @@ Distributed under the Boost Software License, Version 1.0. #include "value.h" #include "variable.h" +#include "mod_args.h" #include "mod_command_db.h" #include "mod_db.h" #include "mod_jam_builtin.h" @@ -825,7 +826,8 @@ void bind_jam(FRAME * f) .bind(sysinfo_module()) .bind(version_module()) .bind(db_module()) - .bind(command_db_module()); + .bind(command_db_module()) + .bind(b2::args::args_module()); } }} // namespace b2::jam diff --git a/src/engine/build.bat b/src/engine/build.bat index 56d47123a..69b65a07f 100644 --- a/src/engine/build.bat +++ b/src/engine/build.bat @@ -174,10 +174,11 @@ set B2_SOURCES=%B2_SOURCES% events.cpp set B2_SOURCES=%B2_SOURCES% execcmd.cpp execnt.cpp execunix.cpp filent.cpp filesys.cpp fileunix.cpp frames.cpp function.cpp set B2_SOURCES=%B2_SOURCES% glob.cpp hash.cpp hcache.cpp hdrmacro.cpp headers.cpp jam.cpp set B2_SOURCES=%B2_SOURCES% jamgram.cpp lists.cpp make.cpp make1.cpp md5.cpp mem.cpp modules.cpp -set B2_SOURCES=%B2_SOURCES% native.cpp option.cpp output.cpp parse.cpp pathnt.cpp +set B2_SOURCES=%B2_SOURCES% native.cpp output.cpp parse.cpp pathnt.cpp set B2_SOURCES=%B2_SOURCES% pathsys.cpp pathunix.cpp regexp.cpp rules.cpp scan.cpp search.cpp jam_strings.cpp set B2_SOURCES=%B2_SOURCES% startup.cpp tasks.cpp set B2_SOURCES=%B2_SOURCES% timestamp.cpp value.cpp variable.cpp w32_getreg.cpp +set B2_SOURCES=%B2_SOURCES% mod_args.cpp set B2_SOURCES=%B2_SOURCES% mod_command_db.cpp set B2_SOURCES=%B2_SOURCES% mod_db.cpp set B2_SOURCES=%B2_SOURCES% mod_jam_builtin.cpp diff --git a/src/engine/build.sh b/src/engine/build.sh index 0b459329f..65a366aa0 100755 --- a/src/engine/build.sh +++ b/src/engine/build.sh @@ -455,7 +455,6 @@ md5.cpp \ mem.cpp \ modules.cpp \ native.cpp \ -option.cpp \ output.cpp \ parse.cpp \ pathnt.cpp \ @@ -471,6 +470,7 @@ timestamp.cpp \ value.cpp \ variable.cpp \ w32_getreg.cpp \ +mod_args.cpp \ mod_command_db.cpp \ mod_db.cpp \ mod_jam_builtin.cpp \ diff --git a/src/engine/builtins.cpp b/src/engine/builtins.cpp index ec9096201..29f1efc57 100644 --- a/src/engine/builtins.cpp +++ b/src/engine/builtins.cpp @@ -1092,7 +1092,7 @@ LIST * builtin_hdrmacro( FRAME * frame, int flags ) TARGET * const t = bindtarget( list_item( iter ) ); /* Scan file for header filename macro definitions. */ - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "scanning '%s' for header file macro definitions\n", object_str( list_item( iter ) ) ); @@ -1480,7 +1480,6 @@ LIST * builtin_update( FRAME * frame, int flags ) return result; } -extern int anyhow; int last_update_now_status; /* Takes a list of target names and immediately updates them. @@ -1503,8 +1502,8 @@ LIST * builtin_update_now( FRAME * frame, int flags ) int status; int original_stdout = 0; int original_stderr = 0; - int original_noexec = 0; - int original_quitquick = 0; + bool original_noexec = false; + bool original_quitquick = false; if ( !list_empty( log ) ) { @@ -1519,16 +1518,16 @@ LIST * builtin_update_now( FRAME * frame, int flags ) if ( !list_empty( force ) ) { original_noexec = globs.noexec; - globs.noexec = 0; + globs.noexec = false; } if ( !list_empty( continue_ ) ) { original_quitquick = globs.quitquick; - globs.quitquick = 0; + globs.quitquick = false; } - status = make( targets, anyhow ); + status = make( targets, globs.anyhow ); if ( !list_empty( force ) ) { diff --git a/src/engine/compile.cpp b/src/engine/compile.cpp index 44ed322b1..921da4276 100644 --- a/src/engine/compile.cpp +++ b/src/engine/compile.cpp @@ -66,7 +66,7 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame ) profile_frame prof[ 1 ]; module_t * prev_module = frame->module; - if ( DEBUG_COMPILE ) + if ( is_debug_compile() ) { /* Try hard to indicate in which module the rule is going to execute. */ char buf[ 256 ] = ""; @@ -98,7 +98,7 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame ) { frame->rulename = object_str( rulename ); /* And enter record profile info. */ - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) profile_enter( function_rulename( rule->procedure ), prof ); } @@ -153,10 +153,10 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame ) result.reset( function_run( function.get(), frame ) ); } - if ( DEBUG_PROFILE && rule->procedure ) + if ( is_debug_profile() && rule->procedure ) profile_exit( prof ); - if ( DEBUG_COMPILE ) + if ( is_debug_compile() ) debug_compile( -1, 0, frame ); return result.release(); diff --git a/src/engine/constants.cpp b/src/engine/constants.cpp index e6091af15..e047ccf41 100644 --- a/src/engine/constants.cpp +++ b/src/engine/constants.cpp @@ -48,15 +48,8 @@ void constants_init( void ) constant_TMPFILE = object_new( "TMPFILE" ); constant_STDOUT = object_new( "STDOUT" ); constant_STDERR = object_new( "STDERR" ); - constant_JAMDATE = object_new( "JAMDATE" ); - constant_JAM_TIMESTAMP_RESOLUTION = object_new( "JAM_TIMESTAMP_RESOLUTION" ); - constant_JAM_VERSION = object_new( "JAM_VERSION" ); - constant_JAMUNAME = object_new( "JAMUNAME" ); constant_ENVIRON = object_new( ".ENVIRON" ); - constant_ARGV = object_new( "ARGV" ); constant_all = object_new( "all" ); - constant_PARALLELISM = object_new( "PARALLELISM" ); - constant_KEEP_GOING = object_new( "KEEP_GOING" ); constant_other = object_new( "[OTHER]" ); constant_total = object_new( "[TOTAL]" ); constant_FILE_DIRSCAN = object_new( "FILE_DIRSCAN" ); @@ -107,15 +100,8 @@ void constants_done( void ) object_free( constant_TMPFILE ); object_free( constant_STDOUT ); object_free( constant_STDERR ); - object_free( constant_JAMDATE ); - object_free( constant_JAM_TIMESTAMP_RESOLUTION ); - object_free( constant_JAM_VERSION ); - object_free( constant_JAMUNAME ); object_free( constant_ENVIRON ); - object_free( constant_ARGV ); object_free( constant_all ); - object_free( constant_PARALLELISM ); - object_free( constant_KEEP_GOING ); object_free( constant_other ); object_free( constant_total ); object_free( constant_FILE_DIRSCAN ); @@ -164,14 +150,8 @@ OBJECT * constant_TMPNAME; OBJECT * constant_TMPFILE; OBJECT * constant_STDOUT; OBJECT * constant_STDERR; -OBJECT * constant_JAMDATE; -OBJECT * constant_JAM_VERSION; -OBJECT * constant_JAMUNAME; OBJECT * constant_ENVIRON; -OBJECT * constant_ARGV; OBJECT * constant_all; -OBJECT * constant_PARALLELISM; -OBJECT * constant_KEEP_GOING; OBJECT * constant_other; OBJECT * constant_total; OBJECT * constant_FILE_DIRSCAN; @@ -184,7 +164,6 @@ OBJECT * constant_BINDMODULE; OBJECT * constant_IMPORT_MODULE; OBJECT * constant_BUILTIN_GLOB_BACK; OBJECT * constant_timestamp; -OBJECT * constant_JAM_TIMESTAMP_RESOLUTION; OBJECT * constant_python; OBJECT * constant_python_interface; OBJECT * constant_FILE_ARCHIVESCAN; diff --git a/src/engine/constants.h b/src/engine/constants.h index 80798b7f9..fad60609d 100644 --- a/src/engine/constants.h +++ b/src/engine/constants.h @@ -45,15 +45,8 @@ extern OBJECT * constant_TMPNAME; /* "TMPNAME" */ extern OBJECT * constant_TMPFILE; /* "TMPFILE" */ extern OBJECT * constant_STDOUT; /* "STDOUT" */ extern OBJECT * constant_STDERR; /* "STDERR" */ -extern OBJECT * constant_JAMDATE; /* "JAMDATE" */ -extern OBJECT * constant_JAM_TIMESTAMP_RESOLUTION; /* "JAM_TIMESTAMP_RESOLUTION" */ -extern OBJECT * constant_JAM_VERSION; /* "JAM_VERSION" */ -extern OBJECT * constant_JAMUNAME; /* "JAMUNAME" */ extern OBJECT * constant_ENVIRON; /* ".ENVIRON" */ -extern OBJECT * constant_ARGV; /* "ARGV" */ extern OBJECT * constant_all; /* "all" */ -extern OBJECT * constant_PARALLELISM; /* "PARALLELISM" */ -extern OBJECT * constant_KEEP_GOING; /* "KEEP_GOING" */ extern OBJECT * constant_other; /* "[OTHER]" */ extern OBJECT * constant_total; /* "[TOTAL]" */ extern OBJECT * constant_FILE_DIRSCAN; /* "FILE_DIRSCAN" */ diff --git a/src/engine/debug.cpp b/src/engine/debug.cpp index f802b8a00..dd4d91dd2 100644 --- a/src/engine/debug.cpp +++ b/src/engine/debug.cpp @@ -20,14 +20,14 @@ static profile_info profile_total = { 0 }; profile_frame * profile_init( OBJECT * rulename, profile_frame * frame ) { - if ( DEBUG_PROFILE ) profile_enter( rulename, frame ); + if ( is_debug_profile() ) profile_enter( rulename, frame ); return frame; } void profile_enter( OBJECT * rulename, profile_frame * frame ) { - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) { double start = profile_clock(); profile_info * p; @@ -75,7 +75,7 @@ void profile_enter( OBJECT * rulename, profile_frame * frame ) void profile_memory( size_t mem ) { - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) if ( profile_stack && profile_stack->info ) profile_stack->info->memory += ((double)mem) / 1024; } @@ -83,7 +83,7 @@ void profile_memory( size_t mem ) void profile_exit( profile_frame * frame ) { - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) { /* Cumulative time for this call. */ double t = profile_clock() - frame->entry_time - frame->overhead; @@ -147,7 +147,7 @@ double profile_clock() OBJECT * profile_make_local( char const * scope ) { - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) { return object_new( scope ); } diff --git a/src/engine/debug.h b/src/engine/debug.h index 115135ef3..6d942b492 100644 --- a/src/engine/debug.h +++ b/src/engine/debug.h @@ -58,7 +58,7 @@ double profile_clock(); OBJECT * profile_make_local( char const * ); #define PROFILE_ENTER_LOCAL( scope ) \ static OBJECT * constant_LOCAL_##scope = 0; \ - if (DEBUG_PROFILE && !constant_LOCAL_##scope) constant_LOCAL_##scope = profile_make_local( #scope ); \ + if (is_debug_profile() && !constant_LOCAL_##scope) constant_LOCAL_##scope = profile_make_local( #scope ); \ PROFILE_ENTER( LOCAL_##scope ) #define PROFILE_EXIT_LOCAL( scope ) PROFILE_EXIT( LOCAL_##scope ) diff --git a/src/engine/debugger.cpp b/src/engine/debugger.cpp index 5b10bb4f2..fc7620c72 100644 --- a/src/engine/debugger.cpp +++ b/src/engine/debugger.cpp @@ -826,11 +826,11 @@ static void debug_mi_error( const char * message ) static void debug_error_( const char * message ) { - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { printf( "%s\n", message ); } - else if ( debug_interface == DEBUG_INTERFACE_MI ) + else if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_error( message ); } @@ -885,11 +885,11 @@ static void debug_error( const char * format, ... ) static void debug_parent_child_exited( int pid, int exit_code ) { - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { printf( "Child %d exited with status %d\n", (int)child_pid, (int)exit_code ); } - else if ( debug_interface == DEBUG_INTERFACE_MI ) + else if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { if ( exit_code == 0 ) printf( "*stopped,reason=\"exited-normally\"\n(gdb) \n" ); @@ -907,11 +907,11 @@ static void debug_parent_child_exited( int pid, int exit_code ) static void debug_parent_child_signalled( int pid, int id ) { - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { printf( "Child %d exited on signal %d\n", child_pid, id ); } - else if ( debug_interface == DEBUG_INTERFACE_MI ) + else if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { const char * name = "unknown"; const char * meaning = "unknown"; @@ -937,14 +937,14 @@ static void debug_parent_on_breakpoint( void ) fprintf( command_output, "info frame\n" ); fflush( command_output ); debug_frame_read( command_child, &base ); - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { printf( "Breakpoint %d, ", id ); debug_print_frame_info( base ); printf( "\n" ); debug_print_source( base.file, base.line ); } - else if ( debug_interface == DEBUG_INTERFACE_MI ) + else if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { printf( "*stopped,reason=\"breakpoint-hit\",bkptno=\"%d\",disp=\"keep\",", id ); debug_mi_print_frame_info( &base ); @@ -964,7 +964,7 @@ static void debug_parent_on_end_stepping( void ) fprintf( command_output, "info frame\n" ); fflush( command_output ); debug_frame_read( command_child, &base ); - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { debug_print_source( base.file, base.line ); } @@ -1142,10 +1142,10 @@ static void debug_start_child( int argc, const char * * argv ) string_copy( command_line, "b2 " ); /* Pass the handles as the first and second arguments. */ string_append( command_line, debugger_opt ); - string_append( command_line, b2::value::format( "%p", pipe1[ 0 ] )->str() ); + string_append( command_line, b2::value::format( "=%p", pipe1[ 0 ] )->str() ); string_push_back( command_line, ' ' ); string_append( command_line, debugger_opt ); - string_append( command_line, b2::value::format( "%p", pipe2[ 1 ] )->str() ); + string_append( command_line, b2::value::format( "=%p", pipe2[ 1 ] )->str() ); /* Pass the rest of the command line. */ { int i; @@ -1265,7 +1265,7 @@ static void debug_parent_run( int argc, const char * * argv ) debug_parent_wait( 1 ); } debug_parent_run_print( argc, argv ); - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { printf( "=thread-created,id=\"1\",group-id=\"i1\"\n" ); debug_mi_format_token(); @@ -1312,7 +1312,7 @@ static void debug_parent_continue( int argc, const char * * argv ) debug_error( "Too many arguments to continue." ); return; } - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^running\n(gdb) \n" ); @@ -1328,7 +1328,7 @@ static void debug_parent_kill( int argc, const char * * argv ) debug_error( "Too many arguments to kill." ); return; } - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^done\n(gdb) \n" ); @@ -1344,7 +1344,7 @@ static void debug_parent_step( int argc, const char * * argv ) debug_error( "Too many arguments to step." ); return; } - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^running\n(gdb) \n" ); @@ -1360,7 +1360,7 @@ static void debug_parent_next( int argc, const char * * argv ) debug_error( "Too many arguments to next." ); return; } - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^running\n(gdb) \n" ); @@ -1376,7 +1376,7 @@ static void debug_parent_finish( int argc, const char * * argv ) debug_error( "Too many arguments to finish." ); return; } - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^running\n(gdb) \n" ); @@ -1400,11 +1400,11 @@ static void debug_parent_break( int argc, const char * * argv ) } id = debug_add_breakpoint( argv[ 1 ] ); debug_parent_forward_nowait( argc, argv, 1, 0 ); - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { printf( "Breakpoint %d set at %s\n", id, argv[ 1 ] ); } - else if ( debug_interface == DEBUG_INTERFACE_MI ) + else if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^done\n(gdb) \n" ); @@ -1453,7 +1453,7 @@ static void debug_parent_disable( int argc, const char * * argv ) } debug_child_disable( argc, argv ); debug_parent_forward_nowait( 2, argv, 1, 0 ); - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^done\n(gdb) \n" ); @@ -1468,7 +1468,7 @@ static void debug_parent_enable( int argc, const char * * argv ) } debug_child_enable( argc, argv ); debug_parent_forward_nowait( 2, argv, 1, 0 ); - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^done\n(gdb) \n" ); @@ -1483,7 +1483,7 @@ static void debug_parent_delete( int argc, const char * * argv ) } debug_child_delete( argc, argv ); debug_parent_forward_nowait( 2, argv, 1, 0 ); - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { debug_mi_format_token(); printf( "^done\n(gdb) \n" ); @@ -1511,7 +1511,7 @@ static void debug_parent_clear( int argc, const char * * argv ) return; } - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { printf( "Deleted breakpoint %d\n", id ); } @@ -1531,12 +1531,12 @@ static void debug_parent_print( int argc, const char * * argv ) } result = debug_list_read( command_child ); - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) { list_print( result ); printf( "\n" ); } - else if ( debug_interface == DEBUG_INTERFACE_MI ) + else if ( globs.debug_interface == DEBUG_INTERFACE_MI ) { printf( "~\"$1 = " ); list_print( result ); @@ -2601,11 +2601,11 @@ int debugger( void ) { command_array = parent_commands; command_input = stdin; - if ( debug_interface == DEBUG_INTERFACE_MI ) + if ( globs.debug_interface == DEBUG_INTERFACE_MI ) printf( "=thread-group-added,id=\"i1\"\n(gdb) \n" ); while ( 1 ) { - if ( debug_interface == DEBUG_INTERFACE_CONSOLE ) + if ( globs.debug_interface == DEBUG_INTERFACE_CONSOLE ) printf("(b2db) "); fflush( stdout ); read_command(); @@ -2727,7 +2727,7 @@ static void debug_listen( void ) } struct debug_child_data_t debug_child_data; -const char debugger_opt[] = "--b2db-internal-debug-handle="; +const char debugger_opt[] = "--b2db-internal-debug-handle"; int debug_interface; void debugger_done() diff --git a/src/engine/debugger.h b/src/engine/debugger.h index 61b03273f..893e018d7 100644 --- a/src/engine/debugger.h +++ b/src/engine/debugger.h @@ -32,13 +32,12 @@ struct debug_child_data_t extern struct debug_child_data_t debug_child_data; extern b2::list_ref debug_print_result; extern const char debugger_opt[]; -extern int debug_interface; -#define DEBUG_INTERFACE_CONSOLE 1 -#define DEBUG_INTERFACE_MI 2 -#define DEBUG_INTERFACE_CHILD 3 +#define DEBUG_INTERFACE_CONSOLE (global_config::debug_interface_console) +#define DEBUG_INTERFACE_MI (global_config::debug_interface_mi) +#define DEBUG_INTERFACE_CHILD (global_config::debug_interface_child) -#define debug_is_debugging() ( debug_interface != 0 ) +#define debug_is_debugging() ( globs.debug_interface != global_config::debug_interface_no ) #define debug_on_enter_function( frame, name, file, line ) \ ( debug_is_debugging()? \ debug_on_enter_function( frame, name, file, line ) : \ diff --git a/src/engine/execnt.cpp b/src/engine/execnt.cpp index 28c3a8607..d7cb25e18 100644 --- a/src/engine/execnt.cpp +++ b/src/engine/execnt.cpp @@ -355,7 +355,7 @@ void exec_cmd if ( list_empty( shell ) ) shell = default_shell; - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) { if ( is_raw_cmd ) out_printf( "Executing raw command directly\n" ); @@ -550,7 +550,7 @@ static void invoke_cmd( char const * const command, int32_t const slot ) /* Let the child inherit stdin, as some commands assume it is available. */ si.hStdInput = GetStdHandle( STD_INPUT_HANDLE ); - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) out_printf( "Command string for CreateProcessA(): '%s'\n", command ); /* Run the command by creating a sub-process for it. */ diff --git a/src/engine/execunix.cpp b/src/engine/execunix.cpp index 5aea799eb..d0e486556 100644 --- a/src/engine/execunix.cpp +++ b/src/engine/execunix.cpp @@ -203,7 +203,7 @@ void exec_cmd */ argv_from_shell( argv, shell, command->value, slot ); - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) { int i; out_printf( "Using shell: " ); diff --git a/src/engine/execvms.cpp b/src/engine/execvms.cpp index 05b6c7a20..cc61cb763 100644 --- a/src/engine/execvms.cpp +++ b/src/engine/execvms.cpp @@ -265,7 +265,7 @@ void exec_cmd fclose( f ); - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) { FILE * f; char buf[ WRTLEN + 1 ] = { 0 }; diff --git a/src/engine/ext_bfgroup_lyra.h b/src/engine/ext_bfgroup_lyra.h index 746930d0f..e7d13352d 100644 --- a/src/engine/ext_bfgroup_lyra.h +++ b/src/engine/ext_bfgroup_lyra.h @@ -1,4 +1,4 @@ -// Copyright 2018-2022 René Ferdinand Rivera Morell +// Copyright René Ferdinand Rivera Morell // Copyright 2021 Max Ferger // Copyright 2017 Two Blue Cubes Ltd. All rights reserved. // @@ -27,152 +27,6 @@ #define LYRA_ARG_HPP -#ifndef LYRA_DETAIL_PRINT_HPP -#define LYRA_DETAIL_PRINT_HPP - -#if LYRA_DEBUG -# include -#endif - -#include - -#ifndef LYRA_DEBUG -# define LYRA_DEBUG 0 -#endif - -namespace lyra { namespace detail { - -constexpr bool is_debug = LYRA_DEBUG; - -template -std::string to_string(T && t) -{ - return std::string(std::move(t)); -} - -using std::to_string; - -#if LYRA_DEBUG - -struct print -{ - print(const char * scope_name = nullptr) - : scope(scope_name) - { - if (is_debug) print::depth() += 1; - if (scope) debug(scope, "..."); - } - - ~print() - { - if (scope) debug("...", scope); - if (is_debug) print::depth() -= 1; - } - - template - void debug(A... arg) - { - if (is_debug) - { - std::cerr << "[DEBUG]" - << std::string((print::depth() - 1) * 2, ' '); - std::string args[] = { to_string(arg)... }; - for (auto & arg_string : args) - { - std::cerr << " " << arg_string; - } - std::cerr << "\n"; - } - } - - private: - const char * scope; - - static std::size_t & depth() - { - static std::size_t d = 0; - return d; - } -}; - -#endif - -}} // namespace lyra::detail - -#if LYRA_DEBUG -# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope -# define LYRA_PRINT_DEBUG lyra_print_scope.debug -#else -# define LYRA_PRINT_SCOPE(...) while (false) -# define LYRA_PRINT_DEBUG(...) while (false) -#endif - -#endif - -#ifndef LYRA_PARSER_HPP -#define LYRA_PARSER_HPP - - -#ifndef LYRA_ARGS_HPP -#define LYRA_ARGS_HPP - -#include -#include -#include - -namespace lyra { - -/* tag::reference[] - -[#lyra_args] -= `lyra::args` - -Transport for raw args (copied from main args, supplied via init list, or from -a pair of iterators). - -*/ // end::reference[] -class args -{ - public: - args(int argc, char const * const * argv) - : m_exeName((argv && (argc >= 1)) ? argv[0] : "") - , m_args((argv && (argc >= 1)) ? argv + 1 : nullptr, argv + argc) - {} - - args(std::initializer_list args_list) - : m_exeName(args_list.size() >= 1 ? *args_list.begin() : "") - , m_args( - args_list.size() >= 1 ? args_list.begin() + 1 : args_list.end(), - args_list.end()) - {} - - template - args(const It & start, const It & end) - : m_exeName(start != end ? *start : "") - , m_args(start != end ? start + 1 : end, end) - {} - - std::string exe_name() const { return m_exeName; } - - std::vector::const_iterator begin() const - { - return m_args.begin(); - } - - std::vector::const_iterator end() const - { - return m_args.end(); - } - - private: - std::string m_exeName; - std::vector m_args; -}; - -} // namespace lyra - -#endif - #ifndef LYRA_DETAIL_BOUND_HPP #define LYRA_DETAIL_BOUND_HPP @@ -193,35 +47,35 @@ template struct is_callable { template - static auto test(U * p) - -> decltype((*p)(std::declval()...), void(), std::true_type()); + static auto test(U * p) -> decltype((*p)(std::declval()...), + void(), + std::true_type()); template static auto test(...) -> decltype(std::false_type()); - static constexpr bool value = decltype(test(0))::value; + static constexpr bool value = decltype(test(nullptr))::value; }; template struct remove_cvref { - typedef - typename std::remove_cv::type>::type - type; + using type = + typename std::remove_cv::type>::type; }; template struct is_invocable { template - static auto test(U * p) - -> decltype((&U::operator()), void(), std::true_type()); + static auto test( + U * p) -> decltype((&U::operator()), void(), std::true_type()); template static auto test(...) -> decltype(std::false_type()); static constexpr bool value - = decltype(test::type>(0))::value; + = decltype(test::type>(nullptr))::value; }; template @@ -243,6 +97,7 @@ struct is_specialization_of, Primary> : std::true_type #endif +#include #include #include #include @@ -371,7 +226,8 @@ inline bool from_string(S const & source, bool & target) { std::string srcLC; to_string(source, srcLC); - for (std::string::value_type & c : srcLC) c = ::tolower(c); + for (std::string::value_type & c : srcLC) + c = static_cast(std::tolower(c)); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; @@ -443,7 +299,6 @@ class result_base explicit operator bool() const { return is_ok(); } bool is_ok() const { return kind_ == result_kind::ok; } std::string message() const { return message_; } - [[deprecated]] std::string errorMessage() const { return message(); } protected: enum class result_kind @@ -584,6 +439,7 @@ class basic_result : public result_value_base }} // namespace lyra::detail #endif + #include namespace lyra { @@ -592,7 +448,8 @@ enum class parser_result_type { matched, no_match, - short_circuit_all + short_circuit_all, + empty_match }; inline std::string to_string(parser_result_type v) @@ -602,6 +459,7 @@ inline std::string to_string(parser_result_type v) case parser_result_type::matched: return "matched"; case parser_result_type::no_match: return "no_match"; case parser_result_type::short_circuit_all: return "short_circuit_all"; + case parser_result_type::empty_match: return "empty_match"; } return "?"; } @@ -614,8 +472,6 @@ using parser_result = detail::basic_result; #endif -#include - namespace lyra { namespace detail { template @@ -699,7 +555,12 @@ inline parser_result invokeLambda(L const & lambda, std::string const & arg) }} // namespace lyra::detail #endif + +#include +#include #include +#include +#include namespace lyra { namespace detail { @@ -747,8 +608,8 @@ struct BoundValueRef : BoundValueRefBase return parse_string(arg, m_ref); } - virtual size_t get_value_count() const override { return 1; } - virtual std::string get_value(size_t i) const override + size_t get_value_count() const override { return 1; } + std::string get_value(size_t i) const override { if (i == 0) { @@ -779,8 +640,8 @@ struct BoundValueRef> : BoundValueRefBase return str_result; } - virtual size_t get_value_count() const override { return m_ref.size(); } - virtual std::string get_value(size_t i) const override + size_t get_value_count() const override { return m_ref.size(); } + std::string get_value(size_t i) const override { if (i < m_ref.size()) { @@ -806,8 +667,8 @@ struct BoundFlagRef : BoundFlagRefBase return parser_result::ok(parser_result_type::matched); } - virtual size_t get_value_count() const override { return 1; } - virtual std::string get_value(size_t i) const override + size_t get_value_count() const override { return 1; } + std::string get_value(size_t i) const override { if (i == 0) return m_ref ? "true" : "false"; return ""; @@ -865,7 +726,7 @@ struct BoundVal : BoundValueRef , value(v) {} - BoundVal(BoundVal && other) + BoundVal(BoundVal && other) noexcept : BoundValueRef(value) , value(std::move(other.value)) {} @@ -880,131 +741,86 @@ struct BoundVal : BoundValueRef #endif -#ifndef LYRA_DETAIL_CHOICES_HPP -#define LYRA_DETAIL_CHOICES_HPP +#ifndef LYRA_DETAIL_PRINT_HPP +#define LYRA_DETAIL_PRINT_HPP + +#if LYRA_DEBUG +# include +#endif -#include #include -#include -#include + +#ifndef LYRA_DEBUG +# define LYRA_DEBUG 0 +#endif namespace lyra { namespace detail { -/* -Type erased base for set of choices. I.e. it's an "interface". -*/ -struct choices_base -{ - virtual ~choices_base() = default; - virtual parser_result contains_value(std::string const & val) const = 0; -}; +constexpr bool is_debug = LYRA_DEBUG; -/* -Stores a set of choice values and provides checking if a given parsed -string value is one of the choices. -*/ template -struct choices_set : choices_base +std::string to_string(T && t) { - std::vector values; + return std::string(std::forward(t)); +} - template - explicit choices_set(Vals... vals) - : choices_set({ vals... }) - {} +using std::to_string; - explicit choices_set(const std::vector & vals) - : values(vals) - {} +#if LYRA_DEBUG - parser_result contains_value(std::string const & val) const override +struct print +{ + print(const char * scope_name = nullptr) + : scope(scope_name) { - T value; - auto parse = parse_string(val, value); - if (!parse) - { - return parser_result::error( - parser_result_type::no_match, parse.message()); - } - for (const T & allowed_value : values) - { - if (allowed_value == value) - return parser_result::ok(parser_result_type::matched); - } - return parser_result::error(parser_result_type::no_match, - "Value '" + val - + "' not expected. Allowed values are: " + this->to_string()); + if (is_debug) print::depth() += 1; + if (scope) debug(scope, "..."); } - std::string to_string() const + ~print() { - std::string text; - for (const T & val : values) - { - if (!text.empty()) text += ", "; - std::string val_string; - if (detail::to_string(val, val_string)) - text += val_string; - else - text += ""; - } - return text; + if (scope) debug("...", scope); + if (is_debug) print::depth() -= 1; } - protected: - explicit choices_set(std::initializer_list const & vals) - : values(vals) - {} -}; - -template <> -struct choices_set : choices_set -{ - template - explicit choices_set(Vals... vals) - : choices_set(vals...) - {} -}; - -/* -Calls a designated function to check if the choice is valid. -*/ -template -struct choices_check : choices_base -{ - static_assert(unary_lambda_traits::isValid, - "Supplied lambda must take exactly one argument"); - static_assert(std::is_same::ReturnType>::value, - "Supplied lambda must return bool"); - - Lambda checker; - using value_type = typename unary_lambda_traits::ArgType; - - explicit choices_check(Lambda const & checker_function) - : checker(checker_function) - {} - - parser_result contains_value(std::string const & val) const override + template + void debug(A... arg) { - value_type value; - auto parse = parse_string(val, value); - if (!parse) + if (is_debug) { - return parser_result::error( - parser_result_type::no_match, parse.message()); + std::cerr << "[DEBUG]" + << std::string((print::depth() - 1) * 2, ' '); + std::string args[] = { to_string(arg)... }; + for (auto & arg_string : args) + { + std::cerr << " " << arg_string; + } + std::cerr << "\n"; } - if (checker(value)) - { - return parser_result::ok(parser_result_type::matched); - } - return parser_result::error( - parser_result_type::no_match, "Value '" + val + "' not expected."); + } + + private: + const char * scope; + + static std::size_t & depth() + { + static std::size_t d = 0; + return d; } }; +#endif + }} // namespace lyra::detail +#if LYRA_DEBUG +# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope +# define LYRA_PRINT_DEBUG lyra_print_scope.debug +#else +# define LYRA_PRINT_SCOPE(...) while (false) +# define LYRA_PRINT_DEBUG(...) while (false) +#endif + #endif #ifndef LYRA_DETAIL_TOKENS_HPP @@ -1014,7 +830,9 @@ struct choices_check : choices_base #ifndef LYRA_OPTION_STYLE_HPP #define LYRA_OPTION_STYLE_HPP +#include #include +#include namespace lyra { @@ -1032,6 +850,7 @@ std::string long_option_prefix; std::size_t long_option_size = 0; std::string short_option_prefix; std::size_t short_option_size = 0; +opt_print_order options_print_order = opt_print_order::per_declaration; ---- * `value_delimiters` -- Specifies a set of characters that are accepted as a @@ -1045,27 +864,46 @@ std::size_t short_option_size = 0; prefix for short options (i.e. single-char multi-options). * `short_option_size` -- The number of prefix characters that indicates a short option. A value of zero (0) indicates that short options are not accepted. +* `options_print_order` -- The order to print the options section of the help + text. Possible values: `per_declaration`, `sorted_short_first`, + `sorted_long_first`. +* `indent_size` -- The character count to indent argument detail description. + This affects the indenting of usage detail, the arguments, and any + sub-commands/arguments recursively. The default is "2". end::reference[] */ struct option_style { + enum class opt_print_order : unsigned char + { + per_declaration = 0, + sorted_short_first, + sorted_long_first + }; + std::string value_delimiters; std::string long_option_prefix; std::size_t long_option_size = 0; std::string short_option_prefix; std::size_t short_option_size = 0; + opt_print_order options_print_order = opt_print_order::per_declaration; + std::size_t indent_size = 2; option_style(std::string && value_delimiters_chars, std::string && long_option_prefix_chars = {}, std::size_t long_option_prefix_size = 0, std::string && short_option_prefix_chars = {}, - std::size_t short_option_prefix_size = 0) + std::size_t short_option_prefix_size = 0, + opt_print_order options_print_order_ = opt_print_order::per_declaration, + std::size_t indent_size_ = 2) : value_delimiters(std::move(value_delimiters_chars)) , long_option_prefix(std::move(long_option_prefix_chars)) , long_option_size(long_option_prefix_size) , short_option_prefix(std::move(short_option_prefix_chars)) , short_option_size(short_option_prefix_size) + , options_print_order(options_print_order_) + , indent_size(indent_size_) {} @@ -1076,6 +914,26 @@ struct option_style static const option_style & posix(); static const option_style & posix_brief(); static const option_style & windows(); + + + bool opt_print_order_less( + const std::string & a, const std::string & b) const + { + const auto l = long_option_string(); + const auto s = short_option_string(); + const bool a_l = a.substr(0, l.size()) == l; + const bool a_s = !a_l && (a.substr(0, s.size()) == s); + const bool b_l = b.substr(0, l.size()) == l; + const bool b_s = !b_l && (b.substr(0, s.size()) == s); + if (!a_l && !a_s) return false; + if (!b_l && !b_s) return true; + if ((a_l == b_l) && (a_s == b_s)) return a < b; + if (options_print_order == opt_print_order::sorted_short_first) + return (a_s && b_l); + else if (options_print_order == opt_print_order::sorted_long_first) + return (b_s && a_l); + return a < b; + } }; /* tag::reference[] @@ -1176,6 +1034,8 @@ inline const option_style & option_style::windows() #endif +#include +#include #include #include @@ -1367,7 +1227,11 @@ class token_iterator token value() const { - if (has_option_prefix() && has_value_delimiter()) + if (has_short_option_prefix() + && (args_i->find_first_of(style.value_delimiters) + == (style.short_option_size + 1))) + return token(token_type::argument, args_i->substr(3)); + else if (has_long_option_prefix() && has_value_delimiter()) return token(token_type::argument, args_i->substr( args_i->find_first_of(style.value_delimiters) + 1)); @@ -1438,9 +1302,142 @@ class token_iterator #endif +#ifndef LYRA_PARSER_HPP +#define LYRA_PARSER_HPP + + +#ifndef LYRA_DETAIL_CHOICES_HPP +#define LYRA_DETAIL_CHOICES_HPP + +#include +#include +#include +#include + +namespace lyra { namespace detail { + +/* +Type erased base for set of choices. I.e. it's an "interface". +*/ +struct choices_base +{ + virtual ~choices_base() = default; + virtual parser_result contains_value(std::string const & val) const = 0; +}; + +/* +Stores a set of choice values and provides checking if a given parsed +string value is one of the choices. +*/ +template +struct choices_set : choices_base +{ + std::vector values; + + template + explicit choices_set(Vals... vals) + : choices_set({ vals... }) + {} + + explicit choices_set(const std::vector & vals) + : values(vals) + {} + + parser_result contains_value(std::string const & val) const override + { + T value; + auto parse = parse_string(val, value); + if (!parse) + { + return parser_result::error( + parser_result_type::no_match, parse.message()); + } + for (const T & allowed_value : values) + { + if (allowed_value == value) + return parser_result::ok(parser_result_type::matched); + } + return parser_result::error(parser_result_type::no_match, + "Value '" + val + + "' not expected. Allowed values are: " + this->to_string()); + } + + std::string to_string() const + { + std::string text; + for (const T & val : values) + { + if (!text.empty()) text += ", "; + std::string val_string; + if (detail::to_string(val, val_string)) + text += val_string; + else + text += ""; + } + return text; + } + + protected: + explicit choices_set(std::initializer_list const & vals) + : values(vals) + {} +}; + +template <> +struct choices_set : choices_set +{ + template + explicit choices_set(Vals... vals) + : choices_set(vals...) + {} +}; + +/* +Calls a designated function to check if the choice is valid. +*/ +template +struct choices_check : choices_base +{ + static_assert(unary_lambda_traits::isValid, + "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ReturnType>::value, + "Supplied lambda must return bool"); + + Lambda checker; + using value_type = typename unary_lambda_traits::ArgType; + + explicit choices_check(Lambda const & checker_function) + : checker(checker_function) + {} + + parser_result contains_value(std::string const & val) const override + { + value_type value; + auto parse = parse_string(val, value); + if (!parse) + { + return parser_result::error( + parser_result_type::no_match, parse.message()); + } + if (checker(value)) + { + return parser_result::ok(parser_result_type::matched); + } + return parser_result::error( + parser_result_type::no_match, "Value '" + val + "' not expected."); + } +}; + +}} // namespace lyra::detail + +#endif + #ifndef LYRA_PRINTER_HPP #define LYRA_PRINTER_HPP + +#include #include #include #include @@ -1459,14 +1456,25 @@ of the output device and any visual arrangement, i.e. padding, coloring, etc. [source] ---- virtual printer & printer::heading( + const option_style & style, const std::string & txt) = 0; virtual printer & printer::paragraph( - const std::string & txt, - std::size_t indent = 0) = 0; + const option_style & style, + const std::string & txt) = 0; virtual printer & printer::option( + const option_style & style, const std::string & opt, - const std::string & description, - std::size_t indent = 0) = 0; + const std::string & description) = 0; +---- + +Indenting levels is implemented at the base and the indent is available for +concrete implementations to apply as needed. + +[source] +---- +virtual printer & indent(int levels = 1); +virtual printer & dedent(int levels = 1); +virtual int get_indent_level() const; ---- You can customize the printing output by implementing a subclass of @@ -1486,13 +1494,34 @@ class printer { public: virtual ~printer() = default; - virtual printer & heading(const std::string & txt) = 0; - virtual printer & paragraph(const std::string & txt, std::size_t indent = 0) + virtual printer & heading( + const option_style & style, const std::string & txt) = 0; - virtual printer & option(const std::string & opt, - const std::string & description, - std::size_t indent = 0) + virtual printer & paragraph( + const option_style & style, const std::string & txt) = 0; + virtual printer & option(const option_style & style, + const std::string & opt, + const std::string & description) + = 0; + virtual printer & indent(int levels = 1) + { + indent_level += levels; + return *this; + } + virtual printer & dedent(int levels = 1) + { + indent_level -= levels; + return *this; + } + virtual int get_indent_level() const + { + if (indent_level < 0) return 0; + return indent_level; + } + + protected: + int indent_level = 0; }; /* tag::reference[] @@ -1511,24 +1540,27 @@ class ostream_printer : public printer explicit ostream_printer(std::ostream & os_) : os(os_) {} - virtual printer & heading(const std::string & txt) override + printer & heading(const option_style &, const std::string & txt) override { os << txt << "\n"; return *this; } - virtual printer & paragraph( - const std::string & txt, std::size_t indent = 0) override + printer & paragraph( + const option_style & style, const std::string & txt) override { - const std::string indent_str(indent, ' '); + const std::string indent_str( + get_indent_level() * style.indent_size, ' '); os << indent_str << txt << "\n\n"; return *this; } - virtual printer & option(const std::string & opt, - const std::string & description, - std::size_t indent = 0) override + printer & option(const option_style & style, + const std::string & opt, + const std::string & description) override { - const std::string indent_str(indent, ' '); - const std::string opt_pad(26 - indent - 1, ' '); + const std::string indent_str( + get_indent_level() * style.indent_size, ' '); + const std::string opt_pad( + 26 - get_indent_level() * style.indent_size - 1, ' '); if (opt.size() > opt_pad.size()) os << indent_str << opt << "\n" << indent_str << opt_pad << " " << description << "\n"; @@ -1552,48 +1584,11 @@ inline std::unique_ptr make_printer(std::ostream & os_) #endif -#ifndef LYRA_VAL_HPP -#define LYRA_VAL_HPP - -#include - -namespace lyra { - -/* tag::reference[] - -[#lyra_val] -= `lyra::val` - -[source] ----- -auto val(T && v); -auto val(const char * v); ----- - -Makes a bound self-contained value of the type of the given r-value. The created -bound values can be used in place of the value references for arguments. And can -be retrieved with the -<> call. - -*/ // end::reference[] -template -detail::BoundVal val(T && v) -{ - return detail::BoundVal(std::forward(v)); -} - -inline detail::BoundVal val(const char * v) -{ - return detail::BoundVal(v); -} - -} // namespace lyra - -#endif - +#include +#include #include +#include #include -#include namespace lyra { @@ -1604,7 +1599,7 @@ class parse_state public: parse_state(parser_result_type type, token_iterator const & remaining_tokens, - size_t parsed_tokens = 0) + std::size_t parsed_tokens = 0) : result_type(type) , tokens(remaining_tokens) { @@ -1622,12 +1617,12 @@ class parse_state struct parser_cardinality { - size_t minimum = 0; - size_t maximum = 0; + std::size_t minimum = 0; + std::size_t maximum = 0; parser_cardinality() = default; - parser_cardinality(size_t a, size_t b) + parser_cardinality(std::size_t a, std::size_t b) : minimum(a) , maximum(b) {} @@ -1645,17 +1640,17 @@ struct parser_cardinality minimum = 0; maximum = 1; } - void required(size_t n = 1) + void required(std::size_t n = 1) { minimum = n; maximum = n; } - void counted(size_t n) + void counted(std::size_t n) { minimum = n; maximum = n; } - void bounded(size_t n, size_t m) + void bounded(std::size_t n, std::size_t m) { minimum = n; maximum = m; @@ -1696,19 +1691,6 @@ end::reference[] */ class parser { public: - struct help_text_item - { - std::string option; - std::string description; - }; - - using help_text = std::vector; - - [[deprecated]] help_text get_help_text() const { return {}; } - [[deprecated]] std::string get_usage_text() const { return ""; } - [[deprecated]] std::string get_description_text() const { return ""; } - - virtual help_text get_help_text(const option_style &) const { return {}; } virtual std::string get_usage_text(const option_style &) const { return ""; @@ -1735,8 +1717,8 @@ class parser if (is_named(n)) return this; return nullptr; } - virtual size_t get_value_count() const { return 0; } - virtual std::string get_value(size_t i) const + virtual std::size_t get_value_count() const { return 0; } + virtual std::string get_value(std::size_t i) const { (void)i; return ""; @@ -1746,27 +1728,60 @@ class parser detail::token_iterator const & tokens, const option_style & style) const = 0; + virtual std::string get_print_order_key(const option_style &) const + { + return ""; + } + + virtual void print_help_text_details(printer &, const option_style &) const + {} + protected: virtual void print_help_text(printer & p, const option_style & style) const { print_help_text_summary(p, style); + p.heading(style, "OPTIONS, ARGUMENTS:"); + p.indent(); print_help_text_details(p, style); + p.dedent(); } - virtual void print_help_text_summary(printer & p, const option_style & style) const - { - std::string usage_test = get_usage_text(style); - if (!usage_test.empty()) - p.heading("USAGE:").paragraph(get_usage_text(style), 2); - std::string description_test = get_description_text(style); - if (!description_test.empty()) p.paragraph(get_description_text(style)); - } - virtual void print_help_text_details(printer & p, const option_style & style) const + virtual void print_help_text_summary( + printer & p, const option_style & style) const { - p.heading("OPTIONS, ARGUMENTS:"); - for (auto const & cols : get_help_text(style)) + std::string usage_text = get_usage_text(style); + if (!usage_text.empty()) + p.heading(style, "USAGE:") + .indent() + .paragraph(style, usage_text) + .dedent(); + + std::string description_text = get_description_text(style); + if (!description_text.empty()) p.paragraph(style, description_text); + } + + template + void for_each_print_ordered_parser( + const option_style & style, I b, I e, F f) const + { + if (style.options_print_order + != option_style::opt_print_order::per_declaration) { - p.option(cols.option, cols.description, 2); + std::vector order_index(std::distance(b, e)); + std::iota(order_index.begin(), order_index.end(), 0); + std::stable_sort(order_index.begin(), order_index.end(), + [&](std::size_t i, std::size_t j) { + const parser & pa = **(b + i); + const parser & pb = **(b + j); + return style.opt_print_order_less( + pa.get_print_order_key(style), + pb.get_print_order_key(style)); + }); + for (auto i : order_index) f(style, **(b + i)); + } + else + { + while (b != e) f(style, **(b++)); } } }; @@ -1776,47 +1791,6 @@ class parser [#lyra_parser_specification] == Specification -[#lyra_parser_help_text_item] -=== `lyra::parser::help_text_item` - -[source] ----- -struct lyra::parser::help_text_item -{ - std::string option; - std::string description; -}; ----- - -Holds the help information for a single argument option. The `option` member is -the long name of the option. And the `description` is the text describing the -option. A list of them is returned from the `lyra::parser::get_help_text` -method. - -[#lyra_parser_help_text] -=== `lyra::parser::help_text` - -[source] ----- -using help_text = std::vector; ----- - -The set of help texts for any options in the sub-parsers to this one, if any. - -[#lyra_parser_get_help_text] -=== `lyra::parser::get_help_text` - -[source] ----- -virtual help_text get_help_text(const option_style &) const; ----- - -Collects, and returns, the set of help items for the sub-parser arguments in -this parser, if any. The default is to return an empty set. Which is what most -parsers will return. Parsers like `arguments`, `group`, and `cli` will return a -set for the arguments defined. This is called to print out the help text from -the stream operator. - [#lyra_parser_get_usage_text] === `lyra::parser::get_usage_text` @@ -1895,6 +1869,13 @@ class bound_parser : public composable_parser m_cardinality = { 0, 0 }; else m_cardinality = { 0, 1 }; + if (!m_ref->isFlag() && !m_ref->isContainer() + && m_ref->get_value_count() == 1) + { + auto value_zero = m_ref->get_value(0); + if (!value_zero.empty()) + m_description = "[default: " + value_zero + "]"; + } } public: @@ -1925,9 +1906,9 @@ class bound_parser : public composable_parser Derived & help(const std::string & text); Derived & operator()(std::string const & description); Derived & optional(); - Derived & required(size_t n = 1); - Derived & cardinality(size_t n); - Derived & cardinality(size_t n, size_t m); + Derived & required(std::size_t n = 1); + Derived & cardinality(std::size_t n); + Derived & cardinality(std::size_t n, std::size_t m); detail::parser_cardinality cardinality() const override { return m_cardinality; @@ -1946,20 +1927,17 @@ class bound_parser : public composable_parser template Derived & choices(const T (&choice_values)[N]); - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return make_clone(this); } - virtual bool is_named(const std::string & n) const override - { - return n == m_hint; - } - virtual size_t get_value_count() const override + bool is_named(const std::string & n) const override { return n == m_hint; } + std::size_t get_value_count() const override { return m_ref->get_value_count(); } - virtual std::string get_value(size_t i) const override + std::string get_value(std::size_t i) const override { return m_ref->get_value(i); } @@ -1998,7 +1976,7 @@ template template bound_parser::bound_parser(Reference & ref, std::string const & hint) : bound_parser( - std::make_shared>(ref), hint) + std::make_shared>(ref), hint) {} template @@ -2037,7 +2015,8 @@ end::reference[] */ template Derived & bound_parser::help(const std::string & help_description_text) { - m_description = help_description_text; + m_description = help_description_text + + (m_description.empty() ? "" : (" " + m_description)); return static_cast(*this); } template @@ -2076,7 +2055,7 @@ Derived & bound_parser::optional() [source] ---- template -Derived& bound_parser::required(size_t n); +Derived& bound_parser::required(std::size_t n); ---- Specifies that the argument needs to given the number of `n` times @@ -2084,7 +2063,7 @@ Specifies that the argument needs to given the number of `n` times end::reference[] */ template -Derived & bound_parser::required(size_t n) +Derived & bound_parser::required(std::size_t n) { if (m_ref->isContainer()) return this->cardinality(1, 0); @@ -2100,10 +2079,10 @@ Derived & bound_parser::required(size_t n) [source] ---- template -Derived& bound_parser::cardinality(size_t n); +Derived& bound_parser::cardinality(std::size_t n); template -Derived& bound_parser::cardinality(size_t n, size_t m); +Derived& bound_parser::cardinality(std::size_t n, std::size_t m); ---- Specifies the number of times the argument can and needs to appear in the list @@ -2113,14 +2092,14 @@ inclusive. end::reference[] */ template -Derived & bound_parser::cardinality(size_t n) +Derived & bound_parser::cardinality(std::size_t n) { m_cardinality = { n, n }; return static_cast(*this); } template -Derived & bound_parser::cardinality(size_t n, size_t m) +Derived & bound_parser::cardinality(std::size_t n, std::size_t m) { m_cardinality = { n, m }; return static_cast(*this); @@ -2214,6 +2193,7 @@ Derived & bound_parser::hint(std::string const & hint) #endif +#include #include namespace lyra { @@ -2234,7 +2214,7 @@ class arg : public bound_parser public: using bound_parser::bound_parser; - virtual std::string get_usage_text(const option_style &) const override + std::string get_usage_text(const option_style &) const override { std::string text; if (!m_hint.empty()) @@ -2242,11 +2222,10 @@ class arg : public bound_parser auto c = cardinality(); if (c.is_required()) { - for (size_t i = 0; i < c.minimum; ++i) + for (std::size_t i = 0; i < c.minimum; ++i) (((text += (i > 0 ? " " : "")) += "<") += m_hint) += ">"; if (c.is_unbounded()) - (((text += (c.is_required() ? " " : "")) += "[<") - += m_hint) + (((text += (c.is_required() ? " " : "")) += "[<") += m_hint) += ">...]"; } else if (c.is_unbounded()) @@ -2261,11 +2240,6 @@ class arg : public bound_parser return text; } - virtual help_text get_help_text(const option_style & style) const override - { - return { { get_usage_text(style), m_description } }; - } - using parser::parse; parse_result parse(detail::token_iterator const & tokens, @@ -2293,7 +2267,8 @@ class arg : public bound_parser { LYRA_PRINT_DEBUG( "(!)", get_usage_text(style), "!=", token.name); - return parse_result(choice_result); + return parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); } } @@ -2312,6 +2287,18 @@ class arg : public bound_parser parser_result_type::matched, remainingTokens)); } } + + protected: + std::string get_print_order_key(const option_style &) const override + { + return this->hint(); + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + p.option(style, get_usage_text(style), m_description); + } }; } // namespace lyra @@ -2322,152 +2309,11 @@ class arg : public bound_parser #define LYRA_ARGUMENTS_HPP -#ifndef LYRA_EXE_NAME_HPP -#define LYRA_EXE_NAME_HPP - - -namespace lyra { - -/* tag::reference[] - -[#lyra_exe_name] -= `lyra::exe_name` - -Specifies the name of the executable. - -Is-a <>. - -end::reference[] */ -class exe_name : public composable_parser -{ - public: - exe_name() - : m_name(std::make_shared("")) - {} - - explicit exe_name(std::string & ref); - - template - explicit exe_name(LambdaT const & lambda); - - std::string name() const; - parser_result set(std::string const & newName); - - virtual parse_result parse(detail::token_iterator const & tokens, - const option_style &) const override - { - return parse_result::ok( - detail::parse_state(parser_result_type::no_match, tokens)); - } - - virtual std::unique_ptr clone() const override - { - return make_clone(this); - } - - private: - std::shared_ptr m_name; - std::shared_ptr m_ref; -}; - -/* tag::reference[] - -[#lyra_exe_name_ctor] -== Construction - -end::reference[] */ - -/* tag::reference[] -[source] ----- -exe_name::exe_name(std::string& ref) ----- - -Constructs with a target string to receive the name of the executable. When -the `cli` is run the target string will contain the exec name. - -end::reference[] */ -inline exe_name::exe_name(std::string & ref) - : exe_name() -{ - m_ref = std::make_shared>(ref); -} - -/* tag::reference[] -[source] ----- -template -exe_name::exe_name(LambdaT const& lambda) ----- - -Construct with a callback that is called with the value of the executable name -when the `cli` runs. - -end::reference[] */ -template -exe_name::exe_name(LambdaT const & lambda) - : exe_name() -{ - m_ref = std::make_shared>(lambda); -} - -/* tag::reference[] - -[#lyra_exe_name_accessors] -== Accessors - -end::reference[] */ - -/* tag::reference[] - -[#lyra_exe_name_name] -=== `lyra::exe_name::name` - -[source] ----- -std::string exe_name::name() const ----- - -Returns the executable name when available. Otherwise it returns a default -value. - -end::reference[] */ -inline std::string exe_name::name() const { return *m_name; } - -/* tag::reference[] - -[#lyra_exe_name_set] -=== `lyra::exe_name::set` - -[source] ----- -parser_result exe_name::set(std::string const& newName) ----- - -Sets the executable name with the `newName` value. The new value is reflected -in the bound string reference or callback. - -end::reference[] */ -inline parser_result exe_name::set(std::string const & newName) -{ - auto lastSlash = newName.find_last_of("\\/"); - auto filename = (lastSlash == std::string::npos) - ? newName - : newName.substr(lastSlash + 1); - - *m_name = filename; - if (m_ref) - return m_ref->setValue(filename); - else - return parser_result::ok(parser_result_type::matched); -} - -} // namespace lyra - -#endif - -#include +#include +#include +#include #include +#include namespace lyra { @@ -2498,8 +2344,9 @@ class arguments : public parser public: enum evaluation { - any = 0, - sequence = 1 + eval_any = 0, + eval_sequence = 1, + eval_relaxed = 2, }; arguments() = default; @@ -2537,13 +2384,13 @@ class arguments : public parser arguments & sequential(); arguments & inclusive(); + arguments & relaxed(); template - T & get(size_t i); + T & get(std::size_t i); - virtual std::string get_usage_text( - const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { std::string text; for (auto const & p : parsers) @@ -2565,8 +2412,7 @@ class arguments : public parser return text; } - virtual std::string get_description_text( - const option_style & style) const override + std::string get_description_text(const option_style & style) const override { std::string text; for (auto const & p : parsers) @@ -2582,23 +2428,9 @@ class arguments : public parser return text; } - virtual help_text get_help_text(const option_style & style) const override - { - help_text text; - for (auto const & p : parsers) - { - auto child_help = p->get_help_text(style); - text.insert(text.end(), child_help.begin(), child_help.end()); - } - return text; - } + detail::parser_cardinality cardinality() const override { return { 0, 0 }; } - virtual detail::parser_cardinality cardinality() const override - { - return { 0, 0 }; - } - - virtual result validate() const override + result validate() const override { for (auto const & p : parsers) { @@ -2613,94 +2445,129 @@ class arguments : public parser { switch (eval_mode) { - case any: return parse_any(tokens, style); - case sequence: return parse_sequence(tokens, style); + case eval_any: + case eval_relaxed: return parse_any(tokens, style); + case eval_sequence: return parse_sequence(tokens, style); } return parse_result::error( detail::parse_state(parser_result_type::no_match, tokens), - "Unknown evaluation mode; not one of 'any', or 'sequence'."); + "Unknown evaluation mode; not one of 'any', 'sequence', or 'relaxed'."); } parse_result parse_any( detail::token_iterator const & tokens, const option_style & style) const { LYRA_PRINT_SCOPE("arguments::parse_any"); - LYRA_PRINT_DEBUG("(?)", get_usage_text(style), - "?=", tokens ? tokens.argument().name : "", ".."); - struct ParserInfo - { - parser const * parser_p = nullptr; - size_t count = 0; - }; - std::vector parser_info(parsers.size()); - { - size_t i = 0; - for (auto const & p : parsers) parser_info[i++].parser_p = p.get(); - } - - auto p_result = parse_result::ok( - detail::parse_state(parser_result_type::matched, tokens)); - auto error_result = parse_result::ok( + std::vector parsing_count(parsers.size(), 0); + auto parsing_result = parse_result::ok( + detail::parse_state(parser_result_type::empty_match, tokens)); + auto nomatch_result = parse_result::ok( detail::parse_state(parser_result_type::no_match, tokens)); - while (p_result.value().remainingTokens()) + + while (parsing_result.value().remainingTokens()) { + LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", + parsing_result.value().remainingTokens() + ? parsing_result.value().remainingTokens().argument().name + : "", + ".."); bool token_parsed = false; - for (auto & parse_info : parser_info) + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) { - auto parser_cardinality = parse_info.parser_p->cardinality(); + auto parser_cardinality = p->cardinality(); if (parser_cardinality.is_unbounded() - || parse_info.count < parser_cardinality.maximum) + || *parsing_count_i < parser_cardinality.maximum) { - auto subparse_result = parse_info.parser_p->parse( - p_result.value().remainingTokens(), style); + auto subparse_result = p->parse( + parsing_result.value().remainingTokens(), style); if (!subparse_result) { LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", - p_result.value().remainingTokens().argument().name); + parsing_result.value() + .remainingTokens() + .argument() + .name); if (subparse_result.has_value() && subparse_result.value().type() == parser_result_type::short_circuit_all) return subparse_result; - if (error_result) - error_result = parse_result(subparse_result); + else if (nomatch_result) + nomatch_result = parse_result(subparse_result); + } + else if (subparse_result + && subparse_result.value().type() + == parser_result_type::empty_match) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==>", subparse_result.value().type()); + parsing_result = parse_result(subparse_result); } else if (subparse_result && subparse_result.value().type() != parser_result_type::no_match) { LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", - p_result.value().remainingTokens().argument().name, + parsing_result.value() + .remainingTokens() + .argument() + .name, "==>", subparse_result.value().type()); - p_result = parse_result(subparse_result); + parsing_result = parse_result(subparse_result); token_parsed = true; - parse_info.count += 1; + *parsing_count_i += 1; break; } } + ++parsing_count_i; } - if (p_result.value().type() + if (parsing_result.value().type() == parser_result_type::short_circuit_all) - return p_result; - if (!token_parsed && !error_result) return error_result; - if (!token_parsed) break; - } - for (auto & parseInfo : parser_info) - { - auto parser_cardinality = parseInfo.parser_p->cardinality(); - if ((parser_cardinality.is_bounded() - && (parseInfo.count < parser_cardinality.minimum - || parser_cardinality.maximum < parseInfo.count)) - || (parser_cardinality.is_required() - && (parseInfo.count < parser_cardinality.minimum))) + return parsing_result; + if (!token_parsed) { - return parse_result::error(p_result.value(), - "Expected: " + parseInfo.parser_p->get_usage_text(style)); + if (eval_mode == eval_relaxed) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==> skipped"); + auto remainingTokens + = parsing_result.value().remainingTokens(); + remainingTokens.pop(remainingTokens.argument()); + parsing_result = parse_result::ok(detail::parse_state( + parser_result_type::empty_match, remainingTokens)); + } + else if (!nomatch_result) + return nomatch_result; + else + break; } } - return p_result; + { + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) + { + auto parser_cardinality = p->cardinality(); + if ((parser_cardinality.is_bounded() + && (*parsing_count_i < parser_cardinality.minimum + || parser_cardinality.maximum < *parsing_count_i)) + || (parser_cardinality.is_required() + && (*parsing_count_i < parser_cardinality.minimum))) + return make_parse_error(tokens, *p, parsing_result, style); + ++parsing_count_i; + } + } + return parsing_result; } parse_result parse_sequence( @@ -2710,69 +2577,81 @@ class arguments : public parser LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", tokens ? tokens.argument().name : "", ".."); - struct ParserInfo - { - parser const * parser_p = nullptr; - size_t count = 0; - }; - std::vector parser_info(parsers.size()); - { - size_t i = 0; - for (auto const & p : parsers) parser_info[i++].parser_p = p.get(); - } - + std::vector parsing_count(parsers.size(), 0); auto p_result = parse_result::ok( - detail::parse_state(parser_result_type::matched, tokens)); + detail::parse_state(parser_result_type::empty_match, tokens)); - for (std::size_t parser_i = 0; parser_i < parsers.size(); ++parser_i) + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) { - auto & parse_info = parser_info[parser_i]; - auto parser_cardinality = parse_info.parser_p->cardinality(); + auto parser_cardinality = p->cardinality(); do { - auto subresult = parse_info.parser_p->parse( - p_result.value().remainingTokens(), style); - if (!subresult) + auto subresult + = p->parse(p_result.value().remainingTokens(), style); + if (!subresult) break; + if (parser_result_type::no_match == subresult.value().type()) { + LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); break; } - if (subresult.value().type() - == parser_result_type::short_circuit_all) - { + if (parser_result_type::short_circuit_all + == subresult.value().type()) return subresult; - } - LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", - p_result.value().remainingTokens() - ? p_result.value().remainingTokens().argument().name - : "", - "==>", subresult.value().type()); - if (subresult.value().type() == parser_result_type::no_match) - { - break; - } - else + if (parser_result_type::matched == subresult.value().type()) { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); + *parsing_count_i += 1; p_result = subresult; - parse_info.count += 1; + } + if (parser_result_type::empty_match == subresult.value().type()) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); + *parsing_count_i += 1; } } while (p_result.value().have_tokens() && (parser_cardinality.is_unbounded() - || parse_info.count < parser_cardinality.maximum)); + || *parsing_count_i < parser_cardinality.maximum)); if ((parser_cardinality.is_bounded() - && (parse_info.count < parser_cardinality.minimum - || parser_cardinality.maximum < parse_info.count)) + && (*parsing_count_i < parser_cardinality.minimum + || parser_cardinality.maximum < *parsing_count_i)) || (parser_cardinality.is_required() - && (parse_info.count < parser_cardinality.minimum))) - { - return parse_result::error(p_result.value(), - "Expected: " + parse_info.parser_p->get_usage_text(style)); - } + && (*parsing_count_i < parser_cardinality.minimum))) + return make_parse_error(tokens, *p, p_result, style); + ++parsing_count_i; } return p_result; } - virtual std::unique_ptr clone() const override + template + parse_result make_parse_error(const detail::token_iterator & tokens, + const parser & p, + const R & p_result, + const option_style & style) const + { + if (tokens) + return parse_result::error(p_result.value(), + "Unrecognized argument '" + tokens.argument().name + + "' while parsing: " + p.get_usage_text(style)); + else + return parse_result::error( + p_result.value(), "Expected: " + p.get_usage_text(style)); + } + + std::unique_ptr clone() const override { return make_clone(this); } @@ -2785,7 +2664,7 @@ class arguments : public parser return os; } - virtual const parser * get_named(const std::string & n) const override + const parser * get_named(const std::string & n) const override { for (auto & p : parsers) { @@ -2798,12 +2677,28 @@ class arguments : public parser protected: std::shared_ptr opt_style; std::vector> parsers; - evaluation eval_mode = any; + evaluation eval_mode = eval_any; option_style get_option_style() const { return opt_style ? *opt_style : option_style::posix(); } + + option_style & ref_option_style() + { + if (!opt_style) + opt_style = std::make_shared(option_style::posix()); + return *opt_style; + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + for_each_print_ordered_parser(style, parsers.begin(), parsers.end(), + [&](const option_style & s, const parser & q) { + q.print_help_text_details(p, s); + }); + } }; /* tag::reference[] @@ -2920,7 +2815,7 @@ This is useful for sub-commands and structured command lines. end::reference[] */ inline arguments & arguments::sequential() { - eval_mode = sequence; + eval_mode = eval_sequence; return *this; } @@ -2939,7 +2834,27 @@ parsers. This means that there is no ordering enforced. end::reference[] */ inline arguments & arguments::inclusive() { - eval_mode = any; + eval_mode = eval_any; + return *this; +} + +/* tag::reference[] +=== `lyra::arguments::relaxed` + +[source] +---- +arguments & arguments::relaxed(); +---- + +Sets the parsing mode for the arguments to "relaxed any". When parsing the +arguments it attempts to match each parsed argument with all the available +parsers. This means that there is no ordering enforced. Unknown, i.e. failed, +parsing are ignored. + +end::reference[] */ +inline arguments & arguments::relaxed() +{ + eval_mode = eval_relaxed; return *this; } @@ -2949,14 +2864,14 @@ inline arguments & arguments::inclusive() [source] ---- template -T & arguments::get(size_t i); +T & arguments::get(std::size_t i); ---- -Get a modifyable reference to one of the parsers specified. +Get a modifiable reference to one of the parsers specified. end::reference[] */ template -T & arguments::get(size_t i) +T & arguments::get(std::size_t i) { return static_cast(*parsers.at(i)); } @@ -2990,6 +2905,66 @@ T & operator<<(T & os, arguments const & a) #define LYRA_CLI_HPP +#ifndef LYRA_ARGS_HPP +#define LYRA_ARGS_HPP + +#include +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_args] += `lyra::args` + +Transport for raw args (copied from main args, supplied via init list, or from +a pair of iterators). + +*/ // end::reference[] +class args +{ + public: + args(int argc, char const * const * argv) + : m_exeName((argv && (argc >= 1)) ? argv[0] : "") + , m_args((argv && (argc >= 1)) ? argv + 1 : nullptr, argv + argc) + {} + + args(std::initializer_list args_list) + : m_exeName(args_list.size() >= 1 ? *args_list.begin() : "") + , m_args( + args_list.size() >= 1 ? args_list.begin() + 1 : args_list.end(), + args_list.end()) + {} + + template + args(const It & start, const It & end) + : m_exeName(start != end ? *start : "") + , m_args(start != end ? start + 1 : end, end) + {} + + std::string exe_name() const { return m_exeName; } + + std::vector::const_iterator begin() const + { + return m_args.begin(); + } + + std::vector::const_iterator end() const + { + return m_args.end(); + } + + private: + std::string m_exeName; + std::vector m_args; +}; + +} // namespace lyra + +#endif + #ifndef LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP #define LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP @@ -3025,6 +3000,7 @@ struct parser_customization { virtual std::string token_delimiters() const = 0; virtual std::string option_prefix() const = 0; + virtual ~parser_customization() {} }; /* tag::reference[] @@ -3049,10 +3025,166 @@ struct default_parser_customization : parser_customization #endif +#ifndef LYRA_EXE_NAME_HPP +#define LYRA_EXE_NAME_HPP + + +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_exe_name] += `lyra::exe_name` + +Specifies the name of the executable. + +Is-a <>. + +end::reference[] */ +class exe_name : public composable_parser +{ + public: + exe_name() + : m_name(std::make_shared("")) + {} + + explicit exe_name(std::string & ref); + + template + explicit exe_name(LambdaT const & lambda); + + std::string name() const; + parser_result set(std::string const & newName); + + parse_result parse(detail::token_iterator const & tokens, + const option_style &) const override + { + return parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); + } + + std::unique_ptr clone() const override + { + return make_clone(this); + } + + protected: + std::string get_print_order_key(const option_style &) const override + { + return m_name ? *m_name : ""; + } + + private: + std::shared_ptr m_name; + std::shared_ptr m_ref; +}; + +/* tag::reference[] + +[#lyra_exe_name_ctor] +== Construction + +end::reference[] */ + +/* tag::reference[] +[source] +---- +exe_name::exe_name(std::string& ref) +---- + +Constructs with a target string to receive the name of the executable. When +the `cli` is run the target string will contain the exec name. + +end::reference[] */ +inline exe_name::exe_name(std::string & ref) + : exe_name() +{ + m_ref = std::make_shared>(ref); +} + +/* tag::reference[] +[source] +---- +template +exe_name::exe_name(LambdaT const& lambda) +---- + +Construct with a callback that is called with the value of the executable name +when the `cli` runs. + +end::reference[] */ +template +exe_name::exe_name(LambdaT const & lambda) + : exe_name() +{ + m_ref = std::make_shared>(lambda); +} + +/* tag::reference[] + +[#lyra_exe_name_accessors] +== Accessors + +end::reference[] */ + +/* tag::reference[] + +[#lyra_exe_name_name] +=== `lyra::exe_name::name` + +[source] +---- +std::string exe_name::name() const +---- + +Returns the executable name when available. Otherwise it returns a default +value. + +end::reference[] */ +inline std::string exe_name::name() const { return *m_name; } + +/* tag::reference[] + +[#lyra_exe_name_set] +=== `lyra::exe_name::set` + +[source] +---- +parser_result exe_name::set(std::string const& newName) +---- + +Sets the executable name with the `newName` value. The new value is reflected +in the bound string reference or callback. + +end::reference[] */ +inline parser_result exe_name::set(std::string const & newName) +{ + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) + ? newName + : newName.substr(lastSlash + 1); + + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return parser_result::ok(parser_result_type::matched); +} + +} // namespace lyra + +#endif + #ifndef LYRA_GROUP_HPP #define LYRA_GROUP_HPP + +#include #include +#include namespace lyra { @@ -3077,7 +3209,7 @@ class group : public arguments group(const group & other); explicit group(const std::function & f); - virtual bool is_group() const override { return true; } + bool is_group() const override { return true; } parse_result parse(detail::token_iterator const & tokens, const option_style & style) const override @@ -3086,7 +3218,10 @@ class group : public arguments LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", tokens ? tokens.argument().name : ""); parse_result p_result = arguments::parse(tokens, style); - if (p_result && p_result.value().type() != parser_result_type::no_match + if (p_result + && (p_result.value().type() == parser_result_type::matched + || p_result.value().type() + == parser_result_type::short_circuit_all) && success_signal) { this->success_signal(*this); @@ -3106,15 +3241,15 @@ class group : public arguments } group & optional(); - group & required(size_t n = 1); - group & cardinality(size_t n); - group & cardinality(size_t n, size_t m); + group & required(std::size_t n = 1); + group & cardinality(std::size_t n); + group & cardinality(std::size_t n, std::size_t m); detail::parser_cardinality cardinality() const override { return m_cardinality; } - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return make_clone(this); } @@ -3210,14 +3345,14 @@ inline group & group::optional() [source] ---- -group & group::required(size_t n); +group & group::required(std::size_t n); ---- Specifies that the argument needs to given the number of `n` times (defaults to *1*). end::reference[] */ -inline group & group::required(size_t n) +inline group & group::required(std::size_t n) { m_cardinality.required(n); return *this; @@ -3230,8 +3365,8 @@ inline group & group::required(size_t n) [source] ---- -group & group::cardinality(size_t n); -group & group::cardinality(size_t n, size_t m); +group & group::cardinality(std::size_t n); +group & group::cardinality(std::size_t n, std::size_t m); ---- Specifies the number of times the argument can and needs to appear in the list @@ -3240,12 +3375,12 @@ the second form it specifies that the argument can appear from `n` to `m` times inclusive. end::reference[] */ -inline group & group::cardinality(size_t n) +inline group & group::cardinality(std::size_t n) { m_cardinality.counted(n); return *this; } -inline group & group::cardinality(size_t n, size_t m) +inline group & group::cardinality(std::size_t n, std::size_t m) { m_cardinality.bounded(n, m); return *this; @@ -3255,7 +3390,12 @@ inline group & group::cardinality(size_t n, size_t m) #endif +#include +#include +#include #include +#include +#include namespace lyra { @@ -3330,7 +3470,7 @@ class cli : protected arguments std::vector converted_value; if (parser_ref) { - for (size_t i = 0; i < parser_ref->get_value_count(); ++i) + for (std::size_t i = 0; i < parser_ref->get_value_count(); ++i) { T v; if (detail::from_string(parser_ref->get_value(i), v)) @@ -3354,6 +3494,8 @@ class cli : protected arguments cli & style(const option_style & style); cli & style(option_style && style); + cli & style_print_short_first(); + cli & style_print_long_first(); template friend T & operator<<(T & os, cli const & c); @@ -3367,19 +3509,15 @@ class cli : protected arguments } parse_result parse(args const & args, const option_style & style) const; - [[deprecated]] parse_result parse( - args const & args, const parser_customization & customize) const - { - return this->parse(args, - option_style(customize.token_delimiters(), - customize.option_prefix(), 2, customize.option_prefix(), 1)); - } + cli & sequential() { return arguments::sequential(), *this; } + cli & inclusive() { return arguments::inclusive(), *this; } + cli & relaxed() { return arguments::relaxed(), *this; } using arguments::parse; using arguments::get_named; - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return std::unique_ptr(new cli(*this)); } @@ -3387,8 +3525,7 @@ class cli : protected arguments protected: mutable exe_name m_exeName; - virtual std::string get_usage_text( - const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { if (!m_exeName.name().empty()) return m_exeName.name() + " " + arguments::get_usage_text(style); @@ -3559,7 +3696,8 @@ inline parse_result cli::parse( parse_result p_result = parse(args_tokens, style); if (p_result && (p_result.value().type() == parser_result_type::no_match - || p_result.value().type() == parser_result_type::matched)) + || p_result.value().type() == parser_result_type::matched + || p_result.value().type() == parser_result_type::empty_match)) { if (p_result.value().have_tokens()) { @@ -3595,6 +3733,44 @@ inline cli & cli::style(option_style && style) return *this; } +/* tag::reference[] +[#lyra_cli_style_print_short_first] +=== `lyra::cli::style_print_short_first` + +[source] +---- +lyra::cli & lyra::cli::style_print_short_first() +---- + +Specifies print options sorted with short options appearing first. + +end::reference[] */ +inline cli & cli::style_print_short_first() +{ + ref_option_style().options_print_order + = option_style::opt_print_order::sorted_short_first; + return *this; +} + +/* tag::reference[] +[#lyra_cli_style_print_long_first] +=== `lyra::cli::style_print_long_first` + +[source] +---- +lyra::cli & lyra::cli::style_print_long_first() +---- + +Specifies print options sorted with long options appearing first. + +end::reference[] */ +inline cli & cli::style_print_long_first() +{ + ref_option_style().options_print_order + = option_style::opt_print_order::sorted_long_first; + return *this; +} + /* tag::reference[] === `lyra::operator<<` @@ -3639,6 +3815,8 @@ using cli_parser = cli; #ifndef LYRA_LITERAL_HPP #define LYRA_LITERAL_HPP + +#include #include namespace lyra { @@ -3661,31 +3839,22 @@ class literal : public parser literal & help(const std::string & help_description_text); literal & operator()(std::string const & help_description_text); - virtual detail::parser_cardinality cardinality() const override - { - return { 1, 1 }; - } + detail::parser_cardinality cardinality() const override { return { 1, 1 }; } - virtual std::string get_usage_text(const option_style &) const override + std::string get_usage_text(const option_style &) const override { return name; } - virtual std::string get_description_text( - const option_style &) const override + std::string get_description_text(const option_style &) const override { return description; } - virtual help_text get_help_text(const option_style &) const override - { - return { { name, description } }; - } - using parser::parse; - virtual parse_result parse(detail::token_iterator const & tokens, + parse_result parse(detail::token_iterator const & tokens, const option_style &) const override { auto validationResult = validate(); @@ -3706,7 +3875,7 @@ class literal : public parser } } - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return make_clone(this); } @@ -3714,6 +3883,17 @@ class literal : public parser protected: std::string name; std::string description; + + std::string get_print_order_key(const option_style &) const override + { + return name; + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + p.option(style, name, description); + } }; /* tag::reference[] @@ -3775,6 +3955,8 @@ inline literal & literal::operator()(std::string const & help_description_text) } // namespace lyra #endif + +#include #include #include @@ -3805,7 +3987,7 @@ lyra::command c = lyra::group() lyra::group & g = c.get(1); ---- -I.e. it's conposed of a `literal` followed by the rest of the command arguments. +I.e. it's composed of a `literal` followed by the rest of the command arguments. Is-a <>. @@ -3826,46 +4008,59 @@ class command : public group command & operator|=(P const & p); command & brief_help(bool brief = true); + command & optional() { return static_cast(group::optional()); } + command & required(std::size_t n = 1) + { + return static_cast(group::required(n)); + } + command & cardinality(std::size_t n) + { + return static_cast(group::cardinality(n)); + } + command & cardinality(std::size_t n, std::size_t m) + { + return static_cast(group::cardinality(n, m)); + } - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return make_clone(this); } - - virtual std::string get_usage_text( - const option_style & style) const override + detail::parser_cardinality cardinality() const override { - return parsers[0]->get_usage_text(style) + " " - + parsers[1]->get_usage_text(style); + return group::cardinality(); } - virtual help_text get_help_text(const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { - if (expanded_help_details) - { - help_text text; - text.push_back({ "", "" }); - auto c = parsers[0]->get_help_text(style); - text.insert(text.end(), c.begin(), c.end()); - text.push_back({ "", "" }); - auto o = parsers[1]->get_help_text(style); - text.insert(text.end(), o.begin(), o.end()); - return text; - } - else - return parsers[0]->get_help_text(style); + auto tail = parsers[1]->get_usage_text(style); + return parsers[0]->get_usage_text(style) + + (tail.empty() ? (tail) : (" " + tail)); } protected: bool expanded_help_details = true; - virtual void print_help_text_details( + std::string get_print_order_key(const option_style & style) const override + { + return parsers[0]->get_print_order_key(style); + } + + void print_help_text_details( printer & p, const option_style & style) const override { - p.heading("OPTIONS, ARGUMENTS:"); - for (auto const & cols : parsers[1]->get_help_text(style)) + if (expanded_help_details) { - p.option(cols.option, cols.description, 2); + p.option(style, "", ""); + parsers[0]->print_help_text_details(p, style); + p.option(style, "", ""); + p.indent(); + parsers[1]->print_help_text_details(p, style); + p.dedent(); + } + else + { + parsers[0]->print_help_text_details(p, style); } } }; @@ -3946,7 +4141,7 @@ template command & command::operator|=(P const & p); ---- -Adds the given argument parser to the considered arguments for this `comand`. +Adds the given argument parser to the considered arguments for this `command`. The argument is added to the sub-group argument instead of this one. Hence it has the effect of adding arguments *after* the command name. @@ -3964,7 +4159,7 @@ command & command::operator|=(P const & p) } /* tag::reference[] -[#lyra_command_abrief_help] +[#lyra_command_brief_help] === `lyra::command::brief_help` [source] @@ -3984,7 +4179,6 @@ inline command & command::brief_help(bool brief) return *this; } - } // namespace lyra #endif @@ -3997,8 +4191,11 @@ inline command & command::brief_help(bool brief) #define LYRA_OPT_HPP +#include #include #include +#include +#include namespace lyra { @@ -4063,8 +4260,7 @@ class opt : public bound_parser opt & operator[](std::string const & opt_name); - virtual std::string get_usage_text( - const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { std::string usage; for (std::size_t o = 0; o < opt_names.size(); ++o) @@ -4076,19 +4272,7 @@ class opt : public bound_parser return usage; } - virtual help_text get_help_text(const option_style & style) const override - { - std::string text; - for (auto const & opt_name : opt_names) - { - if (!text.empty()) text += ", "; - text += format_opt(opt_name, style); - } - if (!m_hint.empty()) ((text += " <") += m_hint) += ">"; - return { { text, m_description } }; - } - - virtual bool is_named(const std::string & n) const override + bool is_named(const std::string & n) const override { if (bound_parser::is_named(n)) return true; for (auto & name : opt_names) @@ -4195,7 +4379,7 @@ class opt : public bound_parser return bound_parser::validate(); } - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return make_clone(this); } @@ -4206,15 +4390,15 @@ class opt : public bound_parser bool is_match( std::string const & opt_name, const option_style & style) const { - auto opt_normalized = normalise_opt(opt_name, style); + auto opt_normalized = normalize_opt(opt_name, style); for (auto const & name : opt_names) { - if (normalise_opt(name, style) == opt_normalized) return true; + if (normalize_opt(name, style) == opt_normalized) return true; } return false; } - std::string normalise_opt( + std::string normalize_opt( std::string const & opt_name, const option_style & style) const { if (detail::token_iterator::is_prefixed( @@ -4238,6 +4422,24 @@ class opt : public bound_parser else return opt_name; } + + std::string get_print_order_key(const option_style & style) const override + { + return format_opt(opt_names[0], style); + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + std::string text; + for (auto const & opt_name : opt_names) + { + if (!text.empty()) text += ", "; + text += format_opt(opt_name, style); + } + if (!m_hint.empty()) ((text += " <") += m_hint) += ">"; + p.option(style, text, m_description); + } }; /* tag::reference[] @@ -4350,6 +4552,9 @@ inline opt & opt::operator[](const std::string & opt_name) #endif +#include +#include + namespace lyra { /* tag::reference[] @@ -4382,13 +4587,12 @@ class help : public opt help & description(const std::string & text); - virtual std::string get_description_text( - const option_style &) const override + std::string get_description_text(const option_style &) const override { return description_text; } - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { return make_clone(this); } @@ -4422,7 +4626,49 @@ inline help & help::description(const std::string & text) #define LYRA_MAIN_HPP +#ifndef LYRA_VAL_HPP +#define LYRA_VAL_HPP + + +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_val] += `lyra::val` + +[source] +---- +auto val(T && v); +auto val(const char * v); +---- + +Makes a bound self-contained value of the type of the given r-value. The created +bound values can be used in place of the value references for arguments. And can +be retrieved with the +<> call. + +*/ // end::reference[] +template +detail::BoundVal val(T && v) +{ + return detail::BoundVal(std::forward(v)); +} + +inline detail::BoundVal val(const char * v) +{ + return detail::BoundVal(v); +} + +} // namespace lyra + +#endif + +#include #include +#include namespace lyra { @@ -4649,4 +4895,4 @@ int main::operator()(int argc, const char ** argv, L action) #endif -#endif // LYRA_HPP_INCLUDED \ No newline at end of file +#endif // LYRA_HPP_INCLUDED diff --git a/src/engine/filent.cpp b/src/engine/filent.cpp index cfe369e01..b702f1673 100644 --- a/src/engine/filent.cpp +++ b/src/engine/filent.cpp @@ -422,7 +422,7 @@ int file_collect_archive_content_( file_archive_info_t * const archive ) offset = SARMAG; - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "scan archive %s\n", path ); while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR ) && diff --git a/src/engine/filesys.cpp b/src/engine/filesys.cpp index 98e1aabe8..5307ec5e8 100644 --- a/src/engine/filesys.cpp +++ b/src/engine/filesys.cpp @@ -150,7 +150,7 @@ void file_archivescan( OBJECT * path, archive_scanback func, void * closure ) void file_build1( PATHNAME * const f, string * file ) { - if ( DEBUG_SEARCH ) + if ( is_debug_search() ) { out_printf( "build file: " ); if ( f->f_root.len ) @@ -384,7 +384,7 @@ static void file_archivescan_impl( OBJECT * path, archive_scanback func, void * /* Lazy collect the archive content information. */ if ( filelist_empty( archive->members ) ) { - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) printf( "scan archive %s\n", object_str( archive->file->name ) ); if ( file_collect_archive_content_( archive ) < 0 ) return; @@ -430,7 +430,7 @@ static void file_dirscan_impl( OBJECT * dir, scanback func, void * closure ) /* Lazy collect the directory content information. */ if ( list_empty( d->files ) ) { - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "scan directory %s\n", object_str( d->name ) ); if ( file_collect_dir_content_( d ) < 0 ) return; diff --git a/src/engine/fileunix.cpp b/src/engine/fileunix.cpp index 3a7907ead..0fd6f65d9 100644 --- a/src/engine/fileunix.cpp +++ b/src/engine/fileunix.cpp @@ -291,7 +291,7 @@ int file_collect_archive_content_( file_archive_info_t * const archive ) offset = SARMAG; - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "scan archive %s\n", path ); while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR ) && @@ -349,7 +349,7 @@ int file_collect_archive_content_( file_archive_info_t * const archive ) while ( ( *++c != ' ' ) && ( *c != '/' ) ); *c = '\0'; - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "archive name %s found\n", lar_name ); auto name = b2::value::format( "%s", lar_name ); @@ -399,7 +399,7 @@ static void collect_archive_content_small( int fd, file_archive_info_t * const a sscanf( fl_hdr.fl_fstmoff, "%ld", &offset ); - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "scan archive %s\n", path ); while ( offset > 0 && lseek( fd, offset, 0 ) >= 0 && @@ -454,7 +454,7 @@ static void collect_archive_content_big( int fd, file_archive_info_t * const arc sscanf( fl_hdr.fl_fstmoff, "%lld", &offset ); - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "scan archive %s\n", path ); while ( offset > 0 && lseek( fd, offset, 0 ) >= 0 && diff --git a/src/engine/filevms.cpp b/src/engine/filevms.cpp index b3b2303e7..9a5f06a6f 100644 --- a/src/engine/filevms.cpp +++ b/src/engine/filevms.cpp @@ -345,7 +345,7 @@ void file_archscan( char const * arch, scanback func, void * closure ) if ( filelist_empty( archive->members ) ) { - if ( DEBUG_BINDSCAN ) + if ( is_debug_bindscan() ) out_printf( "scan archive %s\n", object_str( archive->file->name ) ); if ( file_collect_archive_content_( archive ) < 0 ) diff --git a/src/engine/function.cpp b/src/engine/function.cpp index d60727c42..3a8e91de6 100644 --- a/src/engine/function.cpp +++ b/src/engine/function.cpp @@ -4194,7 +4194,7 @@ LIST * function_execute_write_file( char const * out = object_str( list_front( filename.inner ) ); OBJECT * tmp_filename = nullptr; FILE * out_file = nullptr; - bool out_debug = DEBUG_EXEC != 0; + bool out_debug = is_debug_exec(); /* For stdout/stderr we will create a temp file and generate a * command that outputs the content as needed. diff --git a/src/engine/hash.cpp b/src/engine/hash.cpp index 913dbb73e..f90abf9c5 100644 --- a/src/engine/hash.cpp +++ b/src/engine/hash.cpp @@ -153,7 +153,7 @@ HASHDATA * hash_insert( struct hash * hp, OBJECT * key, int32_t * found ) #ifdef HASH_DEBUG_PROFILE profile_frame prof[ 1 ]; - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) profile_enter( 0, prof ); #endif @@ -186,7 +186,7 @@ HASHDATA * hash_insert( struct hash * hp, OBJECT * key, int32_t * found ) } #ifdef HASH_DEBUG_PROFILE - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) profile_exit( prof ); #endif @@ -205,14 +205,14 @@ HASHDATA * hash_find( struct hash * hp, OBJECT * key ) #ifdef HASH_DEBUG_PROFILE profile_frame prof[ 1 ]; - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) profile_enter( 0, prof ); #endif if ( !hp->items.nel ) { #ifdef HASH_DEBUG_PROFILE - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) profile_exit( prof ); #endif return 0; @@ -221,7 +221,7 @@ HASHDATA * hash_find( struct hash * hp, OBJECT * key ) i = hash_search( hp, keyval, key, 0 ); #ifdef HASH_DEBUG_PROFILE - if ( DEBUG_PROFILE ) + if ( is_debug_profile() ) profile_exit( prof ); #endif @@ -388,7 +388,7 @@ void hashdone( struct hash * hp ) { if ( !hp ) return; - if ( DEBUG_MEM || DEBUG_PROFILE ) + if ( is_debug_mem() || is_debug_profile() ) hashstat( hp ); hash_free( hp ); } diff --git a/src/engine/hcache.cpp b/src/engine/hcache.cpp index 02b8d8c14..34a56a65f 100644 --- a/src/engine/hcache.cpp +++ b/src/engine/hcache.cpp @@ -342,7 +342,7 @@ cleanup: goto bail; } - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "hcache read from file %s\n", hcachename ); bail: @@ -409,7 +409,7 @@ void hcache_done() } write_netstring( f, "%s", CACHE_RECORD_END ); - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "hcache written to %s. %d dependencies, %.0f%% hit rate\n", hcachename, header_count, queries ? 100.0 * hits / queries : 0 ); @@ -458,7 +458,7 @@ LIST * hcache( TARGET * t, int rec, b2::regex::program re[], LIST * hdrscan ) } if ( iter1 != end1 || iter2 != end2 ) { - if ( DEBUG_HEADER ) + if ( is_debug_header() ) { out_printf( "HDRSCAN out of date in cache for %s\n", object_str( t->boundname ) ); @@ -476,7 +476,7 @@ LIST * hcache( TARGET * t, int rec, b2::regex::program re[], LIST * hdrscan ) } else { - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "using header cache for %s\n", object_str( t->boundname ) ); c->age = 0; @@ -486,7 +486,7 @@ LIST * hcache( TARGET * t, int rec, b2::regex::program re[], LIST * hdrscan ) } else { - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf ("header cache out of date for %s\n", object_str( t->boundname ) ); list_free( c->includes ); diff --git a/src/engine/hdrmacro.cpp b/src/engine/hdrmacro.cpp index e0cd09575..926424b93 100644 --- a/src/engine/hdrmacro.cpp +++ b/src/engine/hdrmacro.cpp @@ -73,7 +73,7 @@ void macro_headers( TARGET * t ) FILE * f; char buf[ 1024 ]; - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "macro header scan for %s\n", object_str( t->name ) ); if ( !( f = fopen( object_str( t->boundname ), "r" ) ) ) @@ -97,7 +97,7 @@ void macro_headers( TARGET * t ) std::string macro(re_i[1].begin(), re_i[1].end()); std::string filename(re_i[2].begin(), re_i[2].end()); - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "macro '%s' used to define filename '%s' in '%s'\n", macro.c_str(), filename.c_str(), object_str( t->boundname ) ); @@ -132,7 +132,7 @@ OBJECT * macro_header_get( OBJECT * macro_name ) if ( header_macros_hash && ( v = (HEADER_MACRO *)hash_find( header_macros_hash, macro_name ) ) ) { - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "### macro '%s' evaluated to '%s'\n", object_str( macro_name ), object_str( v->filename ) ); return v->filename; diff --git a/src/engine/headers.cpp b/src/engine/headers.cpp index da2288dde..54b2da4a9 100644 --- a/src/engine/headers.cpp +++ b/src/engine/headers.cpp @@ -71,7 +71,7 @@ void headers( TARGET * t ) if ( list_empty( hdrrule ) ) return; - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "header scan %s\n", object_str( t->name ) ); /* Compile all regular expressions in HDRSCAN */ @@ -128,7 +128,7 @@ LIST * headers1( LIST * l, OBJECT * file, int rec, b2::regex::program re[] ) #ifdef OPT_IMPROVED_PATIENCE_EXT static int count = 0; ++count; - if ( ( ( count == 100 ) || !( count % 1000 ) ) && DEBUG_MAKE ) + if ( ( ( count == 100 ) || !( count % 1000 ) ) && is_debug_make() ) { out_printf( "...patience...\n" ); out_flush(); @@ -158,7 +158,7 @@ LIST * headers1( LIST * l, OBJECT * file, int rec, b2::regex::program re[] ) if ( re_i && re_i[ 1 ].begin() ) { std::string header(re_i[ 1 ].begin(), re_i[ 1 ].end()); - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "header found: %s\n", header.c_str() ); l = list_push_back( l, object_new( header.c_str() ) ); } @@ -170,21 +170,21 @@ LIST * headers1( LIST * l, OBJECT * file, int rec, b2::regex::program re[] ) { std::string macro_name(re_macros_i[ 1 ].begin(), re_macros_i[ 1 ].end()); - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( "macro header found: %s", macro_name.c_str() ); b2::value_ref macro_name_v(macro_name); b2::value_ref header_filename_v(macro_header_get( macro_name_v )); if ( header_filename_v.has_value() ) { - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( " resolved to '%s'\n", header_filename_v->str() ); l = list_push_back( l, header_filename_v ); } else { - if ( DEBUG_HEADER ) + if ( is_debug_header() ) out_printf( " ignored !!\n" ); } } diff --git a/src/engine/jam.cpp b/src/engine/jam.cpp index ad648b258..aac7c616d 100644 --- a/src/engine/jam.cpp +++ b/src/engine/jam.cpp @@ -14,7 +14,7 @@ /* This file is ALSO: * Copyright 2001-2004 David Abrahams. - * Copyright 2018 Rene Rivera + * Copyright 2018-2025 Rene Rivera * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE.txt or copy at * https://www.bfgroup.xyz/b2/LICENSE.txt) @@ -96,11 +96,45 @@ * variable.c - handle jam multi-element variables */ - #include "jam.h" #include "patchlevel.h" +#include "bindjam.h" +#include "builtins.h" +#include "class.h" +#include "compile.h" +#include "constants.h" +#include "cwd.h" +#include "debugger.h" +#include "events.h" +#include "execcmd.h" +#include "filesys.h" +#include "function.h" +#include "hcache.h" +#include "jam_strings.h" +#include "lists.h" +#include "make.h" +#include "mod_args.h" +#include "mod_command_db.h" +#include "mod_sysinfo.h" +#include "modules.h" +#include "object.h" +#include "output.h" +#include "parse.h" +#include "rules.h" +#include "scan.h" +#include "search.h" +#include "startup.h" +#include "timestamp.h" +#include "variable.h" + +#include +#include +#include +#include +#include + /* Keep JAMVERSYM in sync with VERSION. */ /* It can be accessed as $(JAMVERSION) in the Jamfile. */ #define JAM_STRINGIZE(X) JAM_DO_STRINGIZE(X) @@ -111,521 +145,503 @@ #define VERSION VERSION_MAJOR_SYM "." VERSION_MINOR_SYM "." VERSION_PATCH_SYM #define JAMVERSYM "JAMVERSION=" VERSION -#include "bind.h" -#include "bindjam.h" -#include "builtins.h" -#include "class.h" -#include "compile.h" -#include "constants.h" -#include "debugger.h" -#include "events.h" -#include "filesys.h" -#include "function.h" -#include "hcache.h" -#include "lists.h" -#include "make.h" -#include "object.h" -#include "option.h" -#include "output.h" -#include "parse.h" -#include "cwd.h" -#include "rules.h" -#include "scan.h" -#include "search.h" -#include "startup.h" -#include "jam_strings.h" -#include "timestamp.h" -#include "variable.h" -#include "execcmd.h" -#include "mod_sysinfo.h" -#include "mod_command_db.h" - -#include -#include - -/* Macintosh is "special" */ -#ifdef OS_MAC -# include -#endif - /* And UNIX for this. */ #ifdef unix -# include -# include +#include #endif #ifdef WIN32 -# define WIN32_LEAN_AND_MEAN -# include +#define WIN32_LEAN_AND_MEAN +#include #endif #include "ext_bfgroup_lyra.h" -struct globs globs = -{ - 0, /* noexec */ - 1, /* jobs */ - 0, /* quitquick */ - 0, /* newestfirst */ - 0, /* pipes action stdout and stderr merged to action output */ -#ifdef OS_MAC - { 0, 0 }, /* debug - suppress tracing output */ -#else - { 0, 1 }, /* debug ... */ -#endif - 0, /* output commands, not run them */ - 0, /* action timeout */ - 0 /* maximum buffer size zero is all output */ -}; - -/* Symbols to be defined as true for use in Jambase. */ -static const char * othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 }; - - /* on Win32-LCC */ -#if defined( OS_NT ) && defined( __LCC__ ) -# define use_environ _environ +#if defined(OS_NT) && defined(__LCC__) +#define use_environ _environ #endif -#if defined( __MWERKS__) -# define use_environ _environ - extern char * * _environ; +#if defined(__MWERKS__) +#define use_environ _environ +extern char ** _environ; #endif #ifndef use_environ -# define use_environ environ -# if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT ) - extern char **environ; -# endif +#define use_environ environ +#if !defined(__WATCOM__) && !defined(OS_OS2) && !defined(OS_NT) +extern char ** environ; +#endif #endif #if YYDEBUG != 0 - extern int yydebug; +extern int yydebug; #endif #ifndef NDEBUG static void run_unit_tests() { -# if defined( USE_EXECNT ) - extern void execnt_unit_test(); - execnt_unit_test(); -# endif - string_unit_test(); +#if defined(USE_EXECNT) + extern void execnt_unit_test(); + execnt_unit_test(); +#endif + string_unit_test(); } #endif -int anyhow = 0; +char const * saved_argv0 = nullptr; -char const * saved_argv0; +struct global_config globs; -static void usage( const char * progname ) +void global_config::out_print() const { - err_printf("\nusage: %s [ options ] targets...\n\n", progname); - - err_printf("-a Build all targets, even if they are current.\n"); - err_printf("-dx Set the debug level to x (0-13,console,mi).\n"); - err_printf("-fx Read x instead of bootstrap.\n"); - /* err_printf( "-g Build from newest sources first.\n" ); */ - err_printf("-jx Run up to x shell commands concurrently.\n"); - err_printf("-lx Limit actions to x number of seconds after which they are stopped.\n"); - err_printf("-mx Maximum target output saved (kb), default is to save all output.\n"); - err_printf("-n Don't actually execute the updating actions.\n"); - err_printf("-ox Mirror all output to file x.\n"); - err_printf("-px x=0, pipes action stdout and stderr merged into action output.\n"); - err_printf("-q Quit quickly as soon as a target fails.\n"); - err_printf("-sx=y Set variable x=y, overriding environment.\n"); - err_printf("-tx Rebuild x, even if it is up-to-date.\n"); - err_printf("-v Print the version of jam and exit.\n"); - err_printf("--x Option is ignored.\n\n"); - - b2::clean_exit( EXITBAD ); + out_printf("global_config:\n"); + auto true_false = [](bool v) { return v ? "true" : "false"; }; + out_printf(" > anyhow: %s\n", true_false(globs.anyhow)); + out_printf(" > noexec: %s\n", true_false(globs.noexec)); + out_printf(" > jobs: %d\n", globs.jobs); + out_printf(" > quitquick: %s\n", true_false(globs.quitquick)); + out_printf(" > newestfirst: %s\n", true_false(globs.newestfirst)); + out_printf(" > debug:"); + for (auto v : globs.debug) out_printf(" %s", true_false(v)); + out_puts("\n"); + out_printf(" > timeout: %d\n", int(globs.timeout)); + out_printf(" > max_buf: %d\n", globs.max_buf); + out_printf(" > is_debugger: %s\n", true_false(globs.is_debugger)); + out_printf(" > debug_interface: %d\n", int(globs.debug_interface)); } -int guarded_main( int argc, char * * argv ) +struct args_data { - int n; - char * s; - struct bjam_option optv[ N_OPTS ]; - int status = 0; - int arg_c = argc; - char * * arg_v = argv; - char const * progname = argv[ 0 ]; - module_t * environ_module; - int is_debugger; - b2::system_info sys_info; + std::vector targets_to_touch; + std::vector variables_to_set; + std::vector debug_handles; + std::vector files_to_parse; + std::vector extra_args; + std::string out_filename; +} args_data; - saved_argv0 = argv[ 0 ]; - last_update_now_status = 0; +int guarded_main(int argc, char * argv[]) +{ + int status = 0; + int arg_c = argc; + char ** arg_v = argv; + module_t * environ_module; + b2::system_info sys_info; + + auto & cli = b2::args::lyra_cli().relaxed(); + + cli |= lyra::arg(args_data.extra_args, "request") + .help("Targets, requirements, etc.") + .choices([](const std::string & v) { + return v.empty() || v[0] != '-'; + }) + .cardinality(0, 0); + + cli |= lyra::opt(globs.display_help) + .name("-?") + .name("-h") + .help("Display invocation help."); + + /* Version info. */ + cli |= lyra::opt([](bool) { + out_printf("B2 %s (%s, jobs=%i)\n\n", VERSION, OSMINOR, globs.jobs); + b2::clean_exit(b2::exit_result::success); + }) + .name("-v") + .help("Print the version of jam and exit."); + + cli |= lyra::opt(globs.anyhow) + .name("-a") + .help("Build all targets, even if they are current."); + + cli |= lyra::opt([](bool) { + globs.noexec = true; + if (!globs.debug_flag_used) globs.debug[2] = true; + }) + .name("-n") + .help("Don't actually execute the updating actions."); + + /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action + * stdout and stderr. + */ + cli |= lyra::opt(globs.pipe_action, "x") + .name("-p") + .help( + "x=0, pipes action stdout and stderr" + "merged into action output.") + .choices([](int val) { return 0 <= val && val <= 3; }); + + cli |= lyra::opt(globs.quitquick) + .name("-q") + .help("Quit quickly as soon as a target fails."); + + cli |= lyra::opt( + [](const std::string & v) { + globs.quitquick = (v == "on" || v == "yes" || v == "true"); + }, + "x") + .name("--keep-going") + .help( + "Specify if we continue to build after failures or not " + "(--keep-going=no is equivalent to -q)") + .choices("on", "yes", "true", "off", "no", "false"); + + cli |= lyra::opt(globs.jobs, "x") + .name("-j") + .name("--jobs") + .help("Run up to x shell commands concurrently.") + .choices([](int val) { + if (val < 1) + { + err_printf("Invalid value for the '-j' option.\n"); + b2::clean_exit(EXITBAD); + return false; + } + globs.jobs = val; + return true; + }); + + cli |= lyra::opt(globs.newestfirst) + .name("-g") + .help("Build from newest sources first."); + + cli |= lyra::opt(globs.timeout, "x") + .name("-l") + .help( + "Limit actions to x number of seconds" + "after which they are stopped."); + + cli |= lyra::opt( + [](const std::string & x) { + globs.max_buf = std::stoi(x) * 1024; /* convert to kb */ + }, + "x") + .name("-m") + .help( + "Maximum target output saved (kb)," + "default is to save all output."); + + cli |= lyra ::opt( + [](const std::string & val) { + /* Turn on/off debugging */ + /* First -d, turn off defaults. */ + if (!globs.debug_flag_used) + for (bool & d : globs.debug) d = false; + globs.debug_flag_used = true; + if (val == "mi") + { + globs.debug_interface = global_config::debug_interface_mi; + globs.is_debugger = true; + } + else if (val == "console") + { + globs.debug_interface = global_config::debug_interface_console; + globs.is_debugger = true; + } + else if (val[0] == '+') /* +n turns on level n. */ + globs.debug[std::stoi(val.substr(1))] = true; + else + { + /* n turns on levels 1-n. And turns everything else off*/ + int d = std::stoi(val); + globs.debug[0] = false; + for (int i = 1; i < DEBUG_MAX; ++i) globs.debug[i] = (i <= d); + } + }, + "x") + .name("-d") + .help("Set the debug level to x (0-13,console,mi).") + .cardinality(0, 0); + + cli |= lyra::opt(args_data.out_filename, "x") + .name("-o") + .help("Mirror all output to file x."); + + cli |= lyra::opt(args_data.targets_to_touch, "x") + .name("-t") + .help("Rebuild x, even if it is up-to-date.") + .cardinality(0, 0); + + cli |= lyra::opt( + [&](const std::string & v) { args_data.variables_to_set.push_back(v); }, + "x=y") + .name("-s") + .help("Set variable x=y, overriding environment.") + .cardinality(0, 0); + + cli |= lyra::opt(args_data.files_to_parse, "x") + .name("-f") + .help("Read x instead of bootstrap.") + .cardinality(0, 0); + + cli |= lyra::opt(globs.debug_configuration) + .name("--debug-configuration") + .help( + "Display additional debugging information related to " + "locating and loading Boost Build configuration files."); + + cli |= lyra::opt(args_data.debug_handles, "h") + .name(debugger_opt) + .help("Internal debugger.") + .cardinality(0, 2); + + /* Set default parallel jobs to match cpu threads. This can be overridden + the usual way with -jX or PARALLELISM env var. */ + globs.jobs = sys_info.cpu_thread_count(); + + // Set up options. + b2::args::set_args(arg_c, arg_v); + b2::args::process_args(true); + + saved_argv0 = argv[0]; + last_update_now_status = 0; #ifdef JAM_DEBUGGER - is_debugger = 0; - - if ( getoptions( argc - 1, argv + 1, "-:l:m:d:j:p:f:gs:t:ano:qv", optv ) < 0 ) - usage( progname ); - - if ( ( s = getoptval( optv, 'd', 0 ) ) ) - { - if ( strcmp( s, "mi" ) == 0 ) - { - debug_interface = DEBUG_INTERFACE_MI; - is_debugger = 1; - } - else if ( strcmp( s, "console" ) == 0 ) - { - debug_interface = DEBUG_INTERFACE_CONSOLE; - is_debugger = 1; - } - } - #if NT - if ( argc >= 3 ) - { - /* Check whether this instance is being run by the debugger. */ - size_t opt_len = strlen( debugger_opt ); - if ( strncmp( argv[ 1 ], debugger_opt, opt_len ) == 0 && - strncmp( argv[ 2 ], debugger_opt, opt_len ) == 0 ) - { - debug_init_handles( argv[ 1 ] + opt_len, argv[ 2 ] + opt_len ); - /* Fix up argc/argv to hide the internal options */ - arg_c = argc = (argc - 2); - argv[ 2 ] = argv[ 0 ]; - arg_v = argv = (argv + 2); - debug_interface = DEBUG_INTERFACE_CHILD; - } - } + /* Check whether this instance is being run by the debugger. */ + if (args_data.debug_handles.size() >= 2) + { + debug_init_handles(args_data.debug_handles[0].c_str(), + args_data.debug_handles[1].c_str()); + /* Fix up argc/argv to hide the internal options */ + arg_c = argc = (argc - 2); + argv[2] = argv[0]; + arg_v = argv = (argv + 2); + globs.debug_interface = global_config::debug_interface_child; + args_data = {}; + b2::args::set_args(arg_c, arg_v); + b2::args::process_args(true); + } - if ( is_debugger ) - { - return debugger(); - } + if (globs.is_debugger) + { + return debugger(); + } #else - if ( is_debugger ) - { - if ( setjmp( debug_child_data.jmp ) != 0 ) - { - arg_c = argc = debug_child_data.argc; - arg_v = argv = (char * *)debug_child_data.argv; - debug_interface = DEBUG_INTERFACE_CHILD; - } - else - { - return debugger(); - } - } + if (globs.is_debugger) + { + if (setjmp(debug_child_data.jmp) != 0) + { + arg_c = argc = debug_child_data.argc; + arg_v = argv = (char **)debug_child_data.argv; + globs.debug_interface = global_config::debug_interface_child; + args_data = {}; + b2::args::set_args(arg_c, arg_v); + b2::args::process_args(true); + } + else + { + return debugger(); + } + } #endif #endif - --argc; - ++argv; + if (!args_data.out_filename.empty()) + { + /* If an output file is specified, set globs.out to that. */ + globs.out = std::fopen(args_data.out_filename.c_str(), "w"); + if (!globs.out) + { + err_printf("[errno %d] failed to write output file '%s': %s", errno, + args_data.out_filename.c_str(), std::strerror(errno)); + b2::clean_exit(EXITBAD); + } + } - #define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qv" - - if ( getoptions( argc, argv, OPTSTRING, optv ) < 0 ) - { - usage( progname ); - } - - /* Set default parallel jobs to match cpu threads. This can be overridden - the usual way with -jX or PARALLELISM env var. */ - globs.jobs = sys_info.cpu_thread_count(); - - /* Version info. */ - if ( ( s = getoptval( optv, 'v', 0 ) ) ) - { - out_printf( "B2 %s (%s, jobs=%i)\n\n", VERSION, OSMINOR, globs.jobs ); - return EXITOK; - } - - // Process options. - lyra::cli cli; - b2::command_db::declare_args(cli); - cli |= lyra::arg([](const std::string&){}, "").cardinality(0,0); - auto cli_parse_result = cli.parse({arg_c, arg_v}); - - /* Pick up interesting options. */ - if ( ( s = getoptval( optv, 'n', 0 ) ) ) - { - ++globs.noexec; - globs.debug[ 2 ] = 1; - } - - if ( ( s = getoptval( optv, 'p', 0 ) ) ) - { - /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action - * stdout and stderr. - */ - globs.pipe_action = atoi( s ); - if ( globs.pipe_action < 0 || 3 < globs.pipe_action ) - { - err_printf( "Invalid pipe descriptor '%d', valid values are -p[0..3]." - "\n", globs.pipe_action ); - b2::clean_exit( EXITBAD ); - } - } - - if ( ( s = getoptval( optv, 'q', 0 ) ) ) - globs.quitquick = 1; - - if ( ( s = getoptval( optv, 'a', 0 ) ) ) - anyhow++; - - if ( ( s = getoptval( optv, 'j', 0 ) ) ) - { - globs.jobs = atoi( s ); - if ( globs.jobs < 1 ) - { - err_printf( "Invalid value for the '-j' option.\n" ); - b2::clean_exit( EXITBAD ); - } - } - - if ( ( s = getoptval( optv, 'g', 0 ) ) ) - globs.newestfirst = 1; - - if ( ( s = getoptval( optv, 'l', 0 ) ) ) - globs.timeout = atoi( s ); - - if ( ( s = getoptval( optv, 'm', 0 ) ) ) - globs.max_buf = atoi( s ) * 1024; /* convert to kb */ - - /* Turn on/off debugging */ - for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n ) - { - int i; - - /* First -d, turn off defaults. */ - if ( !n ) - for ( i = 0; i < DEBUG_MAX; ++i ) - globs.debug[i] = 0; - - i = atoi( s ); - - if ( ( i < 0 ) || ( i >= DEBUG_MAX ) ) - { - out_printf( "Invalid debug level '%s'.\n", s ); - continue; - } - - /* n turns on levels 1-n. */ - /* +n turns on level n. */ - if ( *s == '+' ) - globs.debug[ i ] = 1; - else while ( i ) - globs.debug[ i-- ] = 1; - } - - /* If an output file is specified, set globs.out to that. */ - if ( ( s = getoptval( optv, 'o', 0 ) ) ) - { - if ( !( globs.out = fopen( s, "w" ) ) ) - { - err_printf( "[errno %d] failed to write output file '%s': %s", - errno, s, strerror(errno) ); - b2::clean_exit( EXITBAD ); - } - /* ++globs.noexec; */ - } - - { - PROFILE_ENTER( MAIN ); + { + PROFILE_ENTER(MAIN); #ifndef NDEBUG - run_unit_tests(); + run_unit_tests(); #endif #if YYDEBUG != 0 - if ( DEBUG_PARSE ) - yydebug = 1; + if (is_debug_parse()) yydebug = 1; #endif - /* Set JAMDATE. */ - { - timestamp current; - timestamp_current( ¤t ); - var_set( root_module(), constant_JAMDATE, list_new( outf_time( - ¤t ) ), VAR_SET ); - } + /* Set JAMDATE. */ + { + timestamp current; + timestamp_current(¤t); + b2::jam::variable("JAMDATE", timestamp_str(¤t)); + } - /* Set JAM_VERSION. */ - var_set( root_module(), constant_JAM_VERSION, - list_push_back( list_push_back( list_new( - object_new( VERSION_MAJOR_SYM ) ), - object_new( VERSION_MINOR_SYM ) ), - object_new( VERSION_PATCH_SYM ) ), - VAR_SET ); + /* Set JAM_VERSION. */ + { + b2::jam::variable jam_version("JAM_VERSION"); + jam_version = VERSION_MAJOR_SYM; + jam_version += VERSION_MINOR_SYM; + jam_version += VERSION_PATCH_SYM; + } - /* Set JAMUNAME. */ + /* Set JAMUNAME. */ #ifdef unix - { - struct utsname u; + { + struct utsname u; - if ( uname( &u ) >= 0 ) - { - var_set( root_module(), constant_JAMUNAME, - list_push_back( - list_push_back( - list_push_back( - list_push_back( - list_new( - object_new( u.sysname ) ), - object_new( u.nodename ) ), - object_new( u.release ) ), - object_new( u.version ) ), - object_new( u.machine ) ), VAR_SET ); - } - } -#endif /* unix */ + if (uname(&u) >= 0) + { + b2::jam::variable jamuname("JAMUNAME"); + jamuname = u.sysname; + jamuname += u.nodename; + jamuname += u.release; + jamuname += u.version; + jamuname += u.machine; + } + } +#endif /* unix */ - /* Set JAM_TIMESTAMP_RESOLUTION. */ - { - timestamp fmt_resolution[ 1 ]; - file_supported_fmt_resolution( fmt_resolution ); - var_set( root_module(), constant_JAM_TIMESTAMP_RESOLUTION, list_new( - object_new( timestamp_timestr( fmt_resolution ) ) ), VAR_SET ); - } + /* Set JAM_TIMESTAMP_RESOLUTION. */ + { + timestamp fmt_resolution[1]; + file_supported_fmt_resolution(fmt_resolution); + b2::jam::variable( + "JAM_TIMESTAMP_RESOLUTION", timestamp_timestr(fmt_resolution)); + } - /* Load up environment variables. */ + /* Load up environment variables. */ - /* First into the global module, with splitting, for backward - * compatibility. - */ - var_defines( root_module(), use_environ, 1 ); + /* First into the global module, with splitting, for backward + * compatibility. + */ + var_defines(root_module(), use_environ, 1); - environ_module = bindmodule( constant_ENVIRON ); - /* Then into .ENVIRON, without splitting. */ - var_defines( environ_module, use_environ, 0 ); + environ_module = bindmodule(constant_ENVIRON); + /* Then into .ENVIRON, without splitting. */ + var_defines(environ_module, use_environ, 0); - /* - * Jam defined variables OS & OSPLAT. We load them after environment, so - * that setting OS in environment does not change Jam's notion of the - * current platform. - */ - var_defines( root_module(), othersyms, 1 ); + /* + * Jam defined variables OS & OSPLAT. We load them after environment, so + * that setting OS in environment does not change Jam's notion of the + * current platform. + */ + { + const char * othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 }; + var_defines(root_module(), othersyms, 1); + } - /* Load up variables set on command line. */ - for ( n = 0; ( s = getoptval( optv, 's', n ) ); ++n ) - { - char * symv[ 2 ]; - symv[ 0 ] = s; - symv[ 1 ] = 0; - var_defines( root_module(), symv, 1 ); - var_defines( environ_module, symv, 0 ); - } + /* Load up variables set on command line. */ + for (const std::string & s : args_data.variables_to_set) + { + const char * symv[2]; + symv[0] = s.c_str(); + symv[1] = nullptr; + var_defines(root_module(), symv, 1); + var_defines(environ_module, symv, 0); + } - /* Set the ARGV to reflect the complete list of arguments of invocation. - */ - for ( n = 0; n < arg_c; ++n ) - var_set( root_module(), constant_ARGV, list_new( object_new( - arg_v[ n ] ) ), VAR_APPEND ); + /* Set the ARGV to reflect the complete list of arguments of invocation. + */ + { + b2::jam::variable argv_v("ARGV"); + for (int n = 0; n < arg_c; ++n) argv_v += arg_v[n]; + } - /* Initialize built-in rules. */ - load_builtins(); - b2::startup::load_builtins(); + /* Initialize built-in rules. */ + load_builtins(); + b2::startup::load_builtins(); - /* Add the targets in the command line to the update list. */ - for ( n = 1; n < arg_c; ++n ) - { - if ( arg_v[ n ][ 0 ] == '-' ) - { - const char * f = "-:l:d:j:f:gs:t:ano:qv"; - for ( ; *f; ++f ) if ( *f == arg_v[ n ][ 1 ] ) break; - if ( f[0] && f[1] && ( f[ 1 ] == ':' ) && ( arg_v[ n ][ 2 ] == '\0' ) ) ++n; - } - else - { - OBJECT * target = object_new( arg_v[ n ] ); - mark_target_for_updating( target ); - object_free( target ); - } - } + /* The build system may set the PARALLELISM variable to override -j + * options. + */ + { + b2::jam::variable parallelism("PARALLELISM"); + if (parallelism) + { + int const j = std::atoi(parallelism[1]->str()); + if (j < 1) + out_printf("Invalid value of PARALLELISM: %s.\n", + parallelism[1]->str()); + else + globs.jobs = j; + } + } - /* The build system may set the PARALLELISM variable to override -j - * options. - */ - { - LIST * const p = var_get( root_module(), constant_PARALLELISM ); - if ( !list_empty( p ) ) - { - int const j = atoi( object_str( list_front( p ) ) ); - if ( j < 1 ) - out_printf( "Invalid value of PARALLELISM: %s.\n", - object_str( list_front( p ) ) ); - else - globs.jobs = j; - } - } + /* KEEP_GOING overrides -q option. */ + { + b2::jam::variable keep_going("KEEP_GOING"); + if (keep_going) + globs.quitquick = std::atoi(keep_going[1]->str()) != 0; + } - /* KEEP_GOING overrides -q option. */ - { - LIST * const p = var_get( root_module(), constant_KEEP_GOING ); - if ( !list_empty( p ) ) - globs.quitquick = atoi( object_str( list_front( p ) ) ) ? 0 : 1; - } + /* Add the targets in the command line to the update list. */ + if (args_data.extra_args.empty()) + mark_target_for_updating(constant_all); + else + for (const std::string & val : args_data.extra_args) + { + b2::value_ref target(val); + mark_target_for_updating(target); + } + /* Load build system. */ + if (args_data.files_to_parse.empty()) + { + FRAME frame; + frame_init(&frame); + /* Initialize the native API bindings. */ + b2::jam::bind_jam(&frame); - if ( list_empty( targets_to_update() ) ) - mark_target_for_updating( constant_all ); + /* Launch the bootstrap to load up the build system. */ + status = b2::startup::bootstrap(&frame) ? 0 : 13; + } - /* Parse ruleset. */ - { - FRAME frame[ 1 ]; - frame_init( frame ); - for ( n = 0; ( s = getoptval( optv, 'f', n ) ); ++n ) - { - OBJECT * filename = object_new( s ); - parse_file( filename, frame ); - object_free( filename ); - } + /* Parse ruleset. */ + if (!args_data.files_to_parse.empty()) + { + FRAME frame; + frame_init(&frame); + for (const std::string & s : args_data.files_to_parse) + { + b2::value_ref filename(s); + parse_file(filename, &frame); + } + } - if ( !n ) - { - /* Initialize the native API bindings. */ - b2::jam::bind_jam(frame); + // Process options. + args_data = {}; + b2::args::set_args(arg_c, arg_v); + b2::args::process_args(); - /* LUnch the bootstrap to load up the build system. */ - status = b2::startup::bootstrap(frame) ? 0 : 13; - } - } + /* FIXME: What shall we do if builtin_update_now, + * the sole place setting last_update_now_status, + * failed earlier? + */ - /* FIXME: What shall we do if builtin_update_now, - * the sole place setting last_update_now_status, - * failed earlier? - */ + if (status == 0) status = yyanyerrors(); + if (status == 0) + { + /* Manually touch -t targets. */ + for (auto const & x : args_data.targets_to_touch) + { + b2::value_ref target(x); + touch_target(target); + } - if ( status == 0 ) - status = yyanyerrors(); - if ( status == 0 ) - { - /* Manually touch -t targets. */ - for ( n = 0; ( s = getoptval( optv, 't', n ) ); ++n ) - { - OBJECT * target = object_new( s ); - touch_target( target ); - object_free( target ); - } + /* Now make target. */ + { + PROFILE_ENTER(MAIN_MAKE); + b2::list_cref targets(targets_to_update()); + if (!targets.empty()) + status |= make(*targets, globs.anyhow); + else + status = last_update_now_status; + PROFILE_EXIT(MAIN_MAKE); + } + } - /* Now make target. */ - { - PROFILE_ENTER( MAIN_MAKE ); - LIST * const targets = targets_to_update(); - if ( !list_empty( targets ) ) - status |= make( targets, anyhow ); - else - status = last_update_now_status; - PROFILE_EXIT( MAIN_MAKE ); - } - } - - PROFILE_EXIT( MAIN ); - } + PROFILE_EXIT(MAIN); + } b2::trigger_event_exit_main(status ? EXITBAD : EXITOK); - return status ? EXITBAD : EXITOK; + return status ? EXITBAD : EXITOK; } #ifdef WIN32 @@ -633,94 +649,87 @@ namespace { struct SetConsoleCodepage { - SetConsoleCodepage() - { - // Check whether UTF-8 is actually the default encoding for this process - if (GetACP() != CP_UTF8) - return; + SetConsoleCodepage() + { + // Check whether UTF-8 is actually the default encoding for this process + if (GetACP() != CP_UTF8) return; - orig_console_cp = GetConsoleCP(); - if (orig_console_cp != 0 && orig_console_cp != CP_UTF8) - SetConsoleCP(CP_UTF8); - orig_console_output_cp = GetConsoleOutputCP(); - if (orig_console_output_cp != 0 && orig_console_output_cp != CP_UTF8) - SetConsoleOutputCP(CP_UTF8); - } + orig_console_cp = GetConsoleCP(); + if (orig_console_cp != 0 && orig_console_cp != CP_UTF8) + SetConsoleCP(CP_UTF8); + orig_console_output_cp = GetConsoleOutputCP(); + if (orig_console_output_cp != 0 && orig_console_output_cp != CP_UTF8) + SetConsoleOutputCP(CP_UTF8); + } - ~SetConsoleCodepage() - { - // Restore original console codepage - if (orig_console_cp != 0 && orig_console_cp != CP_UTF8) - SetConsoleCP(orig_console_cp); - if (orig_console_output_cp != 0 && orig_console_output_cp != CP_UTF8) - SetConsoleOutputCP(orig_console_output_cp); - } + ~SetConsoleCodepage() + { + // Restore original console codepage + if (orig_console_cp != 0 && orig_console_cp != CP_UTF8) + SetConsoleCP(orig_console_cp); + if (orig_console_output_cp != 0 && orig_console_output_cp != CP_UTF8) + SetConsoleOutputCP(orig_console_output_cp); + } -private: - UINT orig_console_cp = 0; - UINT orig_console_output_cp = 0; + private: + UINT orig_console_cp = 0; + UINT orig_console_output_cp = 0; }; -static const SetConsoleCodepage g_console_codepage_setter{}; +static const SetConsoleCodepage g_console_codepage_setter {}; -} +} // namespace #endif -int main( int argc, char * * argv ) +int main(int argc, char ** argv) { - BJAM_MEM_INIT(); + BJAM_MEM_INIT(); -#ifdef OS_MAC - InitGraf( &qd.thePort ); -#endif + cwd_init(); + constants_init(); - cwd_init(); - constants_init(); + int result = EXIT_SUCCESS; + try + { + result = guarded_main(argc, argv); + } + catch (b2::exit_result exit_code) + { + result = (int)exit_code; + out_flush(); + err_flush(); + } - int result = EXIT_SUCCESS; - try - { - result = guarded_main( argc, argv ); - } - catch ( b2::exit_result exit_code ) - { - result = (int)exit_code; - out_flush(); - err_flush(); - } - - if ( DEBUG_PROFILE ) - profile_dump(); + if (is_debug_profile()) profile_dump(); #ifdef OPT_HEADER_CACHE_EXT - hcache_done(); + hcache_done(); #endif - clear_targets_to_update(); + clear_targets_to_update(); - /* Widely scattered cleanup. */ - parse_done(); - debugger_done(); - property_set_done(); - exec_done(); - file_done(); - rules_done(); - timestamp_done(); - search_done(); - class_done(); - modules_done(); - cwd_done(); - path_done(); - function_done(); - list_done(); - constants_done(); - object_done(); + /* Widely scattered cleanup. */ + parse_done(); + debugger_done(); + property_set_done(); + exec_done(); + file_done(); + rules_done(); + timestamp_done(); + search_done(); + class_done(); + modules_done(); + cwd_done(); + path_done(); + function_done(); + list_done(); + constants_done(); + object_done(); - /* Close log out. */ - if ( globs.out ) - fclose( globs.out ); + /* Close log out. */ + if (globs.out) std::fclose(globs.out); - BJAM_MEM_CLOSE(); + BJAM_MEM_CLOSE(); - return result; + return result; } diff --git a/src/engine/jam.h b/src/engine/jam.h index 61ccd1619..fb6e5dabb 100644 --- a/src/engine/jam.h +++ b/src/engine/jam.h @@ -29,15 +29,15 @@ #ifdef VMS -#include +#include #include +#include #include #include -#include #include -#include #include #include +#include #include #include @@ -45,7 +45,7 @@ #define OSMAJOR "VMS=true" #define OS_VMS #define MAXLINE 1024 /* longest 'together' actions */ -#define PATH_DELIM '/' /* use CRTL POSIX-style handling */ +#define PATH_DELIM '/' /* use CRTL POSIX-style handling */ #define SPLITPATH ',' #define EXITOK EXIT_SUCCESS #define EXITBAD EXIT_FAILURE @@ -56,7 +56,7 @@ #define OSPLAT "OSPLAT=VAX" #endif -#define glob jam_glob /* use jam's glob, not CRTL's */ +#define glob jam_glob /* use jam's glob, not CRTL's */ #endif @@ -70,11 +70,11 @@ #include #include #ifndef __MWERKS__ - #include +#include #endif +#include #include #include -#include #include #include @@ -82,7 +82,7 @@ #define OSMINOR "OS=NT" #define OS_NT #define SPLITPATH ';' -#define MAXLINE (undefined__see_execnt_c) /* max chars per command line */ +#define MAXLINE (undefined__see_execnt_c) /* max chars per command line */ #define USE_EXECNT #define USE_PATHNT #define PATH_DELIM '\\' @@ -90,21 +90,20 @@ /* AS400 cross-compile from NT. */ #ifdef AS400 - #undef OSMINOR - #undef OSMAJOR - #define OSMAJOR "AS400=true" - #define OSMINOR "OS=AS400" - #define OS_AS400 +#undef OSMINOR +#undef OSMAJOR +#define OSMAJOR "AS400=true" +#define OSMINOR "OS=AS400" +#define OS_AS400 #endif /* Metrowerks Standard Library on Windows. */ #ifdef __MSL__ - #undef HAVE_POPEN +#undef HAVE_POPEN #endif -#endif /* #ifdef NT */ - +#endif /* #ifdef NT */ /* * Windows MingW32 @@ -112,13 +111,13 @@ #ifdef MINGW -#include -#include -#include #include +#include #include #include #include +#include +#include #include #include @@ -126,13 +125,12 @@ #define OSMINOR "OS=MINGW" #define OS_NT #define SPLITPATH ';' -#define MAXLINE 996 /* max chars per command line */ +#define MAXLINE 996 /* max chars per command line */ #define USE_EXECUNIX #define USE_PATHNT #define PATH_DELIM '\\' -#endif /* #ifdef MINGW */ - +#endif /* #ifdef MINGW */ /* * God fearing UNIX. @@ -147,193 +145,191 @@ #define PATH_DELIM '/' #ifdef _AIX - #ifndef unix - #define unix - #endif - #define MAXLINE 23552 /* 24k - 1k, max chars per command line */ - #define OSMINOR "OS=AIX" - #define OS_AIX - #define NO_VFORK +#define unix +#define MAXLINE 23552 /* 24k - 1k, max chars per command line */ +#define OSMINOR "OS=AIX" +#define OS_AIX +#define NO_VFORK #endif #ifdef AMIGA - #define OSMINOR "OS=AMIGA" - #define OS_AMIGA +#define OSMINOR "OS=AMIGA" +#define OS_AMIGA #endif #ifdef __BEOS__ - #define unix - #define OSMINOR "OS=BEOS" - #define OS_BEOS - #define NO_VFORK +#define unix +#define OSMINOR "OS=BEOS" +#define OS_BEOS +#define NO_VFORK #endif #ifdef __bsdi__ - #define OSMINOR "OS=BSDI" - #define OS_BSDI +#define OSMINOR "OS=BSDI" +#define OS_BSDI #endif -#if defined (COHERENT) && defined (_I386) - #define OSMINOR "OS=COHERENT" - #define OS_COHERENT - #define NO_VFORK +#if defined(COHERENT) && defined(_I386) +#define OSMINOR "OS=COHERENT" +#define OS_COHERENT +#define NO_VFORK #endif #if defined(__cygwin__) || defined(__CYGWIN__) - #define OSMINOR "OS=CYGWIN" - #define OS_CYGWIN +#define OSMINOR "OS=CYGWIN" +#define OS_CYGWIN #endif #if defined(__FreeBSD__) && !defined(__DragonFly__) - #define OSMINOR "OS=FREEBSD" - #define OS_FREEBSD +#define OSMINOR "OS=FREEBSD" +#define OS_FREEBSD #endif #ifdef __DragonFly__ - #define OSMINOR "OS=DRAGONFLYBSD" - #define OS_DRAGONFLYBSD +#define OSMINOR "OS=DRAGONFLYBSD" +#define OS_DRAGONFLYBSD #endif #ifdef __DGUX__ - #define OSMINOR "OS=DGUX" - #define OS_DGUX +#define OSMINOR "OS=DGUX" +#define OS_DGUX #endif #ifdef __GNU__ - #define OSMINOR "OS=HURD" - #define OS_HURD +#define OSMINOR "OS=HURD" +#define OS_HURD #endif #ifdef __hpux - #define OSMINOR "OS=HPUX" - #define OS_HPUX +#define OSMINOR "OS=HPUX" +#define OS_HPUX #endif #ifdef __HAIKU__ - #define unix - #define OSMINOR "OS=HAIKU" - #define OS_HAIKU +#define unix +#define OSMINOR "OS=HAIKU" +#define OS_HAIKU #endif #ifdef __OPENNT - #define unix - #define OSMINOR "OS=INTERIX" - #define OS_INTERIX - #define NO_VFORK +#define unix +#define OSMINOR "OS=INTERIX" +#define OS_INTERIX +#define NO_VFORK #endif #ifdef __sgi - #define OSMINOR "OS=IRIX" - #define OS_IRIX - #define NO_VFORK +#define OSMINOR "OS=IRIX" +#define OS_IRIX +#define NO_VFORK #endif #ifdef __ISC - #define OSMINOR "OS=ISC" - #define OS_ISC - #define NO_VFORK +#define OSMINOR "OS=ISC" +#define OS_ISC +#define NO_VFORK #endif -#if defined(linux) || defined(__linux) || \ - defined(__linux__) || defined(__gnu_linux__) - #define OSMINOR "OS=LINUX" - #define OS_LINUX +#if defined(linux) || defined(__linux) || defined(__linux__) \ + || defined(__gnu_linux__) +#define OSMINOR "OS=LINUX" +#define OS_LINUX #endif #ifdef __Lynx__ - #define OSMINOR "OS=LYNX" - #define OS_LYNX - #define NO_VFORK - #define unix +#define OSMINOR "OS=LYNX" +#define OS_LYNX +#define NO_VFORK +#define unix #endif #ifdef __MACHTEN__ - #define OSMINOR "OS=MACHTEN" - #define OS_MACHTEN +#define OSMINOR "OS=MACHTEN" +#define OS_MACHTEN #endif #ifdef mpeix - #define unix - #define OSMINOR "OS=MPEIX" - #define OS_MPEIX - #define NO_VFORK +#define unix +#define OSMINOR "OS=MPEIX" +#define OS_MPEIX +#define NO_VFORK #endif #ifdef __MVS__ - #define unix - #define OSMINOR "OS=MVS" - #define OS_MVS +#define unix +#define OSMINOR "OS=MVS" +#define OS_MVS #endif #ifdef _ATT4 - #define OSMINOR "OS=NCR" - #define OS_NCR +#define OSMINOR "OS=NCR" +#define OS_NCR #endif #ifdef __NetBSD__ - #define unix - #define OSMINOR "OS=NETBSD" - #define OS_NETBSD - #define NO_VFORK +#define unix +#define OSMINOR "OS=NETBSD" +#define OS_NETBSD +#define NO_VFORK #endif #ifdef __QNX__ - #define unix - #ifdef __QNXNTO__ - #define OSMINOR "OS=QNXNTO" - #define OS_QNXNTO - #else - #define OSMINOR "OS=QNX" - #define OS_QNX - #define NO_VFORK - #define MAXLINE 996 /* max chars per command line */ - #endif +#define unix +#ifdef __QNXNTO__ +#define OSMINOR "OS=QNXNTO" +#define OS_QNXNTO +#else +#define OSMINOR "OS=QNX" +#define OS_QNX +#define NO_VFORK +#define MAXLINE 996 /* max chars per command line */ +#endif #endif #ifdef NeXT - #ifdef __APPLE__ - #define OSMINOR "OS=RHAPSODY" - #define OS_RHAPSODY - #else - #define OSMINOR "OS=NEXT" - #define OS_NEXT - #endif +#ifdef __APPLE__ +#define OSMINOR "OS=RHAPSODY" +#define OS_RHAPSODY +#else +#define OSMINOR "OS=NEXT" +#define OS_NEXT +#endif #endif #ifdef __APPLE__ - #define unix - #define OSMINOR "OS=MACOSX" - #define OS_MACOSX +#define unix +#define OSMINOR "OS=MACOSX" +#define OS_MACOSX #endif #ifdef __osf__ - #ifndef unix - #define unix - #endif - #define OSMINOR "OS=OSF" - #define OS_OSF +#ifndef unix +#define unix +#endif +#define OSMINOR "OS=OSF" +#define OS_OSF #endif #ifdef _SEQUENT_ - #define OSMINOR "OS=PTX" - #define OS_PTX +#define OSMINOR "OS=PTX" +#define OS_PTX #endif #ifdef M_XENIX - #define OSMINOR "OS=SCO" - #define OS_SCO - #define NO_VFORK +#define OSMINOR "OS=SCO" +#define OS_SCO +#define NO_VFORK #endif #ifdef sinix - #define unix - #define OSMINOR "OS=SINIX" - #define OS_SINIX +#define unix +#define OSMINOR "OS=SINIX" +#define OS_SINIX #endif #if defined(__svr4__) || defined(__SVR4) - #define OSMINOR "OS=SOLARIS" - #define OS_SOLARIS +#define OSMINOR "OS=SOLARIS" +#define OS_SOLARIS #elif defined(__sun__) || defined(__sun) || defined(sun) - #define OSMINOR "OS=SUNOS" - #define OS_SUNOS +#define OSMINOR "OS=SUNOS" +#define OS_SUNOS #endif #ifdef ultrix - #define OSMINOR "OS=ULTRIX" - #define OS_ULTRIX +#define OSMINOR "OS=ULTRIX" +#define OS_ULTRIX #endif #ifdef _UNICOS - #define OSMINOR "OS=UNICOS" - #define OS_UNICOS +#define OSMINOR "OS=UNICOS" +#define OS_UNICOS #endif #if defined(__USLC__) && !defined(M_XENIX) - #define OSMINOR "OS=UNIXWARE" - #define OS_UNIXWARE +#define OSMINOR "OS=UNIXWARE" +#define OS_UNIXWARE #endif #ifdef __OpenBSD__ - #define OSMINOR "OS=OPENBSD" - #define OS_OPENBSD - #ifndef unix - #define unix - #endif +#define OSMINOR "OS=OPENBSD" +#define OS_OPENBSD +#ifndef unix +#define unix #endif -#if defined (__FreeBSD_kernel__) && !defined(__FreeBSD__) - #define OSMINOR "OS=KFREEBSD" - #define OS_KFREEBSD +#endif +#if defined(__FreeBSD_kernel__) && !defined(__FreeBSD__) +#define OSMINOR "OS=KFREEBSD" +#define OS_KFREEBSD #endif #ifndef OSMINOR - #define OSMINOR "OS=UNKNOWN" +#define OSMINOR "OS=UNKNOWN" #endif /* All the UNIX includes */ @@ -341,199 +337,217 @@ #include #ifndef OS_MPEIX - #include +#include #endif -#include -#include #include +#include #include +#include #include #include #include #ifndef OS_QNX - #include +#include #endif #ifndef OS_ULTRIX - #include +#include #endif -#if !defined( OS_BSDI ) && \ - !defined( OS_FREEBSD ) && \ - !defined( OS_DRAGONFLYBSD ) && \ - !defined( OS_NEXT ) && \ - !defined( OS_MACHTEN ) && \ - !defined( OS_MACOSX ) && \ - !defined( OS_RHAPSODY ) && \ - !defined( OS_MVS ) && \ - !defined( OS_OPENBSD ) - #include +#if !defined(OS_BSDI) && !defined(OS_FREEBSD) && !defined(OS_DRAGONFLYBSD) \ + && !defined(OS_NEXT) && !defined(OS_MACHTEN) && !defined(OS_MACOSX) \ + && !defined(OS_RHAPSODY) && !defined(OS_MVS) && !defined(OS_OPENBSD) +#include #endif -#endif /* #ifndef OSMINOR */ - +#endif /* #ifndef OSMINOR */ /* * OSPLAT definitions - suppressed when it is a one-of-a-kind. */ -#if defined( _M_PPC ) || \ - defined( PPC ) || \ - defined( ppc ) || \ - defined( __powerpc__ ) || \ - defined( __ppc__ ) - #define OSPLAT "OSPLAT=PPC" +#if defined(_M_PPC) || defined(PPC) || defined(ppc) || defined(__powerpc__) \ + || defined(__ppc__) +#define OSPLAT "OSPLAT=PPC" #endif -#if defined( _ALPHA_ ) || \ - defined( __alpha__ ) - #define OSPLAT "OSPLAT=AXP" +#if defined(_ALPHA_) || defined(__alpha__) +#define OSPLAT "OSPLAT=AXP" #endif -#if defined( _i386_ ) || \ - defined( __i386__ ) || \ - defined( __i386 ) || \ - defined( _M_IX86 ) - #define OSPLAT "OSPLAT=X86" +#if defined(_i386_) || defined(__i386__) || defined(__i386) || defined(_M_IX86) +#define OSPLAT "OSPLAT=X86" #endif -#if defined( __ia64__ ) || \ - defined( __IA64__ ) || \ - defined( __ia64 ) - #define OSPLAT "OSPLAT=IA64" +#if defined(__ia64__) || defined(__IA64__) || defined(__ia64) +#define OSPLAT "OSPLAT=IA64" #endif -#if defined( __x86_64__ ) || \ - defined( __amd64__ ) || \ - defined( _M_AMD64 ) - #define OSPLAT "OSPLAT=X86_64" +#if defined(__x86_64__) || defined(__amd64__) || defined(_M_AMD64) +#define OSPLAT "OSPLAT=X86_64" #endif -#if defined( __sparc__ ) || \ - defined( __sparc ) - #define OSPLAT "OSPLAT=SPARC" +#if defined(__sparc__) || defined(__sparc) +#define OSPLAT "OSPLAT=SPARC" #endif #ifdef __mips__ - #if _MIPS_SIM == _MIPS_SIM_ABI64 - #define OSPLAT "OSPLAT=MIPS64" - #elif _MIPS_SIM == _MIPS_SIM_ABI32 - #define OSPLAT "OSPLAT=MIPS32" - #endif +#if _MIPS_SIM == _MIPS_SIM_ABI64 +#define OSPLAT "OSPLAT=MIPS64" +#elif _MIPS_SIM == _MIPS_SIM_ABI32 +#define OSPLAT "OSPLAT=MIPS32" +#endif #endif -#if defined( __arm__ ) || \ - defined( _M_ARM ) - #define OSPLAT "OSPLAT=ARM" +#if defined(__arm__) || defined(_M_ARM) +#define OSPLAT "OSPLAT=ARM" #endif -#if defined( __aarch64__ ) || \ - defined( _M_ARM64 ) - #define OSPLAT "OSPLAT=ARM64" +#if defined(__aarch64__) || defined(_M_ARM64) +#define OSPLAT "OSPLAT=ARM64" #endif #ifdef __s390__ - #define OSPLAT "OSPLAT=390" +#define OSPLAT "OSPLAT=390" #endif #ifdef __hppa - #define OSPLAT "OSPLAT=PARISC" +#define OSPLAT "OSPLAT=PARISC" #endif -#if defined( __riscv ) || defined( __riscv__ ) - #if __riscv_xlen == 64 - #define OSPLAT "OSPLAT=RISCV64" - #elif __riscv_xlen == 32 - #define OSPLAT "OSPLAT=RISCV32" - #endif +#if defined(__riscv) || defined(__riscv__) +#if __riscv_xlen == 64 +#define OSPLAT "OSPLAT=RISCV64" +#elif __riscv_xlen == 32 +#define OSPLAT "OSPLAT=RISCV32" +#endif #endif #ifndef OSPLAT - #define OSPLAT "" +#define OSPLAT "" #endif - /* * Jam implementation misc. */ #ifndef MAXLINE - #define MAXLINE 102400 /* max chars per command line */ +#define MAXLINE 102400 /* max chars per command line */ #endif #ifndef EXITOK - #define EXITOK 0 - #define EXITBAD 1 +#define EXITOK 0 +#define EXITBAD 1 #endif #ifndef SPLITPATH - #define SPLITPATH ':' +#define SPLITPATH ':' #endif /* You probably do not need to muck with these. */ -#define MAXSYM 1024 /* longest symbol in the environment */ -#define MAXJPATH 1024 /* longest filename */ +#define MAXSYM 1024 /* longest symbol in the environment */ +#define MAXJPATH 1024 /* longest filename */ -#define MAXARGC 32 /* words in $(JAMSHELL) */ +#define MAXARGC 32 /* words in $(JAMSHELL) */ /* Jam private definitions below. */ -#define DEBUG_MAX 14 +#define DEBUG_MAX 14 - -struct globs +struct global_config { - int noexec; - int jobs; - int quitquick; - int newestfirst; /* build newest sources first */ - int pipe_action; - char debug[ DEBUG_MAX ]; - FILE * out; /* mirror output here */ - long timeout; /* number of seconds to limit actions to, - * default 0 for no limit. - */ - int dart; /* output build and test results formatted for - * Dart - */ - int max_buf; /* maximum amount of output saved from target - * (kb) - */ + // Do not execute actions. + bool noexec = false; + // Build all targets. + bool anyhow = false; + // Max number of parallel jobs/actions. + int jobs = 1; + // Abort building on first fail occurrence. + bool quitquick = false; + // Build newest sources first. + bool newestfirst = false; + // Where to send the output streams. + int pipe_action = 0; + // If a debug level flag was given. + bool debug_flag_used = false; + // Debug level flags. + bool debug[DEBUG_MAX] = { false, true }; + // Mirror output here + FILE * out = nullptr; + // Number of seconds to limit actions to, default 0 for no limit. + long timeout = 0; + // Maximum amount of output saved from target (kb) + int max_buf = 0; + // Is behaving as a debugger. + bool is_debugger = false; + // Debugger interface. + enum : char + { + debug_interface_no = 0, + debug_interface_console, + debug_interface_mi, + debug_interface_child + } debug_interface = debug_interface_no; + // Print out information on configuration actions. + bool debug_configuration = false; + // Show help output instead of building. + bool display_help = false; + + void out_print() const; }; -extern struct globs globs; +extern global_config globs; -extern int anyhow; +/* show actions when executed */ +inline bool is_debug_make() { return globs.debug[1]; } +/* show even quiet actions */ +inline bool is_debug_makeq() { return globs.debug[2]; } +/* show text of actions */ +inline bool is_debug_exec() { return globs.debug[2]; } +/* show make0 progress */ +inline bool is_debug_makeprog() { return globs.debug[3]; } +/* show when files bound */ +inline bool is_debug_bind() { return globs.debug[3]; } -#define DEBUG_MAKE ( globs.debug[ 1 ] ) /* show actions when executed */ -#define DEBUG_MAKEQ ( globs.debug[ 2 ] ) /* show even quiet actions */ -#define DEBUG_EXEC ( globs.debug[ 2 ] ) /* show text of actons */ -#define DEBUG_MAKEPROG ( globs.debug[ 3 ] ) /* show make0 progress */ -#define DEBUG_BIND ( globs.debug[ 3 ] ) /* show when files bound */ +/* show execcmds()'s work */ +inline bool is_debug_execcmd() { return globs.debug[4]; } -#define DEBUG_EXECCMD ( globs.debug[ 4 ] ) /* show execcmds()'s work */ +/* show rule invocations */ +inline bool is_debug_compile() { return globs.debug[5]; } -#define DEBUG_COMPILE ( globs.debug[ 5 ] ) /* show rule invocations */ +/* show result of header scan */ +inline bool is_debug_header() { return globs.debug[6]; } +/* show result of dir scan */ +inline bool is_debug_bindscan() { return globs.debug[6]; } +/* show binding attempts */ +inline bool is_debug_search() { return globs.debug[6]; } -#define DEBUG_HEADER ( globs.debug[ 6 ] ) /* show result of header scan */ -#define DEBUG_BINDSCAN ( globs.debug[ 6 ] ) /* show result of dir scan */ -#define DEBUG_SEARCH ( globs.debug[ 6 ] ) /* show binding attempts */ +/* show variable settings */ +inline bool is_debug_varset() { return globs.debug[7]; } +/* show variable fetches */ +inline bool is_debug_varget() { return globs.debug[8]; } +/* show variable expansions */ +inline bool is_debug_varexp() { return globs.debug[8]; } +/* show 'if' calculations */ +inline bool is_debug_if() { return globs.debug[8]; } +/* show list manipulation */ +inline bool is_debug_lists() { return globs.debug[9]; } +/* show scanner tokens */ +inline bool is_debug_scan() { return globs.debug[9]; } +/* show memory use */ +inline bool is_debug_mem() { return globs.debug[9]; } -#define DEBUG_VARSET ( globs.debug[ 7 ] ) /* show variable settings */ -#define DEBUG_VARGET ( globs.debug[ 8 ] ) /* show variable fetches */ -#define DEBUG_VAREXP ( globs.debug[ 8 ] ) /* show variable expansions */ -#define DEBUG_IF ( globs.debug[ 8 ] ) /* show 'if' calculations */ -#define DEBUG_LISTS ( globs.debug[ 9 ] ) /* show list manipulation */ -#define DEBUG_SCAN ( globs.debug[ 9 ] ) /* show scanner tokens */ -#define DEBUG_MEM ( globs.debug[ 9 ] ) /* show memory use */ - -#define DEBUG_PROFILE ( globs.debug[ 10 ] ) /* dump rule execution times */ -#define DEBUG_PARSE ( globs.debug[ 11 ] ) /* debug parsing */ -#define DEBUG_GRAPH ( globs.debug[ 12 ] ) /* debug dependencies */ -#define DEBUG_FATE ( globs.debug[ 13 ] ) /* show fate changes in make0() */ +/* dump rule execution times */ +inline bool is_debug_profile() { return globs.debug[10]; } +/* debug parsing */ +inline bool is_debug_parse() { return globs.debug[11]; } +/* debug dependencies */ +inline bool is_debug_graph() { return globs.debug[12]; } +/* show fate changes in make0() */ +inline bool is_debug_fate() { return globs.debug[13]; } /* Everyone gets the memory definitions. */ #include "mem.h" diff --git a/src/engine/lists.cpp b/src/engine/lists.cpp index d1e110da0..118cbffd6 100644 --- a/src/engine/lists.cpp +++ b/src/engine/lists.cpp @@ -95,7 +95,7 @@ LIST * list_push_back(LIST * head, OBJECT * value) { int32_t size = list_length(head); - if (DEBUG_LISTS) out_printf("list > %s <\n", object_str(value)); + if (is_debug_lists()) out_printf("list > %s <\n", object_str(value)); /* If the size is a power of 2, reallocate. */ if (size == 0) diff --git a/src/engine/make.cpp b/src/engine/make.cpp index 90fbc1783..769632fce 100644 --- a/src/engine/make.cpp +++ b/src/engine/make.cpp @@ -92,7 +92,7 @@ static char const * target_bind[] = * make() - make a target, given its name. */ -int32_t make( LIST * targets, int32_t anyhow ) +int32_t make( LIST * targets, bool anyhow ) { COUNTS counts[ 1 ]; int32_t status = 0; /* 1 if anything fails */ @@ -125,7 +125,7 @@ int32_t make( LIST * targets, int32_t anyhow ) } #ifdef OPT_GRAPH_DEBUG_EXT - if ( DEBUG_GRAPH ) + if ( is_debug_graph() ) { LISTITER iter, end; for ( iter = list_begin( targets ), end = list_end( targets ); iter != end; iter = list_next( iter ) ) @@ -133,7 +133,7 @@ int32_t make( LIST * targets, int32_t anyhow ) } #endif - if ( DEBUG_MAKE ) + if ( is_debug_make() ) { if ( counts->targets ) out_printf( "...found %d target%s...\n", counts->targets, @@ -186,7 +186,7 @@ static void update_dependants( TARGET * t ) { p->fate = T_FATE_UPDATE; - if ( DEBUG_FATE ) + if ( is_debug_fate() ) { out_printf( "fate change %s from %s to %s (as dependent of %s)\n", object_str( p->name ), target_fate[ (int32_t) fate0 ], target_fate[ (int32_t) p->fate ], object_str( t->name ) ); @@ -218,7 +218,7 @@ static void force_rebuilds( TARGET * t ) /* If it is not already being rebuilt for other reasons. */ if ( r->fate < T_FATE_BUILD ) { - if ( DEBUG_FATE ) + if ( is_debug_fate() ) out_printf( "fate change %s from %s to %s (by rebuild)\n", object_str( r->name ), target_fate[ (int32_t) r->fate ], target_fate[ T_FATE_REBUILD ] ); @@ -284,7 +284,7 @@ void make0 TARGET * p, /* parent */ int32_t depth, /* for display purposes */ COUNTS * counts, /* for reporting */ - int32_t anyhow, + bool anyhow, TARGET * rescanning ) /* forcibly touch all (real) targets */ { @@ -303,14 +303,14 @@ void make0 int32_t oldTimeStamp; #endif - if ( DEBUG_MAKEPROG ) + if ( is_debug_makeprog() ) out_printf( "make\t--\t%s%s\n", spaces( depth ), object_str( t->name ) ); /* * Step 1: Initialize. */ - if ( DEBUG_MAKEPROG ) + if ( is_debug_makeprog() ) out_printf( "make\t--\t%s%s\n", spaces( depth ), object_str( t->name ) ); t->fate = T_FATE_MAKING; @@ -381,7 +381,7 @@ void make0 * Pause for a little progress reporting. */ - if ( DEBUG_BIND ) + if ( is_debug_bind() ) { if ( !object_equal( t->name, t->boundname ) ) out_printf( "bind\t--\t%s%s: %s\n", spaces( depth ), @@ -510,7 +510,7 @@ void make0 fate = max( fate, c->target->fate ); #ifdef OPT_GRAPH_DEBUG_EXT - if ( DEBUG_FATE ) + if ( is_debug_fate() ) if ( fate < c->target->fate ) out_printf( "fate change %s from %s to %s by dependency %s\n", object_str( t->name ), target_fate[ (int32_t)fate ], @@ -539,7 +539,7 @@ void make0 if ( t->flags & T_FLAG_NOUPDATE ) { #ifdef OPT_GRAPH_DEBUG_EXT - if ( DEBUG_FATE ) + if ( is_debug_fate() ) if ( fate != T_FATE_STABLE ) out_printf( "fate change %s back to stable, NOUPDATE.\n", object_str( t->name ) ); @@ -636,7 +636,7 @@ void make0 fate = T_FATE_STABLE; } #ifdef OPT_GRAPH_DEBUG_EXT - if ( DEBUG_FATE && ( fate != savedFate ) ) + if ( is_debug_fate() && ( fate != savedFate ) ) { if ( savedFate == T_FATE_STABLE ) out_printf( "fate change %s set to %s%s\n", object_str( t->name ), @@ -659,7 +659,7 @@ void make0 if ( t->flags & T_FLAG_NOCARE ) { #ifdef OPT_GRAPH_DEBUG_EXT - if ( DEBUG_FATE ) + if ( is_debug_fate() ) out_printf( "fate change %s to STABLE from %s, " "no actions, no dependencies and do not care\n", object_str( t->name ), target_fate[ fate ] ); @@ -736,7 +736,7 @@ void make0 #ifdef OPT_IMPROVED_PATIENCE_EXT ++counts->targets; #else - if ( !( ++counts->targets % 1000 ) && DEBUG_MAKE ) + if ( !( ++counts->targets % 1000 ) && is_debug_make() ) { out_printf( "...patience...\n" ); out_flush(); @@ -760,7 +760,7 @@ void make0 &p->time ) > 0 ) flag = "*"; - if ( DEBUG_MAKEPROG ) + if ( is_debug_makeprog() ) out_printf( "made%s\t%s\t%s%s\n", flag, target_fate[ (int32_t)t->fate ], spaces( depth ), object_str( t->name ) ); } diff --git a/src/engine/make.h b/src/engine/make.h index 5fcdb7a2e..5740e8534 100644 --- a/src/engine/make.h +++ b/src/engine/make.h @@ -16,20 +16,20 @@ #include "object.h" #include "rules.h" -int32_t make( LIST * targets, int32_t anyhow ); +int32_t make( LIST * targets, bool anyhow ); int32_t make1( LIST * t ); -typedef struct { +struct COUNTS { int32_t temp; int32_t updating; int32_t cantfind; int32_t cantmake; int32_t targets; int32_t made; -} COUNTS ; +}; -void make0( TARGET * t, TARGET * p, int32_t depth, COUNTS * counts, int32_t anyhow, +void make0( TARGET * t, TARGET * p, int32_t depth, COUNTS * counts, bool anyhow, TARGET * rescanning ); diff --git a/src/engine/make1.cpp b/src/engine/make1.cpp index 7a6e1429b..116c120b0 100644 --- a/src/engine/make1.cpp +++ b/src/engine/make1.cpp @@ -258,12 +258,12 @@ int32_t make1( LIST * targets ) clear_state_freelist(); /* Talk about it. */ - if ( DEBUG_MAKE && counts->made ) + if ( is_debug_make() && counts->made ) { out_printf( "\n...updated %d target%s...\n", counts->made, counts->made > 1 ? "s" : "" ); } - if ( DEBUG_MAKE && counts->skipped ) + if ( is_debug_make() && counts->skipped ) { out_printf( "\n...skipped %d target%s...\n", make_summary->count(targets_skipped), @@ -473,7 +473,7 @@ static void make1b( state * const pState ) break; case T_FATE_ISTMP: - if ( DEBUG_MAKE ) + if ( is_debug_make() ) out_printf( "...using %s...\n", object_str( t->name ) ); break; @@ -490,7 +490,7 @@ static void make1b( state * const pState ) if ( t->actions ) { ++counts->total; - if ( DEBUG_MAKE && !( counts->total % 100 ) ) + if ( is_debug_make() && !( counts->total % 100 ) ) out_printf( "...on %dth target...\n", counts->total ); t->cmds = (char *)make1cmds( t ); @@ -516,7 +516,7 @@ static void make1b( state * const pState ) if ( t->cmds == NULL || --( ( CMD * )t->cmds )->asynccnt == 0 ) push_state( &state_stack, t, NULL, T_STATE_MAKE1C ); - else if ( DEBUG_EXECCMD ) + else if ( is_debug_execcmd() ) { CMD * cmd = ( CMD * )t->cmds; out_printf( "Delaying %s %s: %d targets not ready\n", object_str( cmd->rule->name ), object_str( t->boundname ), cmd->asynccnt ); @@ -569,8 +569,8 @@ static void make1c( state const * const pState ) /* Increment the jobs running counter. */ ++cmdsrunning; - if ( ( globs.jobs == 1 ) && ( DEBUG_MAKEQ || - ( DEBUG_MAKE && !( cmd->rule->actions->flags & RULE_QUIETLY ) ) ) ) + if ( ( globs.jobs == 1 ) && ( is_debug_makeq() || + ( is_debug_make() && !( cmd->rule->actions->flags & RULE_QUIETLY ) ) ) ) { OBJECT * action = cmd->rule->name; OBJECT * target = list_front( lol_get( (LOL *)&cmd->args, 0 ) ); @@ -578,7 +578,7 @@ static void make1c( state const * const pState ) out_printf( "%s %s\n", object_str( action ), object_str( target ) ); /* Print out the command executed if given -d+2. */ - if ( DEBUG_EXEC ) + if ( is_debug_exec() ) { out_puts( cmd->buf->value ); out_putc( '\n' ); @@ -925,8 +925,8 @@ static void make1c_closure t->status = EXEC_CMD_OK; } - if ( DEBUG_MAKEQ || - ( DEBUG_MAKE && !( cmd->rule->actions->flags & RULE_QUIETLY ) ) ) + if ( is_debug_makeq() || + ( is_debug_make() && !( cmd->rule->actions->flags & RULE_QUIETLY ) ) ) { rule_name = object_str( cmd->rule->name ); target_name = object_str( list_front( lol_get( (LOL *)&cmd->args, 0 ) ) @@ -949,7 +949,7 @@ static void make1c_closure if ( !globs.noexec ) { call_timing_rule( t, time ); - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) out_printf( "%f sec system; %f sec user; %f sec clock\n", time->system, time->user, timestamp_delta_seconds(&time->start, &time->end) ); @@ -959,10 +959,10 @@ static void make1c_closure } /* Print command text on failure. */ - if ( t->status == EXEC_CMD_FAIL && DEBUG_MAKE && + if ( t->status == EXEC_CMD_FAIL && is_debug_make() && ! ( t->flags & T_FLAG_FAIL_EXPECTED ) ) { - if ( !DEBUG_EXEC ) + if ( !is_debug_exec() ) out_printf( "%s\n", cmd->buf->value ); out_printf( "...failed %s ", object_str( cmd->rule->name ) ); @@ -1041,7 +1041,7 @@ static void push_cmds( CMDLIST * cmds, int32_t status ) first_target->cmds = (char *)next_cmd; push_state( &state_stack, first_target, NULL, T_STATE_MAKE1C ); } - else if ( DEBUG_EXECCMD ) + else if ( is_debug_execcmd() ) { TARGET * first_target = bindtarget( list_front( lol_get( &next_cmd->args, 0 ) ) ); out_printf( "Delaying %s %s: %d targets not ready\n", object_str( next_cmd->rule->name ), object_str( first_target->boundname ), next_cmd->asynccnt ); @@ -1494,7 +1494,7 @@ static int32_t cmd_sem_lock( TARGET * t ) { if ( iter->target->asynccnt > 0 ) { - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) out_printf( "SEM: %s is busy, delaying launch of %s\n", object_str( iter->target->name ), object_str( t->name ) ); targetentry( iter->target->parents, t ); @@ -1505,7 +1505,7 @@ static int32_t cmd_sem_lock( TARGET * t ) for ( iter = cmd->lock; iter; iter = iter->next.get() ) { ++iter->target->asynccnt; - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) out_printf( "SEM: %s now used by %s\n", object_str( iter->target->name ), object_str( t->name ) ); } @@ -1524,7 +1524,7 @@ static void cmd_sem_unlock( TARGET * t ) /* Release the semaphores. */ for ( iter = cmd->unlock.get(); iter; iter = iter->next.get() ) { - if ( DEBUG_EXECCMD ) + if ( is_debug_execcmd() ) out_printf( "SEM: %s is now free\n", object_str( iter->target->name ) ); --iter->target->asynccnt; diff --git a/src/engine/mod_args.cpp b/src/engine/mod_args.cpp new file mode 100644 index 000000000..0e0171694 --- /dev/null +++ b/src/engine/mod_args.cpp @@ -0,0 +1,145 @@ +/* +Copyright 2024 René Ferdinand Rivera Morell +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) +*/ + +#include "mod_args.h" + +#include "jam.h" +#include "lists.h" +#include "output.h" +#include "startup.h" +#include "value.h" + +#include +#include +#include +#include +#include + +namespace b2 { namespace args { + +namespace { +struct args_reg +{ + static args_reg & ref() + { + static args_reg r; + return r; + } + + lyra::cli cli; + std::unique_ptr result; + std::unordered_map> options; + std::vector args; + bool need_reparse = true; + + args_reg() + { + cli.style_print_short_first(); + } + + void set_args(int argc, char ** argv) + { + args.clear(); + for (int i = 0; i < argc; ++i) + { + args.emplace_back(argv[i]); + } + } + + void reparse() + { + need_reparse = false; + // We can call this multiple times. Hence we go back to a clean state + // on the collected values to get reproducible parsing. + result.reset(); + for (auto & o : options) + { + o.second->reset(); + } + + result.reset(new lyra::parse_result( + cli.parse(lyra::args(args.begin(), args.end())))); + } + + void add_opt(const value_ref & name, + list_cref opts, + const value_ref & help, + list_cref flags) + { + if (options.count(name) > 0) return; + need_reparse = true; + bool is_flag = false; + for (auto f : flags) + { + if (f->equal_to(*value_ref("flag"))) is_flag = true; + } + std::shared_ptr values = std::make_shared(); + std::unique_ptr arg; + if (is_flag) + arg.reset(new lyra::opt([values](bool v) { + values->push_back("true"); + })); + else + arg.reset(new lyra::opt( + [values](const std::string & v) { values->push_back(v); }, + name->str())); + arg->help(help); + for (auto opt : opts) + { + arg->name(opt->str()); + } + cli.add_argument(*arg); + options[name->str()] = values; + } + + list_ref get_opt(const value_ref & name) + { + if (need_reparse) reparse(); + if (options.count(name->str()) > 0) return *options[name->str()]; + if (name == "help") + return globs.display_help ? list_ref("true") : list_ref(); + if (name == "debug-configuration") + return globs.debug_configuration ? list_ref("true") : list_ref(); + return {}; + } +}; +} // namespace + +void add_arg( + const value_ref & name, list_cref opts, const value_ref & help, list_cref flags) +{ + args_reg::ref().add_opt(name, opts, help, flags); +} + +list_ref get_arg(const value_ref & name) +{ + return args_reg::ref().get_opt(name); +} + +void set_args(int argc, char ** argv) { args_reg::ref().set_args(argc, argv); } + +lyra::cli & lyra_cli() { return args_reg::ref().cli; } + +void process_args(bool silent) +{ + args_reg::ref().reparse(); + if (!silent && globs.display_help) + { + std::ostringstream out; + out << args_reg::ref().cli; + err_puts(out.str().c_str()); + b2::clean_exit(EXITOK); + } +} + +const char * args_module::init_code = R"jam( +rule __test__ ( ) +{ + import assert ; +} +)jam"; + +}} // namespace b2::args diff --git a/src/engine/mod_args.h b/src/engine/mod_args.h new file mode 100644 index 000000000..b6877a73d --- /dev/null +++ b/src/engine/mod_args.h @@ -0,0 +1,97 @@ +/* +Copyright 2025 René Ferdinand Rivera Morell +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) +*/ + +#ifndef B2_MOD_ARGS_H +#define B2_MOD_ARGS_H + +#include "bind.h" +#include "ext_bfgroup_lyra.h" +#include "lists.h" +#include "value.h" + +/* tag::reference[] + +[[b2.reference.modules.args]] += `args` module. + +end::reference[] */ + +namespace b2 { namespace args { + +/* tag::reference[] + +== `b2::args::add_arg` + +==== +[horizontal] +Jam:: `rule add-arg ( name : opts + : help : flags * )` +{CPP}:: `void add_arg(const value_ref & name, list_cref opts, const value_ref & +help, list_cref flags);` +==== + +Declares a new command line argument option to accept. When specified the +argument is then available for access with `get-arg`. The `name` is a symbolic +tag, used for both retrieving the option value and for the help (`-h`) +displayed. The `opts` is a set of option names that are accepted for this +argument. Both short and long variations are valid for `opts`. The `help` is +a description of the argument printed out in the help output (`-h`). The `flags` +value can indicate definition characteristics for the argument. Accepted `flags` +are: + +`flag`:: Declares this as an option that has no value. For example `--version` +as opposed to `--prefix=/some/path`. + +end::reference[] */ +void add_arg(const value_ref & name, + list_cref opts, + const value_ref & help, + list_cref flags); + +/* tag::reference[] + +== `b2::args::get_arg` + +==== +[horizontal] +Jam:: `rule get-arg ( name )` +{CPP}:: `list_ref get_arg(value_ref name);` +==== + +Retrieve the value of a previous specified command line argument. + +end::reference[] */ +list_ref get_arg(const value_ref & name); + +void set_args(int argc, char ** argv); +lyra::cli & lyra_cli(); +void process_args(bool silent = false); + +struct args_module : b2::bind::module_ +{ + const char * module_name = "args"; + static const char * init_code; + + template + void def(Binder & binder) + { + binder + .def(&add_arg, "add-arg", + "name" * _1 | "opts" * _1n | "help" * _1 | "flags" * _n) + .def(&get_arg, "get-arg", "name" * _1); + binder.eval(init_code); + binder.loaded(); + } +}; + +template +struct declaration_ +{ + declaration_() { SubType::declare_args(); } +}; + +}} // namespace b2::args + +#endif diff --git a/src/engine/mod_command_db.cpp b/src/engine/mod_command_db.cpp index d0ba30bb1..7ce431f7f 100644 --- a/src/engine/mod_command_db.cpp +++ b/src/engine/mod_command_db.cpp @@ -10,6 +10,7 @@ Distributed under the Boost Software License, Version 1.0. #include "cwd.h" #include "events.h" #include "lists.h" +#include "mod_args.h" #include "mod_db.h" #include "output.h" #include "pathsys.h" @@ -55,9 +56,9 @@ struct database prop_db.reset(new property_db); // Default to all targets and no regular action output. - ++globs.noexec; + globs.noexec = true; for (int i = 0; i < DEBUG_MAX; ++i) globs.debug[i] = 0; - ++anyhow; + globs.anyhow = true; // Events to track the commands and exit to generate the output. add_event_callback(event_tag::pre_exec_cmd, @@ -144,26 +145,32 @@ struct database } }; -void declare_args(lyra::cli & cli) -{ - cli |= lyra::opt( - [](const std::string & f) { database::get().set_output_format(f); }, - "format") - .name("--command-database") - .help("Output a compile commands database as format."); - cli |= lyra::opt( - [](const std::string & f) { database::get().set_output_filename(f); }, - "filename") - .name("--command-database-out") - .help( - "Filename to output the command database to. " - "A relative path for the filename is rooted to the project " - "build-dir."); -} - void set_output_dir(value_ref dirname) { database::get().output_directory = dirname; } }} // namespace b2::command_db + +void b2::command_db_module::declare_args() +{ + b2::args::lyra_cli() + |= lyra::opt( + [](const std::string & f) { + command_db::database::get().set_output_format(f); + }, + "format") + .name("--command-database") + .help("Output a compile commands database as format."); + b2::args::lyra_cli() + |= lyra::opt( + [](const std::string & f) { + command_db::database::get().set_output_filename(f); + }, + "filename") + .name("--command-database-out") + .help( + "Filename to output the command database to. " + "A relative path for the filename is rooted to the project " + "build-dir."); +} diff --git a/src/engine/mod_command_db.h b/src/engine/mod_command_db.h index 37947ef4b..453bc04b3 100644 --- a/src/engine/mod_command_db.h +++ b/src/engine/mod_command_db.h @@ -1,5 +1,5 @@ /* -Copyright 2024 René Ferdinand Rivera Morell +Copyright 2025 René Ferdinand Rivera Morell Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) */ @@ -10,6 +10,7 @@ Distributed under the Boost Software License, Version 1.0. #include "config.h" #include "bind.h" +#include "mod_args.h" #include "value.h" namespace lyra { @@ -20,13 +21,15 @@ namespace b2 { namespace command_db { -void declare_args(lyra::cli &); +// void declare_args(lyra::cli &); void set_output_dir(value_ref dirname); } // namespace command_db -struct command_db_module : b2::bind::module_ +struct command_db_module + : b2::bind::module_ + , b2::args::declaration_ { const char * module_name = "command-db"; @@ -37,6 +40,8 @@ struct command_db_module : b2::bind::module_ &command_db::set_output_dir, "set-output-dir", ("dirname" * _1)); binder.loaded(); } + + static void declare_args(); }; } // namespace b2 diff --git a/src/engine/modules.cpp b/src/engine/modules.cpp index 71cb502c5..d250ee1bc 100644 --- a/src/engine/modules.cpp +++ b/src/engine/modules.cpp @@ -202,7 +202,7 @@ static void stat_module( void * xmodule, void * data ) { module_t *m = (module_t *)xmodule; - if ( DEBUG_MEM || DEBUG_PROFILE ) + if ( is_debug_mem() || is_debug_profile() ) { struct hash * class_info = (struct hash *)data; if ( m->class_module ) @@ -259,7 +259,7 @@ void modules_done() { if ( module_hash ) { - if ( DEBUG_MEM || DEBUG_PROFILE ) + if ( is_debug_mem() || is_debug_profile() ) { struct hash * class_hash = hashinit( sizeof( struct module_stats ), "object info" ); hashenumerate( module_hash, stat_module, (void *)class_hash ); diff --git a/src/engine/option.cpp b/src/engine/option.cpp deleted file mode 100644 index fa9246b02..000000000 --- a/src/engine/option.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 1993, 1995 Christopher Seiwald. - * - * This file is part of Jam - see jam.c for Copyright information. - */ - -# include "jam.h" -# include "option.h" - -/* - * option.c - command line option processing - * - * {o >o - * \<>) "Process command line options as defined in . - * Return the number of argv[] elements used up by options, - * or -1 if an invalid option flag was given or an argument - * was supplied for an option that does not require one." - */ - -int getoptions( int argc, char * * argv, const char * opts, bjam_option * optv ) -{ - int i; - int optc = N_OPTS; - - memset( (char *)optv, '\0', sizeof( *optv ) * N_OPTS ); - - for ( i = 0; i < argc; ++i ) - { - char *arg; - - if ( ( argv[ i ][ 0 ] != '-' ) || - ( ( argv[ i ][ 1 ] != '-' ) && !isalpha( argv[ i ][ 1 ] ) ) ) - continue; - - if ( !optc-- ) - { - printf( "too many options (%d max)\n", N_OPTS ); - return -1; - } - - for ( arg = &argv[ i ][ 1 ]; *arg; ++arg ) - { - const char * f; - - for ( f = opts; *f; ++f ) - if ( *f == *arg ) - break; - - if ( !*f ) - { - printf( "Invalid option: -%c\n", *arg ); - return -1; - } - - optv->flag = *f; - - if ( f[ 1 ] != ':' ) - { - optv++->val = (char *)"true"; - } - else if ( arg[ 1 ] ) - { - optv++->val = &arg[1]; - break; - } - else if ( ++i < argc ) - { - optv++->val = argv[ i ]; - break; - } - else - { - printf( "option: -%c needs argument\n", *f ); - return -1; - } - } - } - - return i; -} - - -/* - * Name: getoptval() - find an option given its character. - */ - -char * getoptval( bjam_option * optv, char opt, int subopt ) -{ - int i; - for ( i = 0; i < N_OPTS; ++i, ++optv ) - if ( ( optv->flag == opt ) && !subopt-- ) - return optv->val; - return 0; -} diff --git a/src/engine/option.h b/src/engine/option.h deleted file mode 100644 index df3063105..000000000 --- a/src/engine/option.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 1993, 1995 Christopher Seiwald. - * - * This file is part of Jam - see jam.c for Copyright information. - */ - -/* - * option.h - command line option processing - * - * {o >o - * \ -) "Command line option." - */ - -#include "config.h" - -typedef struct bjam_option -{ - char flag; /* filled in by getoption() */ - char * val; /* set to random address if true */ -} bjam_option; - -#define N_OPTS 256 - -int getoptions( int argc, char * * argv, const char * opts, bjam_option * optv ); -char * getoptval( bjam_option * optv, char opt, int subopt ); diff --git a/src/engine/output.cpp b/src/engine/output.cpp index d4ab5c5a8..ad86c19fe 100644 --- a/src/engine/output.cpp +++ b/src/engine/output.cpp @@ -120,7 +120,7 @@ void out_action out_printf( "%s %s\n", action, target ); /* Print out the command executed if given -d+2. */ - if ( DEBUG_EXEC ) + if ( is_debug_exec() ) { out_puts( command ); out_putc( '\n' ); diff --git a/src/engine/scan.cpp b/src/engine/scan.cpp index c7f2b16e3..eca4234b4 100644 --- a/src/engine/scan.cpp +++ b/src/engine/scan.cpp @@ -763,7 +763,7 @@ int yylex() do_token_warning(); } - if ( DEBUG_SCAN ) + if ( is_debug_scan() ) out_printf( "scan %s\n", symdump( &yylval ).c_str() ); return yylval.type; diff --git a/src/engine/search.cpp b/src/engine/search.cpp index 2c432f5fc..4e9cf5cc5 100644 --- a/src/engine/search.cpp +++ b/src/engine/search.cpp @@ -105,7 +105,7 @@ void set_explicit_binding( OBJECT * target, OBJECT * locate ) path_build( f, buf ); boundname = object_new( buf->value ); - if ( DEBUG_SEARCH ) + if ( is_debug_search() ) out_printf( "explicit locate %s: %s\n", object_str( target ), buf->value ); string_free( buf ); key = path_as_key( boundname ); @@ -171,7 +171,7 @@ OBJECT * search( OBJECT * target, timestamp * const time, path_build( f, buf ); - if ( DEBUG_SEARCH ) + if ( is_debug_search() ) out_printf( "locate %s: %s\n", object_str( target ), buf->value ); key = object_new( buf->value ); @@ -197,7 +197,7 @@ OBJECT * search( OBJECT * target, timestamp * const time, string_truncate( buf, 0 ); path_build( f, buf ); - if ( DEBUG_SEARCH ) + if ( is_debug_search() ) out_printf( "search %s: %s\n", object_str( target ), buf->value ); test_path = object_new( buf->value ); @@ -208,7 +208,7 @@ OBJECT * search( OBJECT * target, timestamp * const time, if ( ( ba = (BINDING *)hash_find( explicit_bindings, key ) ) ) { - if ( DEBUG_SEARCH ) + if ( is_debug_search() ) out_printf(" search %s: found explicitly located target %s\n", object_str( target ), object_str( ba->target ) ); if ( another_target ) @@ -244,7 +244,7 @@ OBJECT * search( OBJECT * target, timestamp * const time, string_truncate( buf, 0 ); path_build( f, buf ); - if ( DEBUG_SEARCH ) + if ( is_debug_search() ) out_printf( "search %s: %s\n", object_str( target ), buf->value ); key = object_new( buf->value ); diff --git a/src/engine/startup.cpp b/src/engine/startup.cpp index 3869811c1..c4f766b99 100644 --- a/src/engine/startup.cpp +++ b/src/engine/startup.cpp @@ -10,6 +10,7 @@ Distributed under the Boost Software License, Version 1.0. #include "cwd.h" #include "filesys.h" #include "frames.h" +#include "mod_args.h" #include "mod_jam_modules.h" #include "modules.h" #include "object.h" @@ -51,17 +52,14 @@ LIST * b2::startup::builtin_boost_build(FRAME * frame, int flags) { // Do nothing, but keep the rule, for backwards compatability. // But do record the path passed in as a fallback to the loading. - b2::jam::variable(".boost-build-dir") - = b2::list_ref(lol_get(frame->args, 0)); + b2::jam::variable(".boost-build-dir") = b2::list_ref(lol_get(frame->args, 0)); return L0; } extern char const * saved_argv0; -void bootstrap_dirscan(void * dirs, - OBJECT * path, - int found, - timestamp const * const) +void bootstrap_dirscan( + void * dirs, OBJECT * path, int found, timestamp const * const) { if (file_is_file(path) == 1) return; _pathname p(object_str(path)); @@ -72,18 +70,6 @@ void bootstrap_dirscan(void * dirs, bool b2::startup::bootstrap(FRAME * frame) { - b2::value_ref opt_debug_configuration { "--debug-configuration" }; - b2::jam::variable dot_OPTION__debug_configuration { ".OPTION", - "debug-configration" }; - for (auto arg : *b2::jam::variable("ARGV")) - { - if (opt_debug_configuration == arg) - { - dot_OPTION__debug_configuration = "true"; - break; - } - } - // We use the executable path as a root for searches. char * b2_exe_path_pchar = executable_path(saved_argv0); const std::string b2_exe_path { b2_exe_path_pchar }; @@ -162,10 +148,10 @@ bool b2::startup::bootstrap(FRAME * frame) } // Show where we found it, if asked. - if (!b2_file_path.empty() && dot_OPTION__debug_configuration) + if (!b2_file_path.empty() + && !b2::args::get_arg("debug-configuration").empty()) { - out_printf( - "notice: found boost-build.jam at %s\n", b2_file_path.c_str()); + out_printf("notice: found boost-build.jam at %s\n", b2_file_path.c_str()); } // Load the boost-build file if we find it for backwards compatability. We @@ -191,8 +177,8 @@ bool b2::startup::bootstrap(FRAME * frame) // Check various locations relative to executable. if (buildsystem_file.empty()) { - const char * dirs[] = { - // Check relative to the executable for portable install location. + const char * dirs[] = { // Check relative to the executable for portable + // install location. ".b2/", // Check relative to the exec for system install location. "../share/b2/", @@ -246,8 +232,7 @@ bool b2::startup::bootstrap(FRAME * frame) // Last resort, search in the directory referenced by the boost-build rule. if (buildsystem_file.empty()) { - b2::list_cref dot_boost_build_dir - = b2::jam::variable(".boost-build-dir"); + b2::list_cref dot_boost_build_dir = b2::jam::variable(".boost-build-dir"); if (!dot_boost_build_dir.empty()) { std::string dir = b2::value_ref(*dot_boost_build_dir.begin()); @@ -275,7 +260,8 @@ bool b2::startup::bootstrap(FRAME * frame) } // Show where we found the bootstrap, if asked. - if (!buildsystem_file.empty() && dot_OPTION__debug_configuration) + if (!buildsystem_file.empty() + && !b2::args::get_arg("debug-configuration").empty()) { out_printf("notice: loading B2 from %s\n", buildsystem_file.c_str()); } @@ -284,11 +270,10 @@ bool b2::startup::bootstrap(FRAME * frame) // parse_file(b2::value_ref { buildsystem_file }, frame); // Add any subdirs to the build-system.jam to search for the jam files. - std::string buildsystem_dir - = b2::paths::normalize(buildsystem_file + "/.."); + std::string buildsystem_dir = b2::paths::normalize(buildsystem_file + "/.."); std::vector buildsystem_subdirs; - file_dirscan(value_ref(buildsystem_dir), &bootstrap_dirscan, - &buildsystem_subdirs); + file_dirscan( + value_ref(buildsystem_dir), &bootstrap_dirscan, &buildsystem_subdirs); b2::jam::variable boost_build_path_v("BOOST_BUILD_PATH"); for (auto subdir : buildsystem_subdirs) boost_build_path_v += subdir; boost_build_path_v += buildsystem_dir; diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 4ad7e2e08..13bcbba54 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -201,14 +201,14 @@ LIST * var_get( struct module_t * module, OBJECT * symbol ) if ( ( n = module_get_fixed_var( module, symbol ) ) != -1 ) { - if ( DEBUG_VARGET ) + if ( is_debug_varget() ) var_dump( symbol, module->fixed_variables[ n ], "get" ); result = module->fixed_variables[ n ]; } else if ( module->variables && ( v = (VARIABLE *)hash_find( module->variables, symbol ) ) ) { - if ( DEBUG_VARGET ) + if ( is_debug_varget() ) var_dump( v->symbol, v->value, "get" ); result = v->value; } @@ -252,7 +252,7 @@ LIST * var_get( struct module_t * module, OBJECT * symbol ) if ( module->variables && ( v = (VARIABLE *)hash_find( module->variables, symbol ) ) ) { - if ( DEBUG_VARGET ) + if ( is_debug_varget() ) var_dump( v->symbol, v->value, "get" ); result = v->value; } @@ -295,7 +295,7 @@ void var_set( struct module_t * module, OBJECT * symbol, LIST * value, int flag { LIST * * v = var_enter( module, symbol ); - if ( DEBUG_VARSET ) + if ( is_debug_varset() ) var_dump( symbol, value, "set" ); switch ( flag ) @@ -327,7 +327,7 @@ LIST * var_swap( struct module_t * module, OBJECT * symbol, LIST * value ) { LIST * * v = var_enter( module, symbol ); LIST * oldvalue = *v; - if ( DEBUG_VARSET ) + if ( is_debug_varset() ) var_dump( symbol, value, "set" ); *v = value; return oldvalue; diff --git a/src/tools/boostbook.jam b/src/tools/boostbook.jam index f7644e848..6f1478d39 100644 --- a/src/tools/boostbook.jam +++ b/src/tools/boostbook.jam @@ -25,6 +25,7 @@ # is an implicit feature, so for example, typing pdf on the command # line is a short-cut for format=pdf. +import args ; import build-system ; import "class" : new ; import common ; @@ -51,8 +52,7 @@ import xsltproc ; project.initialize $(__name__) ; project boostbook ; -.debug-configuration = [ MATCH ^(--debug-configuration)$ : [ modules.peek : ARGV - ] ] ; +.debug-configuration = [ args.get-arg debug-configuration ] ; feature.feature format : html xhtml htmlhelp onehtml man pdf ps docbook fo tests none diff --git a/src/tools/bzip2.jam b/src/tools/bzip2.jam index 49a0425c5..433dd972e 100644 --- a/src/tools/bzip2.jam +++ b/src/tools/bzip2.jam @@ -11,6 +11,7 @@ # # /bzip2//bzip2 -- The bzip library +import args ; import project ; import ac ; import errors ; @@ -34,10 +35,7 @@ sources = blocksort.c bzlib.c compress.c crctable.c library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the bzip library. # diff --git a/src/tools/clang-darwin.jam b/src/tools/clang-darwin.jam index 61a7551b7..b581e750d 100644 --- a/src/tools/clang-darwin.jam +++ b/src/tools/clang-darwin.jam @@ -5,6 +5,7 @@ # (See accompanying file LICENSE.txt # or copy at https://www.bfgroup.xyz/b2/LICENSE.txt) +import args ; import clang ; import feature : feature ; import os ; @@ -30,10 +31,7 @@ toolset.inherit-flags clang-darwin : clang-linux x86/64 ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Initializes the clang-darwin toolset # version in optional diff --git a/src/tools/clang-linux.jam b/src/tools/clang-linux.jam index 41d03e9da..ce270c584 100644 --- a/src/tools/clang-linux.jam +++ b/src/tools/clang-linux.jam @@ -17,11 +17,11 @@ options as link:#b2.reference.tools.compiler.gcc[`gcc`] toolset. |# # end::doc[] +import args ; import common ; import toolset ; import feature ; import toolset : flags ; - import clang ; import gcc ; import common ; @@ -55,9 +55,7 @@ toolset.inherit-flags clang-linux : gcc : INCLUDE-GCH ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] { - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; rule init ( version ? : command * : options * ) { command = [ common.find-compiler clang-linux : clang++ : $(version) : $(command) ] ; diff --git a/src/tools/clang-vxworks.jam b/src/tools/clang-vxworks.jam index 432c3090e..c12178601 100644 --- a/src/tools/clang-vxworks.jam +++ b/src/tools/clang-vxworks.jam @@ -3,6 +3,7 @@ # (See accompanying file LICENSE.txt # or copy at https://www.bfgroup.xyz/b2/LICENSE.txt) +import args ; import clang ; import feature : feature get-values ; import os ; @@ -27,10 +28,8 @@ toolset.inherit-generators clang-vxworks toolset.inherit-rules clang-vxworks : clang-linux ; toolset.inherit-flags clang-vxworks : clang-linux ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; + # Initializes the clang-vxworks toolset # version in optional # compile and link options allow you to specify addition command line options for each version diff --git a/src/tools/clang-win.jam b/src/tools/clang-win.jam index 708aa1d4e..0c1c415bf 100644 --- a/src/tools/clang-win.jam +++ b/src/tools/clang-win.jam @@ -15,6 +15,7 @@ options as link:#b2.reference.tools.compiler.msvc[`msvc`] toolset. |# # end::doc[] +import args ; import common ; import errors ; import feature ; @@ -34,7 +35,7 @@ toolset.inherit-flags clang-win : msvc : : YLOPTION ; toolset.inherit-rules clang-win : msvc ; -if [ MATCH (--debug-(clang-(win-)?)?configuration) : [ modules.peek : ARGV ] ] +if [ args.get-arg debug-configuration ] { local rule .notice ( messages * ) { diff --git a/src/tools/common.jam b/src/tools/common.jam index 42f2833c9..e2dee93eb 100644 --- a/src/tools/common.jam +++ b/src/tools/common.jam @@ -8,6 +8,7 @@ # Provides actions common to all toolsets, such as creating directories and # removing files. +import args ; import os ; import modules ; import utility ; @@ -21,14 +22,10 @@ import toolset ; import virtual-target ; import numbers ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} -if [ MATCH (--show-configuration) : [ modules.peek : ARGV ] ] -{ - .show-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; + +args.add-arg show-configuration : --show-configuration : "?" ; +.show-configuration = [ args.get-arg show-configuration ] ; # Configurations # diff --git a/src/tools/cray.jam b/src/tools/cray.jam index ba95e9d03..50348f361 100644 --- a/src/tools/cray.jam +++ b/src/tools/cray.jam @@ -51,6 +51,7 @@ # For more information on CCE, search for Cray publication S-2529 on the # Cray publications website (https://pubs.cray.com). +import args ; import "class" : new ; import common ; import feature ; @@ -233,27 +234,10 @@ rule set-cray-feature-defaults ( ) ### Command line options ### -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ +.debug-configuration = [ args.get-arg debug-configuration ] ; - # Check if '--debug-configuration' was passed on the command line. This is - # inspired by 'common.jam' and other modules. - - # Variable names with a '.' prefix are intended to be globals. - # - # Refer to: CONTRIBUTING.adoc - - # The Jam language uses dynamic scoping. Setting '.debug-configuration' in - # this module influences the behavior of methods called from this module. - - .debug-configuration = true ; -} - -if [ MATCH (--debug-driver) : [ modules.peek : ARGV ] ] -{ - - .debug-driver = true ; -} +args.add-arg debug-driver : --debug-driver : "?" ; +.debug-driver = [ args.get-arg debug-driver ] ; ### ### Features diff --git a/src/tools/cw-config.jam b/src/tools/cw-config.jam index a84008d8f..d4f925602 100644 --- a/src/tools/cw-config.jam +++ b/src/tools/cw-config.jam @@ -4,6 +4,7 @@ # Automatic configuration for CodeWarrior toolset. To use, just import this module. +import args ; import os ; import toolset : using ; @@ -24,7 +25,7 @@ if [ os.name ] = NT if $(cw-path) { - if --debug-configuration in [ modules.peek : ARGV ] + if [ args.get-arg debug-configuration ] { ECHO "notice:" using cw ":" $(cw-version) ":" "$(cw-path)\\Other Metrowerks Tools\\Command Line Tools\\mwcc.exe" ; } diff --git a/src/tools/cw.jam b/src/tools/cw.jam index dc80fbb8b..fb99a0466 100644 --- a/src/tools/cw.jam +++ b/src/tools/cw.jam @@ -71,11 +71,9 @@ import feature : feature get-values ; import path ; import sequence : unique ; import common ; +import args ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; feature.extend toolset : cw ; diff --git a/src/tools/darwin.jam b/src/tools/darwin.jam index 01f44f32f..793482ddb 100644 --- a/src/tools/darwin.jam +++ b/src/tools/darwin.jam @@ -20,6 +20,7 @@ import version ; import property-set ; import regex ; import errors ; +import args ; ## Use a framework. feature framework : : free ; @@ -37,10 +38,7 @@ feature force-load : : free dependency incidental ; _ = " " ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; feature.extend toolset : darwin ; import gcc ; diff --git a/src/tools/doxygen.jam b/src/tools/doxygen.jam index bf44bef59..fc5ae9837 100644 --- a/src/tools/doxygen.jam +++ b/src/tools/doxygen.jam @@ -18,6 +18,7 @@ # with the Doxygen html files, and a .html file redirecting to # that directory. +import args ; import alias ; import boostbook ; import "class" : new ; @@ -164,7 +165,7 @@ local rule modify-config ( ) local rule check-doxygen ( ) { - if --debug-configuration in [ modules.peek : ARGV ] + if [ args.get-arg debug-configuration ] { ECHO "notice:" using doxygen ":" $(.doxygen) ; } diff --git a/src/tools/embarcadero.jam b/src/tools/embarcadero.jam index 57aad059a..2baf29e7c 100644 --- a/src/tools/embarcadero.jam +++ b/src/tools/embarcadero.jam @@ -137,6 +137,7 @@ import clang ; import gcc ; import generators ; import errors ; +import args ; feature.extend toolset : embarcadero ; @@ -179,9 +180,7 @@ toolset.inherit-flags embarcadero latest/iso ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] { - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; rule init ( version ? : command * : options * ) { diff --git a/src/tools/emscripten.jam b/src/tools/emscripten.jam index fa758e2c8..0ed289fae 100644 --- a/src/tools/emscripten.jam +++ b/src/tools/emscripten.jam @@ -37,6 +37,7 @@ import toolset ; import common ; import type ; import version ; +import args ; feature.extend toolset : emscripten ; @@ -47,7 +48,7 @@ feature.subfeature debug-symbols : source-map : on : optional propagated ; feature.subfeature exception-handling : demangle-support : on : optional propagated ; feature.subfeature exception-handling : method : js : optional propagated link-incompatible ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] +if [ args.get-arg debug-configuration ] { local rule .debug-configuration ( messages * ) { diff --git a/src/tools/gcc.jam b/src/tools/gcc.jam index e8b1fb513..2b6caed41 100644 --- a/src/tools/gcc.jam +++ b/src/tools/gcc.jam @@ -117,15 +117,13 @@ import type ; import unix ; import virtual-target ; import errors ; +import args ; # Used in actions for multi argument options _ = " " ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; feature.extend toolset : gcc ; diff --git a/src/tools/intel-darwin.jam b/src/tools/intel-darwin.jam index 78bf60b04..c0e5c54c2 100644 --- a/src/tools/intel-darwin.jam +++ b/src/tools/intel-darwin.jam @@ -14,6 +14,7 @@ import common ; import errors ; import generators ; import regex ; +import args ; feature.extend-subfeature toolset intel : platform : darwin ; @@ -36,10 +37,7 @@ toolset.inherit-flags intel-darwin : gcc x86/64 ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Initializes the intel-darwin toolset # version in mandatory diff --git a/src/tools/intel-linux.jam b/src/tools/intel-linux.jam index e5dc3c97c..32cf6a525 100644 --- a/src/tools/intel-linux.jam +++ b/src/tools/intel-linux.jam @@ -8,6 +8,7 @@ import toolset ; import toolset : flags ; +import args ; import common ; import errors ; import feature ; @@ -37,10 +38,7 @@ toolset.inherit-flags intel-linux : gcc off on ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; .home = [ os.home-directories ] ; .home = $(.home[1]) ; diff --git a/src/tools/intel-vxworks.jam b/src/tools/intel-vxworks.jam index 9a9143b7e..bea2849f9 100644 --- a/src/tools/intel-vxworks.jam +++ b/src/tools/intel-vxworks.jam @@ -13,6 +13,7 @@ import common ; import errors ; import generators ; import regex ; +import args ; feature.extend-subfeature toolset intel : platform : vxworks ; @@ -35,10 +36,7 @@ toolset.inherit-flags intel-vxworks : gcc x86/64 ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Initializes the intel-vxworks toolset # version in mandatory diff --git a/src/tools/intel-win.jam b/src/tools/intel-win.jam index 0eedc51e7..aa64e3c3a 100644 --- a/src/tools/intel-win.jam +++ b/src/tools/intel-win.jam @@ -5,7 +5,6 @@ # or copy at https://www.bfgroup.xyz/b2/LICENSE.txt) # Importing common is needed because the rules we inherit here depend on it. -# That is nasty. import common ; import errors ; import feature ; @@ -18,6 +17,7 @@ import generators ; import type ; import path ; import numbers ; +import args ; feature.extend-subfeature toolset intel : platform : win ; @@ -66,7 +66,7 @@ rule init ( version ? : # the compiler version { local msvc-version = [ feature.get-values : $(options) ] ; msvc-version = [ get-msvc-version-from-vc-string $(msvc-version) ] ; - for local v in + for local v in { if [ is-msvc-supported $(v) : $(msvc-version) ] { @@ -294,9 +294,9 @@ local rule configure-really ( version ? : command * : options * : compatibility local default-assembler-intel64 = ml64 ; local default-assembler-ia32_intel64 = ml64 ; local default-assembler-ia32 = "ml -coff" ; - local assembler-flags-intel64 = "-c -Zp4 -Cp -Cx" ; - local assembler-flags-ia32_intel64 = "-c -Zp4 -Cp -Cx" ; - local assembler-flags-ia32 = "-c -Zp4 -Cp -Cx" ; + local assembler-flags-intel64 = "-c -Zp4 -Cp -Cx" ; + local assembler-flags-ia32_intel64 = "-c -Zp4 -Cp -Cx" ; + local assembler-flags-ia32 = "-c -Zp4 -Cp -Cx" ; assembler = [ feature.get-values : $(options) ] ; for local c in $(target_types) @@ -537,10 +537,7 @@ local rule get-msvc-version-from-vc-string ( vc-string ) return $(r[1]) ; } -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Copied from msvc.jam # Supported CPU architectures. diff --git a/src/tools/libjpeg.jam b/src/tools/libjpeg.jam index 55de83f48..70d64a1cc 100644 --- a/src/tools/libjpeg.jam +++ b/src/tools/libjpeg.jam @@ -22,6 +22,7 @@ import modules ; import indirect ; import property ; import property-set ; +import args ; header = jpeglib.h ; @@ -40,10 +41,7 @@ sources = jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the libjpeg library. # diff --git a/src/tools/libpng.jam b/src/tools/libpng.jam index 304140b7f..e5a0db124 100644 --- a/src/tools/libpng.jam +++ b/src/tools/libpng.jam @@ -22,6 +22,7 @@ import modules ; import indirect ; import property ; import property-set ; +import args ; header = png.h ; @@ -35,10 +36,7 @@ sources = png.c pngerror.c pngget.c pngmem.c pngpread.c pngread.c pngrio.c png library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the libpng library. # diff --git a/src/tools/libtiff.jam b/src/tools/libtiff.jam index 7872c16ef..e04b74058 100644 --- a/src/tools/libtiff.jam +++ b/src/tools/libtiff.jam @@ -22,6 +22,7 @@ import modules ; import indirect ; import property ; import property-set ; +import args ; header = tiff.h ; names = tiff ; @@ -34,10 +35,7 @@ sources = tif_aux.c tif_close.c tif_codec.c tif_color.c tif_compress.c tif_dir library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the libtiff library. # diff --git a/src/tools/lzma.jam b/src/tools/lzma.jam index 582054b9c..216aaada8 100644 --- a/src/tools/lzma.jam +++ b/src/tools/lzma.jam @@ -22,6 +22,7 @@ import modules ; import indirect ; import property ; import property-set ; +import args ; header = lzma.h ; # liblzma only needed for VisualC++ builds @@ -29,10 +30,7 @@ names = lzma liblzma ; library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the lzma library. # diff --git a/src/tools/mpi.jam b/src/tools/mpi.jam index 305905581..f65c19f44 100644 --- a/src/tools/mpi.jam +++ b/src/tools/mpi.jam @@ -65,6 +65,7 @@ import testing ; import toolset ; import type ; import path ; +import args ; feature mpi.run-flags : : free incidental ; @@ -72,10 +73,7 @@ feature mpi.run-flags : : free incidental ; project.initialize $(__name__) ; project mpi ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Assuming the first part of the command line is the given prefix # followed by some non-empty value, remove the first argument. Returns diff --git a/src/tools/msvc.jam b/src/tools/msvc.jam index 2f6b91594..b6276bbf4 100644 --- a/src/tools/msvc.jam +++ b/src/tools/msvc.jam @@ -216,6 +216,7 @@ http://blogs.msdn.com/b/vcblog/archive/2014/07/18/using-boost-libraries-in-windo # ################################################################################ +import args ; import "class" : new ; import common ; import feature ; @@ -2112,10 +2113,7 @@ local rule register-configuration ( version : path ? ) # ################################################################################ -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; # Miscellaneous constants. .RM = [ common.rm-command ] ; diff --git a/src/tools/openssl.jam b/src/tools/openssl.jam index 1159d6678..3933cc0e3 100644 --- a/src/tools/openssl.jam +++ b/src/tools/openssl.jam @@ -23,6 +23,7 @@ import indirect ; import os ; import property ; import property-set ; +import args ; header = openssl.h ; ssl_names = ssl ssleay32 ; @@ -30,10 +31,7 @@ crypto_names = crypto libeay32 ; library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the openssl library. # diff --git a/src/tools/package.jam b/src/tools/package.jam index 75925d5e3..001206b40 100644 --- a/src/tools/package.jam +++ b/src/tools/package.jam @@ -40,11 +40,13 @@ import stage ; import targets ; import modules ; import os ; +import args ; feature.feature install-default-prefix : : free incidental ; class package-paths { + import args ; import feature ; import modules ; import option ; @@ -52,10 +54,11 @@ class package-paths import path ; rule __init__ ( default-prefix ) { - local explicit-options = [ MATCH --(prefix|bindir|libdir|includedir|datarootdir)=.* - : [ modules.peek : ARGV ] ] ; - self.has-$(explicit-options) = true ; - if prefix in $(explicit-options) + for local explicit-option in prefix bindir libdir includedir datarootdir + { + self.has-$(explicit-option) = [ args.get-arg $(explicit-option) ] ; + } + if $(self.has-prefix) { # If --prefix is explicitly specified on the command line, # then we need wipe away any settings of libdir/includir that diff --git a/src/tools/python-config.jam b/src/tools/python-config.jam index a2ee025e7..0329d48e0 100644 --- a/src/tools/python-config.jam +++ b/src/tools/python-config.jam @@ -4,6 +4,7 @@ # Automatic configuration for Python tools and librries. To use, just import this module. +import args ; import os ; import toolset : using ; @@ -17,7 +18,7 @@ if [ os.name ] = NT if $(python-path) { - if --debug-configuration in [ modules.peek : ARGV ] + [ args.get-arg debug-configuration ] { ECHO "notice:" using python ":" $(python-version) ":" $(python-path) ; } diff --git a/src/tools/python.jam b/src/tools/python.jam index f8f63f40f..745551324 100644 --- a/src/tools/python.jam +++ b/src/tools/python.jam @@ -35,6 +35,7 @@ import feature ; import set ; import builtin ; import property-set ; +import args ; # Make this module a project. @@ -241,7 +242,7 @@ local rule invokes-cygwin-symlink ( cmd ) local rule debug-message ( message * ) { - if --debug-configuration in [ modules.peek : ARGV ] + if [ args.get-arg debug-configuration ] { ECHO "notice:" "[python-cfg]" $(message) ; } diff --git a/src/tools/qt4.jam b/src/tools/qt4.jam index 0e5c5687d..127047a31 100644 --- a/src/tools/qt4.jam +++ b/src/tools/qt4.jam @@ -63,6 +63,7 @@ import toolset : flags ; import os ; import virtual-target ; import scanner ; +import args ; # Qt3Support control feature # @@ -97,14 +98,11 @@ project qt ; .project = [ project.current ] ; # Helper utils for easy debug output -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = TRUE ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; local rule debug-message ( message * ) { - if $(.debug-configuration) = TRUE + if $(.debug-configuration) { ECHO notice\: "[qt4-cfg]" $(message) ; } diff --git a/src/tools/qt5.jam b/src/tools/qt5.jam index 5fc41153a..9c6c3220a 100644 --- a/src/tools/qt5.jam +++ b/src/tools/qt5.jam @@ -63,6 +63,7 @@ import toolset : flags ; import os ; import virtual-target ; import scanner ; +import args ; # The Qt version used for requirements # Valid are 5.0 or 5.1.0 @@ -80,14 +81,11 @@ project qt5 ; .project = [ project.current ] ; # Helper utils for easy debug output -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = TRUE ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; local rule debug-message ( message * ) { - if $(.debug-configuration) = TRUE + if $(.debug-configuration) { ECHO notice\: "[qt5-cfg]" $(message) ; } diff --git a/src/tools/quickbook-config.jam b/src/tools/quickbook-config.jam index c6c3e7fbc..a6dd831de 100644 --- a/src/tools/quickbook-config.jam +++ b/src/tools/quickbook-config.jam @@ -4,6 +4,7 @@ # Automatic configuration for BoostBook tools. To use, just import this module. +import args ; import os ; import toolset : using ; @@ -21,7 +22,7 @@ if [ os.name ] = NT if $(quickbook-path) { - if --debug-configuration in [ modules.peek : ARGV ] + if [ args.get-arg debug-configuration ] { ECHO "notice:" using quickbook ":" $(quickbook-path) ; } @@ -35,7 +36,7 @@ else if $(quickbook-path) { - if --debug-configuration in [ modules.peek : ARGV ] + if [ args.get-arg debug-configuration ] { ECHO "notice:" using quickbook ":" $(quickbook-path) ; } diff --git a/src/tools/rc.jam b/src/tools/rc.jam index 70fabd453..64ef3d525 100644 --- a/src/tools/rc.jam +++ b/src/tools/rc.jam @@ -9,16 +9,14 @@ # License Version 1.0. (See accompanying file LICENSE.txt or # https://www.bfgroup.xyz/b2/LICENSE.txt) +import args ; import generators ; import feature ; import scanner ; import toolset : flags ; import type ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; type.register RC : rc ; diff --git a/src/tools/stage.jam b/src/tools/stage.jam index fea9df064..4a3275d82 100644 --- a/src/tools/stage.jam +++ b/src/tools/stage.jam @@ -16,6 +16,7 @@ single location. |# # end::doc[] +import args ; import "class" : new ; import feature ; import generators ; @@ -29,6 +30,18 @@ import types/register ; import virtual-target ; +args.add-arg prefix : --prefix + : "Install to a tree rooted at prefix." ; +args.add-arg bindir : --bindir + : "Install binaries to this directory. [default: /bin]" ; +args.add-arg libdir : --libdir + : "Install libraries to this directory. [default: /lib]" ; +args.add-arg includedir : --includedir + : "install include files to this directory. [default: /include]" ; +args.add-arg datarootdir : --datarootdir + : "Install shared data files to this directory. [default: /share]" ; + + feature.feature : off on : incidental ; feature.feature : : free incidental ; feature.feature : : free path ; diff --git a/src/tools/testing-aux.jam b/src/tools/testing-aux.jam index 30309fbb8..bd0110494 100644 --- a/src/tools/testing-aux.jam +++ b/src/tools/testing-aux.jam @@ -1,3 +1,4 @@ +import args ; import feature ; # This module is imported by testing.py. The definitions here are @@ -179,8 +180,10 @@ else } +args.add-arg verbose-test : --verbose-test + : "Include output of tests when run." ; .VERBOSE_TEST = 0 ; -if --verbose-test in [ modules.peek : ARGV ] +if [ args.get-arg verbose-test ] { .VERBOSE_TEST = 1 ; } diff --git a/src/tools/testing.jam b/src/tools/testing.jam index 2fa60c198..0cffb3883 100644 --- a/src/tools/testing.jam +++ b/src/tools/testing.jam @@ -34,6 +34,7 @@ import alias ; +import args ; import build-system ; import "class" ; import common ; @@ -224,7 +225,8 @@ rule dump-tests } } -if ( --dump-tests in [ modules.peek : ARGV ] ) +args.add-arg dump-tests : --dump-tests : "?" ; +if [ args.get-arg dump-tests ] { IMPORT testing : dump-tests : : testing.dump-tests ; build-system.add-pre-build-hook testing.dump-tests ; @@ -243,7 +245,7 @@ local rule get-library-name ( path ) if $(match1) { return $(match1[2]) ; } else if $(match2) { return $(match2[2]) ; } else if $(match3) { return "" ; } - else if --dump-tests in [ modules.peek : ARGV ] + else if [ args.get-arg dump-tests ] { # The 'run' rule and others might be used outside boost. In that case, # just return the path, since the 'library name' makes no sense. @@ -253,7 +255,7 @@ local rule get-library-name ( path ) # Was an XML dump requested? -.out-xml = [ MATCH --out-xml=(.*) : [ modules.peek : ARGV ] ] ; +.out-xml = [ args.get-arg out-xml ] ; # Takes a target (instance of 'basic-target') and prints @@ -517,7 +519,9 @@ toolset.flags testing.capture-output LAUNCHER ; toolset.uses-features testing.capture-output : ; -if --remove-test-targets in [ modules.peek : ARGV ] +args.add-arg remove-test-targets : --remove-test-targets + : "Delete intermediate test files for successful tests." ; +if [ args.get-arg remove-test-targets ] { feature.set-default preserve-test-targets : off ; } @@ -644,8 +648,10 @@ else .NULL_OUT = [ modules.peek common : NULL_OUT ] ; +args.add-arg verbose-test : --verbose-test + : "Include output of tests when run." ; .VERBOSE_TEST = 0 ; -if --verbose-test in [ modules.peek : ARGV ] +if [ args.get-arg verbose-test ] { .VERBOSE_TEST = 1 ; } diff --git a/src/tools/vmsdecc.jam b/src/tools/vmsdecc.jam index c5969147c..3e9b89cd2 100644 --- a/src/tools/vmsdecc.jam +++ b/src/tools/vmsdecc.jam @@ -18,12 +18,10 @@ import common ; import unix ; import path ; import regex ; +import args ; -if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] -{ - .debug-configuration = true ; -} +.debug-configuration = [ args.get-arg debug-configuration ] ; feature.extend toolset : vmsdecc ; diff --git a/src/tools/xsltproc-config.jam b/src/tools/xsltproc-config.jam index 43f0cf14e..ed5ca31ac 100644 --- a/src/tools/xsltproc-config.jam +++ b/src/tools/xsltproc-config.jam @@ -5,6 +5,7 @@ # Automatic configuration for the xsltproc toolset. To use, just import this # module. +import args ; import os ; import toolset : using ; @@ -28,7 +29,7 @@ local rule locate-executable ( name ) local xsltproc-exe = [ locate-executable xsltproc ] ; if $(xsltproc-exe) { - if --debug-configuration in [ modules.peek : ARGV ] + if [ args.get-arg debug-configuration ] { ECHO notice\: using xsltproc ":" $(xsltproc-exe) ; } diff --git a/src/tools/zlib.jam b/src/tools/zlib.jam index 4d552d043..4eb025413 100644 --- a/src/tools/zlib.jam +++ b/src/tools/zlib.jam @@ -23,6 +23,7 @@ import indirect ; import os ; import property ; import property-set ; +import args ; header = zlib.h ; names = z zlib zll zdll ; @@ -33,10 +34,7 @@ sources = adler32.c compress.c library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; # Initializes the zlib library. # diff --git a/src/tools/zstd.jam b/src/tools/zstd.jam index dd6890bf6..6d751cda7 100644 --- a/src/tools/zstd.jam +++ b/src/tools/zstd.jam @@ -22,6 +22,7 @@ import modules ; import indirect ; import property ; import property-set ; +import args ; header = zstd.h ; # libzstd only needed for VisualC++ builds @@ -30,10 +31,7 @@ names = zstd zstd_static libzstd libzstd_static ; library-id = 0 ; -if --debug-configuration in [ modules.peek : ARGV ] -{ - .debug = true ; -} +.debug = [ args.get-arg debug-configuration ] ; rule init ( version ? diff --git a/src/util/option.jam b/src/util/option.jam index 96adf9d89..5e0f31360 100644 --- a/src/util/option.jam +++ b/src/util/option.jam @@ -1,10 +1,12 @@ # Copyright (c) 2005 Vladimir Prus. +# Copyright René Ferdinand Rivera Morell # # Use, modification and distribution is subject to the Boost Software # License Version 1.0. (See accompanying file LICENSE.txt or # https://www.bfgroup.xyz/b2/LICENSE.txt) import modules ; +import args ; # Set a value for a named option, to be used when not overridden on the command # line. @@ -15,27 +17,20 @@ rule set ( name : value ? ) rule get ( name : default-value ? : implied-value ? ) { - local m = [ MATCH --$(name)=(.*) : [ modules.peek : ARGV ] ] ; - if $(m) + local m = [ args.get-arg $(name) ] ; + if $(m) && $(m) != true { return $(m[1]) ; } - else + if $(m) && $(implied-value) { - m = [ MATCH (--$(name)) : [ modules.peek : ARGV ] ] ; - if $(m) && $(implied-value) - { - return $(implied-value) ; - } - else if $(.option.$(name)) - { - return $(.option.$(name)) ; - } - else - { - return $(default-value) ; - } + return $(implied-value) ; } + if $(.option.$(name)) + { + return $(.option.$(name)) ; + } + return $(default-value) ; } diff --git a/test/core-language/test.jam b/test/core-language/test.jam index bd237d902..19bd35c76 100644 --- a/test/core-language/test.jam +++ b/test/core-language/test.jam @@ -1479,7 +1479,7 @@ if $(NT) for local x in $(param:L) { if ! x in $(found) && - $(x) in "addservices" "policies" "deleted device ids" "software" + $(x) in "addservices" "policies" "deleted device ids" "software" "servicestate" { found += $(x) ; }