# `MobDev.Plugin.Merge`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/plugin/merge.ex#L1)

Gathers the contributions of the activated plugins into combined lists the
build pipeline consumes.

Pure: every function takes `plugins` — a list of `{plugin_dir, manifest}` for
the activated plugins — and returns the merged contribution for one build
concern (NIFs, permissions, gradle deps, frameworks, native sources, …).
Path-bearing declarations are resolved to absolute paths against each
plugin's own directory, so contributions from different plugins don't
collide or get misresolved. Tier-0 (nil-manifest) plugins contribute nothing.

Discovery (deps → activated → load manifest) is the caller's job; this module
is the testable transform once the manifests are in hand.

# `plugin`

```elixir
@type plugin() :: {Path.t(), map() | nil}
```

# `android_permissions`

```elixir
@spec android_permissions([plugin()]) :: [String.t()]
```

Unique Android permission strings declared across plugins.

# `android_sources`

```elixir
@spec android_sources([plugin()]) :: [String.t()]
```

Absolute paths of all plugin Android native sources (`bridge_kt`,
`jni_source`) plus NIF `native_dir`s — everything the Android build must
compile in.

# `assets`

```elixir
@spec assets([plugin()]) :: [map()]
```

Asset declarations across plugins, with font/image paths resolved to absolute
and tagged with `:plugin` (used by the `plugin://<name>/<file>` image resolver
and the native font-bundling merge).

# `bridge_classes`

```elixir
@spec bridge_classes([plugin()]) :: [String.t()]
```

Fully-qualified Kotlin class names (e.g. `"io.mob.bluetooth.MobBluetoothBridge"`)
each activated plugin wants registered at startup. `native_build` generates a
`MobPluginBootstrap.registerAll/0` that calls `<class>.register()` for each, so
the plugin's `nativeRegister` thunk can cache its own jclass + method IDs.

# `bridge_kt_sources`

```elixir
@spec bridge_kt_sources([plugin()]) :: [String.t()]
```

Absolute paths of plugin Android `bridge_kt` Kotlin sources. `native_build`
copies each into the app source tree (at its package-derived path) before
`gradle assembleDebug`, so the app's Kotlin sourceSet compiles it.

# `gradle_deps`

```elixir
@spec gradle_deps([plugin()]) :: [String.t()]
```

Unique Android gradle dependency strings across plugins.

# `host_requirements`

```elixir
@spec host_requirements([plugin()]) :: [%{plugin: atom(), requirement: String.t()}]
```

Host-app obligations the plugin system can't automate (e.g. AndroidManifest
fragments — a typed foreground `<service>`, a `FileProvider`), tagged with
`:plugin`. The native build prints these so a host author can't activate a
plugin and only discover the missing manual step at first feature use.

# `ios_frameworks`

```elixir
@spec ios_frameworks([plugin()]) :: [String.t()]
```

Unique iOS framework names across plugins.

# `jni_sources`

```elixir
@spec jni_sources([plugin()]) :: [String.t()]
```

Absolute paths of plugin Android `jni_source` files — plain JNI-thunk C
(e.g. `Java_<pkg>_<Class>_nativeDeliver*`) that the build compiles into the
app `.so` without a NIF-init libname (unlike `nif_sources/1`). Fed to the
build's `-Dplugin_jni_sources` arg.

# `lifecycle`

```elixir
@spec lifecycle([plugin()]) :: [map()]
```

Lifecycle declarations across plugins, each tagged with `:plugin`.

# `migrations`

```elixir
@spec migrations([plugin()]) :: [map()]
```

Migration declarations across plugins, with `:migrations_dir` resolved to an
absolute path and tagged with `:plugin` + `:repo_namespace`.

# `nif_sources`

```elixir
@spec nif_sources([plugin()]) :: [String.t()]
```

Absolute paths of each plugin **C-family** NIF's primary source — entries with
no `:lang`, `lang: :c` (`<module>.c`), or `lang: :objc` (`<module>.m`, compiled
as Objective-C so iOS plugins can drive Apple frameworks like CoreLocation).

