Skip to main content

How to use xpm

This section is intended for those who plan to integrate xpm into their workflows.

The first step in understanding xpm is learning how to manage existing xpm-enabled projects. The subsequent steps involves adding xpm metadata to existing projects or creating new ones.

Use existing xpm-enabled projects

For projects that already include xpm metadata, the basic xpm workflow consists of two primary tasks:

  1. Installing dependencies
  2. Running various actions related to key development tasks

The simplest way to experiment with this workflow is by using an existing project. For example, you can start with a basic C project that prints the traditional "Hello, World!" message.

The first step is to clone the demo project into a temporary folder of your choice:

git clone https://github.com/xpack/xpm-demo-hello1.git

Install dependencies

The next major step in the workflow is to satisfy the project dependencies. In this example, xpm will install clang (since it is more portable than gcc).

cd xpm-demo-hello1
xpm install
info

It should be noted that this command will not install just any version of clang, but a specific version (18.1.8 in this case), which is hard-coded into the project metadata. This ensures that the same version is used across all platforms, enhancing reproducibility.

Run actions

The next steps involve running several actions, beginning with one that prepares the build folder:

xpm run prepare

The next action is to build the executable:

xpm run build

The final action is to execute the program:

xpm run execute

As expected, the result is:

Hello World!
info

It should be noted that all these commands work the same on all major platforms (Windows, macOS, and GNU/Linux).

Create new projects

As demonstrated, the workflow for using existing projects is straightforward.

However, for a more comprehensive understanding, it is beneficial to learn how to create an xpm-enabled project from scratch and add the necessary metadata.

Initialise project

Similarly to npm, a project must be prepared for xpm use.

Run the xpm init command in a temporary folder of your choice:

mkdir xpm-demo-hello && cd xpm-demo-hello
xpm init

This adds an empty package.json where further metadata will be stored.

Add source code

Add a simple C program that prints the traditional Hello World! message:

xpm-demo-hello/src/hello.c
#include <stdio.h>

int
main (int argc, char* argv[])
{
printf ("Hello World!\n");

return 0;
}

Install a toolchain

To compile this C program, we'll use clang 18.

To install it, use the following command:

xpm install @xpack-dev-tools/clang@18.1.8-2.1
tip

LLVM/clang is preferred over GCC due to its greater compatibility. As of this writing (November 2024), GCC on macOS has shown instability across multiple SDK releases and is therefore not recommended.

This command will add the following links to the project:

