The tcc options behavior is non trivial, and it's also susceptible to ambiguities - which do exist (but currently are not painful). - Add a script 'optclash' which detects clashes/ambiguities. - Document the parsing behaviour and current clashes/resolutions as comments in libtcc.c .
Unified · Split
| @@ -1571,6 +1571,30 @@ enum { · 1570 unmodified lines | |||
| 1571 | #define TCC_OPTION_HAS_ARG 0x0001 | 1571 | #define TCC_OPTION_HAS_ARG 0x0001 |
| 1572 | #define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ | 1572 | #define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ |
| 1573 | 1573 | ||
| 1574 | /* | ||
| 1575 | * in tcc_options, if opt-string A is a prefix of opt-string B, | ||
| 1576 | * it's un-ambiguous if and only if option A is without TCC_OPTION_HAS_ARG. | ||
| 1577 | * otherwise (A with HAS_ARG), if, for instance, A is FOO and B is FOOBAR, | ||
| 1578 | * then "-FOOBAR" is either A with arg BAR, or B (-FOOBARX too, if B HAS_ARG). | ||
| 1579 | * | ||
| 1580 | * tcc_parse_args searches tcc_options in order, so if ambiguous: | ||
| 1581 | * - if the shorter (A) is earlier: the longer (B) is completely unreachable. | ||
| 1582 | * - else B wins, and A can't be used with adjacent arg if it also matches B. | ||
| 1583 | * | ||
| 1584 | * there are few clashes currently, and the longer is always earlier/reachable. | ||
| 1585 | * when it's ambiguous, shorter-concat-arg is not useful currently. | ||
| 1586 | * the sh(1) script 'optclash' can identifiy clashes (tcc root dir, try "-h"). | ||
| 1587 | * at the time of writing, running './optclash' prints this: | ||
| 1588 | |||
| 1589 | -Wl,... (1642) overrides -W... (1644) | ||
| 1590 | -Wp,... (1643) overrides -W... (1644) | ||
| 1591 | -dumpmachine (1630) overrides -d... (1632) | ||
| 1592 | -dumpversion (1631) overrides -d... (1632) | ||
| 1593 | -dynamiclib (1623) overrides -d... (1632) | ||
| 1594 | -flat_namespace (1624) overrides -f... (1650) | ||
| 1595 | -mfloat-abi... (1647) overrides -m... (1649) | ||
| 1596 | |||
| 1597 | */ | ||
| 1574 | static const TCCOption tcc_options[] = { | 1598 | static const TCCOption tcc_options[] = { |
| 1575 | { "h", TCC_OPTION_HELP, 0 }, | 1599 | { "h", TCC_OPTION_HELP, 0 }, |
| 1576 | { "-help", TCC_OPTION_HELP, 0 }, | 1600 | { "-help", TCC_OPTION_HELP, 0 }, |
| @@ -1808,6 +1832,27 @@ static void args_parser_add_file(TCCState *s, const char* filename, int filetype · 231 unmodified lines | |||
| 1808 | ++s->nb_libraries; | 1832 | ++s->nb_libraries; |
| 1809 | } | 1833 | } |
| 1810 | 1834 | ||
| 1835 | /* parsing is between getopt(3) and getopt_long(3), and permuting-like: | ||
| 1836 | * - an option is 1 or more chars. | ||
| 1837 | * - at most 1 option per arg in argv. | ||
| 1838 | * - an option in argv is "-OPT[...]" (few are --OPT, if OPT is "-..."). | ||
| 1839 | * - optarg is next arg, or adjacent non-empty (no '='. -std=.. is arg "=.."). | ||
| 1840 | * - supports also adjacent-only optarg (typically optional). | ||
| 1841 | * - supports mixed options and operands ("--" is ignored, except with -run). | ||
| 1842 | * - -OPT[...] can be ambiguous, which is resolved using tcc_options's order. | ||
| 1843 | * (see tcc_options for details) | ||
| 1844 | * | ||
| 1845 | * specifically, per arg of argv, in order: | ||
| 1846 | * - if arg begins with '@' and is not exactly "@": process as @listfile. | ||
| 1847 | * - elif arg is exactly "-" or doesn't begin with '-': process as input file. | ||
| 1848 | * - if -run... is already set: also stop, arg... become argv of run_main. | ||
| 1849 | * - elif arg is "--": | ||
| 1850 | * - if -run... is already set: stop, arg... become argv of run_main. | ||
| 1851 | * - else ignore it. | ||
| 1852 | * - else ("-STRING") try to apply it as option, maybe with next (opt)arg. | ||
| 1853 | * | ||
| 1854 | * after all args, if -run... but no "stop": run_main gets our argv (tcc ...) | ||
| 1855 | */ | ||
| 1811 | /* using * to argc/argv to let "tcc -ar" benefit from @listfile expansion */ | 1856 | /* using * to argc/argv to let "tcc -ar" benefit from @listfile expansion */ |
| 1812 | PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv) | 1857 | PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv) |
| 1813 | { | 1858 | { |
| @@ -0,0 +1,38 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | export LC_ALL=C | ||
| 4 | |||
| 5 | defname=libtcc.c | ||
| 6 | |||
| 7 | # $1 is the line number, $2... is the actual source line | ||
| 8 | extract_opts() { awk '/tcc_options\[\]/ {x=1}; x; x && $2~/^}/ {exit}'; } | ||
| 9 | |||
| 10 | case $1 in -h|--help) | ||
| 11 | echo "Usage: $0 [INFILE]" | ||
| 12 | echo "Detect tcc_options[] clashes in $defname-like INFILE." | ||
| 13 | echo "If INFILE is missing, use $defname at the same dir as this script." | ||
| 14 | echo "Clashes are reported as longer-overrides, or longer-unreachable." | ||
| 15 | exit | ||
| 16 | esac | ||
| 17 | |||
| 18 | f=${1-$(dirname "$0")/$defname} | ||
| 19 | [ -r "$f" ] || { >&2 echo "$0: can't read -- $f"; exit 1; } | ||
| 20 | |||
| 21 | nl -b a <"$f" | extract_opts | tr \" ' ' | awk '$2=="{"' | sort -b -k 3 | | ||
| 22 | # "<line-num> { <unquoted-opt> <rest-of-line>" sorted-up by opt | ||
| 23 | # unavoidable O(N^2). the sort simplifies the awk code - only test prior opts. | ||
| 24 | awk ' | ||
| 25 | { | ||
| 26 | n=$1; opt=$3; h=/HAS_ARG/ | ||
| 27 | for (pn in prevs) { # pn: line-num | ||
| 28 | po = prevs[pn] # po: opt-with-has-arg | ||
| 29 | if (index(opt,po) == 1) { | ||
| 30 | clash=1 | ||
| 31 | printf("-%s%s (%d) %s -%s... (%s)\n", opt,h?"...":"",n, | ||
| 32 | n>pn? "is not reachable! by":"overrides", po,pn) | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | h {prevs[n] = opt} | ||
| 37 | END {if (clash) exit 1; print "no clashes"} | ||
| 38 | ' | ||