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

Fetches the prebuilt bundle a `lang: :cpp_archive` plugin NIF declares under
`:prebuilt`, and resolves the headers and static libraries it contributes.

Some plugin NIFs sit on a large third-party C++ library that ships prebuilt:
mob_scene3d's iOS renderer is Objective-C++ against Filament, whose release
tarball carries the headers plus one static-library xcframework per component.
That can't ride in a Hex package (tens of MB), and the plugin's own `.a`
(`MobDev.Plugin.CppArchive`) only holds objects compiled from its sources. A
cpp_archive entry therefore names the bundle and what to take from it:

    %{
      module: :mob_scene3d_nif,
      lang: :cpp_archive,
      platform: :ios,
      sources: ["priv/native/ios/mob_scene3d_nif.m", "priv/native/ios/MobScene3dView.mm"],
      includes: ["priv/native/ios", {:prebuilt, "filament/include"}],
      nm_symbol: "mob_scene3d_nif_nif_init",
      prebuilt: %{
        url: "https://github.com/google/filament/releases/download/v1.75.1/filament-v1.75.1-ios.tgz",
        sha256: "afdfdccfb0870a667c73400d9e81e5ec69c8604d867fc4430b9fbfe72934ade3",
        static_libs: %{
          ios_sim: ["filament/lib/libfilament.xcframework/ios-arm64_x86_64-simulator/libfilament.a"],
          ios_device: ["filament/lib/libfilament.xcframework/ios-arm64/libfilament.a"]
        }
      }
    }

- `{:prebuilt, subpath}` in `:includes` is a directory inside the extracted
  bundle, passed to the compiler as `-I`.
- `:static_libs` maps a `CppArchive` target id to archives inside the bundle.
  They are linked into the app beside `lib<module>.a`, through the same
  `-Dplugin_static_libs` / `MOB_PLUGIN_STATIC_LIBS` inputs, so the simulator
  and device deploys and `mix mob.release --ios` all link them.

The bundle is downloaded once per `sha256` into
`~/.mob/cache/plugin-prebuilt/` (or `$MOB_CACHE_DIR/plugin-prebuilt/`) and
shared by every project, the `MobDev.MLXDownloader` pattern. The tarball must
hash to the manifest's `sha256` before anything is extracted: the manifest is
covered by the plugin signature, so the pin carries that trust to a file the
signature cannot cover.

## Offline builds and mirrors

Set `MOB_PLUGIN_PREBUILT_DIR=/path/to/dir` to take each tarball from
`<dir>/<file name of the url>` instead of downloading it. The `sha256` is
checked all the same.

# `t`

```elixir
@type t() :: %{
  :url =&gt; String.t(),
  :sha256 =&gt; String.t(),
  optional(:static_libs) =&gt; %{optional(atom()) =&gt; [String.t()]}
}
```

A cpp_archive entry's `:prebuilt` map.

# `bundle_relative?`

```elixir
@spec bundle_relative?(term()) :: boolean()
```

A path inside an extracted bundle: relative, no `..` component. Pure.

# `dir`

```elixir
@spec dir(t()) :: Path.t()
```

Cache directory for a bundle: `<cache>/plugin-prebuilt/<name>-<sha256 prefix>`,
where `<name>` is the URL's file name without its tarball extension. Keyed by
the hash, so a manifest that pins a new file gets a new directory. Pure apart
from reading `MOB_CACHE_DIR` / `HOME`.

# `ensure`

```elixir
@spec ensure(t()) :: {:ok, Path.t()} | {:error, String.t()}
```

Ensure the bundle is downloaded, verified and extracted. Returns
`{:ok, dir}` (see `dir/1`). A cached extraction is reused only when it records
the same `sha256`; anything else in its place is removed and fetched again.

# `errors`

```elixir
@spec errors(term()) :: [String.t()]
```

Structural problems with a `:prebuilt` map, as messages (empty when it is
well formed): the URL must be https, the `sha256` 64 lowercase hex characters,
and `:static_libs` a map of `CppArchive` targets to relative paths that stay
inside the bundle. `MobDev.Plugin.Manifest.validate/1` reports these, and
`prepare/2` refuses a bundle that has any, since a host build does not run
manifest validation. Pure.

# `prepare`

```elixir
@spec prepare(map(), atom()) :: {:ok, map(), [Path.t()]} | {:error, String.t()}
```

Make a `Merge.static_archives/2` spec ready for `CppArchive.build/3` on
`target_id`: fetch its prebuilt bundle (when it declares one), resolve
`{:prebuilt, _}` includes, and return the bundle archives to link.

Returns `{:ok, spec, link_libs}`; a spec without `:prebuilt` comes back
unchanged with `[]`. Fails, with a message, on a malformed `:prebuilt` or
include, a failed download, hash or extraction, or a listed archive missing
from the bundle.

# `resolve_includes`

```elixir
@spec resolve_includes([term()], Path.t()) :: [term()]
```

Replace each `{:prebuilt, subpath}` include with its path under `root`; other
entries pass through unchanged. Pure.

# `static_libs`

```elixir
@spec static_libs(t(), atom(), Path.t()) :: [Path.t()]
```

The bundle's archives to link for `target_id`, as absolute paths under `root`.
A target the manifest doesn't list links none. Pure.

---

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