Cuppa
A simple, extensible build system for use with Scons
Install / Use
/learn @ja11sop/CuppaREADME
Cuppa
A simple, extensible build system for use with Scons. Cuppa is designed to leverage the capabilities of Scons, while allowing developers to focus on the task of describing what needs to be built. In general cuppa supports make like usage on the command-line. That is developers can simply write:
scons -D
and have Scons "do the right thing"; building targets for any sconscript files found in the current directory.
Cuppa can be installed as a normal python package or installed locally into a site_scons directory allowing it to be effortlessly integrated into any Scons setup.
Note:
-Dtellssconsto look for ansconstructfile in the current or parent directories and if it finds one execute thesconscriptfiles as if called from the starting directory. This ensures everything works as expected. For more details refer to the Scons documentation
Table of Contents
- Quick Intro
- Design Principles
- Installation and Dependencies
- Reference
- Supported Dependencies
- Creating your own Dependencies
- Profiles
- Acknowledgements
Quick Intro
Get cuppa
The simpest way to get cuppa is to pip install it using:
pip install cuppa
however there are a few approaches that can be used as described in the Installation and Dependencies section.
Sample sconstruct file
Let's look at a minimal sconstruct that makes use of cuppa. It could look like this:
# Pull in all the Cuppa goodies..
import cuppa
# Call sconscripts to do the work
cuppa.run()
Calling the run method in the cuppa module starts the build process calling sconscript files.
Sample sconscript file
Here is an example sconscript file that builds all *.cpp files in the directory where it resides:
Import( 'env' )
# Build all *.cpp source files as executables
for Source in env.GlobFiles('*.cpp'):
env.Build( str(Source)[:-4], Source )
The env.Build() method is provided by cuppa and does essentially what env.Program() does but in addition is both toolchain and variant aware, and further can provide notifications on progress.
Note: Source[:-4] simply strips off the file extension
.cpp, that is, the last 4 characters of the file name.
If our sconscript file was for a directory containing *.cpp files that are actually tests then we could instead write the sconscript file as:
Import( 'env' )
# Build all *.cpp source files as executables to be run as tests
for Source in env.GlobFiles('*.cpp'):
env.BuildTest( str(Source)[:-4], Source )
The env.BuildTest() method is provided by cuppa and builds the sources specified as env.Build() does.
However, in addition, passing --test on the command-line will also result in the executable produced being run by a runner. The default test runner simply treats each executable as a test case and each directory of executables as a test suite. If the process executes cleanly the test passed, if not it failed.
To run this on the command-line we would write:
scons -D --test
If we only want to build and test debug executables we can instead write this:
scons -D --dbg --test
Or for release only pass --rel.
cuppa also makes it easy to work with dependencies. For example, if boost was a default dependency for all your sconscript files you could write your sconstruct file as follows:
import cuppa
cuppa.run(
default_options = {
'boost-location': '<Location of Boost>'
},
default_dependencies = [
'boost'
]
)
This will automatically ensure that necessary includes and other compile options are set for the boost version that is found at boost-location.
Note: cuppa will even attempt to retrieve the dependency if a version or URL is supplied. Including from source control assuming you have the necessary source control system installed on your machine.
If you need to link against specific boost libraries this can also be done in the sconscript file as follows:
Import('env')
Test = 'my_complex_test'
Sources = [
Test + '.cpp'
]
env.AppendUnique( STATICLIBS =
env.BoostStaticLibs( [
'system',
'log',
'thread',
'timer',
'chrono',
'filesystem'
] )
)
env.BuildTest( Test, Sources )
The BoostStaticLibs() method ensures that the library is built in the correct build variant as required. If you preferred to use dynamic linking then that can also be achieved using BoostSharedLibs().
The point is the complexities of using boost as a dependency are encapsulated and managed separately from the scontruct and sconscript files allowing developers to focus on intent, not method.
Design Principles
cuppa has been written primarily to provide a clean and structured way to leverage the power of Scons without the usual problems of hugely complex scontruct files that diverge between projects. Key goals of cuppa are:
- minimise the need for adding logic into
sconscriptfiles, keeping them as declarative as possible. - allow declarative
sconscripts that are both much clearer and significantly simpler than the equivalentmakefile, without the need to learn a whole new scripting language likemakeorcmake. - provide a clear structure for extending the facilities offered by cuppa
- provide a clear vocabulary for building projects (dependencies, methods, profiles, runners, variants etc.)
- codify Scons best practices into cuppa itself so that users just need to call appropriate methods knowing that cuppa will do the right thing with their intent
- provide a framework that allows experts to focus on providing facilities for others to use. Write once, use everywhere. For example one person who knows how best to make boost available as a dependency can manage and maintain that dependency and allow others to use it seamlessly.
Installation and Dependencies
Installation
cuppa can be made available as a normal python package and this is the preferred method of installation. It can be added directly to a site_scons folder, placed appropriately so Scons will find it. For global use add it to your home directory or for use with a specific project place it beside (or sym-link site_scons beside) the top-level sconstruct file. For more details on using a site_scons folder refer to the Scons man page.
The following sections summarise some of the ways you can get cuppa.
Method 1: Install it as a python package
Use pip install to get the latest:
pip install cuppa
Method 2: Install it locally in your project folder
Install locally in the same folder as your sconstruct file using:
pip install cuppa -t .
Method 3: Bootstrap a local installation from your sconstruct file directly
Adding this to your sconstruct file would pip install cuppa if it was not found:
try:
import cuppa
except ImportError:
print "Cuppa not found, installing..."
import subprocess, shlex
subprocess.call( shlex.split( 'pip install cuppa -t .' ) )
import cuppa
Dependencies
Coloured output
To make use of the colourisation cuppa uses the colorama package.
HTML coverage summaries
cuppa uses the gcovr python library to help post-process the coverage files that gcov produces (used by both the GCC and CLANG toolchains). This produces a nice coverage.html file in your final build folder that links to HTML files for all files for which coverage information was produced.
Reference
Basic Structure
cuppa uses the following terms to mean specific aspects of a build. Understanding these will remove ambiguity in understanding the facilities that cuppa provides.
| Term | Meaning |
| -------------------- | --------- |
| Methods | cuppa provides a number of build methods that can be called inside your sconscript
Related Skills
node-connect
346.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