% tree -a -L2 xpacks
xpacks
├── .bin
│ ├── analyze-build -> ../@xpack-dev-tools/clang/.content/bin/analyze-build
│ ├── clang -> ../@xpack-dev-tools/clang/.content/bin/clang
│ ├── clang++ -> ../@xpack-dev-tools/clang/.content/bin/clang++
│ ├── clang-check -> ../@xpack-dev-tools/clang/.content/bin/clang-check
│ ├── clang-cl -> ../@xpack-dev-tools/clang/.content/bin/clang-cl
│ ├── clang-cpp -> ../@xpack-dev-tools/clang/.content/bin/clang-cpp
│ ├── clang-doc -> ../@xpack-dev-tools/clang/.content/bin/clang-doc
│ ├── clang-format -> ../@xpack-dev-tools/clang/.content/bin/clang-format
│ ├── clang-include-cleaner -> ../@xpack-dev-tools/clang/.content/bin/clang-include-cleaner
│ ├── clang-linker-wrapper -> ../@xpack-dev-tools/clang/.content/bin/clang-linker-wrapper
│ ├── clang-refactor -> ../@xpack-dev-tools/clang/.content/bin/clang-refactor
│ ├── clang-rename -> ../@xpack-dev-tools/clang/.content/bin/clang-rename
│ ├── clang-repl -> ../@xpack-dev-tools/clang/.content/bin/clang-repl
│ ├── clang-scan-deps -> ../@xpack-dev-tools/clang/.content/bin/clang-scan-deps
│ ├── clang-tblgen -> ../@xpack-dev-tools/clang/.content/bin/clang-tblgen
│ ├── clang-tidy -> ../@xpack-dev-tools/clang/.content/bin/clang-tidy
│ ├── clangd -> ../@xpack-dev-tools/clang/.content/bin/clangd
│ ├── darwin-debug -> ../@xpack-dev-tools/clang/.content/bin/darwin-debug
│ ├── diagtool -> ../@xpack-dev-tools/clang/.content/bin/diagtool
│ ├── git-clang-format -> ../@xpack-dev-tools/clang/.content/bin/git-clang-format
│ ├── hmaptool -> ../@xpack-dev-tools/clang/.content/bin/hmaptool
│ ├── ld.lld -> ../@xpack-dev-tools/clang/.content/bin/ld.lld
│ ├── ld64.lld -> ../@xpack-dev-tools/clang/.content/bin/ld64.lld
│ ├── llc -> ../@xpack-dev-tools/clang/.content/bin/llc
│ ├── lld -> ../@xpack-dev-tools/clang/.content/bin/lld
│ ├── lld-link -> ../@xpack-dev-tools/clang/.content/bin/lld-link
│ ├── lldb -> ../@xpack-dev-tools/clang/.content/bin/lldb
│ ├── lldb-argdumper -> ../@xpack-dev-tools/clang/.content/bin/lldb-argdumper
│ ├── lldb-instr -> ../@xpack-dev-tools/clang/.content/bin/lldb-instr
│ ├── lldb-server -> ../@xpack-dev-tools/clang/.content/bin/lldb-server
│ ├── lli -> ../@xpack-dev-tools/clang/.content/bin/lli
│ ├── llvm-addr2line -> ../@xpack-dev-tools/clang/.content/bin/llvm-addr2line
│ ├── llvm-ar -> ../@xpack-dev-tools/clang/.content/bin/llvm-ar
│ ├── llvm-as -> ../@xpack-dev-tools/clang/.content/bin/llvm-as
│ ├── llvm-bitcode-strip -> ../@xpack-dev-tools/clang/.content/bin/llvm-bitcode-strip
│ ├── llvm-config -> ../@xpack-dev-tools/clang/.content/bin/llvm-config
│ ├── llvm-cov -> ../@xpack-dev-tools/clang/.content/bin/llvm-cov
│ ├── llvm-cxxdump -> ../@xpack-dev-tools/clang/.content/bin/llvm-cxxdump
│ ├── llvm-cxxfilt -> ../@xpack-dev-tools/clang/.content/bin/llvm-cxxfilt
│ ├── llvm-cxxmap -> ../@xpack-dev-tools/clang/.content/bin/llvm-cxxmap
│ ├── llvm-debuginfo-analyzer -> ../@xpack-dev-tools/clang/.content/bin/llvm-debuginfo-analyzer
│ ├── llvm-debuginfod -> ../@xpack-dev-tools/clang/.content/bin/llvm-debuginfod
│ ├── llvm-debuginfod-find -> ../@xpack-dev-tools/clang/.content/bin/llvm-debuginfod-find
│ ├── llvm-diff -> ../@xpack-dev-tools/clang/.content/bin/llvm-diff
│ ├── llvm-dis -> ../@xpack-dev-tools/clang/.content/bin/llvm-dis
│ ├── llvm-dlltool -> ../@xpack-dev-tools/clang/.content/bin/llvm-dlltool
│ ├── llvm-dwarfutil -> ../@xpack-dev-tools/clang/.content/bin/llvm-dwarfutil
│ ├── llvm-lib -> ../@xpack-dev-tools/clang/.content/bin/llvm-lib
│ ├── llvm-libtool-darwin -> ../@xpack-dev-tools/clang/.content/bin/llvm-libtool-darwin
│ ├── llvm-nm -> ../@xpack-dev-tools/clang/.content/bin/llvm-nm
│ ├── llvm-objcopy -> ../@xpack-dev-tools/clang/.content/bin/llvm-objcopy
│ ├── llvm-objdump -> ../@xpack-dev-tools/clang/.content/bin/llvm-objdump
│ ├── llvm-otool -> ../@xpack-dev-tools/clang/.content/bin/llvm-otool
│ ├── llvm-profdata -> ../@xpack-dev-tools/clang/.content/bin/llvm-profdata
│ ├── llvm-ranlib -> ../@xpack-dev-tools/clang/.content/bin/llvm-ranlib
│ ├── llvm-rc -> ../@xpack-dev-tools/clang/.content/bin/llvm-rc
│ ├── llvm-readelf -> ../@xpack-dev-tools/clang/.content/bin/llvm-readelf
│ ├── llvm-readobj -> ../@xpack-dev-tools/clang/.content/bin/llvm-readobj
│ ├── llvm-remarkutil -> ../@xpack-dev-tools/clang/.content/bin/llvm-remarkutil
│ ├── llvm-sim -> ../@xpack-dev-tools/clang/.content/bin/llvm-sim
│ ├── llvm-size -> ../@xpack-dev-tools/clang/.content/bin/llvm-size
│ ├── llvm-strings -> ../@xpack-dev-tools/clang/.content/bin/llvm-strings
│ ├── llvm-strip -> ../@xpack-dev-tools/clang/.content/bin/llvm-strip
│ ├── llvm-symbolizer -> ../@xpack-dev-tools/clang/.content/bin/llvm-symbolizer
│ ├── llvm-tblgen -> ../@xpack-dev-tools/clang/.content/bin/llvm-tblgen
│ ├── llvm-tli-checker -> ../@xpack-dev-tools/clang/.content/bin/llvm-tli-checker
│ ├── llvm-windres -> ../@xpack-dev-tools/clang/.content/bin/llvm-windres
│ ├── run-clang-tidy -> ../@xpack-dev-tools/clang/.content/bin/run-clang-tidy
│ ├── scan-build-py -> ../@xpack-dev-tools/clang/.content/bin/scan-build-py
│ ├── set-xcode-analyzer -> ../@xpack-dev-tools/clang/.content/bin/set-xcode-analyzer
│ ├── split-file -> ../@xpack-dev-tools/clang/.content/bin/split-file
│ └── wasm-ld -> ../@xpack-dev-tools/clang/.content/bin/wasm-ld
└── @xpack-dev-tools
└── clang -> /Users/ilg/Library/xPacks/@xpack-dev-tools/clang/18.1.8-2.1

4 directories, 72 files

This command will also remeber the actual clang version in the xpacks.devDependencies section of package.json, ensuring that the project is locked to this version:

{
"xpacks": {
"devDependencies": {
"@xpack-dev-tools/clang": {
"specifier": "18.1.8-2.1",
"local": "link",
"platforms": "all"
}
},
}
}
note

On Windows, where symbolic links to files are problematic, forwarders/shims are used. For example:

...
│ clang
│ clang.cmd
│ clang.ps1

To test if clang starts properly, invoke it via the shim in the .bin folder:

PS C:\Users\ilg\xpm-demo-hello> .\xpacks\.bin\clang --version
xPack x86_64 clang version 18.1.8

Add actions

In traditional development environments, adding xpacks/.bin to the PATH is sufficient to allow any build tools to access the installed executables.

However, the workflow can be further automated, by starting the build tools via xpm run, which automatically manages the PATH.

To experiment with this, edit package.json and add a clang --version command in the xpack.actions section, with a name (for example cc-version):

{
"xpacks": {
"actions": {
"cc-version": "clang --version"
}
}
}

With this definition, the compiler version can be displayed without manually entering the path:

PS C:\Users\ilg\xpm-demo-hello> xpm run cc-version
> clang --version
xPack x86_64 clang version 18.1.8

Once the compiler is functional, additional xpack.actions can be added to manage essential tasks such as preparing the build environment, performing the build, and executing the resulting program:

{
"xpacks": {
"actions": {
"cc-version": "clang --version",
"prepare": "mkdir build",
"build": "clang src/hello.c -o build/hello",
"execute": "build{{path.sep}}hello"
}
}
}
Tip 1

The {{path.sep}} syntax is a LiquidJS substitution used to accommodate platform-specific path separators. It is necessary in the command passed via execute because it is parsed by the system shell. However, it is not required in the compile as the toolchain also understands POSIX paths.

Tip 2

