Introduction

What is uncross?

uncross is a meta build system for C/C++ cmake projects implemented as a python3 command-line interface. The aim is to automate and simplify cross compilation for cmake projects. Currently, it most readily supports Linux cross compilation from an x86_64 host system.

uncross is highly opinionated, supporting a small suite of C and C++ tools that the author has used across several projects.

The core intent is rapid prototyping and rapid delivery of small embedded systems projects. The use case for the author is compiling small test targets for cybersecurity research on embedded Linux networking appliances and IoT devices.

Why uncross?

A project that uses uncross should largely look like any CMake project. Currently, the only intended deviation at the source level is a configuration file, uncross.toml, at the root of a project. In the future, and source level plugin and caching directory will be supported. In most circumstances, uncross should be drop-in compatible with existing projects that use CMake. uncross is not intended to adopt a large amount of logic for sake of compatibility with every edge-case use of CMake. However, failure to build an existing CMake project is considered a bug when the unsupported behavior is a reasonable "base case" feature of CMake.

Connecting various toolchains to a build system is a core task for embedded systems programing. CMake is a popular build system for defining builds in a portable manner. While CMake itself solves a large majority of problems in this space, it is still common to observe numerous utility scripts and scaffolding in large embedded projects. Each team will solve the same task in many different ways. These scripts take developer time and energy to implement and maintain.

uncross is a collection of a few select tools to accomplish the tasks of static analysis, cross compilation, and linting/formatting. These repositories should only need to include source files and build system files to instruct CMake on how to build the project. All other tasks should be completed by the meta build system. Improvements to the meta system should apply to all projects in this format, removing the need to maintain a collection of build, analysis, test, and packaging scripts.

New members of the team should not have to memorize large CMake commands or parse a collection of scripts and Makefiles. These tasks should be automated in a sane, memorable set of commands.

There are a variety of projects that provide toolchains for cross compiling Linux binaries from an x86_64 host to a variety of target architectures. toolchains.bootlin.com was selected as an initial toolchain source, with others to follow. Downloading and connecting these toolchains to the project build system should be an easy task, provided by the meta build system.

What isn't uncross?

uncross does not aim to be a generic build system for all C/C++ projects. It does not support the larger body of C/C++ build systems (meson, bazel, msbuild, autotools, etc). It does not solve the problem of ensuring portability to all possible FOSS targets. It is narrowly tailored to an opinionated layout and employment of a select suite of widely used tooling as the default.

uncross is also not a build system on its own. CMake provides all of the heavy lifting. uncross automates and simplifies the rapid employment of CMake.

The long term plan is to enable extensions/plugins to enable support for a more diverse set of tools.

What is this book?

This documentation is intended to introduce a seasoned C and CMake developer to the options and basic usage of uncross. By design, CMake does a majority of the lifting. For an intoduction to CMake, we recommend Professional CMake: A Practical Guide - 19th Edition.

Contributing

We welcome source contributions and pull requests.

We do ask that any feature work is supported by a firm justification of how the addition will apply to a larger body of projects and use cases. We are happy to look at your C/C++ project source if you are willing to share it to support these additions.

Bug fixes are greatly appreciated and will always be rapidly reviewed and merged as time allows.

This book and the website at uncross.dev are part of the project git repository. Any feature work will be expected to update corresponding documentation prior to merge.

Quick Start

This guide is meant to show basic usage of uncross for a new project.

Project Creation

To begin, create a project with the uncross new command:

uncross new project

This will create a new directory in the current working directory with the structure:

project
├── .git
│   └── <git files>
├── include
│   └── project.h
├── src
│   ├── CMakeLists.txt
│   └── main.c
├── .clang-format
├── .clang-tidy
├── .gitignore
├── CMakeLists.txt
├── Makefile
└── uncross.toml

This project structure has a very small C program that will compile to print "Hello, !" to stdout.

Build Project

To build in debug mode:

uncross build

To build in release mode:

uncross build --release

To build both:

uncross build --all

After building, observe the following directory structure:

project
├── .git
│   └── <git files>
├── build
│   ├── Debug
│   │   └── <debug build files>
│   └── Release
│       └── <release build files>
├── debug
│   └── bin
│       └── project_Linux_x86_64
├── include
│   └── project.h
├── release
│   └── bin
│       └── project_Linux_x86_64
├── src
│   ├── CMakeLists.txt
│   └── main.c
├── .clang-format
├── .clang-tidy
├── .gitignore
├── CMakeLists.txt
├── Makefile
└── uncross.toml

The debug and release directories are a creation of uncross. The behavior of building the debug and release binaries in implemented in the default CMakeLists.txt, and must be reimplemented in existing projects if this behavior is desired.

CodeChecker

uncross automates the creation of HTML reports with CodeChecker using a compilation database from CMake. To run the report and automatically open it in a browser, run:

uncross check --open

Running the above command without the --open flag will not open a browser window, and the files will be available under the release or debug directories. The check command takes the same flags as build to choose the build mode.

Linting and Formatting

uncross enables checking for lint, and automatically formatting source files, via clang-format. uncross lint will check all .c and .h files in the project for lint, and uncross fmt will automatically format them. uncross will pass an argument to clang-format to have it use a .clang-format file in the project root.

Commands

The following pages will provide explanations and notes that are not documented by the --help listings.

uncross build

The build command invokes CMake for each provided preset and toolchain.

With no options, the build command will invoke the system toolchain. If uncross.toml is present, it will build for each toolchain listed in uncross.toml.

For options, run uncross build --help.

uncross check

The check command invokes CodeChecker for each provided preset and toolchain.

NOTE: The project must first be build. Under the hood, CodeChecker is given a path to compile_commands.json, which is provided by the CMake configure step.

With no options, the check command will invoke the system toolchain. If uncross.toml is present, it will check each toolchain listed in uncross.toml.

For options, run uncross check --help.

uncross clean

The clean command removes the ./build, ./debug, ./release, and release.tar.gz by default. When the project directory is a git repository, this will always occur at the project root, regardless of the current working directory (as long as the current working directory is under the project root). When the project directory is not a git repository, this occurs in the working directory.

For options, run uncross clean --help.

uncross fmt

The fmt command invokes clang-format -i on every .c and .h file in the project.

This feature will not work as expected without a .clang-format file committed into the root of the project.

If the project directory is a git repository, this will run on every file/path that ends in .c or .h that is staged or committed. It will not run on new files that are not yet staged for commit.

If the project directory is a git repository, a behavior similar to find . -name "*.c" -exec ... will occur. This can be undesirable if directories under the project root contain temporary or external .c and .h files. For this reason, build, debug, and release are ignored.

For options, run uncross fmt --help.

uncross lint

The lint command invokes clang-format --dry-run on every .c and .h file in the project. This is intended to be used in CI or as a sanity check.

This feature will not work as expected without a .clang-format file committed into the root of the project.

If the project directory is a git repository, this will run on every file/path that ends in .c or .h that is staged or committed. It will not run on new files that are not yet staged for commit.

If the project directory is a git repository, a behavior similar to find . -name "*.c" -exec ... will occur. This can be undesirable if directories under the project root contain temporary or external .c and .h files. For this reason, build, debug, and release are ignored.

For options, run uncross lint --help.

uncross new

The new command creates a new project structure at the provided path.

If the path is a relative or absolute path (containing '.' or '/'), then the --name option must be provided. Otherwise, the name of the project will be the name of the provided directory.

For options, run uncross new --help.

uncross release

The release command creates a gzip compressed tarball of ./release at ./release.tar.gz from the project root.

For options, run uncross release --help.