MobDev.OtpAssetBundle (mob_dev v0.3.37)

Copy Markdown View Source

Builds the assets/otp.zip that release-mode Android Mob apps extract on first launch.

Mirrors the elixir-desktop example-app pattern (their Bridge.kt:unpackZip()) but with Mob-specific stripping inherited from the iOS release pass — drops unused OTP libs and standalone executables to shave bundle size and avoid shipping anything Mob never actually executes.

Called from mix mob.release --android (Workstream 3). Kept as a separate module so the strip rules + zip layout are testable without spinning up the full release pipeline.

Layout produced

otp.zip
 erts-VSN/...           (BEAM emulator support  minus bin/)
 lib/elixir-VSN/...
 lib/logger-VSN/...
 lib/eex-VSN/...
 lib/<other-otp-libs-the-app-uses>/...
 releases/...

The MobBridge.extractOtpIfNeeded() Kotlin function unzips this into <filesDir>/otp/ on first launch, keyed by PackageInfo.lastUpdateTime so app updates trigger a re-extract.

What gets stripped

Three categories, mirroring the iOS strip pass in MobDev.Release:

  1. Whole OTP libs the framework doesn't usemegaco, runtime_tools, wx, observer, debugger, etc. These are dead weight in a Mob app.
  2. Standalone executablespriv/bin/* and erts-*/bin/*. The few that Mob actually executes (erl_child_setup, inet_gethost, epmd) are packaged separately in jniLibs/<abi>/ as lib<name>.so so they get the apk_data_file SELinux label that allows execve from untrusted_app. The rest aren't reachable.
  3. Static archives (.a) — already linked into the app's native lib at build time. Shipping them inside the runtime tree is pure waste.

.so files inside OTP libs (e.g. priv/lib/asn1.so) are KEPT in the zip — they're loaded by the BEAM at runtime via dlopen(), which works fine from app data dir on Android (only execve() is blocked).

Summary

Functions

Builds the OTP asset zip from source_otp_tree into target_zip_path.

Returns the list of OTP lib name prefixes that are stripped by default. Public so tests can assert the policy without re-importing the module-private list.

Functions

build(source_otp_tree, target_zip_path, opts \\ [])

@spec build(Path.t(), Path.t(), keyword()) :: {:ok, map()} | {:error, term()}

Builds the OTP asset zip from source_otp_tree into target_zip_path.

Returns {:ok, %{zipped_files: count, original_size_kb: integer, zip_size_kb: integer}} on success, {:error, reason} on failure.

Options

  • :strip_extra_prefixes — additional OTP lib prefixes to drop on top of the default list. Atoms or strings.
  • :keep_prefixes — prefixes to KEEP even if in the default strip list. Lets a specific app opt back into a stripped lib.

default_stripped_prefixes()

@spec default_stripped_prefixes() :: [String.t()]

Returns the list of OTP lib name prefixes that are stripped by default. Public so tests can assert the policy without re-importing the module-private list.