Golem is a cross-platform build system for C/C++ projects
Project description
Golem
What is it?
Golem is a cross-platform build system for C/C++ projects. It can build projects like CMake does, or manage dependencies like Conan does. It only requires Python and Git to work.
Golem's main goal is to remove the noise in the project file, and favor the developers intents rather than the technical details when unneeded.
Here is how a golemfile.py looks like:
def configure(project):
project.dependency(name='json',
repository='https://github.com/nlohmann/json.git',
version='^3.0.0',
shallow=True)
project.library(name='mylib',
includes=['mylib/include'],
source=['mylib/src'],
defines=['FOO_API_EXPORT'])
project.export(name='mylib',
includes=['mylib/include'],
defines=['FOO_API_IMPORT'])
project.program(name='hello',
source=['src'],
use=['mylib'],
deps=['json'])
But alternatively, you can also define an equivalent golemfile.json.
Have a look at the full example in examples/minimal.
Other examples
TODO: List more elaborate projects here.
🌱 Getting started
How to install?
Requirements: Python 3.10 or later, Git
Using pipx (recommended, creates a virtual environment):
pipx install golemcpp
# Or install it for all users
pipx install --global golemcpp
Alternatively, using pip:
pip install golemcpp
Since Golem is evolving fast, to upgrade it run:
# When using pipx
pipx upgrade golemcpp
# When using pipx for all users
pipx upgrade --global golemcpp
# When using pip
pip install --upgrade golemcpp
First project
Everything starts with golemfile.py. Create it at the root of your project directory.
Here is an example of golemfile.py to compile a Hello World program:
def configure(project):
# The project variable is the entry point to declare dependencies, libraries and programs that make up the project.
project.program(name='hello',
source=['src'])
# 'hello' is the name of the program being compiled (e.g. hello.exe or hello-debug.exe)
# 'src' is the directory where all source files are expected to be found (recusrively) for 'hello'
Here is src/main.cpp:
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
return EXIT_SUCCESS;
}
Have a look at the full example in examples/hello.
Building the project
To build the program, run:
# For a debug build
golem configure --variant=debug
# Or, for a release build
golem configure --variant=release
# In both cases, continue with
golem build
The built artifacts are located in build/bin.
Debug artifacts are suffixed with "-debug" by default.
💻 Commands
All the commands are meant to be called at the root of your project, where the project file (e.g. golemfile.py or golemfile.json) seats.
The commands are presented in the order they are expected to be called, when needed to be called.
golem configure
This command allows you to configure how to build your project. The choices are saved, therefore it needs to be run only once. Modifying the project file will not require re-executing this command.
golem configure [options]
Build options:
# Directory where to build the project (default: ./build)
--dir=<build_dir>
# Variants define a set of default flags/options for your build (default: debug)
--variant=(debug|release)
# Links the runtime dynamically (shared) or statically (static) (default: shared)
--runtime=(shared|static)
# Builds and links libraries dynamically (shared) or staticaly (static) (default: shared)
--link=(shared|static)
# Builds using the specfied architecture (default: <your_os_arch>)
--arch=(x64|x86)
IDE/support options:
# Generates files to enable Microsoft C/C++ Extension's IntelliSense in VSCode (default: False)
--vscode
# Generates files to support clangd (default: False)
--clangd
# Generates compile_commands.json files in ./build/golem/compile_commands/ (default: False)
--compile-commands
Qt options:
# Directory to Qt, for example C:\Qt\6.10.0\msvc2022_64 (default: None)
--qtdir=<qt_dir>
golem resolve (if using dependencies)
When defining dependencies in the project file, this command becomes mandatory after golem configure.
This command resolves the version of each dependency, clones them in the cache system, and configures them.
This is the only command requiring a network access, although Golem can be setup to not require any network access.
golem resolve
About the cache system
By default, Golem stores dependencies in ~/.cache/golem.
To control where the dependencies are being cached, define the following environment variable:
GOLEM_DEFINE_CACHE_DIRECTORIES=<path1>=<regex1>|<path2>=<regex2>|...
# <path> is a directory where the matched depencencies are stored
# <regex> is defining which dependency needs to be stored in <path> based on the repository URL
For example, this will store all dependencies in F:\CACHE:
GOLEM_DEFINE_CACHE_DIRECTORIES=F:\CACHE=^.*$
One interesting use case for this feature is to be able to split the cache in different directories to separate your own dependencies from others. By separating the cache, in a CI environment it allows you to only trigger rebuilds on a specified set of dependencies.
To define a cache directory in read-only mode:
GOLEM_STATIC_CACHE_DIRECTORY=<path>
GOLEM_STATIC_CACHE_DEPENDENCIES_REGEX=<regex>
# <path> is a directory where the matched depencencies are stored
# <regex> is defining which dependency is already stored in <path> based on the repository URL
This makes sure that the dependencies stored in it will stay untouched. In a CI environment, it guarantees that Golem won't mess with the static cache if an artifact is found missing, etc.
Note that a static cache doesn't need to be defined with GOLEM_DEFINE_CACHE_DIRECTORIES to exist. The static cache definition is defined independantly.
Managing dependencies
It is expected in a complex project that dependencies have some dependencies in common, and sometimes they are conflicting with each other.
The master_dependencies.json file solves this issue by overriding how dependencies should be resolved. It's a list of dependencies that golem resolve checks everytime it is encountering a dependency definition to replace it with the one found (if any) in the master_dependencies.json.
The most common use case is to force a specific version on a dependency accross a whole project.
Here is how a master_dependencies.json looks like:
[
{
"repository": "https://github.com/nlohmann/json.git",
"version": "^3.0.0",
"variant": "release",
"shallow": true
}
]
This overrides any reference to this dependency with the version ^3.0.0 and the release variant.
The master_dependencies.json can be specified in multiple ways. By order of precedence:
# Call golem configure with an option pointing to the file
--master-dependencies-configuration=<path_to_file>
# Define in the project file where to find the file
project.master_dependencies_configuration = '<path_to_file>'
# Define an environment variable pointing to the file
GOLEM_MASTER_DEPENDENCIES_CONFIGURATION=<path_to_file>
# Define in the project file the repository where to find the file
project.master_dependencies_repository = '<repository_url>'
# Define an environment variable pointing to a repository containing: master_dependencies.json
GOLEM_MASTER_DEPENDENCIES_REPOSITORY=<repository_url>
Although useful to quickly try a
master_dependencies.json, it is not recommended to define it in the project file itself for most projects.
Managing recipes
Golem aware dependencies, those having Golem project file defined at their root, can seemlessly refer to each other. But, when refering to a dependency unaware of Golem, Golem provides a recipe mechanism.
By default, Golem provides a recipe repository to find a corresponding project file for these dependencies unaware of Golem.
Dependencies are uniquely identified by their repository URL. Their ID is constructed such as "https://github.com/nlohmann/json.git" becomes "json@com.github.nlohmann".
A recipe repository contains directories named after these dependency IDs, and each directory contains a project file.
This is how it looks like:
.
├── boost@com.github.boostorg/
│ └── golemfile.py
├── json@com.github.nlohmann/
│ └── golemfile.py
├── spdlog@com.github.gabime/
│ └── golemfile.py
└── <etc>
└── golemfile.py
For now, there is no project file per version mechanism, but this is in the Roadmap.
A golemfile.py can use scripting to handle any build system, any situation.
To override the default Golem recipe repository, and possibly have multiple sources for recipes:
# Define an environment variable pointing to the repositories
GOLEM_RECIPES_REPOSITORIES=<repository_url_1>|<reposiroty_url_2>|...
For now, there is no possibility to define a directory instead of a repository, but this is in the Roadmap.
When a dependency is missing, or not building properly, it is recommended to fork the Golem recipe repository, make the needed changes and create a Pull Request. Contributions are very welcome!
golem dependencies (if using dependencies)
When defining dependencies in the project file, this command becomes mandatory after golem resolve, and expects it to have run successfully.
This command builds the dependencies needed to build the project.
golem dependencies
golem build
This command builds the libraries and programs defined in the project file (e.g. golemfile.py or golemfile.json).
If any dependency is needed, the artifacts are expected to be built using golem resolve and golem dependencies before hand.
# Build all the targets
golem build
# Build and show the compile commands
golem build -v
# Build specific targets
golem build --targets=foo,bar
golem package
This command generates the packages defined in the project file.
golem package
For now, Golem can generate:
- MSI files for Windows with WiX
- DMG files for MacOS
- DEB files for Debian-based distributions
Golem also provides a hook mechanism for scripting purposes after a package is generated.
golem clean
This command cleans up the objects built with golem build.
golem clean
golem distclean
This command deletes the build directory.
golem distclean
🚀 Roadmap
Here is a list of important features to add as a priority:
- Add command to initialize a project
- Add command to initialize a recipe (takes a URL and an option for the build system, include comments in project file)
- Add the ability for a project file to include another one
- Set default value for shallow on dependencies to True, or 'auto' (when version is a tag then shallow=True, otherwise for branches and commit hashes shallow=false) (this new behavior requires to check how version_template will behave, and it requires to fix how golem projects generate artifacts with the asked version to no break dependencies)
- Generate an implicit export on a library when a program tries to use it
- Support downloadable archives instead of git repositories
- Add commands to manage the dependencies in the cache system
- Allow the recipes to be a local folder instead of a repository
- Supporting libraries mixing compiled targets and header only targets (e.g. boost)
- Add a Visual Studio solution generator (investigate waf capabilities and in slnx too)
- Add an option to choose the runtime variant (debug or release, important on Windows)
- Add the ability to remove the default flags of a variant
- Add the ability to have different recipes for different versions of the dependency
- Make an empty version on a dependency default to the latest available version
- Create a pip package
- Consider packaging Golem for Windows, Linux, MacOS (see https://pyinstaller.org/en/stable/)
- Add
c_standard/cxx_standardon the Configuration (library, program, dependency) - Remove v prefix from versions (see
Version.py) - Detect automatically Qt if in
C:\Qtor other obvious paths on other platforms - Return a sensible error message to the user when running golem commands in the wrong order
- Generate API header and associated defines for libraries when
auto_api_name='MYLIB_API'is defined (can possibly switch later to a systematic generation with a switch to disable the generation) - Add or improve recipes for the most popular dependencies (increase support for configuration options)
- Add support for cppfront
- Add support for C++ modules
Here is a list of important improvements to work on the long term:
- Add more documentation
- Add integration tests
- Add unit tests
- Add the ability to create user-defined variants
- Use the task mechanism of Waf for everything (e.g. resolving, building dependencies)
- Improve available helper functions to build dependencies using other build systems (recipes)
- Define default security profiles (allow creation and customization,
security_profile='all')
Here is a list of other nice improvements to work on:
- Properly log messages instead of using print() (needs anlaysis, consider using waflibs.Logs)
- Properly abort execution when encoutering an error instead of raising an exception (needs anlaysis, consider using config.fatal(''), raise Waf.Error(), etc.)
- Show the full path of the compiler when in a NixOS shell (issue on Waf's side)
- Detect when
/external:Ior-isystemare available before using them - Merge
useanddepswith a properly defined convention to differentiate the dependencies (e.g. @json, needs analysis) - Generate by default
qmldirand aqrcfile for all the found QML files (allow to customize the namespace, or to disable generation)
Contributions are very welcome!
Do not hesitate to create a PR with no change to start the conversation about something you'd be interested in developing. Do not hesitate to create issues to open the conversation about a problem or a need.
Of course, much remains to be done to make Golem the best build system!
💖 Thanks
A big thank you to:
- mythicsoul & wtfblub for their early testing, feedback, ideas, and support!
❓ FAQ
Why another build system?
It all started back in 2016, with accumulated frustrations about the absence of proper dependency management in the tools of that time. In July 2016, Conan was not even a thing. CMake wasn't as widely adopted, but was definitely ramping up to become the success it is. At the time, I already went through a lot of thinking about how to solve the needs I had with previous scripting attempts, and being tired of it; I decided to start a proper tool on top of Waf to do it. Golem was born.
Years have passed, Golem ended up serving me better than I anticipated in the first place. I witnessed the rise of Conan and CMake, and I thought that Golem had something they didn't have. Time passed again, and I finally found the time to focus on sharing it properly, publishing it (Dec 27, 2025), documenting it and work on what's missing for it to not just be my tool, but a tool for everyone.
C++ has progressed a lot in the meantime; safety concerns, C++ modules, etc. Since the beginning, Golem's spirit is to be helpful and aware of how C++ projects are made today. It is made to be simple to use. Golem's goals are to provide premium support for both the bleeding edge features C++ can offer and the best safety and programming practices. This is how Golem's development will continue.
After the neccessary improvements, I'll advertise Golem to a broader audience.
Known issues
golemalone should welcome the user with a basic recap of the useful commands- The cache system accumulates the dependencies and there are no commands yet to clean it up (requires manual deletion)
- Failure on a dependency processed by
golem resolvemay put this dependency in an unrecoverable state, requiring to delete it manually from the cache - Errors of often not user friendly (raised exceptions)
- In some specific environments, such as NixOS, the path to the compiler is not a full path (not a blocking issue, need to fixed on Waf's side)
- When dealing with conflicting variants of a same dependency, there is no message to warn the user, and Golem attemps to link both anyway (master_dependencies.json is a good workaround for most cases)
- Only 1 template among those having the same source will get processed (bug caused by
if str(version_template_src) in self.context_tasks: continue) - No support for specifying header files in include parameter to export a library (needs to be a directory for now)
Additionnally to this non-exhaustive list, there are edge cases where the behavior isn't properly defined yet.
How is it designed?
Golem is powered by Waf, but provides a completely different API. It's a sophisticated frontend to Waf that adds many features and simplifies for the users how to define their project.
Among the added features, Golem provides dependency management with a cache system and recipes.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file golemcpp-1.0.3.tar.gz.
File metadata
- Download URL: golemcpp-1.0.3.tar.gz
- Upload date:
- Size: 73.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95a055e2c2bafc87ab0b74ccee6fc8fa96fb09a68b72e32937344f086f56bcbc
|
|
| MD5 |
cb2337b9ddba94f3a1898c08b257b4d6
|
|
| BLAKE2b-256 |
c3438adcf6cce9a5b55c3636d34d58f53192154bf5ca9cbac366cc0bc2ee6d0e
|
Provenance
The following attestation bundles were made for golemcpp-1.0.3.tar.gz:
Publisher:
python-publish.yml on GolemCpp/golem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
golemcpp-1.0.3.tar.gz -
Subject digest:
95a055e2c2bafc87ab0b74ccee6fc8fa96fb09a68b72e32937344f086f56bcbc - Sigstore transparency entry: 813590949
- Sigstore integration time:
-
Permalink:
GolemCpp/golem@36982526ae1e3433c2ec19644a1620c062c445cc -
Branch / Tag:
refs/tags/1.0.3 - Owner: https://github.com/GolemCpp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@36982526ae1e3433c2ec19644a1620c062c445cc -
Trigger Event:
push
-
Statement type:
File details
Details for the file golemcpp-1.0.3-py3-none-any.whl.
File metadata
- Download URL: golemcpp-1.0.3-py3-none-any.whl
- Upload date:
- Size: 83.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e665b6a8f9d25ec14280169de5a0b7fe063f7f002714e65176a044b2fe5e35f4
|
|
| MD5 |
ab05b95f25d59b1bec511cec3efcb187
|
|
| BLAKE2b-256 |
717bf6ac04828314a6ae116f0f36ebaac0ab40f4232d531881d39a55c02fcf06
|
Provenance
The following attestation bundles were made for golemcpp-1.0.3-py3-none-any.whl:
Publisher:
python-publish.yml on GolemCpp/golem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
golemcpp-1.0.3-py3-none-any.whl -
Subject digest:
e665b6a8f9d25ec14280169de5a0b7fe063f7f002714e65176a044b2fe5e35f4 - Sigstore transparency entry: 813590955
- Sigstore integration time:
-
Permalink:
GolemCpp/golem@36982526ae1e3433c2ec19644a1620c062c445cc -
Branch / Tag:
refs/tags/1.0.3 - Owner: https://github.com/GolemCpp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@36982526ae1e3433c2ec19644a1620c062c445cc -
Trigger Event:
push
-
Statement type: