Skip to content

Write a Blueprint from Scratch

This guide walks through writing a complete ARM Cortex-M blueprint from scratch: manifest, task files (using the layers approach), and testing locally before publishing.


What we'll build

An ARM Cortex-M development environment with:

  • Base OS packages (cmake, ninja, git, python3)
  • ARM GCC 13.3 cross-compilation toolchain
  • PATH and CMake toolchain file configuration

By the end you'll have a working blueprint that can provision a native Linux machine, a VM, or a Docker container.


Step 1: Create the directory

mkdir my-arm-env && cd my-arm-env

Step 2: Write manifest.yml

name: ARM Cortex-M Development Environment
version: 1.0.0
description: ARM GCC 13.3 toolchain for Cortex-M embedded development.

variables:
  ARM_TOOLCHAIN_DEST: /opt/arm-none-eabi

run_order:
  - 00-system.yml
  - 10-toolchain.yml
  - 20-environment.yml

Start with the minimum required fields: name, version, and run_order. Add variables: for any value that appears in more than one task file, or that you might want to override.


Step 3: Write variables.yml (optional)

For larger blueprints, move variable definitions to a separate file to keep the manifest clean:

# variables.yml
ARM_TOOLCHAIN_DEST: /opt/arm-none-eabi
TOOLCHAIN_VERSION: "13.3.rel1"

If you use variables.yml, you can remove the same keys from manifest.yml variables:. The manifest always takes precedence on conflict.


Step 4: Write the system layer (00-system.yml)

- name: Update APT cache
  action: run_command
  reason: apt-get update must run before apt_install; no dedicated primitive for cache-only update
  command: apt-get update
  always_run: true

- name: Install build essentials
  action: apt_install
  packages:
    - git
    - cmake
    - ninja-build
    - python3
    - wget
    - curl
    - ca-certificates
    - xz-utils
    - gdb-multiarch

Step 5: Write the toolchain layer (10-toolchain.yml)

Download and extract the ARM GCC toolchain. Use per_arch to support both amd64 and arm64 host machines:

- name: Install ARM GCC 13.3 toolchain
  action: unarchive_from_url
  dest: "{{.Vars.ARM_TOOLCHAIN_DEST}}"
  strip_components: 1
  creates: "{{.Vars.ARM_TOOLCHAIN_DEST}}/bin/arm-none-eabi-gcc"
  per_arch:
    amd64:
      url: https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
      sha256: "aaabbbccc111222333444555666777888999000aaabbbccc111222333444555666"
    arm64:
      url: https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-aarch64-arm-none-eabi.tar.xz
      sha256: "dddeeefff111222333444555666777888999000dddeeefff111222333444555666"

SHA256 at run time

The provisioner requires sha256 on archive/file downloads when you run install and fails if the hash does not match. Validators and --dry-run may not catch a missing hash until a real install.

To find the SHA256:

sha256sum arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz

Step 6: Write the environment layer (20-environment.yml)

Configure the shell environment and generate a CMake toolchain file:

- name: Add ARM GCC to PATH
  action: write_env_file
  file: /etc/profile.d/10-arm-toolchain.sh
  content: |
    export PATH=$PATH:{{.Vars.ARM_TOOLCHAIN_DEST}}/bin
  owner: root:root
  mode: "0644"

- name: Generate CMake toolchain file
  action: cmake_toolchain
  file: /opt/nordic/cmake/nrf9160.cmake
  target_system: Generic
  target_processor: ARM
  compiler_prefix: arm-none-eabi-
  compiler_path: "{{.Vars.ARM_TOOLCHAIN_DEST}}/bin"
  owner: root:root
  mode: "0644"

Step 7: Validate the blueprint

Check for syntax errors before provisioning:

alloy-host validate ./my-arm-env   # syntax only; empty apt package lists are not caught here

This catches missing required fields, unknown action types, and YAML syntax errors without starting a VM.


Step 8: Test by provisioning

On a Linux machine or VM:

sudo alloy-provisioner install --dry-run --blueprint-dir ./my-arm-env
sudo alloy-provisioner install --blueprint-dir ./my-arm-env

Watch each task execute. If a task fails, fix the issue and re-run; completed tasks are skipped per alloy.state.yml and creates: guards.

To try a published blueprint with alloy-host instead, init with a Hub name:tag (not a local path)—see With Alloy Host.


Step 9: Verify inside the environment

bash -l -c 'arm-none-eabi-gcc --version'
# or inside alloy-host ssh (VM):
arm-none-eabi-gcc --version
# arm-none-eabi-gcc (Arm GNU Toolchain 13.3.Rel1) 13.3.1

cmake --version
ninja --version
ls /opt/nordic/cmake/nrf9160.cmake

Try building a real project:

cd /vagrant/my-project
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/nordic/cmake/nrf9160.cmake -B build
ninja -C build

Step 10: Resolve the lockfile (if using catalog refs)

If you referenced catalog toolchains in manifest.yml toolchains:, you need an alloy.lock.yml. alloy-host resolve in 2.1.0 does not work in a plain authoring folder; maintain the lockfile with your catalog workflow or explicit URLs (as in this guide).


Step 11: Commit

# Add to .gitignore
echo "alloy.state.yml" >> .gitignore
echo ".env" >> .gitignore

git add manifest.yml 00-system.yml 10-toolchain.yml 20-environment.yml
git add alloy.lock.yml  # if it exists
git commit -m "feat: add ARM Cortex-M development environment blueprint"

Complete file listing

my-arm-env/
├── .gitignore
├── manifest.yml
├── 00-system.yml
├── 10-toolchain.yml
└── 20-environment.yml

Next steps