In early March, the Nerves Project published its v0.5.0 release, marking another big step forward in usability and completeness of the platform. In this post, I’d like to describe the major changes to be aware of as you upgrade existing projects or create new ones using Nerves 0.5.
Refreshed Docs and Examples
If you’re new to Nerves or you’ve been using it for a long time and just never got around to it, you may want to check out the Getting Started guides and documentation available on Hexdocs and the example projects available on GitHub.
These have all been reviewed and updated to match what’s available in Nerves 0.5, so it’s a great way to quickly see how everything works.
In particular, the nerves-examples
projects can be used as a reference if you get stuck trying to start a new project or feature, or when upgrading an existing project.
The Default Build Target is Now host
The biggest change in Nerves 0.5 is that projects now default to building with a target of host
instead of always cross-compiling for different hardware.
The reason for this change is that it is very difficult to test and debug your code on your host when it is cross-compiled for another target architeture.
In order to support this change, we have added some additional structure to the mix.exs
configuration file that gets generated by default for new Nerves projects.
Let’s generate a new project and walk through the important sections of mix.exs
.
$ mix nerves.new example
This @target
module attribute is what controls the cross-compiling environment used by Mix.
If the MIX_TARGET
environment variable is set in your shell, it will be used to choose which Nerves System to use.
If it is not set, Mix will default to the host
target.
If you’re familiar with previous versions of Nerves, you may also notice that we have changed the target-selection envirnoment variable from NERVES_TARGET
to MIX_TARGET
.
# example/mix.exs
defmodule Example.Mixfile do
use Mix.Project
@target System.get_env("MIX_TARGET") || "host"
Nerves 0.5 projects depend on version 0.3 of the nerves_bootstrap
Mix archive.
If you have a relatively recent version of nerves_bootstrap
already, you can upgrade by running mix local.nerves
.
If that doesn’t work, you can run mix archive.install https://github.com/nerves-project/archives/raw/main/nerves_bootstrap.ez
.
Also note that the aliases()
definition now takes the @target
as a parameter.
We’ll see what that does for us shortly.
def project do
[app: :example,
# ...
target: @target,
archives: [nerves_bootstrap: "~> 0.3.0"],
aliases: aliases(@target),
deps: deps()]
end
Here, we set the Application
s to be started, based on which @target
is being used.
This gives us the opportunity to automatically start the supervision tree when running on the target device, but not when running in iex
on the host.
Also note that we have switched to using the extra_applications
convention for specifying which applications to include in the release.
This will automatically include all applications in dependencies unless they are defined as runtime: false
.
Using extra_applications
allows us to only list non-dependency applications (like Logger
), rather than explicitly specifying them all in applications
.
def application, do: application(@target)
def application("host") do
[extra_applications: [:logger]]
end
def application(_target) do
[mod: {Example.Application, []},
extra_applications: [:logger]]
end
Similarly, we choose which dependencies to include based on the @target
selection.
This gives an opportunity to easily swap out mock dependencies or simply not load any that are designed to run only on the target (e.g. to interface with specific hardware features).
Note that the nerves
and nerves_system_*
dependencies have now been marked as runtime: false
so that they will not be included in the release on the device.
These are only used during the compilation process to tell Mix how to properly build for the target hardware, so they aren’t needed at runtime.
Instead, there is a new nerves_runtime
dependency that contains only the components of Nerves that are usable from your running application.
There isn’t much included today with nerves_runtime
, but the intention of the Nerves team is to build out a standard platform of runtime components that many Nerves projects are likely to need.
def deps do
[{:nerves, "~> 0.5.0", runtime: false}] ++
deps(@target)
end
# Specify target specific dependencies
def deps("host"), do: []
def deps(target) do
[{:nerves_runtime, "~> 0.1.0"},
{:"nerves_system_#{target}", "~> 0.11.0", runtime: false}]
end
Finally, we use the @target
attribute passed to the aliases()
function to allow us to only inject the Nerves cross-compiling envinroment into Mix when we’re not targeting the host
.
# We do not invoke the Nerves Env when running on the Host
def aliases("host"), do: []
def aliases(_target) do
["deps.precompile": ["nerves.precompile", "deps.precompile"],
"deps.loadpaths": ["deps.loadpaths", "nerves.loadpaths"]]
end
end
Improved Console Interaction
One common complaint from previous versions of Nerves was that the console output was confusing during a Nerves firmware build. There were normal warnings that needed to be ignored and overly-verbose output from several tools without enough context. With Nerves 0.5, we have eliminated the noisy warnings and added high-contrast headers to outline the high-level build stages.
You may also notice that new Nerves projects are now generated with some helpful versions information being shown by default whenever you build you project.
If you want to change what is displayed, you can easily modify the following code near the top of your mix.exs
file.
# example/mix.exs
# ...
Mix.shell.info([:green, """
Env
MIX_TARGET: #{@target}
MIX_ENV: #{Mix.env}
""", :reset])
# ...
In order to help with troubleshooting, especially when using a custom Nerves System, we have also added a new mix nerves.info
task, which shows more-detailed information:
Other Notable Changes
New images_path
Option
You can now set the images_path
option in your Mix.Project
configuration.
The default images_path
location is #{build_path}/nerves/images
, but you can override it if you want to place your built images elsewhere.
Better Project Generation by Default
Something that consistently tripped-up new users of Nerves in past versions was that they needed to run mix nerves.release.init
and mix deps.get
prior to mix firmware
.
In order to ease that process, the mix nerves.new
project generator now asks if they would like to have it run these commands during project generation.
This makes it much easier to do the right thing by default, only deviating if you know you need to.