From 0ccaca144e5085208846eaebe3df74da76d58265 Mon Sep 17 00:00:00 2001 From: Dominic Letz Date: Sun, 9 Aug 2026 01:07:03 +0800 Subject: [PATCH] extra_applications(:host): probe fs for :wx instead of code:lib_dir/1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After PR #80 the `extra_applications(:host)` check uses `:code.lib_dir(:wx)` to decide whether to include `:wx` in the generated `desktop.app`. That call consults Erlang's `NameDb` ETS table, which only knows about apps that have been loaded into the running VM. `mix deps.compile` runs each dep's `compile.all` in sequence, and each `compile.all` prunes the code path to only the apps the dep declared (see `Code.delete_paths(current_paths -- loaded_paths)` in `Mix.Tasks.Compile.All`). After the first dep that does not list `:wx` in its apps finishes, `:wx` is removed from both the code path AND from `NameDb`. By the time `desktop.application/0` is evaluated for the `:desktop` dep itself, `code:lib_dir(:wx)` returns `{:error, :bad_name}`, so `:wx` is dropped from `desktop.app` even though the wx app is still on disk and `wx.hrl` is still resolvable. The concrete failure window this opens: 1. `ensure_desktop_wx_erl!/0` runs while `NameDb` is still populated (before `compile.all` prunes), so `wx_headers_resolvable?/0` is true and `src/desktop_wx.erl` is generated with `-include_lib("wx/include/wx.hrl")`. 2. `extra_applications(:host)` runs after pruning, so `:wx` is not added to the app file. 3. `compile.erlang` then fails with `can't find include lib "wx/include/wx.hrl"`. (Side note: `mix deps.compile` passes `--no-code-path-pruning` to disable pruning, but `Mix.Tasks.Compile.All` only honors `--no-prune-code-paths` — different strings — so pruning still happens regardless of the intent.) Fix: probe the filesystem directly, mirroring what `wx_headers_resolvable?/0` already does. The filesystem state is independent of `NameDb` and stays consistent across the build. Tested locally on diode-drive: a fresh `mix deps.compile` previously failed with `can't find include lib "wx/include/wx.hrl"`; after this patch it succeeds and `_build/dev/lib/desktop/ebin/desktop.app` lists `:wx` in its `applications` entry. The behavior on hosts without `:wx` is preserved — `wx_app_on_disk?/0` returns false and the stub backend is used. Co-authored-by: Cursor --- mix.exs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index b13b348..fe71444 100644 --- a/mix.exs +++ b/mix.exs @@ -84,7 +84,14 @@ defmodule Desktop.MixProject do # source file `src/desktop_wx.erl` already adapts to missing wx headers # via `desktop_wx_stub.exs`, so a host build without `:wx` simply # compiles the stub backend. - if :code.lib_dir(:wx) |> is_list() do + # + # NOTE: `:code.lib_dir/1` is unreliable here because it consults Erlang's + # `NameDb` ETS table which can be empty by the time `extra_applications/1` + # is evaluated under `mix deps.compile` — the previous deps' `compile.all` + # prunes the code path and forgets the apps it never listed. Check the + # filesystem directly instead, mirroring what `desktop_wx_stub.exs` + # already does via `wx_headers_resolvable?/0`. + if wx_app_on_disk?() do [:wx] else [] @@ -95,6 +102,17 @@ defmodule Desktop.MixProject do [] end + defp wx_app_on_disk? do + root = List.to_string(:code.root_dir()) + + with {:ok, entries} <- File.ls(Path.join(root, "lib")), + wx_dir <- Enum.find(entries, &String.starts_with?(&1, "wx-")) do + File.exists?(Path.join([root, "lib", wx_dir, "include", "wx.hrl"])) + else + _ -> false + end + end + defp aliases() do [ "test.fast": ["test --exclude wx"],