From 95fddd3c189a062eca4caf18a627a9e7fd1715c3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 8 Feb 2026 03:44:01 -0600 Subject: [PATCH] scripts: runners: Renamed a bunch of flags Mainly to make space for some planned bench flags, while also preferring "step" over "period" (for consistency), and "runfreq" over "freq" (to differentiate from "simfreq" in the future). In runners: - -s/--step -> --step - --trace-period -> --trace-step - --trace-freq -> --trace-runfreq In scripts: - --record -> -e/--record - --perf-period -> --perf-step - --perf-freq -> --perf-runfreq - --include -> -i/--include --- One thing that makes this work is the new sys.argv regex trick, where we try to predict what mode the script will run in by prematching known mode-switch flags before handing things off to argparse. Note: - Hiding flags from argparse risks confusing help-text, so we include all flags if we see -h/--help in sys.argv. This doesn't work for the help-text printed if argparse errors, but we can only do so much. Maybe argparse only showing relevant flags for the given mode is ok? - We use -[^-]*[hf].* for shortform flags, which should also match multiple shortform flags in a single arg (-fhfhfh). - This requires the conflict_handler='ignore' hack to work, but these scripts already needed it anyways. --- runners/bench_runner.c | 55 ++++++++++++++------------- runners/test_runner.c | 57 ++++++++++++++-------------- scripts/bench.py | 79 ++++++++++++++++++++------------------ scripts/perf.py | 66 ++++++++++++++++---------------- scripts/test.py | 86 ++++++++++++++++++++++-------------------- 5 files changed, 178 insertions(+), 165 deletions(-) diff --git a/runners/bench_runner.c b/runners/bench_runner.c index 474d2fcb..352ec7bc 100644 --- a/runners/bench_runner.c +++ b/runners/bench_runner.c @@ -432,8 +432,8 @@ bench_flags_t bench_mask = 0; const char *bench_disk_path = NULL; const char *bench_trace_path = NULL; bool bench_trace_backtrace = false; -uint32_t bench_trace_period = 0; -uint32_t bench_trace_freq = 0; +uint32_t bench_trace_step = 0; +uint32_t bench_trace_runfreq = 0; FILE *bench_trace_file = NULL; uint32_t bench_trace_cycles = 0; uint64_t bench_trace_time = 0; @@ -456,9 +456,9 @@ void bench_trace(const char *fmt, ...) { BENCH_HEAP_PAUSE(); if (bench_trace_path) { - // sample at a specific period? - if (bench_trace_period) { - if (bench_trace_cycles % bench_trace_period != 0) { + // sample at a specific step? + if (bench_trace_step) { + if (bench_trace_cycles % bench_trace_step != 0) { bench_trace_cycles += 1; goto done; } @@ -466,12 +466,13 @@ void bench_trace(const char *fmt, ...) { } // sample at a specific frequency? - if (bench_trace_freq) { + if (bench_trace_runfreq) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000 + (uint64_t)t.tv_nsec; - if (now - bench_trace_time < (1000*1000*1000) / bench_trace_freq) { + if (now - bench_trace_time + < (1000*1000*1000) / bench_trace_runfreq) { goto done; } bench_trace_time = now; @@ -2042,21 +2043,21 @@ enum opt_flags { OPT_LIST_IMPLICIT_DEFINES = 5, OPT_DEFINE = 'D', OPT_DEFINE_DEPTH = 6, - OPT_STEP = 's', - OPT_FORCE = 7, - OPT_NO_INTERNAL = 8, - OPT_NO_LITMUS = 9, + OPT_STEP = 7, + OPT_FORCE = 8, + OPT_NO_INTERNAL = 9, + OPT_NO_LITMUS = 10, OPT_DISK = 'd', OPT_TRACE = 't', - OPT_TRACE_BACKTRACE = 10, - OPT_TRACE_PERIOD = 11, - OPT_TRACE_FREQ = 12, - OPT_READ_SLEEP = 13, - OPT_PROG_SLEEP = 14, - OPT_ERASE_SLEEP = 15, + OPT_TRACE_BACKTRACE = 11, + OPT_TRACE_STEP = 12, + OPT_TRACE_RUNFREQ = 13, + OPT_READ_SLEEP = 14, + OPT_PROG_SLEEP = 15, + OPT_ERASE_SLEEP = 16, }; -const char *short_opts = "hYlLD:s:d:t:"; +const char *short_opts = "hYlLD:d:t:"; const struct option long_opts[] = { {"help", no_argument, NULL, OPT_HELP}, @@ -2079,8 +2080,8 @@ const struct option long_opts[] = { {"disk", required_argument, NULL, OPT_DISK}, {"trace", required_argument, NULL, OPT_TRACE}, {"trace-backtrace", no_argument, NULL, OPT_TRACE_BACKTRACE}, - {"trace-period", required_argument, NULL, OPT_TRACE_PERIOD}, - {"trace-freq", required_argument, NULL, OPT_TRACE_FREQ}, + {"trace-step", required_argument, NULL, OPT_TRACE_STEP}, + {"trace-runfreq", required_argument, NULL, OPT_TRACE_RUNFREQ}, {"read-sleep", required_argument, NULL, OPT_READ_SLEEP}, {"prog-sleep", required_argument, NULL, OPT_PROG_SLEEP}, {"erase-sleep", required_argument, NULL, OPT_ERASE_SLEEP}, @@ -2106,7 +2107,7 @@ const char *const help_text[] = { "Direct block device operations to this file.", "Direct trace output to this file.", "Include a backtrace with every trace statement.", - "Sample trace output at this period in cycles.", + "Sample trace output every n steps.", "Sample trace output at this frequency in hz.", "Artificial read delay in seconds.", "Artificial prog delay in seconds.", @@ -2449,20 +2450,20 @@ int main(int argc, char **argv) { bench_trace_backtrace = true; break; - case OPT_TRACE_PERIOD:; + case OPT_TRACE_STEP:; parsed = NULL; - bench_trace_period = strtoumax(optarg, &parsed, 0); + bench_trace_step = strtoumax(optarg, &parsed, 0); if (parsed == optarg) { - fprintf(stderr, "error: invalid trace-period: %s\n", optarg); + fprintf(stderr, "error: invalid trace-step: %s\n", optarg); exit(-1); } break; - case OPT_TRACE_FREQ:; + case OPT_TRACE_RUNFREQ:; parsed = NULL; - bench_trace_freq = strtoumax(optarg, &parsed, 0); + bench_trace_runfreq = strtoumax(optarg, &parsed, 0); if (parsed == optarg) { - fprintf(stderr, "error: invalid trace-freq: %s\n", optarg); + fprintf(stderr, "error: invalid trace-runfreq: %s\n", optarg); exit(-1); } break; diff --git a/runners/test_runner.c b/runners/test_runner.c index e984bcad..621e373d 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -443,8 +443,8 @@ test_flags_t test_mask = 0; const char *test_disk_path = NULL; const char *test_trace_path = NULL; bool test_trace_backtrace = false; -uint32_t test_trace_period = 0; -uint32_t test_trace_freq = 0; +uint32_t test_trace_step = 0; +uint32_t test_trace_runfreq = 0; FILE *test_trace_file = NULL; uint32_t test_trace_cycles = 0; uint64_t test_trace_time = 0; @@ -470,9 +470,9 @@ void *test_trace_backtrace_buffer[ // trace printing void test_trace(const char *fmt, ...) { if (test_trace_path) { - // sample at a specific period? - if (test_trace_period) { - if (test_trace_cycles % test_trace_period != 0) { + // sample at a specific step? + if (test_trace_step) { + if (test_trace_cycles % test_trace_step != 0) { test_trace_cycles += 1; goto done; } @@ -480,12 +480,13 @@ void test_trace(const char *fmt, ...) { } // sample at a specific frequency? - if (test_trace_freq) { + if (test_trace_runfreq) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000 + (uint64_t)t.tv_nsec; - if (now - test_trace_time < (1000*1000*1000) / test_trace_freq) { + if (now - test_trace_time + < (1000*1000*1000) / test_trace_runfreq) { goto done; } test_trace_time = now; @@ -1980,22 +1981,22 @@ enum opt_flags { OPT_DEFINE = 'D', OPT_DEFINE_DEPTH = 7, OPT_POWERLOSS = 'P', - OPT_STEP = 's', - OPT_FORCE = 8, - OPT_NO_INTERNAL = 9, - OPT_NO_REENTRANT = 10, - OPT_NO_FUZZ = 11, + OPT_STEP = 8, + OPT_FORCE = 9, + OPT_NO_INTERNAL = 10, + OPT_NO_REENTRANT = 11, + OPT_NO_FUZZ = 12, OPT_DISK = 'd', OPT_TRACE = 't', - OPT_TRACE_BACKTRACE = 12, - OPT_TRACE_PERIOD = 13, - OPT_TRACE_FREQ = 14, - OPT_READ_SLEEP = 15, - OPT_PROG_SLEEP = 16, - OPT_ERASE_SLEEP = 17, + OPT_TRACE_BACKTRACE = 13, + OPT_TRACE_STEP = 14, + OPT_TRACE_RUNFREQ = 15, + OPT_READ_SLEEP = 16, + OPT_PROG_SLEEP = 17, + OPT_ERASE_SLEEP = 18, }; -const char *short_opts = "hYlLD:P:s:d:t:"; +const char *short_opts = "hYlLD:P:d:t:"; const struct option long_opts[] = { {"help", no_argument, NULL, OPT_HELP}, @@ -2021,8 +2022,8 @@ const struct option long_opts[] = { {"disk", required_argument, NULL, OPT_DISK}, {"trace", required_argument, NULL, OPT_TRACE}, {"trace-backtrace", no_argument, NULL, OPT_TRACE_BACKTRACE}, - {"trace-period", required_argument, NULL, OPT_TRACE_PERIOD}, - {"trace-freq", required_argument, NULL, OPT_TRACE_FREQ}, + {"trace-step", required_argument, NULL, OPT_TRACE_STEP}, + {"trace-runfreq", required_argument, NULL, OPT_TRACE_RUNFREQ}, {"read-sleep", required_argument, NULL, OPT_READ_SLEEP}, {"prog-sleep", required_argument, NULL, OPT_PROG_SLEEP}, {"erase-sleep", required_argument, NULL, OPT_ERASE_SLEEP}, @@ -2051,7 +2052,7 @@ const char *const help_text[] = { "Direct block device operations to this file.", "Direct trace output to this file.", "Include a backtrace with every trace statement.", - "Sample trace output at this period in cycles.", + "Sample trace output every n steps.", "Sample trace output at this frequency in hz.", "Artificial read delay in seconds.", "Artificial prog delay in seconds.", @@ -2569,21 +2570,21 @@ int main(int argc, char **argv) { test_trace_backtrace = true; break; - case OPT_TRACE_PERIOD:; + case OPT_TRACE_STEP:; parsed = NULL; - test_trace_period = strtoumax(optarg, &parsed, 0); + test_trace_step = strtoumax(optarg, &parsed, 0); if (parsed == optarg) { - fprintf(stderr, "error: invalid trace-period: %s\n", + fprintf(stderr, "error: invalid trace-step: %s\n", optarg); exit(-1); } break; - case OPT_TRACE_FREQ:; + case OPT_TRACE_RUNFREQ:; parsed = NULL; - test_trace_freq = strtoumax(optarg, &parsed, 0); + test_trace_runfreq = strtoumax(optarg, &parsed, 0); if (parsed == optarg) { - fprintf(stderr, "error: invalid trace-freq: %s\n", optarg); + fprintf(stderr, "error: invalid trace-runfreq: %s\n", optarg); exit(-1); } break; diff --git a/scripts/bench.py b/scripts/bench.py index d1ce9424..d6818419 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -792,11 +792,11 @@ def find_runner(runner, id=None, main=True, **args): # run under perf? if args.get('perf'): cmd[:0] = args['perf_script'] + list(filter(None, [ - '--record', - '--perf-freq=%s' % args['perf_freq'] - if args.get('perf_freq') else None, - '--perf-period=%s' % args['perf_period'] - if args.get('perf_period') else None, + '-e', + '--perf-step=%s' % args['perf_step'] + if args.get('perf_step') else None, + '--perf-runfreq=%s' % args['perf_runfreq'] + if args.get('perf_runfreq') else None, '--perf-events=%s' % args['perf_events'] if args.get('perf_events') else None, '--perf-path=%s' % args['perf_path'] @@ -822,10 +822,10 @@ def find_runner(runner, id=None, main=True, **args): cmd.append('-t%s' % args['trace']) if args.get('trace_backtrace'): cmd.append('--trace-backtrace') - if args.get('trace_period'): - cmd.append('--trace-period=%s' % args['trace_period']) - if args.get('trace_freq'): - cmd.append('--trace-freq=%s' % args['trace_freq']) + if args.get('trace_step'): + cmd.append('--trace-step=%s' % args['trace_step']) + if args.get('trace_runfreq'): + cmd.append('--trace-runfreq=%s' % args['trace_runfreq']) if args.get('read_sleep'): cmd.append('--read-sleep=%s' % args['read_sleep']) if args.get('prog_sleep'): @@ -1305,9 +1305,9 @@ def run_stage(name, runner, bench_ids, stdout_, trace_, output_, **args): while start < total_perms: runner_ = find_runner(runner, main=main, **args) if args.get('isolate') or args.get('valgrind'): - runner_.append('-s%s,%s,%s' % (start, start+step, step)) + runner_.append('--step=%s,%s,%s' % (start, start+step, step)) elif start != 0 or step != 1: - runner_.append('-s%s,,%s' % (start, step)) + runner_.append('--step=%s,,%s' % (start, step)) runner_.extend(bench_ids) @@ -1649,6 +1649,7 @@ def main(**args): if __name__ == "__main__": import argparse import sys + import re argparse.ArgumentParser._handle_conflict_ignore = lambda *_: None argparse._ArgumentGroup._handle_conflict_ignore = lambda *_: None parser = argparse.ArgumentParser( @@ -1743,10 +1744,10 @@ if __name__ == "__main__": action='store_true', help="Include a backtrace with every trace statement.") bench_parser.add_argument( - '--trace-period', - help="Sample trace output at this period in cycles.") + '--trace-step', + help="Sample trace output every n steps.") bench_parser.add_argument( - '--trace-freq', + '--trace-runfreq', help="Sample trace output at this frequency in hz.") bench_parser.add_argument( '-O', '--stdout', @@ -1847,13 +1848,13 @@ if __name__ == "__main__": help="Run under Linux's perf to sample performance counters, " "writing samples to this file.") bench_parser.add_argument( - '--perf-freq', + '--perf-step', + help="perf sampling step. This is passed directly to the perf " + "script.") + bench_parser.add_argument( + '--perf-runfreq', help="perf sampling frequency. This is passed directly to the " "perf script.") - bench_parser.add_argument( - '--perf-period', - help="perf sampling period. This is passed directly to the perf " - "script.") bench_parser.add_argument( '--perf-events', help="perf events to record. This is passed directly to the perf " @@ -1880,27 +1881,31 @@ if __name__ == "__main__": '-c', '--compile', action='store_true', help="Compile a bench suite or source file.") - comp_parser.add_argument( - '-o', '--output', - help="Output file.") - comp_parser.add_argument( - '-s', '--source', - help="Source file to compile, possibly injecting internal benches.") - comp_parser.add_argument( - '--include', - help="Inject these header files into every compiled bench file. " - "Defaults to %r." % HEADER_PATHS) - comp_parser.add_argument( - '--no-internal', - action='store_true', - help="Don't build internal benches.") - comp_parser.add_argument( - '--no-litmus', - action='store_true', - help="Don't build litmus benches.") + if any(re.fullmatch('-[^-]*[hc].*|--help|--compile', a) for a in sys.argv): + comp_parser.add_argument( + '-o', '--output', + help="Output file.") + comp_parser.add_argument( + '-s', '--source', + help="Source file to compile, possibly injecting internal " + "benches.") + comp_parser.add_argument( + '-i', '--include', + action='append', + help="Inject these header files into every compiled bench " + "file. Defaults to %r." % HEADER_PATHS) + comp_parser.add_argument( + '--no-internal', + action='store_true', + help="Don't build internal benches.") + comp_parser.add_argument( + '--no-litmus', + action='store_true', + help="Don't build litmus benches.") # do the thing args = parser.parse_intermixed_args() + # bench_paths/bench_ids overlap, so need to do some munging args.bench_paths = args.bench_ids sys.exit(main(**{k: v for k, v in vars(args).items() diff --git a/scripts/perf.py b/scripts/perf.py index faf51afb..e1a677cd 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -3,7 +3,7 @@ # Script to aggregate and report Linux perf results. # # Example: -# ./scripts/perf.py --record -obench.perf ./runners/bench_runner +# ./scripts/perf.py -e -obench.perf ./runners/bench_runner # ./scripts/perf.py bench.perf -j -Flfs.c -Flfs_util.c -Scycles # # Copyright (c) 2022, The littlefs authors. @@ -39,7 +39,7 @@ import zipfile PERF_PATH = ['perf'] PERF_EVENTS = 'cycles,branch-misses,branches,cache-misses,cache-references' -PERF_FREQ = 100 +PERF_RUNFREQ = 100 OBJDUMP_PATH = ['objdump'] THRESHOLD = (0.5, 0.85) @@ -202,8 +202,8 @@ def openio(path, mode='r', buffering=-1): # run perf as a subprocess, storing measurements into a zip file def record(command, *, output=None, - perf_freq=PERF_FREQ, - perf_period=None, + perf_step=None, + perf_runfreq=PERF_RUNFREQ, perf_events=PERF_EVENTS, perf_path=PERF_PATH, **args): @@ -213,11 +213,11 @@ def record(command, *, # figure out our perf invocation perf = perf_path + list(filter(None, [ 'record', - '-F%s' % perf_freq - if perf_freq is not None - and perf_period is None else None, - '-c%s' % perf_period - if perf_period is not None else None, + '-c%s' % perf_step + if perf_step is not None else None, + '-F%s' % perf_runfreq + if perf_runfreq is not None + and perf_step is None else None, '-B', '-g', '--all-user', @@ -1660,11 +1660,12 @@ def main(**args): if __name__ == "__main__": import argparse import sys + import re # bit of a hack, but parse_intermixed_args and REMAINDER are # incompatible, so we need to figure out what we want before running # argparse - if '--record' in sys.argv: + if any(re.fullmatch('-[^-]*[e].*|--record', a) for a in sys.argv): nargs = argparse.REMAINDER else: nargs = '*' @@ -1900,29 +1901,30 @@ if __name__ == "__main__": nargs=nargs, help="Command to run.") record_parser.add_argument( - '--record', + '-e', '--record', action='store_true', help="Run a command and aggregate perf measurements.") - record_parser.add_argument( - '-o', '--output', - help="Output file. Uses flock to synchronize. This is stored as a " - "zip-file of multiple perf results.") - record_parser.add_argument( - '--perf-freq', - help="perf sampling frequency. This is passed directly to perf. " - "Defaults to %r." % PERF_FREQ) - record_parser.add_argument( - '--perf-period', - help="perf sampling period. This is passed directly to perf.") - record_parser.add_argument( - '--perf-events', - help="perf events to record. This is passed directly to perf. " - "Defaults to %r." % PERF_EVENTS) - record_parser.add_argument( - '--perf-path', - type=lambda x: x.split(), - help="Path to the perf executable, may include flags. " - "Defaults to %r." % PERF_PATH) + if any(re.fullmatch('-[^-]*[he].*|--help|--record', a) for a in sys.argv): + record_parser.add_argument( + '-o', '--output', + help="Output file. Uses flock to synchronize. This is stored " + "as a zip-file of multiple perf results.") + record_parser.add_argument( + '--perf-step', + help="perf sampling step. This is passed directly to perf.") + record_parser.add_argument( + '--perf-runfreq', + help="perf sampling frequency. This is passed directly to " + "perf. Defaults to %r." % PERF_RUNFREQ) + record_parser.add_argument( + '--perf-events', + help="perf events to record. This is passed directly to " + "perf. Defaults to %r." % PERF_EVENTS) + record_parser.add_argument( + '--perf-path', + type=lambda x: x.split(), + help="Path to the perf executable, may include flags. " + "Defaults to %r." % PERF_PATH) # avoid intermixed/REMAINDER conflict, see above if nargs == argparse.REMAINDER: @@ -1930,7 +1932,7 @@ if __name__ == "__main__": else: args = parser.parse_intermixed_args() - # perf_paths/command overlap, so need to do some munging here + # perf_paths/command overlap, so need to do some munging args.command = args.perf_paths if args.record: if not args.command: diff --git a/scripts/test.py b/scripts/test.py index ce4e80b6..b5d4f4ce 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -805,11 +805,11 @@ def find_runner(runner, id=None, main=True, **args): # run under perf? if args.get('perf'): cmd[:0] = args['perf_script'] + list(filter(None, [ - '--record', - '--perf-freq=%s' % args['perf_freq'] - if args.get('perf_freq') else None, - '--perf-period=%s' % args['perf_period'] - if args.get('perf_period') else None, + '-e', + '--perf-step=%s' % args['perf_step'] + if args.get('perf_step') else None, + '--perf-runfreq=%s' % args['perf_runfreq'] + if args.get('perf_runfreq') else None, '--perf-events=%s' % args['perf_events'] if args.get('perf_events') else None, '--perf-path=%s' % args['perf_path'] @@ -839,10 +839,10 @@ def find_runner(runner, id=None, main=True, **args): cmd.append('-t%s' % args['trace']) if args.get('trace_backtrace'): cmd.append('--trace-backtrace') - if args.get('trace_period'): - cmd.append('--trace-period=%s' % args['trace_period']) - if args.get('trace_freq'): - cmd.append('--trace-freq=%s' % args['trace_freq']) + if args.get('trace_step'): + cmd.append('--trace-step=%s' % args['trace_step']) + if args.get('trace_runfreq'): + cmd.append('--trace-runfreq=%s' % args['trace_runfreq']) if args.get('read_sleep'): cmd.append('--read-sleep=%s' % args['read_sleep']) if args.get('prog_sleep'): @@ -1269,9 +1269,9 @@ def run_stage(name, runner, test_ids, stdout_, trace_, output_, **args): while start < total_perms: runner_ = find_runner(runner, main=main, **args) if args.get('isolate') or args.get('valgrind'): - runner_.append('-s%s,%s,%s' % (start, start+step, step)) + runner_.append('--step=%s,%s,%s' % (start, start+step, step)) elif start != 0 or step != 1: - runner_.append('-s%s,,%s' % (start, step)) + runner_.append('--step=%s,,%s' % (start, step)) runner_.extend(test_ids) @@ -1655,6 +1655,7 @@ def main(**args): if __name__ == "__main__": import argparse import sys + import re argparse.ArgumentParser._handle_conflict_ignore = lambda *_: None argparse._ArgumentGroup._handle_conflict_ignore = lambda *_: None parser = argparse.ArgumentParser( @@ -1760,10 +1761,10 @@ if __name__ == "__main__": action='store_true', help="Include a backtrace with every trace statement.") test_parser.add_argument( - '--trace-period', - help="Sample trace output at this period in cycles.") + '--trace-step', + help="Sample trace output every n steps.") test_parser.add_argument( - '--trace-freq', + '--trace-runfreq', help="Sample trace output at this frequency in hz.") test_parser.add_argument( '-O', '--stdout', @@ -1876,13 +1877,13 @@ if __name__ == "__main__": help="Run under Linux's perf to sample performance counters, " "writing samples to this file.") test_parser.add_argument( - '--perf-freq', + '--perf-step', + help="perf sampling step. This is passed directly to the perf " + "script.") + test_parser.add_argument( + '--perf-runfreq', help="perf sampling frequency. This is passed directly to the " "perf script.") - test_parser.add_argument( - '--perf-period', - help="perf sampling period. This is passed directly to the perf " - "script.") test_parser.add_argument( '--perf-events', help="perf events to record. This is passed directly to the perf " @@ -1909,31 +1910,34 @@ if __name__ == "__main__": '-c', '--compile', action='store_true', help="Compile a test suite or source file.") - comp_parser.add_argument( - '-o', '--output', - help="Output file.") - comp_parser.add_argument( - '-s', '--source', - help="Source file to compile, possibly injecting internal tests.") - comp_parser.add_argument( - '--include', - help="Inject these header files into every compiled test file. " - "Defaults to %r." % HEADER_PATHS) - comp_parser.add_argument( - '--no-internal', - action='store_true', - help="Don't build internal tests.") - comp_parser.add_argument( - '--no-reentrant', - action='store_true', - help="Don't build reentrant tests.") - comp_parser.add_argument( - '--no-fuzz', - action='store_true', - help="Don't build fuzz tests.") + if any(re.fullmatch('-[^-]*[hc].*|--help|--compile', a) for a in sys.argv): + comp_parser.add_argument( + '-o', '--output', + help="Output file.") + comp_parser.add_argument( + '-s', '--source', + help="Source file to compile, possibly injecting internal " + "tests.") + comp_parser.add_argument( + '-i', '--include', + help="Inject these header files into every compiled test " + "file. Defaults to %r." % HEADER_PATHS) + comp_parser.add_argument( + '--no-internal', + action='store_true', + help="Don't build internal tests.") + comp_parser.add_argument( + '--no-reentrant', + action='store_true', + help="Don't build reentrant tests.") + comp_parser.add_argument( + '--no-fuzz', + action='store_true', + help="Don't build fuzz tests.") # do the thing args = parser.parse_intermixed_args() + # test_paths/test_ids overlap, so need to do some munging args.test_paths = args.test_ids sys.exit(main(**{k: v for k, v in vars(args).items()