Systems
Using a Nerves System
Single Target
To use a Nerves system in project with a single target, you can directly include it as part of your application dependencies.
# mix.exs
def deps do
[{:nerves_system_bbb, ">=0.0.0"}]
end
Multi Target
Multi target configurations can be handled a number of ways. You can allow all nerves_system_*
projects by interpolating target
def deps(target) do
[{:"nerves_system_#{target}", ">=0.0.0"}]
end
Or you could switch between different configurations of the same target.
def deps(:dev = env) do
[{:my_custom_bbb_dev, ">=0.0.0"}]
end
def deps(:prod = env) do
[{:my_custom_bbb_prod, ">=0.0.0"}]
end
Since its all done in Elixir, you can choose which configuration works best for you. Just be sure that there is only ever one system
present when compiling your Nerves application.
Designing a Nerves System
Nerves System dependencies are a collection of configurations to be fed into the the system build platform. Currently, Nerves Systems are all built using the Buildroot platform. The project structure of a Nerves System is as follows:
# nerves_system_*
mix.exs
nerves_defconfig
nerves.exs
rootfs-additions
VERSION
The mix file will contain the dependencies the System has. Typically, all that is included here is the Toolchain and the build platform. Here is an example of the Raspberry Pi 3 nerves_system
definition:
defmodule NervesSystemRpi3.Mixfile do
use Mix.Project
@version Path.join(__DIR__, "VERSION")
|> File.read!
|> String.strip
def project do
[app: :nerves_system_rpi3,
version: @version,
elixir: "~> 1.2",
compilers: Mix.compilers ++ [:nerves_system],
description: description,
package: package,
deps: deps]
end
def application do
[]
end
defp deps do
[{:nerves_system, "~> 0.1.2"},
{:nerves_system_br, "~> 0.5.0"},
{:nerves_toolchain_arm_unknown_linux_gnueabihf, "~> 0.6.0"}]
end
defp package do
[maintainers: ["Frank Hunleth", "Justin Schneck"],
files: ["LICENSE", "mix.exs", "nerves_defconfig", "nerves.exs", "README.md", "VERSION", "rootfs-additions"],
licenses: ["Apache 2.0"],
links: %{"Github" => "https://github.com/nerves-project/nerves_system_rpi3"}]
end
end
Nerves Systems have a few requirements in the mix file:
- The compilers must include the
:nerves_system
compiler after theMix.compilers
have executed. - There must be a dependency for the toolchain and the build platform.
- You need to list all files in the
package
files:
list so they are present when downloading from Hex.
Package Configuration
In addition to the mix file, Nerves packages read from a special nerves.exs
configuration file in the root of the package names. This file contains configuration information that Nerves loads before any application or dependency code is compiled. It is used to store metadata about a package. Here is an example from the nerves.exs
file for rpi3
:
use Mix.Config
version =
Path.join(__DIR__, "VERSION")
|> File.read!
|> String.strip
config :nerves_system_rpi3, :nerves_env,
type: :system,
mirrors: [
"https://github.com/nerves-project/nerves_system_rpi3/releases/download/v#{version}/nerves_system_rpi3-v#{version}.tar.gz"],
build_platform: Nerves.System.Platforms.BR,
build_config: [
defconfig: "nerves_defconfig",
package_files: [
"rootfs-additions"
]
]
There are a few important and required keys present in this file:
type The type of Nerves Package. Options are: system
, system_compiler
, system_platform
, system_package
, toolchain
, toolchain_compiler
, toolchain_platform
.
mirrors The URL(s) of cached assets. For nerves systems, we upload the finalized assets to Github releases so others can download them.
build_platform The build platform to use for the system or toolchain.
build_config The collection of configuration files. This collection contains the following keys:
defconfig For
Nerves.System.Platforms.BR
, this is the Buildroot defconfig fragment used to build the system.kconfig Buildroot requires a
Config.in
kconfig file to be present in the config directory. If this is omitted, a default empty file is used.package_files Additional files required to be present for the defconfig. Directories listed here will be expanded and all subfiles and directories will be copied over, too.
Building Nerves Systems
Nerves system dependencies are light-weight, configuration-based dependencies that, at compile time, request to either download from cache, or locally build the dependency. You can control which route nerves_system
will take by setting some environment variables on your machine:
NERVES_SYSTEM_CACHE
Options are none
, http
, local
NERVES_SYSTEM_COMPILER
Options are none
, local
Currently, Nerves systems can only be compiled using the local
compiler on a specially-configured Linux machine.
Nerves cache and compiler adhere to the Nerves.System.Provider
behaviour. Therefore, the system is laid out to allow additional compiler and cache providers, to facilitate other options in the future like Vagrant or Docker. This will be helpful when you want to start a Buildroot build on your Mac or Windows host machine.
Using Local Cache Provider
Nerves systems can take up a lot of space on your machine. This is because the dependency needs to be fetched for each project | target | env. To save space, you can enable the local cache.
$ export NERVES_SYSTEM_CACHE=local
With the local cache enabled, Nerves will attempt to find a cached version of the system in the cache dir. The default cache dir is located at ~/.nerves/cache/system
You can override this location by setting NERVES_SYSTEM_CACHE_DIR
env variable.
If the system your project is attempting to use is not present in the cache, mix will prompt you asking if you would like to download it.
$ mix compile
...
==> nerves_system_rpi3
[nerves_system][compile]
[nerves_system][local] Checking Cache for nerves_system_rpi3-0.5.1
nerves_system_rpi3-0.5.1 was not found in your cache.
cache dir: /Users/jschneck/.nerves/cache/system
Would you like to download the system to your cache? [Yn] Y
This will invoke the http provider and attempt to resolve the dependency.
Creating or Modifying a Nerves System with Buildroot
For some applications, the pre-built Nerves Systems won’t meet your needs. For example, you may want to include one or more additional Linux packages or run on hardware that isn’t in the list of Nerves-supported Targets yet. In order to build a customized system, you’ll need to either use Linux (e.g. in a virtual machine or container).
First, make sure that you have all of the dependencies. On Debian and Ubuntu, run the following:
sudo apt-get install git g++ libssl-dev libncurses5-dev bc m4 make unzip cmake
Then, set up a working directory.
In the example below, we use the nerves_build
directory, but this can be anything.
The nerves_system_br
project contains the base scripts and configuration for using Buildroot with Nerves.
Go to the working directory and clone the repository:
mkdir nerves_build
cd nerves_build
git clone https://github.com/nerves-project/nerves_system_br.git
While you can start a System build from scratch, it is easiest to modify an existing one and then rename it later when you have something to share or save. For example, if you’re targeting a Raspberry Pi 2, do the following:
git clone https://github.com/nerves-project/nerves_system_rpi2.git
Once that completes, create an output directory for the build products.
The name of the output directory is up to you, but we will just call it rpi2_out
in this example.
It is also possible to have multiple output directories if you have several configurations that you would like to work with simultaneously.
./nerves_system_br/create-build.sh nerves_system_rpi2/nerves_defconfig rpi2_out
The create-build.sh
script will prompt you with the next steps:
cd rpi2_out
make
This process will take quite a while (about 30 minutes).
When it finishes, you will have confirmed that you can successfully build the standard rpi2
System.
The next section will describe how to make changes and re-build the System.
If you ever update nerves_system_br
, be sure to run the create-build.sh
script again.
You can point it to the same location and it will update properly.
It is best to make clean
and then make
to rebuild everything after updating nerves_system_br
.
Additional Package Configuration
The workflow for customizing a Nerves System is the standard Buildroot procedure using make menuconfig
.
The packages are divided into three categories:
- Select base packages by running
make menuconfig
- Modify the Linux kernel and kernel modules with
make linux-menuconfig
- Enable more command line utilities using
make busybox-menuconfig
If you followed the steps in the previous section, make sure you have changed directory to rpi2_out
first.
When you quit from the menuconfig
interface, the changes are stored temporarily.
To save them back in your System, follow the appropriate steps below:
- Run
make savedefconfig
aftermake menuconfig
to update thenerves_defconfig
in your System. - Run
make linux-savedefconfig
andcp build/linux-x.y.z/defconfig <your system>
aftermake linux-menuconfig
. If your system doesn’t contain a custom Linux configuration yet, you’ll need to update the Buildroot configuration to point to the new Linux defconfig in your system directory. The path is usually something like$(NERVES_DEFCONFIG_DIR)/linux-x.y_defconfig
. - For Busybox, the convention is to copy
build/busybox-x.y.z/.config
to a file in the System repository. Like the Linux configuration, the Buildroot configuration will need to be updated to point to the custom config.
The Buildroot user manual can be very helpful especially if you need to add a package. The various Nerves System repositories have examples of many common use cases, so check them out as well.
How to Use Your New System
By default, Nerves downloads pre-built Systems from the Internet and caches them on your hard drive. To force a local build and not cache (so you can re-build it), set the following environment variables:
NERVES_SYSTEM_CACHE=none
NERVES_SYSTEM_COMPILER=local
To use your new System in your firmware build, specify it with the NERVES_SYSTEM
environment variable.
Assuming you followed the steps in the previous section, you can do this:
cd rpi2_out
export NERVES_SYSTEM=$PWD
Then, when you do the mix firmware
step from your project directory, your custom System will be used.
Make sure you still specify the matching NERVES_TARGET
(rpi2
in this example) in your project’s mix.exs
configuration, since it won’t be automatically detected for a custom System like this.
Once you’re happy with your System, you can package it by changing to the rpi2_out
directory and running:
make system
This will create a <system>.tar.gz
file that can be hosted on a web server and referenced from a Hex package just like the official Nerves Systems are.
Supporting New Target Hardware
If you’re trying to support a new Target, there may be quite a bit more work involved, depending on how mature the support for that hardware is in the Buildroot community. If you’re not familiar with Buildroot, you should learn about that first, using the excellent training materials on their website.
If you can find an existing Buildroot configuration for your intended hardware and you want to get it working with Nerves:
Follow their procedure and confirm your target boots (independent of Nerves).
Figure out how to get everything working with the version of Buildroot Nerves uses. See the
NERVES_BR_VERSION
variable increate-build.sh
.
- Look for packages and board configs can need to be copied into your System.
- Look for patches to existing packages that are needed.
- Create a defconfig that mimics the one from step 1, and get
nerves_system_br
to build it.
NOTE: You probably want to disable any userland packages that may be included by default to avoid distraction.