Guide: Building Code
Note
This presently only relates to our C and C++ projects. Build instructions for other projects will be added later.
The first time you work with our repositories code, you’ll want to set up your development environment.
Our builds are currently tested for Ubuntu 22.04 LTS, but they should work on any up-to-date Debian-based distro. They’ll also probably work on almost any modern Linux system (though we haven’t tested.) We will be adding official support for building on macOS and Windows later.
A Note On Branches
You should generally use the same primary branch for all connected repositories.
That is, if you’re working on devel
for the repository you’re developing on,
you should use devel
for all repositories it depends on. Similarly, if you
are using the fresh
branch of a repository, you should use the fresh
branch for all repositories it depends on.
To switch branches on any repository, run the following, replacing devel
with whatever branch name you’re switching to:
git checkout -b devel origin/devel
Dependencies
System Dependencies
We use a few libraries which are presently easiest to install directly on the target system, instead of being packaged in our libdeps repository. (We hope to change that sooner than later.)
On Ubuntu/Debian systems, or Windows Subsystem for Linux, these can be installed via:
sudo apt install libcairo2-dev libsdl2-dev
On Fedora:
sudo yum install cairo-devel SDL2-devel
On macOS:
sudo port install cairo sdl2
Although it’s not officially supported by MousePaw Media, if you’re compiling
on Windows directly (without WSL), you can install cairo
and SDL2
with vcpkg
. Alternatively, you may be able to find binaries or compile it
yourself (see project sites for instructions.)
libdeps
We track all our statically-linked and header-only third-party C++ dependencies
in a single repository we control, libdeps
. This allows us to control the
exact version of any given library, and ensure all our developers are using it.
We try to keep our library dependencies to a minimum.
For the complete list of libraries and their versions, see the
CHANGELOG.md
file in the libdeps
repository.
Updating libdeps
Clone libdeps into your repositories folder. For best results, all MousePaw Media repositories you clone should be in the same directory. Then run…
cd libdeps
Next, we’ll build the dependencies. All you need to do is run make
<library>
, where <library>
is the name of the library you want to build.
Alternatively, run make ready
to build them all.
Once the build is done, you can find all the headers in
libdeps/libs/include
, and the compiled static libraries in
libdeps/libs/lib
. The build systems in all of our other repositories
look for the library files at these locations by default.
Warning
To make it easier to update libdeps
later, DO NOT EVER commit
changes on the devel
, fresh
, or stable
branches directly.
Fixing Opus “aclocal” Build Errors
If the Opus build fails on an Ubuntu-based system with a message about “aclocal”, you can quickly fix this by running…
make ubuntu-fix-aclocal
make opus
Hereafter, you can just run make opus
to build that library (or make
ready
to build it along with all the others.)
Updating libdeps
When changes happen in libdeps
, you only need to pull them in and rebuild.
While make clean
commands are provided, it’s generally more effective to
just clean up the git repository itself.
If you’re working on the devel
branch, run the following. For other
branches, just replace devel
with whatever primary branch you’re
working on (fresh
or stable
)
git clean -xdff
git pull origin devel
make ready
Building Projects as Dependency
We have developed a number of libraries that we use throughout our projects.
We also have a few other projects:
Clone the desired repository into your repositories folder. For best results, all MousePaw Media repositories you clone should be in the same directory.
Consult with README.md
in the repository you’re building for what
dependencies the projects has, and clone/build each of those first.
Then, run the following within the repository you clone:
make ready
The library is now available to all of the other repositories.
Building for Development
If you want to help build or test any of our projects, the build command will be slightly different for you.
First, for development, make sure you’re working on the devel
branch:
git checkout devel
Important
Remember to create a new branch if you plan to make any changes!
Each of our library projects have a dedicated tester command-line application in the same repository. This allows us to run arbitrary code, as well as our tests (via Goldilocks). To build and run the tester with debugging symbols, run the following within the repository:
make tester_debug
./tester_debug
To build and run the tester without debugging symbols – and thus, with better performance – run the following within the repository:
make tester
./tester
You can configure your IDE to use these as the build and execute commands.
Repository Build Systems
All of our own project repositories follow the same structure, and have similar
build systems. You can learn more about a repository’s build system by running
the make
command from the root of the repository.
The canonical template build system is maintained in the test repository. You can also use this repository for testing arbitrary code within our standard build environment.
File Structure
All C++ project repositories have the same basic directory structure,
demonstrated below. Non-library projects would effectively have a project
folder instead of library-tester
, and would lack the library-source
folder.
Folders marked with (*) are untracked in the Git repository:
Repository
├── build_system ← the source code for the build system itself.
├── docs ← Sphinx documentation.
│ ├── build (*) ← the compiled documentation.
│ ├── source ← the documentation source files.
│ │ └── _themes ← the Sphinx theming files.
│ └── Makefile ← the Makefile that automatically runs CMake.
├── library (*) ← where 'make ready' puts the compiled library and its headers.
├── library-source ← the library source code.
│ ├── build_temp (*) ← temporary build stuff. Also where CMake is run from.
│ ├── include
│ │ └── library ← the library's header files (.hpp).
│ ├── lib (*) ← the compiled library (copied from here to ../library)
│ ├── obj (*) ← temporary build stuff.
│ ├── src ← the library's implementation files (.cpp).
│ ├── CMakeLists.txt ← the CMake build instructions for the library.
│ └── Makefile ← the Makefile that automatically runs CMake.
├── library-tester ← the library tester executable source code.
│ ├── bin (*) ← the compiled tester.
│ ├── build_temp (*) ← temporary build stuff. Also where CMake is run from.
│ ├── include ← the tester's header files (.hpp).
│ ├── src ← the tester's implementation files (.cpp).
│ ├── CMakeLists.txt ← the CMake build instructions for the tester.
│ └── Makefile ← the Makefile that automatically runs CMake.
├── .gitignore ← untracks temporary build stuff and other cruft.
├── build.config.txt ← the template configuration file.
├── BUILDING.md ← user instructions for building.
├── CHANGELOG.md ← the list of versions and their changes.
├── default.config ← the default configuration file.
├── LICENSE.md ← the project's license.
├── Makefile ← the project's master Makefile.
└── README.md ←the README file.
Adding New Files
To add a new file to a project build, you need to edit that project’s
CMakeLists.txt
file. Look for the add_library
or add_executable
section, where all the project files are listed. Add your file paths
(relative to the location of CMakeLists.txt
) to that list.
For example, some project’s add_executable
command might look like this:
add_executable(${TARGET_NAME}
include/someproject/classA.hpp
include/someproject/classB.hpp
include/someproject/classC.hpp
main.cpp
src/classA.cpp
src/classB.cpp
src/classC.cpp
)
Note
Please be sure to list files in alphabetical order, in two groups: header files and source files. Keep this section clean!
Switching Dependency Locations
Although our build systems are pre-configured to use libdeps, you can override this behavior.
In the root of the repository you’re building, open build.config.txt
in
that directory, and save it with another name ending in .config
. Then,
modify the file following the instructions to specify alternative paths to the
dependency libraries.
Important
You must save the .config
file in the root of the repository.
All levels of the build system will look for it there.
Finally, tell the build system to use your new config file, using the
CONFIG=<filename>
argument on the make
command, where <filename>
is
the name of the config file (without the .conf
extension). For example, if
the name of the config file was mybuild.conf
, then we would include the
argument CONFIG=mybuild
on our make
command.
Using Sanitizers
If you’re compiling with Clang/LLVM, you can use the sanitizers in any of our projects. Simply
include the SAN=<sanitizer>
argument, where <sanitizer>
is one of the Clang sanitizers.
SAN=address
compiles with AddressSanitizer.SAN=leak
compiles with LeakSanitizer (which is also part of AddressSanitizer).SAN=memory
compiles with MemorySanitizer.SAN=thread
compiles with ThreadSanitizer.SAN=undefined
compiles with UndefinedBehaviorSanitizer.
If you’re not using Clang, this argument will be ignored.
32/64-Bit Architecture
If your system is configured for cross-compiling, you can ask the compiler to
build for an x86 (32-bit) or x64 (64-bit) system by including the ARCH=32
or
ARCH=64
arguments, respectively.
Warning
Our dependency libraries (libdeps
) are not
currently configured to switch architectures. You will need to manually
compile these and point to them using a .config
file.