In this simple demo case, the prepare action is straightforward and can be directly executed in the terminal. However, for real-life projects, preparing the build often involves using tools like CMake or meson. Therefore, it is recommended as a best practice to always define a prepare action.

With these actions defined, the workflow becomes:

PS C:\Users\ilg\xpm-demo-hello> xpm run prepare
> mkdir build

PS C:\Users\ilg\xpm-demo-hello> xpm run build
> clang src/hello.c -o build/hello

PS C:\Users\ilg\xpm-demo-hello> xpm run execute
> build\hello
Hello World!

Visual Studio Code

For convenience, when using Visual Studio Code with the xPack C/C++ Managed Build Tools extension, the actions are displayed in the bottom left corner and can be executed with a single mouse click.

cc-version

Instantiate a template

While the resulting projects above are functional, they remain quite simple.

A real-world project will likely have multiple configurations, at least to build separate debug and release executables, and possibly to run tests in various environments.

Additionally, more complex projects will probably use tools like CMake or meson.

These types of projects can be created manually, but the process can be further automated by using project templates.

To generate the traditional "Hello World!" project, there is an xpack/hello-world-template-xpack project, also available from npmjs.com as @xpack/hello-world-template.

Both C and C++ are supported, with CMake and meson as build system generators.

Instantiating the template in interactive mode looks like this:

% mkdir my-project && cd my-project
% xpm init --template @xpack/hello-world-template@latest

Checking package @xpack/hello-world-template@latest metadata...
Processing @xpack/hello-world-template@0.6.1...

Programming language? (c, cpp, ?) [cpp]: c
Build System? (cmake, meson, autotools, ?) [cmake]:
Toolchain? (gcc, clang, system, ?) [gcc]: clang
Creating the C project 'my-project'...
File 'include/hello-world.h' copied.
File 'src/hello-world.c' copied.
File 'libs/adder/include/add/add.h' copied.
File 'libs/adder/src/add.c' copied.
Folder 'cmake' copied.
File 'CMakeLists.txt' generated.
File '.vscode/tasks.json' copied.
File '.vscode/settings.json' copied.
File '.gitignore' copied.
File '.npmignore' copied.
File 'README.md' generated.
File 'LICENSE' generated.
File 'package.json' generated.
tip

As mentioned before, for portability reasons it is recommended to use clang.

The next step is to satisfy the project dependencies:

