Tutorial¶
This tutorial walks you through the process of building a simple C++ project using Pajama. The project introduces several key Pajama features.
Download and Install Pajama¶
Note
The version number shown here will change as new versions of Pajama are released. If you are reading this to reinstall Pajama, first refresh this page.
curl -LO https://joshcameron.github.io/pajama-docs/pajama-0.1.20250702012117-py3-none-any.whl
python3 -m pip install pajama-0.1.20250702012117-py3-none-any.whl
The Pajama Python package contains a command line tool called pajama
which is used to build
projects. You can choose to install Pajama in your system Python as shown above, or in a
virtual environment. If you choose to install Pajama in a virtual environment, you will need to
activate the virtual environment before running the pajama
command.
Clone the tutorial code¶
An example project is available on GitHub. Clone it to your local machine and follow along with the documentation below to learn how to use Pajama.
Understand the project¶
In pajama-tutorial, you will find a simple C++ project containing the following files and directories:
pajama-tutorial
├── .pajama
├── .pajama-project
│ └── config.toml
└── src
├── .pajama
├── a.cpp
├── a.h
├── b.cpp
├── b.h
├── c.cpp
└── main.cpp
A Pajama project / The .pajama-project directory¶
The .pajama-project
directory identifies the parent directory as the "project directory" -- the
root directory of a Pajama project.
When you use the pajama
command at the prompt, the first thing it does is attempt to determine
what Pajama project it is working with. It looks in the current directory and upwards through
the parent directory hierarchy for a .pajama-project
subdirectory.
The .pajama-project
directory initially contains a config.toml
file, which specifies
configuration options for the project. As you work with Pajama, other files may be created in this
directory.
.pajama files¶
Your project directory and the src
directory below it both contain .pajama
files. These files
describe how to build the project. Each file describes how to build the contents of its directory.
.pajama
files are Python files. If you already know Python, you are well on your way to writing
Pajama code.
Let's take a look at the .pajama
file in the src
directory. It looks like this:
Just like a standard Python file, we begin by importing modules we need in the code below.
The cpp
module provides functions for selecting a compiler and compiling C++ code. In this file,
we use cpp.select_compiler()
to select the clang.cpp.compiler
tool. Subsequent calls to the
cpp.compile_to_object()
and cpp.compile_to_executable()
functions use the clang
compiler
to compile the code.
The cpp
module is an example of what is called an abstract tool. When a function from the cpp
module is invoked in a .pajama
file, the action taken by the function will be performed using the
currently selected concrete tool. There are many possible C++ compilers, of course, and in this
example we select clang.cpp.compiler
to perform our C++ compiles. Using the functions provided
by a generic tool like cpp
allows us to write code which is agnostic of the actual compiler
being used. This makes it easy to change the compiler used for all or part of the project.
The settings
module provides functions for setting and getting build settings.
Settings are used implicitly by the tools they are associated with. For example, the
clang.cpp.flags
setting is used by clang.cpp.compiler
to set the flags used when compiling C++
code. Settings can also be used explicitly, as shown in this code where we use the
build.current_src_dir
and build.current_build_dir
settings to set the include and library paths
for the compiler.
The libtool
module provides functions for creating static and dynamic libraries. In this file, we
use libtool.create_library()
to create a static or dynamic library from the object files. The
decision to create a static or dynamic library is controlled by an ordinary Python if statement and
an ordinary Python variable.
The return values of settings.get()
, cpp.compile_to_object()
, libtool.create_library()
and
cpp.compile_to_executable()
are all build artifacts. Artifacts represent entities like files,
directories, and strings used in the build process. Some artifacts are created by actions of the
build process, while others -- such as source files -- exist prior to invoking the build.
Initial build / full build / clean build¶
To build pajama-tutorial for the first time:
The pajama build --clean
command deletes all existing output artifacts (if any) and performs a
full build -- all actions necessary to build all output artifacts described by the
build's .pajama
files.
If ever you want to ensure every output artifact of your project gets rebuilt, you can do so with
pajama build --clean
.
Incremental build¶
During your day to day work, you will typically perform an incremental
build. This means using Pajama to build only the output artifacts which need
to be built, based on the current state of the project. For example, if you modify main.cpp
,
you can run:
Pajama will then only rebuild the output artifacts which depend on main.cpp
, such as a.out
, the
executable created from main.cpp
. It will not rebuild the static or dynamic libraries, since
neither a.cpp
nor b.cpp
have changed.
Pajama infers the dependencies of the build based on how build artifacts are created from other
artifacts. For example, main.cpp
is an input artifact to a build action which creates output
artifact a.out
. Pajama has also cached information about the actions performed in the previous
build. It knows if main.cpp
has not been modified, and the settings relevant to the action which
creates a.out
have not changed, then a.out
does not need to be created again. In an incremental
build, Pajama does only what needs to be done.
Note
When building a project for the first time, a pajama build
command is equivalent to a pajama
build --clean
command, because none of the output artifacts exist yet.
Features¶
- Library to share code among multiple applications
- .cpp to .o
- .o to .so (dynamic library)
- .o to .a (static library)
- executable
- full build, then incremental: note that incremental is fast
- add .cpp to the build: note that other .cpps don't need to be rebuilt.
Current limitations¶
Pajama is currently in a very early alpha state.
- It is only tested on macOS
- The only supported toolset is clang
- Esoteric clang flags might not work. Let me know if something you need is not supported.
- Parallelism is not yet supported.
The focus is on demonstrating the key differentiating feature of Pajama: it allows you to work with the build system in a more intuitive way, because it's so similar to how you would manually build code from the command line.