Skip to content

long options big mess 🐞 #562

@paolopas

Description

@paolopas

Let's start with a simple empty Jamroot and ask b2 what it can do

[pax@computer long_opts]$ touch Jamroot
[pax@computer long_opts]$ b2 -h
USAGE:
  b2 [[<request>...]] [-?|-h] [-v] [-a] [-n] [-p <x>] [-q] [--keep-going <x>] [-j|--jobs <x>] [-g] [-l <x>] [-m <x>] [-d <x>] [-o <x>] [-t <x>] [-s <x=y>] [-f <x>] [--debug-configuration] [--b2db-internal-debug-handle <h>] [--command-database <format>] [--command-database-out <filename>] [--abbreviate-paths] [--hash] [--debug-generators] [--ignore-toolset-requirements] [--show-configuration] [--project-search <project-search>] [--debug-loading] [--debug-project-search] [--build-dir <build-dir>] [--use-package-manager <use-package-manager>] [--debug-building] [--prefix <prefix>] [--bindir <bindir>] [--libdir <libdir>] [--includedir <includedir>] [--datarootdir <datarootdir>] [--reconfigure] [--out-xml <out-xml>] [--test-config <test-config>] [--config <all-config>] [--site-config <site-config>] [--ignore-site-config] [--user-config <user-config>] [--project-config <project-config>] [--toolset <toolsets>] [--version] [--clean] [--clean-all] [--dump-generators] [--durations <durations>]
...

Well, this certainly isn't the usage we'd all like to see. Typically, when a CLI has too much stuff, options are grouped or ordered to make them seem more logical. This alone deserves an issue.
But let's focus on long options and try to do

[pax@computer long_opts]$ b2 --jobs hello
notice: could not find main target hello
notice: assuming it is a name of file to create.
don't know how to make <e>hello
...found 1 target...
...can't find 1 target...

The problem here isn't that a check wasn't made to ensure a positive value was provided for the option (if only it were), but rather that hello was used as the identifier for a target to update!

You can repeat the experiment with any other long option that accepts a value, for example

[pax@computer long_opts]$ b2 --prefix world
notice: could not find main target world
notice: assuming it is a name of file to create.
don't know how to make <e>world
...found 1 target...
...can't find 1 target...

Fortunately, the problem only occurs with long options, and only when the compact option=value syntax isn't used

[pax@computer long_opts]$ b2 -j hello
...found 1 target...
[pax@computer long_opts]$ b2 --jobs=hello
...found 1 target...

The documentation generally uses the compact syntax (see §4.4.2. Options), but the CLI help uses the expanded notation (without the =). In an era of graphical interfaces, users can't be expected to know the syntax of command-line options, so for those of Generation Z (and later), I must add that both notations should be supported, that is, there should be no surprises whether or not they use the = sign.

This happens because the logic that processes CLI options isn't centralized in one place, namely in mod_args, but there are parts of the build-system that try to replicate it (unsuccessfully). Silly design, sure, but since the value supplied to the option is correctly loaded—that is, args.get-arg returns it—the fact that this value is also used as the target to update causes some rather subtle bugs.

Here's an excerpt from the build-system.jam code responsible for the problem:

    # Parse command line for targets and properties. Note that this requires
    # that all project files already be loaded.
    # FIXME: This is not entirely true. Additional project files may be loaded
    # only later via the project.find() rule when dereferencing encountered
    # target ids containing explicit project references. See what to do about
    # those as such 'lazy loading' may cause problems that are then extremely
    # difficult to debug.
    local build-request = [ build-request.from-command-line $(.argv)
        $(extra-properties) ] ;
    local target-ids = [ $(build-request).get-at 1 ] ;
    local properties = [ $(build-request).get-at 2 ] ;

As you can imagine, it's enough to ensure that the from-command-line doesn't take the values ​​passed to the long options for targets as it does currently; if you look closely, the current implementation of from-command-line, in addition to not considering the long options at all, also makes errors with the short options -m and -p (which it doesn't yet know), as well as incorrectly considering the argument separator -- (which shouldn't apply to b2).

To fix this:

  1. I modified mod_args to ask if a string matches an option for which a value is expected. Currently, the comparison is case-sensitive (as is now in the case for short options, and I don't plan to make any other changes at this time).
  2. I modified the from-command-line to ask mod-args whether it should skip an option value instead of considering it a target.

It seems simple, but if you reread the comment in the code snippet above, you'll realize that this solution, even centralizing all the options logic in mod-args, has the following weakness: all options must be declared (with args.add-arg) before the call to from-command-line, (in build-system.jam for example you should anticipate the declaration of --durations for that,) but it certainly won't be possible to consider those options that might be added later by "lazy-loaded" projects.

I'll conclude by saying that I'm tired of finding bugs everywhere I look.
I had started a review of the build-system, but I had to interrupt it due to the many problems I encountered. This is certainly the most serious, but there are several other things that don't work at all as they should.
And I happen to be talking about long options again, such as:

  • --out-xml which was broken in 2022 with commit d717294, but nowadays trying to use --out-xml means having to deal with 3 different bugs (the one described in this issue, the one due to the indicated commit, and the one fixed by fixed missing JAMUNAME on LINUX #561)
  • --durations which was broken in 2025 with commit dbbf110 for which it is obviously not possible to provide a default value

and I've already fixed these too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions