defmodule Mix.Linx.Preflight do
@moduledoc false
# Shared preflight for the native compilers (`compile.netlink_nif`,
# `compile.linx_process`, etc.). Run at the top of each task's `run/1`, before
# any `cc` invocation, so a build on the wrong machine fails (or warns) with a
# clear, actionable message instead of a raw compiler dump.
#
# Guards only — the duplicated compile scaffolding in the tasks is intentionally
# left as-is for now (see docs/release/06_PLAN_build_portability.md).
# The supported kernel floor, matching the README. The C syscall-availability
# floor is lower (~5.8); this is the version Linx is built and tested against.
@kernel_floor {6, 6}
@doc "Platform/toolchain preflight. `cc` is the C compiler command."
@spec check!(String.t()) :: :ok
def check!(cc) do
os_guard!()
cc_guard!(cc)
kernel_warn()
:ok
end
defp os_guard! do
case :os.type() do
{:unix, :linux} ->
:ok
other ->
Mix.raise(
"Linx provides Linux kernel interfaces and only builds on Linux. " <>
"Detected: #{inspect(other)}. See the README requirements."
)
end
end
defp cc_guard!(cc) do
if System.find_executable(cc) do
:ok
else
Mix.raise(
"Linx needs a C compiler (`#{cc}`) to build its native code, but none was " <>
"found on PATH. Install one — Debian/Ubuntu: `sudo apt install build-essential`; " <>
"Arch: `sudo pacman -S base-devel` — or set the `CC` environment variable. " <>
"See the README build prerequisites."
)
end
end
# Warn (do not fail) when the build-host kernel is below the supported floor:
# cross-compiling for a newer target (e.g. Nerves) is legitimate. Emitted once
# per `mix compile`, not once per native compiler.
defp kernel_warn do
if :persistent_term.get({__MODULE__, :warned}, false) do
:ok
else
:persistent_term.put({__MODULE__, :warned}, true)
maybe_warn_kernel()
end
end
defp maybe_warn_kernel do
with {:ok, raw} <- File.read("/proc/sys/kernel/osrelease"),
rel = String.trim(raw),
[maj, min | _] <- String.split(rel, "."),
{maj, _} <- Integer.parse(maj),
{min, _} <- Integer.parse(min),
true <- {maj, min} < @kernel_floor do
{fmaj, fmin} = @kernel_floor
Mix.shell().info(
"[linx] building on kernel #{rel}; Linx targets #{fmaj}.#{fmin} LTS or newer. " <>
"Older kernels may hit ENOSYS at runtime (ignore if cross-compiling for a newer target)."
)
else
_ -> :ok
end
end
end