Packages
nerves
1.7.11
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.0
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.3
1.9.2
retired
1.9.1
1.9.0
1.8.0
1.7.17
1.7.16
1.7.15
1.7.14
retired
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
retired
1.7.1
retired
1.7.0
retired
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.11.0
0.10.1
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-rc.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
Craft and deploy bulletproof embedded software
Current section
Files
Jump to
Current section
Files
lib/nerves/release.ex
defmodule Nerves.Release do
# No leading '/' here since this is passed to mksquashfs and it
# doesn't like the leading slash.
@target_release_path "srv/erlang"
def init(%{options: options} = release) do
opts = Keyword.merge(options, release_opts())
release = %{
release
| options: opts,
steps: release.steps ++ [&Nerves.Release.finalize/1]
}
File.rm_rf!(release.path)
if Code.ensure_loaded?(Shoehorn.Release) do
apply(Shoehorn.Release, :init, [release])
else
release
end
end
def finalize(release) do
bootfile_path = Path.join([release.version_path, bootfile()])
case File.read(bootfile_path) do
{:ok, bootfile} ->
Nerves.Release.write_rootfs_priorities(release.applications, release.path, bootfile)
_ ->
Nerves.Utils.Shell.warn("""
Unable to load bootfile: #{inspect(bootfile_path)}
Skipping rootfs priority file generation
""")
end
release
end
def bootfile() do
Application.get_env(:nerves, :firmware)[:bootfile] || "shoehorn.boot"
end
def erts() do
if Nerves.Env.loaded?() do
System.get_env("ERTS_DIR")
else
true
end
end
def write_rootfs_priorities(applications, host_release_path, bootfile) do
target_release_path = @target_release_path
applications = normalize_applications(applications)
{:script, _, boot_script} = :erlang.binary_to_term(bootfile)
target_beam_files = target_beam_files(boot_script, host_release_path, target_release_path)
target_app_files = target_app_files(applications, target_release_path)
target_priv_dirs = target_priv_dirs(applications, target_release_path)
priorities =
(target_beam_files ++ target_app_files ++ target_priv_dirs)
|> List.flatten()
|> Enum.zip(32_000..1_000)
|> Enum.map(fn {file, priority} ->
file <> " " <> to_string(priority)
end)
|> Enum.join("\n")
build_path = Path.join([Mix.Project.build_path(), "nerves"])
File.mkdir_p!(build_path)
Path.join(build_path, "rootfs.priorities")
|> File.write(priorities)
end
defp target_beam_files(boot_script, host_release_path, target_release_path) do
{_, loaded} =
Enum.reduce(boot_script, {nil, []}, fn
{:path, paths}, {_, loaded} ->
{rel_paths(paths), loaded}
{:primLoad, files}, {paths, loaded} ->
load =
Enum.reduce(paths, [], fn path, loaded ->
load =
Enum.reduce(files, [], fn file, loaded ->
filename = to_string(file) <> ".beam"
path = Path.join(["lib", path, filename])
host_path = Path.join(host_release_path, path) |> Path.expand()
if File.exists?(host_path) do
[expand_target_path(target_release_path, path) | loaded]
else
loaded
end
end)
loaded ++ load
end)
{paths, [load | loaded]}
_, acc ->
acc
end)
loaded
|> Enum.reverse()
|> List.flatten()
end
defp target_app_files(applications, target_release_path) do
Enum.reduce(applications, [], fn
{app, vsn, path}, app_files ->
host_path = Path.join([path, "ebin", app <> ".app"])
if File.exists?(host_path) do
app_file_path =
Path.join([
target_release_path,
"lib",
app <> "-" <> vsn,
"ebin",
app <> ".app"
])
[app_file_path | app_files]
else
app_files
end
end)
end
defp target_priv_dirs(applications, target_release_path) do
Enum.reduce(applications, [], fn
{app, vsn, path}, priv_dirs ->
host_priv_dir = Path.join(path, "priv")
if File.dir?(host_priv_dir) and not_empty_dir(host_priv_dir) do
priv_dir = Path.join([target_release_path, "lib", app <> "-" <> to_string(vsn), "priv"])
[priv_dir | priv_dirs]
else
priv_dirs
end
end)
end
defp rel_paths(paths) do
paths
|> Enum.map(&to_string/1)
|> Enum.map(&Path.split/1)
|> Enum.map(fn [_root | path] ->
Path.join(path)
end)
end
defp release_opts do
[
quiet: true,
include_executables_for: [],
include_erts: &Nerves.Release.erts/0,
boot_scripts: []
]
end
defp not_empty_dir(dir) do
File.ls(dir) != {:ok, []}
end
defp expand_target_path(target_release_path, path) do
Path.join(["/", target_release_path, path])
|> Path.expand(target_release_path)
|> String.trim_leading("/")
end
defp normalize_applications(applications) do
Enum.map(applications, fn
{app, opts} ->
{to_string(app), to_string(opts[:vsn]), Path.expand(to_string(opts[:path]))}
end)
end
end