A npm module with an extra xpack property.

Oh, no, not another package format!

This is probably the first thought many people might have when hearing about xPacks. There are enough package formats and package managers, why would we need yet another one?

Well, first, the xPacks project does not introduce a new package format, but uses exactly the same format as npm, and xPacks can be stored in the same repositories, public or private.

And xpm, the project manager, is built on top of npm, the very popular JavaScript package manager, extending it with new language neutral features, so things are not that scary as they seemed initially.

I’m perfectly happy with my development environment, why would I bother with xPacks?

The current development environments are indeed great, and rolling distributions like Arch and Homebrew do a tremendous job in keeping the systems up-to-date, but not rarely the latest and greatest available versions break older applications and various complex processes, like build scripts, that depend on older versions.

The xPacks dependency mechanism is intended to solve exactly this problem, by providing a simple and uniform solution of installing different versions of the same package in different folders, and managing dependencies.

Nix also provides reproducible builds, plus lots of packages, why bother with xPacks?

Nix is a great project, and takes a very strict path in controlling the versions, down to the individual library. However, by design, it uses links to files, which are not available (or are not reliable) on Windows, thus Nix is currently available only on GNU/Linux and macOS. Plus that the Nix language requires quite some efforts to master.

Why could npm not be used as-is and why exactly was it necessary to invent xpm?

Because each package manager implements a strategy for managing dependencies. npm/yarn/pnpm all implement various strategies specific to ECMAScript dependencies.

Unfortunately these strategies do not work for other languages, like C/C++; plus that it would be very difficult and require ugly hacks to accommodate development dependencies to binary archives (for tools like compilers).

Why bother to manage versions when auto-configure can sort out the differences?

The traditional way of dealing with different versions and variations between systems is to add a complex auto-configure mechanism that is able to detect which tools/components are present, which versions, and with this input do its best to parametrise the process (for example a project build), to accommodate for all these differences.

This approach started with GNU configure, and today use even more complex solutions, like CMake or meson scripts.

Well, instead of permanently wondering how to make use of the new versions, and making the auto-configure scripts more and more complex with every day, why not allow the application to ask for the exact versions that are known to be compatible, and let an automated tool handle the dependencies?

The xPacks project manager can do just this, bring in the project exactly the versions needed, thus making the auto-configure step superfluous.

But what are xPacks?

In brief, xPacks are general purpose multi-version software packages.

By multi-version it is understood not only that packages can have multiple versions, but they can be installed in parallel, allowing each project to have its own set of dependencies.

Based on the installed content, there are currently two types of xPacks: source and binary:

  • source xPacks are packages that install source files, generally libraries used by the project.
  • binary xPacks are packages that install standalone executables/binary files, generally tools used during the build process, like toolchains, builders, etc; standalone means they are self-contained and do not depend on other shared libraries or tools.

Where does the name come from?

xPack, pronounced ɛks-pæk, can be understood as Universal Package, or Any Type Package, and tries to suggest that, when compared to npm packages (which are basically JavaScript packages), xPacks are more general, and not linked/limited to a specific programming language.

Initially the x came from eXtended, so eXtended Packages can be another choice.

What exactly is an xPack?

The full definition is:

An xPack is a folder which includes a package.json file, defining at least the package name, the package version, and an xpack property, even empty.

Given the direct inheritance from npm packages, a canonical definition might be:

An xPack is a npm package with an additional xpack property defined in package.json.
A binary xPack is an xPack with two additional xpack.binaries and xpack.bin properties defined in package.json.

Package formats

As for npm, a package is any of the following:

  1. A folder containing a package.json file.
  2. A gzipped tarball containing (1).
  3. A URL that resolves to (2).
  4. A <name>@<version> that is published on the registry with (3).
  5. A <name>@<tag> that points to (4).
  6. A <name> that has a latest tag satisfying (5).
  7. A git url that, when cloned, results in (1).

How to convert my project to an xPack?

Create a package.json file in the root folder of your project with at least the following definitions:

  "name": "my-awesome-project",
  "version": "1.0.0",
  "xpack": {}

A more automated method is to use xpm init:

% mkdir xyz && cd xyz
% xpm init --verbose
xPack manager - create an xPack, empty or from a template
Creating project 'xyz'...
File 'package.json' generated.
File 'LICENSE' generated.