Convention: for a manifest entry `%{module: :foo_nif, native_dir: "priv/jni"}`
the source is `<plugin_dir>/priv/jni/foo_nif.c` (or `.m` for `lang: :objc`).
This is the `<name>.c` pattern the build.zig templates already use for
project-level NIFs (`c_src/<name>.c`), extended to plugins. Returned paths feed
the build's `-Dplugin_c_nifs` arg; build.zig derives the NIF name from the
basename and applies `-DSTATIC_ERLANG_NIF_LIBNAME=<name>` so ERL_NIF_INIT emits
the static-init symbol the driver table references (and adds `-fobjc-arc` for
`.m` sources).

# `nif_sources`

```elixir
@spec nif_sources([plugin()], :ios | :android | :all) :: [String.t()]
```

Like `nif_sources/1` but restricted to NIFs the given platform compiles.

A NIF entry with `platform: :ios | :android` is only compiled on that
platform; an entry with no `:platform` is compiled everywhere. Lets a
cross-platform plugin ship an iOS C/ObjC NIF and an Android NIF for the same
module without the iOS source (which may reference iOS-only symbols) ending up
in the Android build, and vice-versa. `:all` keeps every entry.

# `nifs`

```elixir
@spec nifs([plugin()]) :: [map()]
```

Combined NIF entries across all plugins, with `:native_dir` resolved to an
absolute path. Shape matches `MobDev.StaticNifs` entries (`:module` plus the
plugin's native source dir), so the result can be fed straight into
`StaticNifs.resolve/1`.

# `notification_handlers`

```elixir
@spec notification_handlers([plugin()]) :: [map()]
```

Notification handlers across all plugins, flattened in plugin-then-declaration
order (first match wins at dispatch), each tagged with `:plugin`.

# `plist_keys`

```elixir
@spec plist_keys([plugin()]) :: map()
```

Merged iOS `plist_keys` across plugins (later plugins win on conflict).

# `screens`

```elixir
@spec screens([plugin()]) :: [map()]
```

Static screen declarations across plugins, each tagged with `:plugin`.

Generator-produced screens (spec-v2 `:screens_generator`) are folded in
separately by the runtime-manifest codegen; this is the static `:screens` half.

# `settings`

```elixir
@spec settings([plugin()]) :: [map()]
```

Settings declarations across plugins, each tagged with `:plugin`.

# `static_archives`

```elixir
@spec static_archives([plugin()], :ios | :android | :all) :: [map()]
```

Cross-compiled C++ static-archive contributions (`lang: :cpp_archive`) across
plugins, resolved for one platform.

Unlike `nif_sources/2` (a single source `build.zig` compiles inline), a
`:cpp_archive` NIF is a set of C++ sources cross-compiled into a `libNAME.a`
by `MobDev.Plugin.CppArchive` and static-linked into the app — the path for
heavyweight NIFs like the Nx/Eigen CPU backend (Eigen headers, RTTI/exceptions,
per-arch CXXFLAGS) that don't fit the single-source model.

Each returned spec has `:sources` (absolute) and `:includes` resolved: a
plugin-relative string becomes absolute against the plugin dir, while a
`{:dep, name, subpath}` token is passed through unchanged for the build to
resolve against `Mix.Project.deps_path/0` (Eigen/Fine live in the plugin's
*deps*, not its own tree, and this module stays Mix-free/pure). CXXFLAGS and
`:nm_symbol` pass through; the entry is tagged with `:plugin`.

# `swift_files`

```elixir
@spec swift_files([plugin()]) :: [String.t()]
```

Absolute paths of all plugin iOS Swift source files.

# `ui_components`

```elixir
@spec ui_components([plugin()]) :: [map()]
```

Combined `ui_components` entries across plugins.

# `zig_nif_sources`

```elixir
@spec zig_nif_sources([plugin()]) :: [String.t()]
```

Absolute paths of each plugin **zig** NIF's primary source (NIFs whose
manifest entry has `lang: :zig`).

Same `<plugin_dir>/<native_dir>/<module>.zig` convention as the C path, but
fed to the build's `-Dplugin_zig_nifs` arg and compiled via `addZigObject`.
Unlike C, no `-DSTATIC_ERLANG_NIF_LIBNAME` is needed — the zig source names
its own `export fn <module>_nif_init()` directly. The plugin source reaches
mob-core bindings via the named imports `@import("erts")` / `@import("jni")`
that build.zig wires for plugin zig objects.

# `zig_nif_sources`

```elixir
@spec zig_nif_sources([plugin()], :ios | :android | :all) :: [String.t()]
```

Like `zig_nif_sources/1` but restricted to the given platform (see `nif_sources/2`).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
