feat: pipeline to create a full mac app
This commit is contained in:
98
lib/mix/tasks/bds.bundle.macos.ex
Normal file
98
lib/mix/tasks/bds.bundle.macos.ex
Normal file
@@ -0,0 +1,98 @@
|
||||
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
|
||||
Reference in New Issue
Block a user