Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ Full module documentation: [hexdocs.pm/mob_dev](https://hexdocs.pm/mob_dev).

---

## [0.5.17]

### Fixed
- **iOS simulator deploy now boots from a clean `mix mob.deploy --native`** (was device-only). Three gaps made the sim deploy incomplete vs the device path, so the sim crashed on boot even though the device worked:
- The Elixir-distribution apps `elixir`/`logger` were staged only under `lib/<app>/ebin`, which the sim's `mob_beam.m` doesn't add to the code path — boot failed at `ensure_all_started(:elixir)` with "elixir.app not found". They're now flattened into the flat BEAMS_DIR alongside `eex` (which already needed this), where the path resolves.
- `priv/` was only partially staged (`repo/migrations`), so `Application.app_dir(:<app>, "priv/cacerts.pem")` was `:enoent` and `Mob.Certs.load_cacerts!` crashed the boot. The whole `priv/` is now rsynced into the flat dir (cacerts, `mix`/`hex` ebins, vendored static, …), matching the device release.
- `Paths.sim_runtime_dir/0` fell back to `/tmp/otp-ios-sim` for zig-based projects (no `ios/build.sh`), but the runtime is synced to `~/.mob/runtime/ios-sim` — so the launcher and staging disagreed. It now recognizes `ios/build.zig` and returns the default runtime dir.
Verified: a clean `mob.deploy --native` to an iPhone 11 Pro Max sim boots Io, Phoenix endpoint up, embedded Livebook home renders — no manual runtime fixups.

## [0.5.16]

### Fixed
Expand Down
48 changes: 31 additions & 17 deletions lib/mob_dev/native_build.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1988,15 +1988,22 @@ defmodule MobDev.NativeBuild do
end

defp copy_priv_repo_assets(otp_root, app_module) do
src = "priv/repo/migrations"

if File.dir?(src) do
IO.puts(" === Copying priv/repo assets")
dst = Path.join([otp_root, app_module, "priv/repo/migrations"])
# Copy the WHOLE priv/ into BEAMS_DIR/priv (not just repo/migrations), so
# Application.app_dir(:<app>, "priv/...") resolves on device/sim — e.g.
# priv/cacerts.pem (Mob.Certs.load_cacerts!), priv/mix + priv/hex ebins
# (on-device Mix.install), a vendored lib's priv/ (Livebook's static), etc.
# Mirrors the device release's full-priv rsync; without it the sim boot
# crashed at Mob.Certs.load_cacerts! with :enoent on priv/cacerts.pem.
if File.dir?("priv") do
IO.puts(" === Copying priv/ (full)")
dst = Path.join([otp_root, app_module, "priv"])
File.mkdir_p!(dst)
chmod_writable(dst)

{_, status} =
System.cmd("rsync", ["-a", "--no-perms", "priv/", "#{dst}/"], stderr_to_stdout: true)

Path.wildcard("#{src}/*.exs")
|> Enum.each(&File.cp!(&1, Path.join(dst, Path.basename(&1))))
if status != 0, do: raise("rsync priv/ -> #{dst} failed")
end

:ok
Expand Down Expand Up @@ -2027,20 +2034,27 @@ defmodule MobDev.NativeBuild do
end

defp copy_eex_stdlib_to_app(elixir_lib, otp_root, app_module) do
# EEx is part of Elixir but mob_beam.m doesn't add it to the code path.
# Drop it into BEAMS_DIR (flat) so code:where_is_file("eex.app") resolves
# — Ecto's startup needs it.
IO.puts(" === Copying EEx stdlib")
# The iOS sim's mob_beam.m doesn't add lib/<app>/ebin to the code path, so
# the Elixir-distribution apps that copy_elixir_stdlib_to_otp drops under
# lib/ (elixir, logger) — plus eex — are invisible there: boot fails at
# `ensure_all_started(:elixir)` with "elixir.app not found". Drop their .app
# + beams into BEAMS_DIR (flat), which IS on the path, so they resolve.
# (eex was already needed for Ecto's startup; elixir/logger are needed for
# the sim to boot at all. Harmless on device, which also has them in lib/.)
IO.puts(" === Copying Elixir-distribution apps (elixir, logger, eex) to BEAMS_DIR")
dst = Path.join(otp_root, app_module)
File.mkdir_p!(dst)
src_ebin = Path.join([elixir_lib, "eex", "ebin"])

if File.dir?(src_ebin) do
Path.wildcard("#{src_ebin}/*.beam")
|> Enum.each(&File.cp!(&1, Path.join(dst, Path.basename(&1))))
for app <- ~w(elixir logger eex) do
src_ebin = Path.join([elixir_lib, app, "ebin"])

if File.dir?(src_ebin) do
Path.wildcard("#{src_ebin}/*.beam")
|> Enum.each(&File.cp!(&1, Path.join(dst, Path.basename(&1))))

app_file = Path.join(src_ebin, "eex.app")
if File.exists?(app_file), do: File.cp!(app_file, Path.join(dst, "eex.app"))
app_file = Path.join(src_ebin, "#{app}.app")
if File.exists?(app_file), do: File.cp!(app_file, Path.join(dst, "#{app}.app"))
end
end

:ok
Expand Down
13 changes: 13 additions & 0 deletions lib/mob_dev/paths.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,24 @@ defmodule MobDev.Paths do
build_sh_aware?(project_dir) ->
default_runtime_dir()

build_zig?(project_dir) ->
# Zig-based iOS builds (ios/build.zig, no ios/build.sh) sync the runtime
# to default_runtime_dir() in sync_otp_runtime_sim. The launcher must
# agree, or the sim looks in /tmp/otp-ios-sim and boots a non-existent
# runtime ("elixir.app not found"). Match the staging path.
default_runtime_dir()

true ->
legacy_tmp_path()
end
end

@doc false
@spec build_zig?(String.t()) :: boolean()
def build_zig?(project_dir) do
File.exists?(Path.join([project_dir, "ios", "build.zig"]))
end

@doc """
The new default runtime path — under `~/.mob/runtime/` so `mix mob.cache`
can list and clear it the same way it handles the OTP cache.
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule MobDev.MixProject do
def project do
[
app: :mob_dev,
version: "0.5.16",
version: "0.5.17",
elixir: "~> 1.19",
description: "Development tooling for the Mob mobile framework",
source_url: "https://github.com/genericjam/mob_dev",
Expand Down
7 changes: 7 additions & 0 deletions test/mob_dev/paths_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,12 @@ defmodule MobDev.PathsTest do
File.write!(Path.join([project, "ios", "build.sh"]), "echo old\n")
assert Paths.sim_runtime_dir(project_dir: project) == Paths.legacy_tmp_path()
end

test "new default for zig-based iOS projects (ios/build.zig, no build.sh)",
%{project: project} do
System.delete_env("MOB_SIM_RUNTIME_DIR")
File.write!(Path.join([project, "ios", "build.zig"]), "// zig build\n")
assert Paths.sim_runtime_dir(project_dir: project) == Paths.default_runtime_dir()
end
end
end
Loading