Why does Swift treat double dash command line arguments differently?

this error stumped me for an embarrassingly long amount of time:

$ swift build --configuration release \
    --Xcxx -I/home/ubuntu/x86_64/swift/usr/lib/swift \
    --Xcxx -I/home/ubuntu/x86_64/swift/usr/lib/swift/Block

error: Missing value for '-c <configuration>'
Help:  -c <configuration>  Build with configuration
Usage: swift build <options>
  See 'build -help' for more information.

huh?

as it turns out, swift build really doesn’t like the double-dashed prefix on -Xcxx.


```text
$ swift build --configuration release \
+   -Xcxx -I/home/ubuntu/x86_64/swift/usr/lib/swift \
+   -Xcxx -I/home/ubuntu/x86_64/swift/usr/lib/swift/Block

why does this matter?

2 Likes

I think it only matters for these compiler flags that you can pass to swift build. I can't say "why" they defined them with a single dash, but all these flags have only single dashes:

  • -Xswiftc
  • -Xcc
  • -Xcxx
  • -Xfrontend

Maybe someone can explain the reasoning for these being defined this way? Not that it bothers me, but curious.

I think it's purely historical convention. Compilers like clang and gcc have long used the -X prefix to indicate that you're passing a flag to an underlying tool—for example, -Xpreprocessor, -Xlinker, -Xassembler. Similarly, clang's driver has -Xclang to pass flags directly to the frontend, and then the Swift driver adopted similar nomenclature with flags like -Xfrontend, -Xcc, and -Xllvm. So the SwiftPM flags are based on that naming pattern, for consistency.

4 Likes

More generally, options with double dashes have been pretty universally treated differently from single dashes since GNU getopt introduced the double dash syntax for “long form” options. But the failure mode here seems unique.

-X is an argument that takes 2 additional arguments, the first one is an enum of which subsystem to pass the second argument to, in this case it's the c++ compiler. And as any shortform argument, it can be packed together with the first argument into for example -Xcxx. The long version for -Xcxx -I/home/ubuntu/x86_64/swift/usr/lib/swift is -X cxx -I -X cxx /home/ubuntu/x86_64/swift/usr/lib/swift

1 Like

this doesn’t explain why --Xcxx interferes with the --configuration mode, though.

If you write swift build --configuration release --Xcxx -Ick (-Ick as in part of -I/home/ubuntu/x86_64/swift/usr/lib/swift/Block), this will produce the same message or even just swift build --configuration release -Ick or swift build --configuration release -ck or swift build --configuration release -c.

But if you write swift build --configuration release --Xcxx then you will get the actual error: error: Unknown option '--Xcxx'. Did you mean '-Xcxx'?

If you write swift build --configuration release -c k then you will get the following error message: error: The value 'k' is invalid for '-c <configuration>'. Please provide one of 'debug' and 'release'.

So there are multiple bugs with handling errors during parsing the options, the main one being that it doesn't stop at the first error, so the error it prints can be completely unrelated. So pretty much the same as the Swift compiler sometimes.

3 Likes

i don't think these options are directly interfering with each other, as you will get the same behavior with this:

$ swift build -/random/stuff/then/a/letter/of/a/real/option/like/c/or/j

error: Missing value for '-c <configuration>'
Help:  -c <configuration>  Build with configuration
Usage: swift build <options>
  See 'build -help' for more information.

and if you swap the 'c' and 'j', then the argument parser seems to think you're specifying a different option:

$ swift build -/random/stuff/then/a/letter/of/a/real/option/like/j/or/c

error: Missing value for '-j <jobs>'
Help:  -j <jobs>  The number of jobs to spawn in parallel during the build process
Usage: swift build <options>
  See 'build -help' for more information.

i think the issue stems from how swift-argument-parser handles single-dash-prefixed options. seems it will scan through them interpreting each value as a potential short-form argument name and then if a known option is found without a corresponding value, you get this type of error. if you specify all expected arguments found in this manner, then it will inform you that the option is unrecognized.

if we replace all valid short-form option names with a 'z' in your original example (i.e. the occurrences of 'h' & 'c'), we get:

$ swift build --configuration release \
    --Xcxx -I/zome/ubuntu/x86_64/swift/usr/lib/swift \
    --Xcxx -I/zome/ubuntu/x86_64/swift/usr/lib/swift/Blozk

error: Unknown option '--Xcxx'. Did you mean '-Xcxx'?
Usage: swift build <options>
  See 'build -help' for more information.

which is presumably the sort of error that would have saved you some time (though then we may not have gotten to enjoy this lovely thread). perhaps there is a feature request to be made against the argument parser library here?

1 Like

Please file a GitHub issue for this bug (ideally with a unit test)

done! Confusing error output when passing a single-dash unknown option that contains some valid short label option names · Issue #709 · apple/swift-argument-parser · GitHub

2 Likes