% xpm install
@my-scope/my-project...
+ @xpack-dev-tools/ninja-build@1.11.1-3.1
+ @xpack-dev-tools/cmake@3.28.6-1.1
+ @xpack-dev-tools/clang@18.1.8-2.1
'xpacks/@xpack-dev-tools/cmake' -> '/Users/ilg/Library/xPacks/@xpack-dev-tools/cmake/3.28.6-1.1'
'xpacks/@xpack-dev-tools/clang' -> '/Users/ilg/Library/xPacks/@xpack-dev-tools/clang/18.1.8-2.1'
'xpacks/.bin/ccmake' -> '../@xpack-dev-tools/cmake/.content/bin/ccmake'
'xpacks/@xpack-dev-tools/ninja-build' -> '/Users/ilg/Library/xPacks/@xpack-dev-tools/ninja-build/1.11.1-3.1'
'xpacks/.bin/analyze-build' -> '../@xpack-dev-tools/clang/.content/bin/analyze-build'
'xpacks/.bin/cmake' -> '../@xpack-dev-tools/cmake/.content/bin/cmake'
'xpacks/.bin/clang' -> '../@xpack-dev-tools/clang/.content/bin/clang'
'xpacks/.bin/ninja' -> '../@xpack-dev-tools/ninja-build/.content/bin/ninja'
'xpacks/.bin/cpack' -> '../@xpack-dev-tools/cmake/.content/bin/cpack'
'xpacks/.bin/clang++' -> '../@xpack-dev-tools/clang/.content/bin/clang++'
'xpacks/.bin/ctest' -> '../@xpack-dev-tools/cmake/.content/bin/ctest'
'xpacks/.bin/clang-check' -> '../@xpack-dev-tools/clang/.content/bin/clang-check'
'xpacks/.bin/clang-cl' -> '../@xpack-dev-tools/clang/.content/bin/clang-cl'
'xpacks/.bin/clang-cpp' -> '../@xpack-dev-tools/clang/.content/bin/clang-cpp'
'xpacks/.bin/clang-doc' -> '../@xpack-dev-tools/clang/.content/bin/clang-doc'
'xpacks/.bin/clang-format' -> '../@xpack-dev-tools/clang/.content/bin/clang-format'
'xpacks/.bin/clang-include-cleaner' -> '../@xpack-dev-tools/clang/.content/bin/clang-include-cleaner'
'xpacks/.bin/clang-linker-wrapper' -> '../@xpack-dev-tools/clang/.content/bin/clang-linker-wrapper'
'xpacks/.bin/clang-refactor' -> '../@xpack-dev-tools/clang/.content/bin/clang-refactor'
'xpacks/.bin/clang-rename' -> '../@xpack-dev-tools/clang/.content/bin/clang-rename'
'xpacks/.bin/clang-repl' -> '../@xpack-dev-tools/clang/.content/bin/clang-repl'
'xpacks/.bin/clang-scan-deps' -> '../@xpack-dev-tools/clang/.content/bin/clang-scan-deps'
'xpacks/.bin/clang-tblgen' -> '../@xpack-dev-tools/clang/.content/bin/clang-tblgen'
'xpacks/.bin/clang-tidy' -> '../@xpack-dev-tools/clang/.content/bin/clang-tidy'
'xpacks/.bin/clangd' -> '../@xpack-dev-tools/clang/.content/bin/clangd'
'xpacks/.bin/darwin-debug' -> '../@xpack-dev-tools/clang/.content/bin/darwin-debug'
'xpacks/.bin/diagtool' -> '../@xpack-dev-tools/clang/.content/bin/diagtool'
'xpacks/.bin/git-clang-format' -> '../@xpack-dev-tools/clang/.content/bin/git-clang-format'
'xpacks/.bin/hmaptool' -> '../@xpack-dev-tools/clang/.content/bin/hmaptool'
'xpacks/.bin/ld.lld' -> '../@xpack-dev-tools/clang/.content/bin/ld.lld'
'xpacks/.bin/ld64.lld' -> '../@xpack-dev-tools/clang/.content/bin/ld64.lld'
'xpacks/.bin/llc' -> '../@xpack-dev-tools/clang/.content/bin/llc'
'xpacks/.bin/lld' -> '../@xpack-dev-tools/clang/.content/bin/lld'
'xpacks/.bin/lld-link' -> '../@xpack-dev-tools/clang/.content/bin/lld-link'
'xpacks/.bin/lldb' -> '../@xpack-dev-tools/clang/.content/bin/lldb'
'xpacks/.bin/lldb-argdumper' -> '../@xpack-dev-tools/clang/.content/bin/lldb-argdumper'
'xpacks/.bin/lldb-instr' -> '../@xpack-dev-tools/clang/.content/bin/lldb-instr'
'xpacks/.bin/lldb-server' -> '../@xpack-dev-tools/clang/.content/bin/lldb-server'
'xpacks/.bin/lli' -> '../@xpack-dev-tools/clang/.content/bin/lli'
'xpacks/.bin/llvm-addr2line' -> '../@xpack-dev-tools/clang/.content/bin/llvm-addr2line'
'xpacks/.bin/llvm-ar' -> '../@xpack-dev-tools/clang/.content/bin/llvm-ar'
'xpacks/.bin/llvm-as' -> '../@xpack-dev-tools/clang/.content/bin/llvm-as'
'xpacks/.bin/llvm-bitcode-strip' -> '../@xpack-dev-tools/clang/.content/bin/llvm-bitcode-strip'
'xpacks/.bin/llvm-config' -> '../@xpack-dev-tools/clang/.content/bin/llvm-config'
'xpacks/.bin/llvm-cov' -> '../@xpack-dev-tools/clang/.content/bin/llvm-cov'
'xpacks/.bin/llvm-cxxdump' -> '../@xpack-dev-tools/clang/.content/bin/llvm-cxxdump'
'xpacks/.bin/llvm-cxxfilt' -> '../@xpack-dev-tools/clang/.content/bin/llvm-cxxfilt'
'xpacks/.bin/llvm-cxxmap' -> '../@xpack-dev-tools/clang/.content/bin/llvm-cxxmap'
'xpacks/.bin/llvm-debuginfo-analyzer' -> '../@xpack-dev-tools/clang/.content/bin/llvm-debuginfo-analyzer'
'xpacks/.bin/llvm-debuginfod' -> '../@xpack-dev-tools/clang/.content/bin/llvm-debuginfod'
'xpacks/.bin/llvm-debuginfod-find' -> '../@xpack-dev-tools/clang/.content/bin/llvm-debuginfod-find'
'xpacks/.bin/llvm-diff' -> '../@xpack-dev-tools/clang/.content/bin/llvm-diff'
'xpacks/.bin/llvm-dis' -> '../@xpack-dev-tools/clang/.content/bin/llvm-dis'
'xpacks/.bin/llvm-dlltool' -> '../@xpack-dev-tools/clang/.content/bin/llvm-dlltool'
'xpacks/.bin/llvm-dwarfutil' -> '../@xpack-dev-tools/clang/.content/bin/llvm-dwarfutil'
'xpacks/.bin/llvm-lib' -> '../@xpack-dev-tools/clang/.content/bin/llvm-lib'
'xpacks/.bin/llvm-libtool-darwin' -> '../@xpack-dev-tools/clang/.content/bin/llvm-libtool-darwin'
'xpacks/.bin/llvm-nm' -> '../@xpack-dev-tools/clang/.content/bin/llvm-nm'
'xpacks/.bin/llvm-objcopy' -> '../@xpack-dev-tools/clang/.content/bin/llvm-objcopy'
'xpacks/.bin/llvm-objdump' -> '../@xpack-dev-tools/clang/.content/bin/llvm-objdump'
'xpacks/.bin/llvm-otool' -> '../@xpack-dev-tools/clang/.content/bin/llvm-otool'
'xpacks/.bin/llvm-profdata' -> '../@xpack-dev-tools/clang/.content/bin/llvm-profdata'
'xpacks/.bin/llvm-ranlib' -> '../@xpack-dev-tools/clang/.content/bin/llvm-ranlib'
'xpacks/.bin/llvm-rc' -> '../@xpack-dev-tools/clang/.content/bin/llvm-rc'
'xpacks/.bin/llvm-readelf' -> '../@xpack-dev-tools/clang/.content/bin/llvm-readelf'
'xpacks/.bin/llvm-readobj' -> '../@xpack-dev-tools/clang/.content/bin/llvm-readobj'
'xpacks/.bin/llvm-remarkutil' -> '../@xpack-dev-tools/clang/.content/bin/llvm-remarkutil'
'xpacks/.bin/llvm-sim' -> '../@xpack-dev-tools/clang/.content/bin/llvm-sim'
'xpacks/.bin/llvm-size' -> '../@xpack-dev-tools/clang/.content/bin/llvm-size'
'xpacks/.bin/llvm-strings' -> '../@xpack-dev-tools/clang/.content/bin/llvm-strings'
'xpacks/.bin/llvm-strip' -> '../@xpack-dev-tools/clang/.content/bin/llvm-strip'
'xpacks/.bin/llvm-symbolizer' -> '../@xpack-dev-tools/clang/.content/bin/llvm-symbolizer'
'xpacks/.bin/llvm-tblgen' -> '../@xpack-dev-tools/clang/.content/bin/llvm-tblgen'
'xpacks/.bin/llvm-tli-checker' -> '../@xpack-dev-tools/clang/.content/bin/llvm-tli-checker'
'xpacks/.bin/llvm-windres' -> '../@xpack-dev-tools/clang/.content/bin/llvm-windres'
'xpacks/.bin/run-clang-tidy' -> '../@xpack-dev-tools/clang/.content/bin/run-clang-tidy'
'xpacks/.bin/scan-build-py' -> '../@xpack-dev-tools/clang/.content/bin/scan-build-py'
'xpacks/.bin/set-xcode-analyzer' -> '../@xpack-dev-tools/clang/.content/bin/set-xcode-analyzer'
'xpacks/.bin/split-file' -> '../@xpack-dev-tools/clang/.content/bin/split-file'
'xpacks/.bin/wasm-ld' -> '../@xpack-dev-tools/clang/.content/bin/wasm-ld'

