Warnings for the two ways a plugin's NIF silently misses the installed app (MOB-281).
A plugin's on_load tolerates a missing NIF, so neither case fails a build
or a boot — the first signal is {:nif_not_loaded, ...} from the first NIF
call, with nothing pointing at the cause:
Installed but not activated. The plugin is in
mix.exsdeps but not inconfig :mob, :pluginsinmob.exs. Activation is the deliberate second opt-in step (seeMOB_PLUGINS.md), so the native build compiles none of its NIFs.mix mob.deploy --nativeandmix mob.doctorname every such device-runtime dep (notonly: :dev/runtime: false) that declaresnifs:and print the exactconfigline.Activated after the last native build. The plugin is activated, but only a BEAM-only
mix mob.deploy/mix mob.pushran since, so the installed binary predates it. Every successful native build records, per platform, which activated plugins it compiled NIFs for (mob_native_plugins.txtunderMix.Project.build_path/0); a BEAM-only deploy or push compares the current activation against that record and names the plugins the installed app was built without.
The checks themselves are advisory: warnings, never errors, and a failure to
write the record is itself only a warning. (A mob.exs that fails to load
still raises, as every mob.exs reader does — MOB-280.) The record describes the last native
build on this machine for this MIX_ENV, not what a particular device has
installed — a device last installed from another checkout or before a
mix clean can still disagree with it. A missing record (a project built
before this check existed, or a wiped _build) is reported separately from
a known stale build.
Manifests are read through MobDev.Plugin.Verify.load_verified/2, like
every other build path (MOB-74): a dep whose manifest fails verification is
never evaluated, so its NIFs are unknown and it is not reported here.
Summary
Types
Per-platform plugin names compiled into the last native build.
{dep_name, manifest} — nil for a non-plugin dep or an unverifiable manifest.
One stale-build finding: the plugin, the platform, and why.
Functions
The activated plugin names, non-atom entries dropped.
The config :mob, :plugins, [...] line that activates inactive on top of
the currently activated plugins — the whole list, so it replaces the
existing line verbatim.
Every dep as {name, manifest}, manifests read through
Verify.load_verified/2 (honouring :acknowledge_unsafe_plugins, like
MobDev.Plugin.activated_with_verify/0). Unverifiable manifests are nil.
Plugins activated now (current, from nif_plugins_by_platform/2) that the
recorded native build for each of platforms lacks. :not_built when that
platform has a record without the plugin; :no_record when that platform
was never recorded. Sorted by plugin, then platform.
The yellow warning block for drift/3's result, or nil when empty.
inactive_nif_plugins/3 for this project, against the activated plugins.
Device-runtime deps that ship a manifest declaring at least one NIF but are
not in activated. runtime is the set of dep names that ship to the device
(MobDev.HotPush.runtime_lib_names/0): an only: :dev or runtime: false
dep never reaches the app, so activating it would fix nothing. Tier-0
plugins (no manifest), manifests without nifs:, and activated plugins are
never flagged. Sorted.
The yellow warning block for inactive_nif_plugins/3's result, or nil
when it is empty. activated is the current config :mob, :plugins list;
the printed config line is that list plus the inactive plugins, so it can
replace the existing line verbatim.
For each platform, the activated plugins whose manifest declares a NIF for
it (a NIF without :platform counts for both). Every platform is a key,
possibly with []. Names are strings, the form the record stores.
The platforms of connected device nodes of the project app app, from the
node names MobDev.Device.node_name/1 builds: <app>_android[_<suffix>]@…
and <app>_ios[_<suffix>]@…. The app prefix is stripped before reading the
platform, so an app named e.g. my_ios_app isn't mistaken for iOS. Nodes of
other apps are ignored.
Parses the record file's text. One line per platform — the platform name
followed by the space-separated plugin names; # lines are comments.
Unknown platforms and malformed lines are ignored.
record with platform's entry replaced by names; other platforms kept.
Reads the record at path; a missing or unreadable file is %{}.
Records that a native build for each of platforms just succeeded with the
currently activated NIF plugins. Other platforms' entries are kept.
Merges current's entries for platforms into the record at path. The
record is advisory: a write failure prints a warning and returns :ok
rather than failing the build that just succeeded.
Where the native-build record lives: under Mix.Project.build_path/0.
Renders a record as the text parse_record/1 reads.
Prints inactive_warning/2 for this project, if any.
Prints drift_warning/1 for a BEAM-only deploy or push to platforms,
if any.
Types
@type build_record() :: %{optional(:android | :ios) => [String.t()]}
Per-platform plugin names compiled into the last native build.
{dep_name, manifest} — nil for a non-plugin dep or an unverifiable manifest.
@type drift() :: {atom(), :android | :ios, :not_built | :no_record}
One stale-build finding: the plugin, the platform, and why.
Functions
@spec activated() :: [atom()]
The activated plugin names, non-atom entries dropped.
The config :mob, :plugins, [...] line that activates inactive on top of
the currently activated plugins — the whole list, so it replaces the
existing line verbatim.
@spec dep_manifests() :: [dep_manifest()]
Every dep as {name, manifest}, manifests read through
Verify.load_verified/2 (honouring :acknowledge_unsafe_plugins, like
MobDev.Plugin.activated_with_verify/0). Unverifiable manifests are nil.
@spec drift(build_record(), build_record(), [:android | :ios]) :: [drift()]
Plugins activated now (current, from nif_plugins_by_platform/2) that the
recorded native build for each of platforms lacks. :not_built when that
platform has a record without the plugin; :no_record when that platform
was never recorded. Sorted by plugin, then platform.
The yellow warning block for drift/3's result, or nil when empty.
inactive_nif_plugins/3 for this project, against the activated plugins.
@spec inactive_nif_plugins([dep_manifest()], [atom()], MapSet.t(String.t())) :: [ atom() ]
Device-runtime deps that ship a manifest declaring at least one NIF but are
not in activated. runtime is the set of dep names that ship to the device
(MobDev.HotPush.runtime_lib_names/0): an only: :dev or runtime: false
dep never reaches the app, so activating it would fix nothing. Tier-0
plugins (no manifest), manifests without nifs:, and activated plugins are
never flagged. Sorted.
The yellow warning block for inactive_nif_plugins/3's result, or nil
when it is empty. activated is the current config :mob, :plugins list;
the printed config line is that list plus the inactive plugins, so it can
replace the existing line verbatim.
@spec nif_plugins_by_platform([dep_manifest()], [atom()]) :: build_record()
For each platform, the activated plugins whose manifest declares a NIF for
it (a NIF without :platform counts for both). Every platform is a key,
possibly with []. Names are strings, the form the record stores.
The platforms of connected device nodes of the project app app, from the
node names MobDev.Device.node_name/1 builds: <app>_android[_<suffix>]@…
and <app>_ios[_<suffix>]@…. The app prefix is stripped before reading the
platform, so an app named e.g. my_ios_app isn't mistaken for iOS. Nodes of
other apps are ignored.
@spec parse_record(String.t()) :: build_record()
Parses the record file's text. One line per platform — the platform name
followed by the space-separated plugin names; # lines are comments.
Unknown platforms and malformed lines are ignored.
@spec put_record(build_record(), :android | :ios, [String.t()]) :: build_record()
record with platform's entry replaced by names; other platforms kept.
@spec read_record(Path.t()) :: build_record()
Reads the record at path; a missing or unreadable file is %{}.
@spec record_native_build([:android | :ios]) :: :ok
Records that a native build for each of platforms just succeeded with the
currently activated NIF plugins. Other platforms' entries are kept.
@spec record_native_build([:android | :ios], build_record(), Path.t()) :: :ok
Merges current's entries for platforms into the record at path. The
record is advisory: a write failure prints a warning and returns :ok
rather than failing the build that just succeeded.
@spec record_path() :: Path.t()
Where the native-build record lives: under Mix.Project.build_path/0.
@spec render_record(build_record()) :: String.t()
Renders a record as the text parse_record/1 reads.
@spec warn_inactive() :: :ok
Prints inactive_warning/2 for this project, if any.
@spec warn_stale_build([:android | :ios]) :: :ok
Prints drift_warning/1 for a BEAM-only deploy or push to platforms,
if any.