defmodule Mix.Tasks.Firmware.Unpack do
use Mix.Task
import Mix.Nerves.Utils
alias Mix.Nerves.Preflight
@shortdoc "Unpack a firmware bundle for inspection"
@moduledoc """
Unpack the firmware so that its contents can be inspected locally.
## Usage
mix firmware.unpack [--output output directory] [--fw path to firmware]
## Command line options
* `--fw` - (Optional) The path to the .fw file for unpacking.
Defaults to `Nerves.Env.firmware_path/1`
* `--output` - (Optional) The output directory for the unpacked firmware.
Defaults to the name of the firmware bundle with the extension replaced
with `.unpacked`.
## Examples
```
# Create a firmware bundle. It will be under the _build directory
mix firmware
# Unpack the built firmware
mix firmware.unpack --output firmware_contents
# Unpack a specified fw file
mix firmware.unpack --fw hello_nerves.fw
# Inspect it
ls hello_nerves.unpacked/
```
"""
@switches [output: :string, fw: :string]
@aliases [o: :output, f: :fw]
@impl true
def run(args) do
Preflight.check!()
debug_info("Nerves Firmware Unpack")
config = Mix.Project.config()
{opts, _, _} = OptionParser.parse(args, strict: @switches, alies: @aliases)
fw = opts[:fw] || Nerves.Env.firmware_path(config)
output = opts[:output] || "#{Path.rootname(Path.basename(fw))}.unpacked"
_ = check_nerves_system_is_set!()
_ = check_nerves_toolchain_is_set!()
unless File.exists?(fw) do
Mix.raise("""
Firmware not found.
Please supply a valid firmware path with `--fw` or run `mix firmware`
""")
end
unpack(fw, output)
end
defp unpack(fw, output_path) do
abs_output_path = Path.expand(output_path)
rootfs_output_path = Path.join(abs_output_path, "rootfs")
rootfs_image = Path.join([abs_output_path, "data", "rootfs.img"])
Mix.shell().info("Unpacking to #{output_path}...")
_ = File.rm_rf!(abs_output_path)
File.mkdir_p!(abs_output_path)
{_, 0} = shell("unzip", [fw, "-d", abs_output_path])
{_, 0} = shell("unsquashfs", ["-d", rootfs_output_path, "-no-xattrs", rootfs_image])
:ok
end
end