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,
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
.