'xpm init' completed in 167 ms.
% ls -l
total 16
-rw-r--r--  1 ilg  staff  1070 Feb  6 19:31 LICENSE
-rw-r--r--  1 ilg  staff   818 Feb  6 19:31 package.json
% cat package.json
  "name": "@<scope>/xyz",
  "version": "0.1.0",
  "description": "A source xPack with <your-description-here>",
  "main": "",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  "repository": {
    "type": "git",
    "url": "https://github.com/<user-id>/xyz.git"
  "homepage": "https://github.com/<user-id>/xyz/",
  "bugs": {
    "url": "https://github.com/<user-id>/xyz/issues/"
  "keywords": [
  "author": {
    "name": "<author-name>",
    "email": "<author-email>",
    "url": "<author-url>"
  "license": "MIT",
  "config": {},
  "dependencies": {},
  "devDependencies": {},
  "xpack": {
    "minimumXpmRequired": "0.14.8",
    "dependencies": {},
    "devDependencies": {},
    "properties": {},
    "actions": {},
    "buildConfigurations": {}

Why would I convert my project to an xPack?

The top reasons are:

  • to automate dependencies management
  • to automate path management for dependent tools
  • to manage multi-configuration builds

These are both simple and complicated things. To better understand them, please continue to read how binary and source xPacks work.

How do binary xPacks work?

Let’s assume that ‘my-awesome-project’ needs the arm-none-eabi-gcc toolchain to build, and not any version but a specific one, like 12.2.1; it also needs the latest CMake, ninja and the liquidjs npm module.

For this, in the project folder, issue the following command, which will install the required tools in a global xPacks store location, and add links to them.

% xpm install @xpack-dev-tools/arm-none-eabi-gcc@12.2.1-1.2.1 @xpack-dev-tools/cmake@latest @xpack-dev-tools/ninja-build@latest --verbose
xPack manager - install package(s)

Processing @xpack-dev-tools/arm-none-eabi-gcc@12.2.1-1.2.1...
Folder 'xpacks/xpack-dev-tools-arm-none-eabi-gcc' linked to global '@xpack-dev-tools/arm-none-eabi-gcc/12.2.1-1.2.1'.
File 'xpacks/.bin/arm-none-eabi-addr2line' linked to '../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-addr2line'.
File 'xpacks/.bin/arm-none-eabi-ar' linked to '../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-ar'.
File 'xpacks/.bin/arm-none-eabi-g++' linked to '../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-g++'.
File 'xpacks/.bin/arm-none-eabi-gcc' linked to '../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcc'.
File 'xpacks/.bin/arm-none-eabi-strip' linked to '../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-strip'.
Adding '@xpack-dev-tools/arm-none-eabi-gcc' to 'devDependencies'...

Processing @xpack-dev-tools/cmake@3.23.5-1.1...
Folder 'xpacks/xpack-dev-tools-cmake' linked to global '@xpack-dev-tools/cmake/3.23.5-1.1'.
File 'xpacks/.bin/ccmake' linked to '../xpack-dev-tools-cmake/.content/bin/ccmake'.
File 'xpacks/.bin/cmake' linked to '../xpack-dev-tools-cmake/.content/bin/cmake'.
File 'xpacks/.bin/cpack' linked to '../xpack-dev-tools-cmake/.content/bin/cpack'.
File 'xpacks/.bin/ctest' linked to '../xpack-dev-tools-cmake/.content/bin/ctest'.
Adding '@xpack-dev-tools/cmake' to 'devDependencies'...

Processing @xpack-dev-tools/ninja-build@1.11.1-2.1...
Folder 'xpacks/xpack-dev-tools-ninja-build' linked to global '@xpack-dev-tools/ninja-build/1.11.1-2.1'.
File 'xpacks/.bin/ninja' linked to '../xpack-dev-tools-ninja-build/.content/bin/ninja'.
Adding '@xpack-dev-tools/ninja-build' to 'devDependencies'...

'xpm install' completed in 194 ms.
% npm install liquidjs --save-dev

added 1 package, and audited 2 packages in 594ms

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities

This will also update the package.json with details about the dependencies:

  "name": "@<scope>/xyz",
  "version": "0.1.0",
  "...": "...",
  "dependencies": {},
  "xpack": {
    "minimumXpmRequired": "0.14.8",
    "dependencies": {},
    "devDependencies": {
      "@xpack-dev-tools/arm-none-eabi-gcc": "12.2.1-1.2.1",
      "@xpack-dev-tools/cmake": "3.23.5-1.1",
      "@xpack-dev-tools/ninja-build": "1.11.1-2.1"
    "properties": {},
    "actions": {},
    "buildConfigurations": {}
  "devDependencies": {
    "liquidjs": "^10.4.0"

The result is the tools being downloaded and installed in the global xPack store (a folder in user’s home) and links from the project to that folders created in the local xpacks folder, with links to individual programs added in xpacks/.bin (or .cmd stubs on Windows).

Similarly for liquidjs, which is a Node.js CLI npm module, after installing the module, npm will add a folder node_modules/.bin where links to the executables will be created (or .cmd stubs on Windows).

$ tree -a
├── node_modules
│   ├── .bin
│   │   ├── liquid -> ../liquidjs/bin/liquid.js
│   │   └── liquidjs -> ../liquidjs/bin/liquid.js
│   ├── .package-lock.json
│   └── liquidjs
│       ├── CHANGELOG.md
│       ├── LICENSE
│       ├── README.md
│       ├── bin
│       │   └── liquid.js
│       ├── dist
│       │   └── ...
│       └── package.json
├── package-lock.json
├── package.json
└── xpacks
    ├── .bin
    │   ├── arm-none-eabi-addr2line -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-addr2line
    │   ├── arm-none-eabi-ar -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-ar
    │   ├── arm-none-eabi-as -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-as
    │   ├── arm-none-eabi-c++ -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-c++
    │   ├── arm-none-eabi-c++filt -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-c++filt
    │   ├── arm-none-eabi-cpp -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-cpp
    │   ├── arm-none-eabi-elfedit -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-elfedit
    │   ├── arm-none-eabi-g++ -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-g++
    │   ├── arm-none-eabi-gcc -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcc
    │   ├── arm-none-eabi-gcc-ar -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcc-ar
    │   ├── arm-none-eabi-gcc-nm -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcc-nm
    │   ├── arm-none-eabi-gcc-ranlib -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcc-ranlib
    │   ├── arm-none-eabi-gcov -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcov
    │   ├── arm-none-eabi-gcov-dump -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcov-dump
    │   ├── arm-none-eabi-gcov-tool -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gcov-tool
    │   ├── arm-none-eabi-gdb -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gdb
    │   ├── arm-none-eabi-gdb-add-index -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gdb-add-index
    │   ├── arm-none-eabi-gdb-add-index-py3 -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gdb-add-index-py3
    │   ├── arm-none-eabi-gdb-py3 -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gdb-py3
    │   ├── arm-none-eabi-gfortran -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gfortran
    │   ├── arm-none-eabi-gprof -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-gprof
    │   ├── arm-none-eabi-ld -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-ld
    │   ├── arm-none-eabi-ld.bfd -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-ld.bfd
    │   ├── arm-none-eabi-lto-dump -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-lto-dump
    │   ├── arm-none-eabi-nm -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-nm
    │   ├── arm-none-eabi-objcopy -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-objcopy
    │   ├── arm-none-eabi-objdump -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-objdump
    │   ├── arm-none-eabi-ranlib -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-ranlib
    │   ├── arm-none-eabi-readelf -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-readelf
    │   ├── arm-none-eabi-size -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-size
    │   ├── arm-none-eabi-strings -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-strings
    │   ├── arm-none-eabi-strip -> ../xpack-dev-tools-arm-none-eabi-gcc/.content/bin/arm-none-eabi-strip
    │   ├── ccmake -> ../xpack-dev-tools-cmake/.content/bin/ccmake
    │   ├── cmake -> ../xpack-dev-tools-cmake/.content/bin/cmake
    │   ├── cpack -> ../xpack-dev-tools-cmake/.content/bin/cpack
    │   ├── ctest -> ../xpack-dev-tools-cmake/.content/bin/ctest
    │   └── ninja -> ../xpack-dev-tools-ninja-build/.content/bin/ninja
    ├── xpack-dev-tools-arm-none-eabi-gcc -> /Users/ilg/Library/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/12.2.1-1.2.1
    ├── xpack-dev-tools-cmake -> /Users/ilg/Library/xPacks/@xpack-dev-tools/cmake/3.23.5-1.1
    └── xpack-dev-tools-ninja-build -> /Users/ilg/Library/xPacks/@xpack-dev-tools/ninja-build/1.11.1-2.1

22 directories, 172 files

When later running actions like xpm run build, the PATH is automatically adjusted to xpacks/.bin:node_modules/.bin:$PATH, so the exact versions of the tools required in the dependencies list will be preferred over any existing tools with the same names that might be present in the PATH.

Binary xPacks are probably huge, aren’t they?

Not at all; on the contrary, binary xPacks are very small, just a package.json file, since they include only the URLs where the binaries are actually stored (for example links to GitHub Releases), not the binaries themselves.

How do source xPacks work?

Even simpler. Let’s assume that the ‘xyz project’ also needs the µOS++ trace support, which is available as the source xPack @micro-os-plus/diag-trace.

% xpm install @micro-os-plus/diag-trace --verbose
xPack manager - install package(s)

Processing @micro-os-plus/diag-trace@4.2.0...
Adding to central store '/Users/ilg/Library/xPacks/@micro-os-plus/diag-trace/4.2.0'...
Changing permissions to read-only...
Folder 'xpacks/micro-os-plus-diag-trace' linked to global '@micro-os-plus/diag-trace/4.2.0'.
Adding '@micro-os-plus/diag-trace' to 'dependencies'...

'xpm install' completed in 3.693 sec.

This results in another link in the xpacks folder (mind the linearised package name):

% tree xpacks
├── micro-os-plus-diag-trace -> /Users/ilg/Library/xPacks/@micro-os-plus/diag-trace/4.2.0
├── xpack-dev-tools-arm-none-eabi-gcc -> /Users/ilg/Library/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/12.2.1-1.2.1
├── xpack-dev-tools-cmake -> /Users/ilg/Library/xPacks/@xpack-dev-tools/cmake/3.23.5-1.1
└── xpack-dev-tools-ninja-build -> /Users/ilg/Library/xPacks/@xpack-dev-tools/ninja-build/1.11.1-2.1

4 directories, 0 files
% tree xpacks/micro-os-plus-diag-trace
├── CMakeLists.txt
├── README.md
├── include
│   └── micro-os-plus
│       └── diag
│           └── trace.h
├── meson.build
├── package.json
├── src
│   └── trace.cpp
└── xpack.json

4 directories, 9 files

and the addition of a new dependency in package.json:

  "name": "@<scope>/xyz",
  "version": "0.1.0",
  "...": "...",
  "xpack": {
    "minimumXpmRequired": "0.14.8",
    "dependencies": {
      "@micro-os-plus/diag-trace": "^4.2.0"
    "devDependencies": {
      "@xpack-dev-tools/arm-none-eabi-gcc": "12.2.1-1.2.1",
      "@xpack-dev-tools/cmake": "3.23.5-1.1",
      "@xpack-dev-tools/ninja-build": "1.11.1-2.1"
    "properties": {},
    "actions": {},
    "buildConfigurations": {}
  "devDependencies": {
    "liquidjs": "^10.4.0"


A real example is an embedded project that lists as dependencies two source xPacks, one Node module and three binary xPacks:

  "name": "h1b",
  "version": "1.0.0",
  "description": "An xPack with a blinky application running on HiFive1",
  "...": "...",
  "devDependencies": {
    "xmake": "^0.3.9"
  "xpack": {
    "dependencies": {
      "@micro-os-plus/diag-trace": "^1.0.6",
      "@sifive/hifive1-board": "^1.0.3"
    "devDependencies": {
      "@gnu-mcu-eclipse/riscv-none-gcc": "^7.2.0-2.1",
      "@gnu-mcu-eclipse/openocd": "^0.10.0-7.1",
      "@gnu-mcu-eclipse/windows-build-tools": "^2.10.1"

The two source xPacks actually pull in six source xPacks, and the binary xPacks contribute a lot of links to xpacks/.bin:

$ cd /tmp/hifive1-blinky-cpp
$ xpm install --verbose
xPack manager - install package(s)

Installing dependencies for 'hifive1-blinky-cpp'...
Folder 'micro-os-plus-diag-trace' linked to '@micro-os-plus/diag-trace/1.0.6'.
Folder 'sifive-hifive1-board' linked to '@sifive/hifive1-board/1.0.3'.
Folder 'sifive-devices' linked to '@sifive/devices/1.0.2'.
Folder 'micro-os-plus-riscv-arch' linked to '@micro-os-plus/riscv-arch/1.0.2'.
Folder 'micro-os-plus-startup' linked to '@micro-os-plus/startup/1.0.7'.
Folder 'micro-os-plus-c-libs' linked to '@micro-os-plus/c-libs/1.0.6'.
Folder 'micro-os-plus-cpp-libs' linked to '@micro-os-plus/cpp-libs/1.0.4'.
Folder 'xmake' linked to 'xmake/0.3.8'.
File 'xmake' linked to 'xmake/bin/xmake.js'
Folder 'gnu-mcu-eclipse-riscv-none-gcc' linked to '@gnu-mcu-eclipse/riscv-none-gcc/7.2.0-2.1'.
File 'riscv-none-embed-addr2line' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-addr2line'
File 'riscv-none-embed-ar' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-ar'
File 'riscv-none-embed-as' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-as'
File 'riscv-none-embed-c++' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-c++'
File 'riscv-none-embed-c++filt' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-c++filt'
File 'riscv-none-embed-cpp' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-cpp'
File 'riscv-none-embed-elfedit' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-elfedit'
File 'riscv-none-embed-g++' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-g++'
File 'riscv-none-embed-gcc' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcc'
File 'riscv-none-embed-gcc-7.2.0' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcc-7.2.0'
File 'riscv-none-embed-gcc-ar' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcc-ar'
File 'riscv-none-embed-gcc-nm' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcc-nm'
File 'riscv-none-embed-gcc-ranlib' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcc-ranlib'
File 'riscv-none-embed-gcov' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcov'
File 'riscv-none-embed-gcov-dump' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcov-dump'
File 'riscv-none-embed-gcov-tool' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gcov-tool'
File 'riscv-none-embed-gdb' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gdb'
File 'riscv-none-embed-gprof' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-gprof'
File 'riscv-none-embed-ld' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-ld'
File 'riscv-none-embed-ld.bfd' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-ld.bfd'
File 'riscv-none-embed-nm' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-nm'
File 'riscv-none-embed-objcopy' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-objcopy'
File 'riscv-none-embed-objdump' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-objdump'
File 'riscv-none-embed-ranlib' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-ranlib'
File 'riscv-none-embed-readelf' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-readelf'
File 'riscv-none-embed-run' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-run'
File 'riscv-none-embed-size' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-size'
File 'riscv-none-embed-strings' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-strings'
File 'riscv-none-embed-strip' linked to 'gnu-mcu-eclipse-riscv-none-gcc/.content/bin/riscv-none-embed-strip'
Folder 'gnu-mcu-eclipse-openocd' linked to '@gnu-mcu-eclipse/openocd/0.10.0-7.1'.
File 'openocd' linked to 'gnu-mcu-eclipse-openocd/.content/bin/openocd'
Folder 'gnu-mcu-eclipse-windows-build-tools' linked to '@gnu-mcu-eclipse/windows-build-tools/2.10.1'.

'xpm install' completed in 6.086 sec.

TODO: update for a more recent blinky project.

Continuous Integration use cases

The high degree of automation provided by xpm is of great help for automated test environments.

GitHub Actions

For example, with the proper scripts configured, a multi-platform test configuration for an xPack project may look like this:

name: CI on Push


    name: 'CI tests'

    runs-on: $

        node-version: [14, 16, 18]
        # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
        os: [ubuntu-22.04, macos-12, windows-2022]

    - name: Checkout
      uses: actions/checkout@v2
        fetch-depth: 3

    - name: Setup Node.js $ on $
      # https://github.com/actions/setup-node
      uses: actions/setup-node@v2
        node-version: $

    - name: Install xpm on Linux/macOS
      if: runner.os != 'Windows'
      run: sudo npm install --global xpm@latest

    - name: Install xpm on Windows
      if: runner.os == 'Windows'
      run: npm install --global xpm@latest

    - name: Satisfy project dependencies
      run: xpm install --quiet

    - name: Run test
      run: xpm run test