Packages
nerves
1.13.2
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/mix/tasks/burn.ex
# SPDX-FileCopyrightText: 2018 Joel Byler
# SPDX-FileCopyrightText: 2019 Greg Mefford
# SPDX-FileCopyrightText: 2019 Justin Schneck
# SPDX-FileCopyrightText: 2019 Matt Willy
# SPDX-FileCopyrightText: 2020 Frank Hunleth
# SPDX-FileCopyrightText: 2022 Jon Carstens
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule Mix.Tasks.Burn do
@shortdoc "Write a firmware image to an SDCard"
@moduledoc """
Writes the generated firmware image to an attached SDCard or file.
By default, this task detects attached SDCards and then invokes `fwup`
to overwrite the contents of the selected SDCard with the new image.
Data on the SDCard will be lost, so be careful.
## Command line options
* `--device <filename>` - skip SDCard detection and write the image to
the specified filename. SDCard paths depend on the operating system, but
have a form like `/dev/sdc` or `/dev/mmcblk0`. You may also specify a
filename to create an image that can be used with a bulk memory programmer
or copied to an SDCard manually with a utility like `dd`.
* `--task <name>` - apply the specified `fwup` task. See the `fwup.conf`
file that was used to create the firmware image for options. By
convention, the `complete` task writes everything to the SDCard including
bootloader and application data partitions. The `upgrade` task only
modifies the parts of the SDCard required to run the new software.
* `--firmware <name>` - (Optional) The path to the fw file to use.
Defaults to `<image_path>/<otp_app>.fw`
## Examples
```
# Upgrade the contents of the SDCard located at /dev/mmcblk0
mix burn --device /dev/mmcblk0 --task upgrade
```
"""
use Mix.Task
import Mix.Nerves.Utils
alias Mix.Nerves.Preflight
alias Nerves.Utils.WSL
@switches [device: :string, task: :string, firmware: :string]
@aliases [d: :device, t: :task, i: :firmware]
@impl Mix.Task
def run(argv) do
Preflight.check!()
debug_info("Nerves Burn")
{opts, argv, _} = OptionParser.parse(argv, switches: @switches, aliases: @aliases)
firmware_config = Application.get_env(:nerves, :firmware)
target = Mix.target()
_ = check_nerves_system_is_set!()
_ = check_nerves_toolchain_is_set!()
fw = firmware_file(opts)
if !File.exists?(fw) do
Mix.raise("Firmware for target #{target} not found at #{fw} run `mix firmware` to build")
end
{fw, firmware_location} =
WSL.make_file_accessible(fw, WSL.running_on_wsl?(), WSL.has_wslpath?())
dev =
case opts[:device] do
nil -> prompt_dev()
dev -> dev
end
set_provisioning(firmware_config[:provisioning])
burn(fw, dev, opts, argv)
# Remove the temporary .fw file
WSL.cleanup_file(fw, firmware_location)
end
defp burn(fw, dev, opts, argv) do
task = opts[:task] || "complete"
args = ["-a", "-i", fw, "-t", task, "-d", dev] ++ argv
{cmd, args} =
case :os.type() do
{_, :darwin} ->
{"fwup", args}
{_, :linux} ->
if WSL.running_on_wsl?() do
WSL.admin_powershell_command("fwup", Enum.join(args, " "))
else
maybe_elevated_user_fwup(dev, args)
end
{_, :nt} ->
{"fwup", args}
{_, type} ->
Mix.raise("Unable to burn firmware on your host #{inspect(type)}")
end
case InteractiveCmd.cmd(cmd, args) do
{_, 0} -> :ok
{_, status} -> Mix.raise("fwup exited with an error status: #{status}")
end
end
defp maybe_elevated_user_fwup(dev, args) do
fwup = System.find_executable("fwup")
case File.stat(dev) do
{:ok, %File.Stat{access: :read_write}} ->
{"fwup", args}
{:error, :enoent} ->
case File.touch(dev, System.os_time(:second)) do
:ok ->
{"fwup", args}
{:error, :eacces} ->
{"sudo", asdf_aware_env(fwup) ++ [fwup] ++ args}
end
_ ->
{"sudo", asdf_aware_env(fwup) ++ [fwup] ++ args}
end
end
# Check if fwup is managed by asdf and return appropriate environment variables
defp asdf_aware_env(fwup) do
if fwup_managed_by_asdf?(fwup) do
provision_env() ++ asdf_env_vars()
else
provision_env()
end
end
# Detect if fwup is managed by asdf by checking if it's in the asdf shims directory
defp fwup_managed_by_asdf?(fwup) do
String.contains?(fwup, ".asdf/shims")
end
# Get the appropriate asdf environment variables
defp asdf_env_vars() do
# Check for ASDF_DATA_DIR first (0.16+), then ASDF_DIR (0.15 and earlier)
cond do
data_dir = System.get_env("ASDF_DATA_DIR") ->
["ASDF_DATA_DIR=#{data_dir}"]
dir = System.get_env("ASDF_DIR") ->
["ASDF_DIR=#{dir}"]
true ->
Mix.raise("""
fwup is installed via asdf, but the required environment variable is not set.
For asdf 0.16 and later, you need to set ASDF_DATA_DIR in your shell configuration.
For asdf 0.15 and earlier, you need to set ASDF_DIR in your shell configuration.
Please add one of the following to your shell configuration file (~/.bashrc, ~/.zshrc, etc.):
export ASDF_DATA_DIR="$HOME/.asdf" # for asdf 0.16+
or
export ASDF_DIR="$HOME/.asdf" # for asdf 0.15 and earlier
After updating your shell configuration, restart your terminal or run `source ~/.bashrc` (or equivalent).
""")
end
end
# This is a fix for linux when running through sudo.
# Sudo will strip the environment and therefore any variables
# that are set during device provisioning.
defp provision_env() do
System.get_env()
|> Enum.filter(fn {k, _} ->
String.starts_with?(k, "NERVES_") or String.equivalent?(k, "SERIAL_NUMBER")
end)
|> Enum.map(fn {k, v} -> k <> "=" <> v end)
end
@spec firmware_file(keyword()) :: String.t()
def firmware_file(opts) do
fw =
(opts[:firmware] || Nerves.Env.firmware_path())
|> Path.expand()
if File.exists?(fw) do
fw
else
Mix.raise("The firmware file #{fw} does not exist")
end
end
end