The final step is to run the tests:

% xpm run test-all
> xpm run test-debug
> xpm run prepare --config debug
> cmake -S . -B build/debug -G Ninja -D CMAKE_BUILD_TYPE=Debug -D CMAKE_EXPORT_COMPILE_COMMANDS=ON -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang.cmake
-- The C compiler identification is Clang 18.1.8
-- The CXX compiler identification is Clang 18.1.8
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Users/ilg/tmp/my-project/xpacks/.bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Users/ilg/tmp/my-project/xpacks/.bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The ASM compiler identification is Clang with GNU-like command-line
-- Found assembler: /Users/ilg/tmp/my-project/xpacks/.bin/clang
-- Build type: Debug
-- Project path: /Users/ilg/tmp/my-project
-- PATH: /Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/.nvm/versions/node/v22.11.0/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
-- CMake version: 3.28.6
-- Compiler: Clang 18.1.8
-- A> hello-world
-- Configuring done (2.5s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/ilg/tmp/my-project/build/debug
> xpm run build --config debug
> cmake -S . -B build/debug -G Ninja -D CMAKE_BUILD_TYPE=Debug -D CMAKE_EXPORT_COMPILE_COMMANDS=ON
-- Build type: Debug
-- Project path: /Users/ilg/tmp/my-project
-- PATH: /Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/.nvm/versions/node/v22.11.0/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
-- CMake version: 3.28.6
-- Compiler: Clang 18.1.8
-- A> hello-world
-- Configuring done (0.2s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/ilg/tmp/my-project/build/debug
> cmake --build build/debug --verbose
Change Dir: '/Users/ilg/tmp/my-project/build/debug'

Run Build Command(s): /Users/ilg/tmp/my-project/xpacks/.bin/ninja -v
[1/3] /Users/ilg/tmp/my-project/xpacks/.bin/clang -DDEBUG -I/Users/ilg/tmp/my-project/include -I/Users/ilg/tmp/my-project/libs/adder/include -O0 -g3 -std=c11 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -flto -MD -MT CMakeFiles/hello-world.dir/libs/adder/src/add.c.o -MF CMakeFiles/hello-world.dir/libs/adder/src/add.c.o.d -o CMakeFiles/hello-world.dir/libs/adder/src/add.c.o -c /Users/ilg/tmp/my-project/libs/adder/src/add.c
[2/3] /Users/ilg/tmp/my-project/xpacks/.bin/clang -DDEBUG -I/Users/ilg/tmp/my-project/include -I/Users/ilg/tmp/my-project/libs/adder/include -O0 -g3 -std=c11 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -flto -MD -MT CMakeFiles/hello-world.dir/src/hello-world.c.o -MF CMakeFiles/hello-world.dir/src/hello-world.c.o.d -o CMakeFiles/hello-world.dir/src/hello-world.c.o -c /Users/ilg/tmp/my-project/src/hello-world.c
[3/3] : && /Users/ilg/tmp/my-project/xpacks/.bin/clang -O0 -g3 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -flto -Wl,-dead_strip CMakeFiles/hello-world.dir/src/hello-world.c.o CMakeFiles/hello-world.dir/libs/adder/src/add.c.o -o hello-world && :
-macosx_version_min has been renamed to -macos_version_min
-macosx_version_min has been renamed to -macos_version_min

> xpm run execute --config debug
> build/debug/hello-world
Hello World!
(in debug mode)
Check adder lib: 41 + 1 = 42
> xpm run test-release
> xpm run prepare --config release
> cmake -S . -B build/release -G Ninja -D CMAKE_BUILD_TYPE=Release -D CMAKE_EXPORT_COMPILE_COMMANDS=ON -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang.cmake
-- The C compiler identification is Clang 18.1.8
-- The CXX compiler identification is Clang 18.1.8
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Users/ilg/tmp/my-project/xpacks/.bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Users/ilg/tmp/my-project/xpacks/.bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The ASM compiler identification is Clang with GNU-like command-line
-- Found assembler: /Users/ilg/tmp/my-project/xpacks/.bin/clang
-- Build type: Release
-- Project path: /Users/ilg/tmp/my-project
-- PATH: /Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/.nvm/versions/node/v22.11.0/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
-- CMake version: 3.28.6
-- Compiler: Clang 18.1.8
-- A> hello-world
-- Configuring done (2.3s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/ilg/tmp/my-project/build/release
> xpm run build --config release
> cmake -S . -B build/release -G Ninja -D CMAKE_BUILD_TYPE=Release -D CMAKE_EXPORT_COMPILE_COMMANDS=ON
-- Build type: Release
-- Project path: /Users/ilg/tmp/my-project
-- PATH: /Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/tmp/my-project/xpacks/.bin:/Users/ilg/.nvm/versions/node/v22.11.0/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
-- CMake version: 3.28.6
-- Compiler: Clang 18.1.8
-- A> hello-world
-- Configuring done (0.1s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/ilg/tmp/my-project/build/release
> cmake --build build/release --verbose
Change Dir: '/Users/ilg/tmp/my-project/build/release'

Run Build Command(s): /Users/ilg/tmp/my-project/xpacks/.bin/ninja -v
[1/3] /Users/ilg/tmp/my-project/xpacks/.bin/clang -I/Users/ilg/tmp/my-project/include -I/Users/ilg/tmp/my-project/libs/adder/include -O3 -DNDEBUG -std=c11 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -flto -MD -MT CMakeFiles/hello-world.dir/libs/adder/src/add.c.o -MF CMakeFiles/hello-world.dir/libs/adder/src/add.c.o.d -o CMakeFiles/hello-world.dir/libs/adder/src/add.c.o -c /Users/ilg/tmp/my-project/libs/adder/src/add.c
[2/3] /Users/ilg/tmp/my-project/xpacks/.bin/clang -I/Users/ilg/tmp/my-project/include -I/Users/ilg/tmp/my-project/libs/adder/include -O3 -DNDEBUG -std=c11 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -flto -MD -MT CMakeFiles/hello-world.dir/src/hello-world.c.o -MF CMakeFiles/hello-world.dir/src/hello-world.c.o.d -o CMakeFiles/hello-world.dir/src/hello-world.c.o -c /Users/ilg/tmp/my-project/src/hello-world.c
[3/3] : && /Users/ilg/tmp/my-project/xpacks/.bin/clang -O3 -DNDEBUG -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -flto -Wl,-dead_strip CMakeFiles/hello-world.dir/src/hello-world.c.o CMakeFiles/hello-world.dir/libs/adder/src/add.c.o -o hello-world && :
-macosx_version_min has been renamed to -macos_version_min
-macosx_version_min has been renamed to -macos_version_min

> xpm run execute --config release
> build/release/hello-world
Hello World!
(in release mode)
(no asserts)
Check adder lib: 41 + 1 = 42

Final considerations

The xpm workflow described above can be successfully applied for various types of projects::

  • Source code libraries: Projects that can be included as dependencies in other projects.
  • Projects with executables: Projects that provide development tools and can also be included as dependencies in other projects.
  • Final projects: Projects that produce various artifacts that run natively or on embedded platforms.

Understanding the mechanism for defining xpack.actions enables the implementation of highly complex workflows. For instance, intricate build configurations can be created for elaborate multi-platform tests.

For final projects not intended to be used as dependencies in other projects, having complex logic in the top-level package.json is not a significant issue.

However, for projects intended to be used as dependencies, it is advisable to keep only user-related metadata in the top-level package.json and move all development metadata to separate folders.

Separate tests/package.json

To reduce the clutter, it is recommended to move all test-related metadata to a separate tests folder, with its own package.json.

Separate build_assets/package.json

Similarly, if the project produces executables to be used as dependencies, it is recommended to move all build related files into a separate folder, for example build-assets, with its own package.json.