@openmixer/plugin-qualify 0.13.1 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +151 -14
  2. package/bin/plugin-qualify-sync.mjs +57 -0
  3. package/bin/plugin-qualify.mjs +66 -15
  4. package/dist/adjust-cli.d.ts +83 -0
  5. package/dist/adjust-cli.js +214 -0
  6. package/dist/adjust-cli.js.map +1 -0
  7. package/dist/clap-loader.d.ts +3 -3
  8. package/dist/clap-loader.js +11 -11
  9. package/dist/clap-loader.js.map +1 -1
  10. package/dist/history-adjust.d.ts +77 -0
  11. package/dist/history-adjust.js +99 -0
  12. package/dist/history-adjust.js.map +1 -0
  13. package/dist/history-file.d.ts +73 -0
  14. package/dist/history-file.js +133 -0
  15. package/dist/history-file.js.map +1 -0
  16. package/dist/host-admission.d.ts +53 -0
  17. package/dist/host-admission.js +96 -0
  18. package/dist/host-admission.js.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +6 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/lv2-loader.d.ts +16 -2
  23. package/dist/lv2-loader.js +168 -17
  24. package/dist/lv2-loader.js.map +1 -1
  25. package/dist/measurement/cli.d.ts +6 -4
  26. package/dist/measurement/cli.js +12 -19
  27. package/dist/measurement/cli.js.map +1 -1
  28. package/dist/pins-file.d.ts +8 -2
  29. package/dist/pins-file.js +17 -3
  30. package/dist/pins-file.js.map +1 -1
  31. package/dist/probe-judge.d.ts +11 -1
  32. package/dist/probe-judge.js +20 -1
  33. package/dist/probe-judge.js.map +1 -1
  34. package/dist/probe-output-fixtures.d.ts +10 -0
  35. package/dist/probe-output-fixtures.js +86 -0
  36. package/dist/probe-output-fixtures.js.map +1 -1
  37. package/dist/probe-output.d.ts +2 -0
  38. package/dist/probe-output.js +1 -0
  39. package/dist/probe-output.js.map +1 -1
  40. package/dist/probe-runner.d.ts +9 -3
  41. package/dist/probe-runner.js +36 -12
  42. package/dist/probe-runner.js.map +1 -1
  43. package/dist/qualification-record.d.ts +109 -3
  44. package/dist/qualification-record.js +79 -13
  45. package/dist/qualification-record.js.map +1 -1
  46. package/dist/qualify-cli.d.ts +0 -1
  47. package/dist/qualify-cli.js +2 -4
  48. package/dist/qualify-cli.js.map +1 -1
  49. package/dist/qualify-declarations.d.ts +26 -0
  50. package/dist/qualify-declarations.js +26 -0
  51. package/dist/qualify-declarations.js.map +1 -1
  52. package/dist/qualify-options.js +1 -2
  53. package/dist/qualify-options.js.map +1 -1
  54. package/dist/qualify-sync.d.ts +202 -0
  55. package/dist/qualify-sync.js +731 -0
  56. package/dist/qualify-sync.js.map +1 -0
  57. package/dist/whole-file.d.ts +3 -0
  58. package/dist/whole-file.js +36 -0
  59. package/dist/whole-file.js.map +1 -0
  60. package/fixtures/admission/2026-10-26.json +19 -0
  61. package/fixtures/clap-scan/fake.json +24 -24
  62. package/package.json +5 -4
  63. package/src/adjust-cli.ts +262 -0
  64. package/src/clap-loader.ts +9 -13
  65. package/src/history-adjust.ts +156 -0
  66. package/src/history-file.ts +177 -0
  67. package/src/host-admission.ts +136 -0
  68. package/src/index.ts +6 -0
  69. package/src/lv2-loader.ts +150 -15
  70. package/src/measurement/cli.ts +11 -17
  71. package/src/pins-file.ts +17 -3
  72. package/src/probe-judge.ts +27 -1
  73. package/src/probe-output-fixtures.ts +86 -0
  74. package/src/probe-output.ts +3 -0
  75. package/src/probe-runner.ts +45 -14
  76. package/src/qualification-record.ts +174 -15
  77. package/src/qualify-cli.ts +2 -5
  78. package/src/qualify-declarations.ts +29 -0
  79. package/src/qualify-options.ts +1 -2
  80. package/src/qualify-sync.ts +830 -0
  81. package/src/whole-file.ts +35 -0
  82. package/tools/_declarations.py +11 -0
  83. package/tools/audit-overlap.sh +250 -0
  84. package/tools/descriptors.py +382 -0
  85. package/tools/gen-hosting-codes.mjs +1 -1
  86. package/tools/gen-python-declarations.mjs +13 -1
  87. package/tools/mod-host-admit.mjs +74 -0
  88. package/tools/scan.py +260 -4
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
+ /**
4
+ * A file written whole or not at all: the bytes go to `<path>.new` beside it, are flushed to the disk, and the name is
5
+ * moved over the old file, then the directory is flushed so the rename itself survives a power cut. A reader of
6
+ * `path`, plugin-hostd at start among them, sees the old file or the whole new one, never a half.
7
+ */
8
+ import { closeSync, fsyncSync, mkdirSync, openSync, renameSync, writeSync } from "node:fs";
9
+ import { dirname } from "node:path";
10
+
11
+ /** The bytes one write call takes; a large file goes out in pieces of this size. */
12
+ const PIECE_BYTES = 1 << 16;
13
+
14
+ /** The name the new bytes are written under before the rename. */
15
+ export const pendingPath = (path: string): string => `${path}.new`;
16
+
17
+ export function writeFileWhole(path: string, content: string | Uint8Array): void {
18
+ mkdirSync(dirname(path), { recursive: true });
19
+ const bytes = typeof content === "string" ? Buffer.from(content, "utf8") : content;
20
+ const pending = pendingPath(path);
21
+ const fd = openSync(pending, "w", 0o644);
22
+ try {
23
+ for (let at = 0; at < bytes.length; at += PIECE_BYTES) writeSync(fd, bytes, at, Math.min(PIECE_BYTES, bytes.length - at));
24
+ fsyncSync(fd);
25
+ } finally {
26
+ closeSync(fd);
27
+ }
28
+ renameSync(pending, path);
29
+ const dir = openSync(dirname(path), "r");
30
+ try {
31
+ fsyncSync(dir);
32
+ } finally {
33
+ closeSync(dir);
34
+ }
35
+ }
@@ -19,3 +19,14 @@ QUALIFY_QUANTA = (128, 256, 512, 1024)
19
19
  PLUGIN_TIMEOUT_S = 15
20
20
  BOOT_TIMEOUT_S = 120
21
21
  COST_TIMEOUT_S = 60
22
+ DESCRIPTOR_DIR_KEY = "descriptor_dir"
23
+ DESCRIPTOR_DIR_STATE_SUBDIR = "omx/descriptors"
24
+ DESCRIPTOR_DIR_SYSTEM = "/var/lib/omx/descriptors"
25
+ DESCRIPTOR_HASH_PREFIX = 12
26
+ DESCRIPTOR_SCHEMA_ENV = "OMX_CONTRACT_SCHEMA"
27
+ DESCRIPTOR_SCHEMA_INSTALLED = "/usr/share/omx-contract/omx-contract.schema.json"
28
+ DESCRIPTOR_SCHEMA_FLOOR = ">= 3.4.0"
29
+ DESCRIPTOR_CODE_SCHEMA = "contract.file.invalid"
30
+ DESCRIPTOR_CODE_SCHEMA_MISSING = "qualify.descriptor.schema-missing"
31
+ DESCRIPTOR_CODE_BINARY_UNHASHED = "qualify.descriptor.binary-unhashed"
32
+ DESCRIPTOR_CODE_DIR_UNWRITABLE = "qualify.descriptor.dir-unwritable"
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env bash
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+ # Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
4
+ #
5
+ # audit-overlap.sh — every number in docs/audit-qualify-vs-compat.md, from source, by one command each.
6
+ #
7
+ # tools/audit-overlap.sh <omx-clap-core dir> <plugin-hostd dir>
8
+ # tools/audit-overlap.sh --self-test
9
+ #
10
+ # It prints one line per unit (a function, or a set of functions, in one repository):
11
+ # unit <id> <repo> <file>:<first>-<last> lines=<n>
12
+ # one line per pair that does the same job in two places:
13
+ # pair <a> <b> shared=<s> a=<na> b=<nb> overlap=<s/min(na,nb) %> diff=<changed lines or "-"> strings=<both/a/b>
14
+ # and one line per counted marker:
15
+ # count <id> <repo> <n>
16
+ # A unit is found by its name (a C or TypeScript function runs to the first column-0 `}`, a Python def or class
17
+ # to the next column-0 statement), never by a line number, so the numbers follow the code. The audited commits are
18
+ # printed first (`tree <repo> <commit>`); the document states which ones it quotes.
19
+ #
20
+ # Similarity: both bodies are normalised (comments, blank lines and lone braces dropped, whitespace squeezed), then
21
+ # `shared` is the number of normalised lines the two have in common (comm -12 over the sorted lines, duplicates
22
+ # kept) and `diff` the number of lines `diff` adds or removes between them. Code in two languages shares no line;
23
+ # for it the pair prints overlap=0 and diff=-. `strings` is shared/a/b of the distinct string literals (three or
24
+ # more characters) each body spells: messages, field names and codes, which survive a change of brace style or of
25
+ # language, so a port of the same code shows there when its lines do not.
26
+ set -euo pipefail
27
+ export LC_ALL=C
28
+ Q="$(cd "$(dirname "$0")/.." && pwd)"
29
+
30
+ die() { echo "audit-overlap: $*" >&2; exit 2; }
31
+
32
+ # span <lang> <file> <name> — print "<first> <last>" of the named unit, nothing when there is none
33
+ span() { # <lang> <file> <name>
34
+ awk -v lang="$1" -v name="$3" '
35
+ function starts(l) {
36
+ if (lang == "py") return l ~ ("^(def|class) " name "[(:]")
37
+ if (lang == "ts") return l ~ ("^(export )?(async )?function " name "[(<]")
38
+ return l ~ ("^[A-Za-z].*[ *]" name "\\(") && l !~ /;[ \t]*$/
39
+ }
40
+ !on && starts($0) { on = 1; first = NR; last = NR; next }
41
+ on && lang != "py" && /^}/ { last = NR; print first, last; exit }
42
+ on && lang == "py" && /^[^ \t#\n]/ { print first, last; exit }
43
+ on && lang == "py" && /[^ \t]/ { last = NR }
44
+ END { if (on && lang == "py") print first, last }
45
+ ' "$2"
46
+ }
47
+
48
+ WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
49
+ declare -A ROOT LANG_OF
50
+
51
+ # unit <id> <repo> <lang> <file> <name>... — one unit, the names' bodies concatenated
52
+ unit() {
53
+ local id="$1" repo="$2" lang="$3" file="$4"; shift 4
54
+ local root="${ROOT[$repo]}" n total=0 where="" s
55
+ : > "$WORK/$id"
56
+ for n in "$@"; do
57
+ s="$(span "$lang" "$root/$file" "$n")"
58
+ [ -n "$s" ] || die "$repo $file: no $lang unit named $n"
59
+ local a b; read -r a b <<<"$s"
60
+ sed -n "${a},${b}p" "$root/$file" >> "$WORK/$id"
61
+ total=$((total + b - a + 1)); where="${where:+$where,}$a-$b"
62
+ done
63
+ LANG_OF[$id]="$lang"
64
+ echo "unit $id $repo $file:$where lines=$total"
65
+ }
66
+
67
+ # whole <id> <repo> <lang> <file> — a whole file as one unit
68
+ whole() {
69
+ local id="$1" repo="$2" lang="$3" file="$4" n
70
+ cp "${ROOT[$repo]}/$file" "$WORK/$id"; n=$(wc -l < "$WORK/$id")
71
+ LANG_OF[$id]="$lang"
72
+ echo "unit $id $repo $file:1-$n lines=$n"
73
+ }
74
+
75
+ norm() { # <lang> < body
76
+ case "$1" in
77
+ py) sed -E 's/#.*$//' ;;
78
+ *) sed -E 's#//.*$##; s#/\*.*\*/##g; /^[[:space:]]*\*( |\/|$)/d; /^[[:space:]]*\/\*/d' ;;
79
+ esac | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' | grep -vE '^[{}();, ]*$' || true
80
+ }
81
+
82
+ lits() { grep -oE "\"[^\"\\\\]{3,}\"|'[^'\\\\]{3,}'" | sed -E "s/^.(.*).$/\\1/"; } # quotes dropped: '' and "" alike
83
+
84
+ pair() { # <a> <b>
85
+ local a="$1" b="$2" na nb s d m l
86
+ norm "${LANG_OF[$a]}" < "$WORK/$a" | sort > "$WORK/$a.n"
87
+ norm "${LANG_OF[$b]}" < "$WORK/$b" | sort > "$WORK/$b.n"
88
+ na=$(wc -l < "$WORK/$a.n"); nb=$(wc -l < "$WORK/$b.n")
89
+ s=$(comm -12 "$WORK/$a.n" "$WORK/$b.n" | wc -l)
90
+ m=$(( na < nb ? na : nb ))
91
+ if [ "${LANG_OF[$a]}" = "${LANG_OF[$b]}" ]; then
92
+ norm "${LANG_OF[$a]}" < "$WORK/$a" > "$WORK/$a.o"; norm "${LANG_OF[$b]}" < "$WORK/$b" > "$WORK/$b.o"
93
+ d=$(diff "$WORK/$a.o" "$WORK/$b.o" | grep -c '^[<>]' || true)
94
+ else d="-"; fi
95
+ # the string literals (messages, field names, codes) both spell: what survives a change of style or language
96
+ norm "${LANG_OF[$a]}" < "$WORK/$a" | lits | sort -u > "$WORK/$a.l" || true
97
+ norm "${LANG_OF[$b]}" < "$WORK/$b" | lits | sort -u > "$WORK/$b.l" || true
98
+ l="$(comm -12 "$WORK/$a.l" "$WORK/$b.l" | wc -l)/$(wc -l < "$WORK/$a.l")/$(wc -l < "$WORK/$b.l")"
99
+ echo "pair $a $b shared=$s a=$na b=$nb overlap=$(( m ? 100 * s / m : 0 ))% diff=$d strings=$l"
100
+ }
101
+
102
+ count() { # <id> <repo> <git grep args> -- <pathspec>... (this audit's own two files are never counted)
103
+ local id="$1" repo="$2"; shift 2
104
+ local self=(":(exclude)tools/audit-overlap.sh" ":(exclude)docs/audit-qualify-vs-compat.md")
105
+ echo "count $id $repo $(git -C "${ROOT[$repo]}" grep -IE "$@" "${self[@]}" | wc -l)"
106
+ }
107
+
108
+ audit() {
109
+ local r
110
+ for r in qualify clap-core hostd; do echo "tree $r $(git -C "${ROOT[$r]}" rev-parse --short HEAD)"; done
111
+
112
+ # 1. plugin enumeration
113
+ unit q.clap-walk qualify c native/clap-qualify.c is_clap walk scan
114
+ unit c.clap-walk clap-core c src/scan.c has_suffix walk main
115
+ unit q.clap-fork qualify c native/clap-qualify.c scan_file
116
+ unit c.clap-fork clap-core c src/scan.c scan_file
117
+ unit q.clap-describe qualify c native/clap-qualify.c scan_file_entry scan_plugin scan_params scan_audio_ports
118
+ unit c.clap-describe clap-core c src/scan.c put_file put_plugin put_descriptor put_params put_audio_ports
119
+ unit q.clap-path qualify ts src/clap-loader.ts clapSearchPath
120
+ unit c.clap-path clap-core c src/scan.c default_paths
121
+ unit q.lv2-manifest qualify ts src/lv2-loader.ts pluginsInManifest lv2BundlesOnPath
122
+ unit c.lv2-manifest clap-core c src/lv2_locate.c lv2_locate_manifest_names walk_dir lv2_locate
123
+ unit q.lv2-lilv qualify py tools/scan.py Scanner
124
+ unit c.lv2-lilv clap-core py tests/lv2_parity.py lilv_facts
125
+ pair q.clap-walk c.clap-walk
126
+ pair q.clap-fork c.clap-fork
127
+ pair q.clap-describe c.clap-describe
128
+ pair q.clap-path c.clap-path
129
+ pair q.lv2-manifest c.lv2-manifest
130
+ pair q.lv2-lilv c.lv2-lilv
131
+ count q.calls-omx-clap-scan qualify -e 'omx-clap-scan' -- src
132
+ # clap-qualify's whole scan mode (--json), which writes the document omx-clap-scan writes, plus labels and pages
133
+ unit q.scan-mode qualify c native/clap-qualify.c put_labels scan_params scan_remote_controls scan_audio_ports \
134
+ scan_plugin put_file_error scan_file_entry scan_file list_add is_clap walk scan
135
+ count c.scan-labels-pages clap-core -i -e 'remote_controls|enum_labels|"labels"' -- src/scan.c
136
+
137
+ # 2. deterministic input
138
+ count q.lcg-nr qualify -e '1664525u' -- native
139
+ count q.lcg-glibc qualify -e '1103515245u' -- native
140
+ count c.lcg-nr clap-core -e '1664525u' -- tests src
141
+ unit q.noise-lv2 qualify c native/lv2-measure.c fill_input
142
+ unit c.signal clap-core c tests/jack_parity.c noise signal_at
143
+ pair q.noise-lv2 c.signal
144
+ unit q.notes qualify c native/clap-qualify.c note_event note_midi note_key note_block note_dialect
145
+ unit q.notes-py qualify py tools/benchmark.py held_keys midi_sequence
146
+ unit c.notes clap-core c tests/jack_parity.c midi_events
147
+ pair q.notes c.notes
148
+ pair q.notes-py q.notes
149
+
150
+ # 3. isolated PipeWire, the feeder
151
+ count q.pipewire qualify -i -e 'pipewire|unshare' -- src native tools bin
152
+ count c.unshare clap-core -e 'unshare' -- tests
153
+ unit c.pw-parity clap-core py tests/lv2_parity.py start_pipewire
154
+ unit c.pw-coverage clap-core py tests/clap_coverage.py start_pipewire
155
+ unit c.ns-parity clap-core py tests/lv2_parity.py inside_run cmd_run
156
+ unit c.ns-coverage clap-core py tests/clap_coverage.py inside_host cmd_host
157
+ unit c.clock clap-core c tests/jack_parity.c clock_process clock_state clock_wait clock_start clock_cycle clock_run
158
+ pair c.pw-parity c.pw-coverage
159
+ pair c.ns-parity c.ns-coverage
160
+
161
+ # 4. host launch, teardown, crash and hang
162
+ unit q.probe-runner qualify ts src/probe-runner.ts runOne probeTarget
163
+ unit h.run-child hostd c src/probe.c apply_limits kill_tree run_child child_end
164
+ unit q.bench-worker qualify py tools/benchmark.py WorkerQueue _Worker
165
+ unit c.host-parity clap-core py tests/lv2_parity.py Host run_plugin
166
+ unit c.host-coverage clap-core py tests/clap_coverage.py Host
167
+ pair c.host-parity c.host-coverage
168
+ pair q.bench-worker c.host-parity
169
+ count h.runs-clap-qualify hostd -e 'clap-qualify|clap_qualify' -- src
170
+ count h.runs-lv2-measure hostd -e 'lv2_measure|lv2-measure' -- src
171
+
172
+ # 5. records, classification, vocabulary, reports
173
+ unit q.records qualify ts src/measurement/sweep-jsonl.ts measuredPluginsFromSweep parseJsonl
174
+ unit c.tsv-parity clap-core py tests/lv2_parity.py read_tsv write_tsv
175
+ unit c.tsv-coverage clap-core py tests/clap_coverage.py read_tsv write_tsv append_tsv
176
+ pair c.tsv-parity c.tsv-coverage
177
+ unit q.classify qualify ts src/hosting-suitability.ts classifyPluginHosting
178
+ unit c.classify clap-core py tests/lv2_parity.py _classify
179
+ pair q.classify c.classify
180
+ unit c.report clap-core py tests/lv2_parity.py report_text
181
+ count q.contract-imports qualify -e "from \"@openmixer/omx-contract" -- src
182
+ count c.contract-macros clap-core -e 'OMX_HOSTING_[A-Z_]+' -- src lv2
183
+ count q.handtyped-hosting qualify -e '"hosting\.[a-z-]+\.[a-z-]+"' -- 'src/*.ts' ':!src/*.test.ts'
184
+
185
+ # 6. cost and latency
186
+ unit q.pct-c qualify c native/clap-qualify.c percentile_sorted
187
+ unit q.pct-py qualify py tools/benchmark.py percentile
188
+ unit q.cost-c qualify c native/clap-qualify.c cost_rate
189
+ unit c.stage-bench clap-core c tests/stage_bench.c main
190
+ pair q.pct-c q.pct-py
191
+ pair q.cost-c c.stage-bench
192
+
193
+ # the probing programs plugin-hostd's probe runs as its children today, and what the move into its child deletes
194
+ whole q.prober-clap qualify c native/clap-qualify.c
195
+ whole q.prober-lv2 qualify c native/lv2-measure.c
196
+ whole q.benchmark qualify py tools/benchmark.py
197
+
198
+ # 7. mod-host in plugin-qualify: code that runs it, and words that name it
199
+ count q.modhost-spawn qualify -e "spawn[A-Za-z]*\\(['\"]mod-host|execvp?\\(\"mod-host" -- src native tools bin
200
+ count q.modhost-words qualify -i -e 'mod-host' -- .
201
+ count c.modhost-words clap-core -i -e 'mod-host' -- tests
202
+ }
203
+
204
+ self_test() {
205
+ T="$(mktemp -d)"; local t="$T"; trap 'rm -rf "$T" "$WORK"' EXIT
206
+ for r in a b c; do mkdir -p "$t/$r"; git -C "$t/$r" init -q; done
207
+ cat > "$t/a/x.c" <<'EOF'
208
+ static int one(int v)
209
+ {
210
+ /* a comment */
211
+ return v + !"same"; /* "comment" */
212
+ }
213
+ static int two(int v) {
214
+ return v * 2;
215
+ }
216
+ EOF
217
+ cat > "$t/b/y.py" <<'EOF'
218
+ def one(v):
219
+ # a comment
220
+ return v + len("same") # "comment"
221
+
222
+ X = 1
223
+ EOF
224
+ cp "$t/a/x.c" "$t/c/z.c"; sed -i 's/v \* 2/v * 3/' "$t/c/z.c"
225
+ for r in a b c; do git -C "$t/$r" add -A; git -C "$t/$r" -c user.name=t -c user.email=t@t commit -qm t; done
226
+ ROOT[qualify]="$t/a"; ROOT[clap-core]="$t/b"; ROOT[hostd]="$t/c"
227
+ local out fail=0
228
+ expect() { grep -qxF -- "$1" <<<"$out" || { echo "self-test: missing: $1" >&2; fail=1; }; }
229
+ out="$(unit u1 qualify c x.c one; unit u2 qualify c x.c two; unit p1 clap-core py y.py one
230
+ unit h1 hostd c z.c one two; unit h2 qualify c x.c one two
231
+ pair h2 h1; pair u1 p1; count n qualify -e 'return' -- .)"
232
+ expect "unit u1 qualify x.c:1-5 lines=5"
233
+ expect "unit u2 qualify x.c:6-8 lines=3"
234
+ expect "unit p1 clap-core y.py:1-3 lines=3"
235
+ expect "pair h2 h1 shared=3 a=4 b=4 overlap=75% diff=2 strings=1/1/1"
236
+ expect "pair u1 p1 shared=0 a=2 b=2 overlap=0% diff=- strings=1/1/1"
237
+ expect "count n qualify 2"
238
+ # a name that is not there is refused, never counted as zero
239
+ if (unit u3 qualify c x.c three) 2>/dev/null; then echo "self-test: a missing unit was not refused" >&2; fail=1; fi
240
+ [ "$fail" = 0 ] && echo "audit-overlap self-test: ok"
241
+ return "$fail"
242
+ }
243
+
244
+ case "${1:-}" in
245
+ --self-test) self_test ;;
246
+ -h|--help|"") sed -n '5,9p' "$0"; [ -n "${1:-}" ] ;;
247
+ *) [ $# = 2 ] || die "usage: $0 <omx-clap-core dir> <plugin-hostd dir>"
248
+ ROOT[qualify]="$Q"; ROOT[clap-core]="$(cd "$1" && pwd)"; ROOT[hostd]="$(cd "$2" && pwd)"
249
+ audit ;;
250
+ esac