Files
bDS2/lib/mix/tasks/bds.bundle.macos.ex

99 lines
3.0 KiB
Elixir

defmodule Mix.Tasks.Bds.Bundle.Macos do
@moduledoc """
Build a self-contained, ad-hoc-signed `BDS2.app` macOS bundle.
mix bds.bundle.macos [--app-release PATH] [--output DIR] [--version V]
[--regen-icon] [--register] [--no-codesign]
Assumes the `:bds` release has been built (`MIX_ENV=prod mix release bds`); pass
`--app-release` to point elsewhere. Generates the red-pen icon on first run (or
with `--regen-icon`), assembles the bundle, relocates the wxWidgets dylibs so it
runs on a clean Mac, ad-hoc codesigns it, and (with `--register`) registers the
`bds2://` URL scheme via LaunchServices.
"""
use Mix.Task
alias BDS.MacBundle
alias BDS.MacBundle.Icon
@shortdoc "Builds a self-contained macOS .app bundle (BDS2.app)"
@lsregister "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
@impl Mix.Task
def run(args) do
unless match?({:unix, :darwin}, :os.type()) do
Mix.raise("mix bds.bundle.macos only runs on macOS")
end
{opts, _positional, _invalid} =
OptionParser.parse(args,
strict: [
app_release: :string,
output: :string,
version: :string,
regen_icon: :boolean,
register: :boolean,
codesign: :boolean
]
)
Mix.Task.run("app.config")
version = opts[:version] || Mix.Project.config()[:version]
env_name = Atom.to_string(Mix.env())
release_dir = opts[:app_release] || Path.expand("_build/#{env_name}/rel/bds", File.cwd!())
output_dir = opts[:output] || Path.expand("dist/macos", File.cwd!())
unless File.dir?(release_dir) do
Mix.raise("Release not found at #{release_dir}. Run `MIX_ENV=prod mix release bds` first.")
end
icns = ensure_icon(opts[:regen_icon] == true)
Mix.shell().info("Assembling BDS2.app (version #{version})…")
case MacBundle.assemble(
release_dir: release_dir,
output_dir: output_dir,
icns_path: icns,
version: version,
skip_codesign: opts[:codesign] == false
) do
{:ok, app} ->
maybe_register(app, opts[:register] == true)
Mix.shell().info("Built #{app}")
{:error, reason} ->
Mix.raise("Bundle failed: #{inspect(reason)}")
end
end
defp ensure_icon(regen?) do
icns = Icon.icns_path()
if regen? or not File.exists?(icns) do
Mix.shell().info("Generating AppIcon.icns (blue→red)…")
case Icon.generate() do
{:ok, path} -> path
{:error, reason} -> Mix.raise("Icon generation failed: #{inspect(reason)}")
end
else
icns
end
end
defp maybe_register(_app, false), do: :ok
defp maybe_register(app, true) do
if File.exists?(@lsregister) do
System.cmd(@lsregister, ["-f", app])
Mix.shell().info("Registered bds2:// scheme via LaunchServices")
else
Mix.shell().info("lsregister not found; skip scheme registration")
end
end
end