mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-01-09 00:17:59 +08:00
App bundling improvements (#1279)
This commit is contained in:
parent
5a3f5ff4b6
commit
4ec919f60b
11 changed files with 110 additions and 54 deletions
|
@ -46,7 +46,6 @@ defmodule WxDemo.MixProject do
|
|||
]
|
||||
]
|
||||
],
|
||||
event_handler: WxDemo.Window,
|
||||
macos: [
|
||||
build_dmg: macos_notarization != nil,
|
||||
notarization: macos_notarization
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AppBuilder do
|
||||
def bundle(release) do
|
||||
options = validate_options(release.options[:app])
|
||||
options = validate_options(release.options[:app] || [])
|
||||
|
||||
case os() do
|
||||
:macos ->
|
||||
|
@ -19,25 +19,28 @@ defmodule AppBuilder do
|
|||
end
|
||||
|
||||
def init do
|
||||
{:ok, _} = Registry.register(AppBuilder.Registry, "app_event_subscribers", [])
|
||||
|
||||
if input = System.get_env("APP_BUILDER_INPUT") do
|
||||
__rpc__(self(), input)
|
||||
__rpc__(input)
|
||||
end
|
||||
end
|
||||
|
||||
def __rpc__(event_handler) do
|
||||
input = IO.read(:line) |> String.trim()
|
||||
__rpc__(event_handler, input)
|
||||
def __rpc__ do
|
||||
IO.read(:line)
|
||||
|> String.trim()
|
||||
|> __rpc__()
|
||||
end
|
||||
|
||||
def __rpc__(event_handler, "open_app") do
|
||||
send(event_handler, :open_app)
|
||||
def __rpc__("open_app") do
|
||||
dispatch(:open_app)
|
||||
end
|
||||
|
||||
def __rpc__(event_handler, "open_url:" <> url) do
|
||||
send(event_handler, {:open_url, url})
|
||||
def __rpc__("open_url:" <> url) do
|
||||
dispatch({:open_url, url})
|
||||
end
|
||||
|
||||
def __rpc__(event_handler, "open_file:" <> path) do
|
||||
def __rpc__("open_file:" <> path) do
|
||||
path =
|
||||
if os() == :windows do
|
||||
String.replace(path, "\\", "/")
|
||||
|
@ -45,7 +48,13 @@ defmodule AppBuilder do
|
|||
path
|
||||
end
|
||||
|
||||
send(event_handler, {:open_file, path})
|
||||
dispatch({:open_file, path})
|
||||
end
|
||||
|
||||
defp dispatch(message) do
|
||||
Registry.dispatch(AppBuilder.Registry, "app_event_subscribers", fn entries ->
|
||||
for {pid, _} <- entries, do: send(pid, message)
|
||||
end)
|
||||
end
|
||||
|
||||
defp validate_options(options) do
|
||||
|
@ -55,7 +64,6 @@ defmodule AppBuilder do
|
|||
all: [
|
||||
:name,
|
||||
:icon_path,
|
||||
:event_handler,
|
||||
url_schemes: [],
|
||||
document_types: [],
|
||||
additional_paths: []
|
||||
|
@ -83,15 +91,20 @@ defmodule AppBuilder do
|
|||
windows: []
|
||||
}
|
||||
|
||||
options = validate_options(options, root_allowed_options, os)
|
||||
|
||||
Keyword.update!(options, :document_types, fn document_types ->
|
||||
options
|
||||
|> validate_options(root_allowed_options, os)
|
||||
|> Keyword.put_new_lazy(:name, &default_name/0)
|
||||
|> Keyword.update!(:document_types, fn document_types ->
|
||||
Enum.map(document_types, fn options ->
|
||||
validate_options(options, document_type_allowed_options, os)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp default_name do
|
||||
Mix.Project.config()[:app] |> to_string |> Macro.camelize()
|
||||
end
|
||||
|
||||
defp validate_options(options, allowed, os) do
|
||||
{macos_options, options} = Keyword.pop(options, :macos, [])
|
||||
{windows_options, options} = Keyword.pop(options, :windows, [])
|
||||
|
|
13
app_builder/lib/app_builder/application.ex
Normal file
13
app_builder/lib/app_builder/application.ex
Normal file
|
@ -0,0 +1,13 @@
|
|||
defmodule AppBuilder.Application do
|
||||
@moduledoc false
|
||||
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
children = [
|
||||
{Registry, keys: :duplicate, name: AppBuilder.Registry}
|
||||
]
|
||||
|
||||
Supervisor.start_link(children, strategy: :one_for_one, name: AppBuilder.Supervisor)
|
||||
end
|
||||
end
|
|
@ -1,13 +1,8 @@
|
|||
<%
|
||||
|
||||
event_handler = @app_options |> Keyword.fetch!(:event_handler) |> inspect()
|
||||
additional_paths = Enum.map_join(@app_options[:additional_paths], ":", &"\\(resourcePath)/#{&1}")
|
||||
|
||||
additional_paths =
|
||||
["rel/erts-#{@release.erts_version}/bin"] ++ @app_options[:additional_paths]
|
||||
|> Enum.map_join(":", &"\\(resourcePath)/#{&1}")
|
||||
|
||||
%>
|
||||
import Cocoa
|
||||
%>import Cocoa
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
var releaseTask: Process!
|
||||
|
@ -45,7 +40,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func startRelease(_ input : String) -> Process {
|
||||
|
@ -78,7 +72,7 @@ func rpc(_ event: String) {
|
|||
let task = buildReleaseTask()
|
||||
task.standardInput = input
|
||||
input.fileHandleForWriting.write("\(event)\n".data(using: .utf8)!)
|
||||
task.arguments = ["rpc", "AppBuilder.__rpc__(<%= event_handler %>)"]
|
||||
task.arguments = ["rpc", "AppBuilder.__rpc__()"]
|
||||
try! task.run()
|
||||
task.waitUntilExit()
|
||||
|
||||
|
@ -88,17 +82,17 @@ func rpc(_ event: String) {
|
|||
}
|
||||
|
||||
func buildReleaseTask() -> Process {
|
||||
let releaseScriptPath = Bundle.main.path(forResource: "rel/bin/<%= @release.name %>", ofType: "")!
|
||||
let task = Process()
|
||||
task.launchPath = Bundle.main.path(forResource: "rel/bin/<%= @release.name %>", ofType: "")!
|
||||
task.environment = ProcessInfo.processInfo.environment
|
||||
|
||||
<%= if additional_paths != "" do %>
|
||||
let resourcePath = Bundle.main.resourcePath ?? ""
|
||||
let additionalPaths = "<%= additional_paths %>"
|
||||
let path = task.environment!["PATH"] ?? ""
|
||||
task.environment!["PATH"] = "\(additionalPaths):\(path)"
|
||||
<% end %>
|
||||
|
||||
var environment = ProcessInfo.processInfo.environment
|
||||
let path = environment["PATH"] ?? ""
|
||||
environment["PATH"] = "\(additionalPaths):\(path)"
|
||||
|
||||
let task = Process()
|
||||
task.environment = environment
|
||||
task.launchPath = releaseScriptPath
|
||||
task.standardOutput = logFile
|
||||
task.standardError = logFile
|
||||
return task
|
||||
|
|
|
@ -7,6 +7,7 @@ app_name = Keyword.fetch!(@app_options, :name)
|
|||
;General
|
||||
|
||||
Name "<%= app_name %>"
|
||||
ManifestDPIAware true
|
||||
OutFile "<%= app_name %>Install.exe"
|
||||
Unicode True
|
||||
InstallDir "$LOCALAPPDATA\<%= app_name %>"
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<%
|
||||
|
||||
event_handler = @app_options |> Keyword.fetch!(:event_handler) |> inspect()
|
||||
|
||||
additional_paths =
|
||||
["rel/erts-#{@release.erts_version}/bin"] ++ @app_options[:additional_paths]
|
||||
|> Enum.map(&("root & \"" <> String.replace(&1, "/", "\\") <> ";\" & "))
|
||||
for path <- Keyword.fetch!(@app_options, :additional_paths), into: "" do
|
||||
"root & \"" <> String.replace(path, "/", "\\") <> ";\" & "
|
||||
end
|
||||
|
||||
%>
|
||||
|
||||
|
@ -19,10 +18,12 @@ End If
|
|||
|
||||
Set shell = CreateObject("WScript.Shell")
|
||||
Set env = shell.Environment("Process")
|
||||
<%= if additional_paths != "" do %>
|
||||
env("PATH") = <%= additional_paths %>env("PATH")
|
||||
<% end %>
|
||||
|
||||
' try release rpc, if release is down, this will fail but that's ok.
|
||||
cmd = "echo " & input & " | """ & script & """ rpc ""AppBuilder.__rpc__(<%= event_handler %>)"""
|
||||
cmd = "echo " & input & " | """ & script & """ rpc ""AppBuilder.__rpc__()"""
|
||||
status = shell.Run("cmd /c " & cmd, 0, true)
|
||||
|
||||
' try release start, if release is up, this will fail but that's ok.
|
||||
|
|
|
@ -20,6 +20,7 @@ defmodule AppBuilder.MixProject do
|
|||
|
||||
def application do
|
||||
[
|
||||
mod: {AppBuilder.Application, []},
|
||||
extra_applications: [:logger, :eex, :inets, :ssl, :crypto]
|
||||
]
|
||||
end
|
||||
|
|
5
mix.exs
5
mix.exs
|
@ -6,6 +6,7 @@ defmodule Livebook.MixProject do
|
|||
@description "Interactive and collaborative code notebooks - made with Phoenix LiveView"
|
||||
|
||||
@app_elixir_version "1.13.4"
|
||||
@app_rebar3_version "3.19.0"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
@ -160,8 +161,8 @@ defmodule Livebook.MixProject do
|
|||
]
|
||||
]
|
||||
],
|
||||
event_handler: LivebookApp,
|
||||
additional_paths: [
|
||||
"rel/erts-#{:erlang.system_info(:version)}/bin",
|
||||
"rel/vendor/elixir/bin"
|
||||
],
|
||||
macos: [
|
||||
|
@ -201,5 +202,7 @@ defmodule Livebook.MixProject do
|
|||
release
|
||||
|> Standalone.copy_otp()
|
||||
|> Standalone.copy_elixir(@app_elixir_version)
|
||||
|> Standalone.copy_hex()
|
||||
|> Standalone.copy_rebar3(@app_rebar3_version)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
set RELEASE_NODE=livebook_app
|
||||
set RELEASE_MODE=interactive
|
||||
set MIX_ARCHIVES=!RELEASE_ROOT!\vendor\archives
|
||||
set MIX_REBAR3=!RELEASE_ROOT!\vendor\rebar3
|
||||
set LIVEBOOK_SHUTDOWN_ENABLED=true
|
||||
|
||||
set cookie_path="!RELEASE_ROOT!\releases\COOKIE"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
export RELEASE_NODE=livebook_app
|
||||
export RELEASE_MODE=interactive
|
||||
export LIVEBOOK_SHUTDOWN_ENABLED=true
|
||||
export MIX_ARCHIVES="${RELEASE_ROOT}/vendor/archives"
|
||||
export MIX_REBAR3="${RELEASE_ROOT}/vendor/rebar3"
|
||||
export WX_MACOS_NON_GUI_APP=1
|
||||
export LIVEBOOK_SHUTDOWN_ENABLED=true
|
||||
|
||||
cookie_path="${RELEASE_ROOT}/releases/COOKIE"
|
||||
if [ ! -f $cookie_path ]; then
|
||||
|
|
|
@ -7,7 +7,9 @@ defmodule Standalone do
|
|||
"""
|
||||
@spec copy_otp(Mix.Release.t()) :: Mix.Release.t()
|
||||
def copy_otp(release) do
|
||||
{erts_source, otp_bin_dir, otp_lib_dir} = otp_dirs()
|
||||
erts_source = Path.join(:code.root_dir(), "erts-#{release.erts_version}")
|
||||
otp_bin_dir = Path.join(:code.root_dir(), "bin")
|
||||
otp_lib_dir = :code.lib_dir()
|
||||
|
||||
# 1. copy erts/bin
|
||||
release_erts_bin_dir = Path.join(release.path, "erts-#{release.erts_version}/bin")
|
||||
|
@ -39,6 +41,10 @@ defmodule Standalone do
|
|||
release_lib_dir = Path.join(release.path, "lib")
|
||||
cp_r!(otp_lib_dir, release_lib_dir)
|
||||
|
||||
for dir <- Path.wildcard("#{release_lib_dir}/*/doc/{xml,html,pdf}") do
|
||||
File.rm_rf!(dir)
|
||||
end
|
||||
|
||||
# 3. copy boot files
|
||||
release_bin_dir = Path.join(release.path, "bin")
|
||||
|
||||
|
@ -58,7 +64,7 @@ defmodule Standalone do
|
|||
download_elixir_at_destination(standalone_destination, elixir_version)
|
||||
|
||||
filenames =
|
||||
case os() do
|
||||
case AppBuilder.os() do
|
||||
:macos ->
|
||||
["elixir", "elixirc", "mix", "iex"]
|
||||
|
||||
|
@ -83,12 +89,40 @@ defmodule Standalone do
|
|||
:zip.unzip(String.to_charlist(path), cwd: destination)
|
||||
end
|
||||
|
||||
defp otp_dirs do
|
||||
version = :erlang.system_info(:version)
|
||||
root_dir = :code.root_dir()
|
||||
@doc """
|
||||
Copies Hex into the release.
|
||||
"""
|
||||
@spec copy_hex(Mix.Release.t()) :: Mix.Release.t()
|
||||
def copy_hex(release) do
|
||||
release_archives_dir = Path.join(release.path, "vendor/archives")
|
||||
File.mkdir_p!(release_archives_dir)
|
||||
|
||||
{:filename.join(root_dir, 'erts-#{version}'), :filename.join(root_dir, 'bin'),
|
||||
:code.lib_dir()}
|
||||
hex_version = Keyword.fetch!(Application.spec(:hex), :vsn)
|
||||
source_hex_path = Path.join(Mix.path_for(:archives), "hex-#{hex_version}")
|
||||
release_hex_path = Path.join(release_archives_dir, "hex-#{hex_version}")
|
||||
cp_r!(source_hex_path, release_hex_path)
|
||||
|
||||
release
|
||||
end
|
||||
|
||||
@doc """
|
||||
Copies Rebar3 into the release.
|
||||
"""
|
||||
@spec copy_rebar3(Mix.Release.t(), version :: String.t()) :: Mix.Release.t()
|
||||
def copy_rebar3(release, version) do
|
||||
url = "https://github.com/erlang/rebar3/releases/download/#{version}/rebar3"
|
||||
path = Path.join(System.tmp_dir!(), "rebar3_#{version}")
|
||||
|
||||
unless File.exists?(path) do
|
||||
binary = fetch_body!(url)
|
||||
File.write!(path, binary, [:binary])
|
||||
end
|
||||
|
||||
destination = Path.join(release.path, "vendor/rebar3")
|
||||
File.cp!(path, destination)
|
||||
make_executable(destination)
|
||||
|
||||
release
|
||||
end
|
||||
|
||||
defp fetch_body!(url) do
|
||||
|
@ -105,13 +139,6 @@ defmodule Standalone do
|
|||
|
||||
defp make_executable(path), do: File.chmod!(path, 0o755)
|
||||
|
||||
defp os() do
|
||||
case :os.type() do
|
||||
{:unix, :darwin} -> :macos
|
||||
{:win32, _} -> :windows
|
||||
end
|
||||
end
|
||||
|
||||
defp cp_r!(source, destination) do
|
||||
File.cp_r!(source, destination, fn _, _ -> false end)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue