Zero-configuration build tool for C and C++ projects.
Have you ever had a single main.cpp file that you just want to compile, without having to make sure the order of flags are correct and ideally without having to provide any flags at all?
Slay handles compiler detection, flag ordering, library discovery via pkg-config, testing, formatting, cross-compilation, etc., without a single configuration file.
It should be possible to compile all of the examples in the examples directory, simply by running slay in each directory, as long as the right packages and libraries have been installed.
This is a Go port of xyproto/cxx (which does approximately the same, but uses Python + Scons instead).
slay # build the project
slay run # build and run
slay clean # remove built filesNo configuration files are needed, but the project needs to either be very simple (a single main.cpp) or have an slay-friendly directory structure.
The auto-detection of external libraries and headers relies on them being included in the main source file.
If you like, you can add this shield to your project to indicate that it can be compiled without any particular build-related configuration:
Just make sure that it builds with slay first, then add this to your README.md:
[](https://github.com/xyproto/slay)It is also possible to add it to the list of projects that compiles with Slay.
git clone https://github.com/xyproto/slay
cd slay
make
sudo make installgit clone https://github.com/xyproto/slay
cd slay
make
sudo make install # use gmake on BSDgit clone https://github.com/xyproto/slay
cd slay
make
make installOr with go install:
go install github.com/xyproto/slay/cmd/slay@latest
sudo ln -sf ~/go/bin/slay /usr/local/bin/slaySlay uses a composable command syntax: combine modifiers with an action.
clang use clang/clang++ compiler
zap use zapcc++ compiler
debug enable debug flags and sanitizers
nosan disable sanitizers (use with debug)
opt enable optimizations (-Ofast/-O3, -flto)
strict enable strict warning flags
sloppy enable permissive flags
small optimize for size (-Os)
tiny minimize size (-Os + sstrip/upx)
win64 cross-compile for 64-bit Windows
build compile the project (default)
run build and run
debug debug build and launch debugger
rebuild clean and build
clean remove built files
fastclean only remove executable and *.o
test build and run tests
testbuild build tests (without running)
pgo profile-guided optimization (build, run, rebuild)
fmt format source code with clang-format
generate generate CMakeLists.txt
makefile generate a standalone Makefile
cmake build with cmake (prefers ninja, falls back to make)
make build with make (falls back to cmake+make)
ninja build with ninja (falls back to cmake+ninja)
install install the project (PREFIX, DESTDIR)
pkg package the project into pkg/
export export a standalone Makefile and build.sh
script generate build.sh and clean.sh
valgrind build and profile with valgrind
pro generate QtCreator project file
version show version
ninjainstall install from ninja build
ninjaclean clean ninja build
makeinstall install from make/cmake+make build
makeclean clean make/cmake+make build
slay # standard build
slay clang # build with clang
slay clang strict # build with clang and strict warnings
slay debug # debug build and launch debugger
slay debug build # debug build (without launching debugger)
slay clang debug # clang debug build and launch debugger
slay opt run # optimized build and run
slay small win64 # size-optimized cross-compile for Windows
slay -C <dir> ... # run in the given directoryLegacy compound commands (debugbuild, clangstrict, smallwin64, etc.) are still accepted.
Create a main.cpp file:
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <string>
using namespace std::string_literals;
class Point {
public:
double x;
double y;
double z;
};
std::ostream& operator<<(std::ostream& output, const Point& p)
{
using std::setfill;
using std::setw;
output << "{ "s << setfill(' ') << setw(3) << p.x << ", "s << setfill(' ') << setw(3) << p.y
<< ", "s << setfill(' ') << setw(3) << p.z << " }"s;
return output;
}
Point operator+(const Point& a, const Point& b)
{
return Point { .x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z };
}
Point operator*(const Point& a, const Point& b)
{
return Point { .x = a.x * b.x, .y = a.y * b.y, .z = a.z * b.z };
}
int main(int argc, char** argv)
{
Point p1 { .x = 1, .y = 2, .z = 3 };
Point p2 { .y = 42 };
using std::cout;
using std::endl;
cout << " p1 = " << p1 << endl;
cout << " p2 = " << p2 << endl;
cout << "p1 + p2 = " << p1 + p2 << endl;
cout << "p1 * p2 = " << p1 * p2 << endl;
return EXIT_SUCCESS;
}Then build and run:
slay runRebuild from scratch:
slay rebuildBuild with profile-guided optimization:
slay pgo # builds, runs (collecting profiling data), then rebuilds with PGO
slay # subsequent builds use the profiling datamyproject/
├── main.cpp # main source (or main.cc, main.c)
├── include/ # project headers (.h, .hpp)
│ └── hello.h
├── common/ # shared source files
│ ├── hello.cpp
│ └── hello_test.cpp # test file (must contain main())
├── img/ # images
├── shaders/ # shaders
├── data/ # data files
├── share/ # shared data files (or shared/)
└── scripts/ # script files
- The main source file can live in the project root or
src/. - The executable name matches the parent directory name.
- Files ending with
_test.*are compiled separately byslay test. include/andcommon/can also be at../includeand../common.
These defines are passed to the compiler, with paths that work both during development and after installation:
| Define | Development | Installed |
|---|---|---|
DATADIR |
./data or ../data |
$PREFIX/share/$app/data |
IMGDIR |
./img or ../img |
$PREFIX/share/$app/img |
SHADERDIR |
./shaders or ../shaders |
$PREFIX/share/$app/shaders |
SHAREDIR |
./share or ../share |
$PREFIX/share/$app |
RESOURCEDIR |
./resources or ../resources |
$PREFIX/share/$app/resources |
RESDIR |
./res or ../res |
$PREFIX/share/$app/res |
SCRIPTDIR |
./scripts or ../scripts |
$PREFIX/share/$app/scripts |
See examples/sdl2, examples/win64crate (uses IMGDIR) and examples/mixer (uses RESOURCEDIR).
- Source files can have corresponding
_testfiles (e.g.quaternions.cc→quaternions_test.cc). - Each
_test.*file must contain its ownmainfunction. - Run with
slay test.
Slay auto-detects libraries from #include directives in your source files using pkg-config. Supported libraries include:
- Graphics: OpenGL, GLUT, GLFW, GLEW, GLM, Vulkan, SDL (2 & 3), SFML (2 & 3), raylib
- GUI: GTK (2, 3 & 4), Qt6, VTE
- Audio: OpenAL, SDL2_mixer, PipeWire, rtaudio
- Physics: Box2D, ReactPhysics3D
- Other: Boost, libconfig++, FastCGI, Gio/GLib, X11
For versioned libraries, the newest available version is preferred (e.g. GTK 4 over GTK 3, SFML 3 over SFML 2).
When a build fails due to a missing header, Slay will suggest which package to install (using pkgfile on Arch Linux or apt-file on Debian/Ubuntu).
Over 40 examples are included in the examples/ directory:
| Category | Examples |
|---|---|
| Basics | hello, args, lambda, defer, invoke, visit, async, designated, entities, validorder, findfiles, platforms, config |
| Graphics | sfml, sfml_audio, bisqwit, sdl2, sdl2_opengl, sdl3, gles3_sdl3, gl4_spirv, gles2_glfw, gles3_glfw, glm, raylib, raylib5, vulkan, vulkan_glfw, x11, x11_opengl, smallpt |
| GUI | gtk4, gtk4ui, dunnetgtk, qt6 |
| Audio | openal, synth, mixer, pipewire, rtaudio |
| Physics | box2d, reactphysics |
| Other | boost, boost_thread, notify, fastcgi, tinyhello, win64crate |
Build all examples:
make examplesInstall to a package directory:
DESTDIR="$pkgdir" PREFIX=/usr slay installOr package into a local pkg/ directory:
slay pkgGenerate standalone build files for users without slay:
slay export # generates Makefile + build.sh + clean.shBuild for 64-bit Windows (requires x86_64-w64-mingw32-g++ or Docker):
slay win64
slay small win64
slay tiny win64Test Windows executables with Wine:
slay run # after slay win64, uses wine automaticallyslay fmt # formats source code using clang-format (Webkit style)The formatting style is fixed and not configurable, on purpose.
g++with C++23 support (or later)pkg-configmake(for the project Makefile, not for building C++ projects)
clang++— build withslay clanglldborgdb— for debuggingpkgfile(Arch Linux) orapt-file(Debian/Ubuntu) — for missing-package suggestionsx86_64-w64-mingw32-g++ordocker— for Windows cross-compilationwine— for testing Windows executablesvalgrind— for profiling (slay valgrind)clang-format— forslay fmtninja— forslay ninja/slay cmake ninja
sudo pacman -S --needed base-devel boost box2d fcgi freeglut glew glfw glibmm glm glu \
gtk4 libconfig libpipewire libx11 openal qt6-base raylib \
rtaudio sdl2-compat sdl2_mixer sdl3 sfml vte4 vulkan-headers vulkan-icd-loadersudo apt-get install -y build-essential pkg-config \
libboost-all-dev libconfig++-dev libfcgi-dev libglew-dev libglfw3-dev \
libglibmm-2.4-dev libglm-dev libglu1-mesa-dev libgtk-4-dev libopenal-dev \
libpipewire-0.3-dev libsdl2-dev libsdl2-mixer-dev libsfml-dev \
libvte-2.91-gtk4-dev libvulkan-dev libx11-dev freeglut3-dev qt6-base-devNote: raylib and reactphysics3d are not available in Ubuntu repositories and reactphysics3d is no longer in the official Arch Linux repositories. Ubuntu 24.04 ships SFML 2 and rtaudio 5, while the included examples use SFML 3 and rtaudio 6 APIs — those examples will be skipped on Ubuntu. Examples that depend on unavailable libraries are automatically skipped in CI.
Install a recent GCC and dependencies with Homebrew:
brew install gcc pkg-configUse gmake instead of make. Install dependencies:
# FreeBSD
pkg install pkgconf gmake
# NetBSD
pkgin install pkgconf gmakeInstall g++ 11+ and build with slay CXX=eg++.
Windows is supported via two development environments:
MSYS2 (recommended): Install MSYS2, then use pacman to install libraries:
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-pkg-config
pacman -S mingw-w64-x86_64-SDL2 # example: install SDL2Slay auto-detects the MSYS2 environment via the MSYSTEM variable and uses pacman for package resolution, similar to Arch Linux.
vcpkg: Install vcpkg and set VCPKG_ROOT or add vcpkg to your PATH:
vcpkg install sdl2 # example: install SDL2Slay uses vcpkg's pkg-config files and installed tree for library resolution. The default triplet is x64-windows (override with VCPKG_DEFAULT_TRIPLET).
In both cases, a GCC or Clang compiler must be available on PATH.
- No configuration files needed — follows the directory structure conventions above.
- Auto-detection of compiler flags, includes and libraries via
pkg-configand platform-specific package managers. - Incremental compilation — only recompiles changed source files.
- Profile-guided optimization —
slay reccollects profiling data, subsequent builds use it. - Built-in support for testing, debugging, cross-compilation, and code generation.
- Meant for building executables, not libraries.
- Generated
CMakeLists.txtis specific to the system it was generated on.
- License: BSD-3
- Version: 1.3.2
- Author: Alexander F. Rødseth <xyproto@archlinux.org>