{erl_opts, [debug_info]}.
%% OTP floor: the device resource type registers a `down` callback via
%% enif_init_resource_type (the ErlNifResourceTypeInit members/dyncall form),
%% an OTP-24 API. midiio cannot build on OTP < 24.
{minimum_otp_vsn, "24"}.
{deps, []}.
{project_plugins, [rebar3_hex]}.
%% ── NIF build wiring (port_compiler) ───────────────────────────────────────
%% Compiles the single NIF translation unit into priv/midiio_nif.so. On Darwin
%% pc links the bundle that erlang:load_nif/2 resolves from "midiio_nif" (no
%% extension); the per-OS link flags pull in the platform MIDI backend.
{plugins, [pc]}.
{provider_hooks, [
{pre, [
{compile, {pc, compile}},
{clean, {pc, clean}}
]}
]}.
{port_specs, [
{"darwin", "priv/midiio_nif.so", ["c_src/midiio_nif.c"]},
{"linux", "priv/midiio_nif.so", ["c_src/midiio_nif.c"]}
]}.
{port_env, [
{"darwin", "CFLAGS", "$CFLAGS -std=c11 -Wall -Wextra"},
{"darwin", "LDFLAGS", "$LDFLAGS -framework CoreMIDI"},
%% _GNU_SOURCE: strict -std=c11 hides POSIX symbols (clock_gettime,
%% CLOCK_MONOTONIC) and makes ALSA's <global.h> redefine struct timespec.
%% Opt back into the POSIX/GNU feature set so <alsa/asoundlib.h> compiles.
{"linux", "CFLAGS", "$CFLAGS -std=c11 -D_GNU_SOURCE -Wall -Wextra"},
{"linux", "LDFLAGS", "$LDFLAGS -lasound -lpthread"}
]}.
{artifacts, ["priv/midiio_nif.so"]}.
%% ── Coverage strategy (NIF-aware) ──────────────────────────────────────────
%% `midiio` is a NIF-binding module: nearly every function is a `?NOT_LOADED`
%% stub whose Erlang body is unreachable once the .so loads (the C NIF replaces
%% it). Line coverage on it is structurally near-zero and *decays* as each slice
%% adds NIFs (slice 1: 33%, slice 3: 22%). So it is excluded from the cover
%% metric — its surface is verified by **eunit + the ASan harness**, not by
%% `midiio.beam` line %. The `min_coverage` floor (below) therefore gates the
%% real-logic Erlang modules; there are none yet (the per-device gen_server +
%% helpers land in arc 2/3), so the gate is dormant now and becomes a true gate
%% automatically when they arrive — without ever forcing the floor down again.
{cover_excl_mods, [midiio]}.
{xref_checks, [
undefined_function_calls, undefined_functions, locals_not_used,
deprecated_function_calls, deprecated_functions
]}.
{dialyzer, [
{warnings, [unknown]}
%% plt_extra_apps is scoped to the test profile (below): only the `as test`
%% path analyses the eunit/proper test modules and needs those apps in the
%% PLT. A bare `rebar3 dialyzer` (default profile) sees only src and must NOT
%% reference proper (a test-only dep), or it fails "Could not find
%% application: proper" (slice-4 finding #2 / F3).
]}.
{profiles, [
{test, [
{deps, [
{proper, {git, "https://github.com/proper-testing/proper", {tag, "v1.3"}}}
]},
{plugins, [
{rebar3_proper, {git, "https://github.com/ferd/rebar3_proper", {tag, "0.12.1"}}}
]},
{eunit_opts, [verbose]},
%% Run eunit cover-instrumented so the `coverage` step has real coverdata
%% for midiio.beam (the NIF upgrade callback makes the module reloadable).
{cover_enabled, true},
%% Test-profile-only: the `as test` dialyzer/check path also analyses the
%% eunit/proper test modules, so teach the PLT about those apps here (not
%% top-level, so a bare default-profile `rebar3 dialyzer` stays clean).
{dialyzer, [{plt_extra_apps, [eunit, proper]}]}
%% F2/Fix-2 (gate seam_roundtrip + the other test NIFs behind -DMIDIIO_TEST)
%% was attempted again in the slice-2 S2 remediation and reverted — the same
%% L18 hazard: pc builds one shared priv/*.so keyed on source mtime (not
%% CFLAGS), so a profile macro doesn't trigger a rebuild. Forcing a rebuild
%% via a compile pre_hook (rm the .so/.o) breaks rebar3's {artifacts,...}
%% check ("Missing artifact priv/midiio_nif.so"). Unexported-NIF gating also
%% fails — load_nif requires the NIF exported ({bad_lib,"Function not
%% found"}). So the test NIFs stay in the surface; they are MEMORY-SAFE
%% (seam_roundtrip self-defends on length, Fix 1, ASan-proven). Re-entry:
%% a per-profile .so artifact path. S3 hygiene; safety is closed.
]},
{dev, [
{deps, [lfe]},
{plugins, [rebar3_lfe]}
]},
{maintainer, [
{plugins, [rebar3_hex]}
]}
]}.
{alias, [
{coverage, [
{proper, "-c"},
%% Real target for real-logic modules (see cover_excl_mods above). With
%% the NIF-binding module excluded there are no logic modules yet, so the
%% gate is dormant; this floor becomes a true gate when arc-2/3 modules
%% land. Adding another NIF no longer forces the floor down.
{cover, "-v --min_coverage=80"}
]},
{check, [
compile,
xref,
dialyzer,
eunit,
coverage
]},
{publish, [
compile,
{hex, "publish package"}
]}
]}.