fix: made the macos app smaller
This commit is contained in:
@@ -32,6 +32,11 @@ defmodule BDS.Embeddings.Backends.Neural do
|
||||
* Everywhere else — and as a fallback when EMLX is unavailable or explicitly
|
||||
disabled — it runs on optimised native CPU via XLA (`compiler: EXLA`).
|
||||
|
||||
Packaged releases contain only the backend that can run on their platform
|
||||
(`runtime:` conditional deps in mix.exs): macOS releases ship EMLX and no
|
||||
EXLA, Linux/Windows ship EXLA and no EMLX (MLX is Apple-only). The selected
|
||||
backend is therefore started on demand here, never assumed at boot.
|
||||
|
||||
The accelerator can be pinned with `config :bds, :embeddings, accelerator:`
|
||||
to `:auto` (default), `:emlx`, or `:exla`.
|
||||
"""
|
||||
@@ -171,7 +176,20 @@ defmodule BDS.Embeddings.Backends.Neural do
|
||||
@doc false
|
||||
@spec current_accelerator() :: :emlx | :exla
|
||||
def current_accelerator do
|
||||
select_accelerator(configured_accelerator(), emlx_available?(), apple_silicon?())
|
||||
case select_accelerator(
|
||||
configured_accelerator(),
|
||||
emlx_available?(),
|
||||
exla_available?(),
|
||||
apple_silicon?()
|
||||
) do
|
||||
:none ->
|
||||
raise RuntimeError,
|
||||
"no embeddings accelerator available: neither the EMLX (Apple GPU) nor the " <>
|
||||
"EXLA (CPU) application could be started on this machine"
|
||||
|
||||
accelerator ->
|
||||
accelerator
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -179,22 +197,43 @@ defmodule BDS.Embeddings.Backends.Neural do
|
||||
|
||||
Prefer the Apple GPU (EMLX) under `:auto` only when it is both available and
|
||||
running on Apple Silicon; honour an explicit `:emlx`/`:exla` request, but
|
||||
degrade a forced `:emlx` to EXLA when EMLX is not loaded so a misconfigured
|
||||
host still gets working CPU inference instead of crashing.
|
||||
degrade to the other backend when the requested one is not available so a
|
||||
misconfigured host still gets working inference instead of crashing. Only
|
||||
one backend ships per platform (macOS releases exclude EXLA, Linux/Windows
|
||||
exclude EMLX), so the unavailable side is the norm in production, not an
|
||||
edge case.
|
||||
"""
|
||||
@spec select_accelerator(:auto | :emlx | :exla, boolean(), boolean()) :: :emlx | :exla
|
||||
def select_accelerator(:exla, _emlx_available?, _apple_silicon?), do: :exla
|
||||
def select_accelerator(:emlx, true, _apple_silicon?), do: :emlx
|
||||
def select_accelerator(:emlx, false, _apple_silicon?), do: :exla
|
||||
def select_accelerator(:auto, true, true), do: :emlx
|
||||
def select_accelerator(:auto, _emlx_available?, _apple_silicon?), do: :exla
|
||||
@spec select_accelerator(:auto | :emlx | :exla, boolean(), boolean(), boolean()) ::
|
||||
:emlx | :exla | :none
|
||||
def select_accelerator(_configured, false, false, _apple_silicon?), do: :none
|
||||
def select_accelerator(:exla, _emlx?, true, _apple_silicon?), do: :exla
|
||||
def select_accelerator(:exla, true, false, _apple_silicon?), do: :emlx
|
||||
def select_accelerator(:emlx, true, _exla?, _apple_silicon?), do: :emlx
|
||||
def select_accelerator(:emlx, false, true, _apple_silicon?), do: :exla
|
||||
def select_accelerator(:auto, true, _exla?, true), do: :emlx
|
||||
def select_accelerator(:auto, _emlx?, true, _apple_silicon?), do: :exla
|
||||
def select_accelerator(:auto, true, false, _apple_silicon?), do: :emlx
|
||||
|
||||
defp configured_accelerator do
|
||||
config() |> Keyword.get(:accelerator, @default_accelerator)
|
||||
end
|
||||
|
||||
# The backend apps are runtime: false deps on the platforms that can't run
|
||||
# them, so releases exclude one of them entirely and neither is auto-started
|
||||
# in dev/test. Availability therefore means "the app actually starts here":
|
||||
# ensure_all_started errors when the app is absent (or its NIF can't load).
|
||||
# Starting the app is also exactly what the chosen backend needs before
|
||||
# building the serving.
|
||||
defp emlx_available? do
|
||||
Code.ensure_loaded?(EMLX) and Code.ensure_loaded?(EMLX.Backend)
|
||||
started?(:emlx) and Code.ensure_loaded?(EMLX.Backend)
|
||||
end
|
||||
|
||||
defp exla_available? do
|
||||
started?(:exla)
|
||||
end
|
||||
|
||||
defp started?(app) do
|
||||
match?({:ok, _apps}, Application.ensure_all_started(app))
|
||||
end
|
||||
|
||||
defp apple_silicon? do
|
||||
|
||||
@@ -70,17 +70,20 @@ defmodule BDS.MacBundle do
|
||||
frameworks = Path.join(contents, "Frameworks")
|
||||
rel = Path.join(resources, "rel")
|
||||
|
||||
File.rm_rf!(app)
|
||||
Enum.each([macos, resources, frameworks], &File.mkdir_p!/1)
|
||||
# Validate before rm_rf! so a refused build leaves any previous bundle intact.
|
||||
with :ok <- BDS.ReleasePackaging.validate_release_source(release_dir) do
|
||||
File.rm_rf!(app)
|
||||
Enum.each([macos, resources, frameworks], &File.mkdir_p!/1)
|
||||
|
||||
with {:ok, _} <- File.cp_r(release_dir, rel),
|
||||
:ok <- File.cp(icns_path, Path.join(resources, "#{@icon_name}.icns")),
|
||||
:ok <- File.write(Path.join(contents, "Info.plist"), info_plist(version: version)),
|
||||
:ok <- File.write(Path.join(contents, "PkgInfo"), "APPL????"),
|
||||
:ok <- write_launcher(Path.join(macos, @executable)),
|
||||
:ok <- maybe_relocate_dylibs(rel, frameworks, opts),
|
||||
:ok <- maybe_codesign(app, opts) do
|
||||
{:ok, app}
|
||||
with {:ok, _} <- File.cp_r(release_dir, rel),
|
||||
:ok <- File.cp(icns_path, Path.join(resources, "#{@icon_name}.icns")),
|
||||
:ok <- File.write(Path.join(contents, "Info.plist"), info_plist(version: version)),
|
||||
:ok <- File.write(Path.join(contents, "PkgInfo"), "APPL????"),
|
||||
:ok <- write_launcher(Path.join(macos, @executable)),
|
||||
:ok <- maybe_relocate_dylibs(rel, frameworks, opts),
|
||||
:ok <- maybe_codesign(app, opts) do
|
||||
{:ok, app}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@ defmodule BDS.ReleasePackaging do
|
||||
app_release_source = Keyword.fetch!(opts, :app_release_source)
|
||||
mcp_release_source = Keyword.fetch!(opts, :mcp_release_source)
|
||||
|
||||
with :ok <- reset_output(metadata),
|
||||
with :ok <- validate_release_source(app_release_source),
|
||||
:ok <- reset_output(metadata),
|
||||
:ok <- copy_release(app_release_source, metadata.app_root),
|
||||
:ok <- copy_release(mcp_release_source, metadata.resources_root),
|
||||
:ok <- write_manifest(metadata),
|
||||
@@ -65,6 +66,20 @@ defmodule BDS.ReleasePackaging do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Rejects half-assembled releases. A failed `mix release` run can leave `lib/`
|
||||
copied but no `releases/<version>/` (no `env.sh`, no boot scripts) — packaging
|
||||
that produces an app that dies on launch. Refuse it up front instead.
|
||||
"""
|
||||
def validate_release_source(release_dir) do
|
||||
if release_dir |> Path.join("releases/*/env.sh") |> Path.wildcard() == [] do
|
||||
{:error,
|
||||
{:incomplete_release, "#{release_dir} has no releases/*/env.sh — did `mix release` fail?"}}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_platform(platform) when platform in [:macos, :linux, :windows], do: platform
|
||||
defp normalize_platform(:darwin), do: :macos
|
||||
|
||||
|
||||
Reference in New Issue
Block a user