DEV Community

Aidomx Dev
Aidomx Dev

Posted on Edited on AI-assisted

Satria: A Simple Builder for You — Beyond CMake Fatigue

Building C and C++ projects shouldn't be more complicated than writing the code itself.

If you're a C or C++ developer, you've probably experienced what I call CMake fatigue.

You start with a simple project. A few source files, some headers, and perhaps a library.

Then suddenly, you're dealing with configuration scripts, build generators, dependency graphs, and platform-specific linker behavior.

CMake is powerful. Ninja is fast. Both have earned their place in modern software development.

But what if you could build your project without managing all that complexity yourself?

Meet Satria, formerly known as rbot (Rupa Builder Of Target).

Satria is a lightweight, declarative build system for C and C++ projects, built around one philosophy:

A simple builder for you.

It aims to make project configuration straightforward, workspace orchestration practical, and incremental builds fast—without sacrificing the actual work required to produce correct build artifacts.

Let's explore what makes it different.


1. Start Simple: Zero Boilerplate

Imagine a typical C project:

myproject/
├── src/
│   ├── main.c
│   └── utils.c
└── Buildfile
Enter fullscreen mode Exit fullscreen mode

With Satria, your configuration can be as simple as:

use project
Enter fullscreen mode Exit fullscreen mode

That's it.

Satria follows the principle of convention over configuration.

Instead of requiring you to explicitly enumerate every source file and compiler command, it can discover sources from the conventional project layout.

To initialize a project:

satria init
Enter fullscreen mode Exit fullscreen mode

Then build it:

satria
Enter fullscreen mode Exit fullscreen mode

Satria handles source discovery, compilation, dependency tracking, and linking.

You can still customize the configuration when necessary.

For example:

use project

compiler = gcc, clang
std = gnu11
flags = Wall, Wextra
Enter fullscreen mode Exit fullscreen mode

The goal isn't to eliminate configuration.

It's to make configuration optional when reasonable and explicit when necessary.

C++ Support

Satria also supports C++ projects.

Interactive initialization makes getting started easier:

satria init -p
Enter fullscreen mode Exit fullscreen mode

You can select the project language and let Satria generate the initial project structure.

For a C++ project, a Buildfile might look like:

use project

std = c++17
Enter fullscreen mode Exit fullscreen mode

Satria also supports compilation database generation for development tools such as clangd.

This allows you to use modern editor features without maintaining a separate build configuration just for your language server.


2. Workspaces Without Configuration Chaos

Single-project builds are relatively straightforward.

Workspaces are where build systems become more interesting.

Consider a project containing several interconnected components:

workspace/
├── Buildfile.ws
├── library/
├── rupamod/
├── rupa/
│   ├── include/
│   └── src/
└── ruka/
    ├── include/
    └── src/
Enter fullscreen mode Exit fullscreen mode

Some projects may produce libraries.

Others may produce executables.

Another project might package resources into an archive that gets embedded into a binary.

And sometimes, dependencies between components are circular.

The DAG Problem

Ninja is designed around a Directed Acyclic Graph (DAG).

This is a sensible architecture for a low-level build executor, but circular dependencies require careful modeling.

For example:

Rupa ──────► Ruka
  ▲           │
  └───────────┘
Enter fullscreen mode Exit fullscreen mode

A naive project-level dependency graph would contain a cycle.

Ninja cannot directly execute a cyclic build graph. The build configuration must represent the actual operations through an acyclic dependency structure.

This can involve separating compilation from linking, adjusting dependency edges, or handling circular static-library references at link time.

Satria approaches the problem at a higher level.

Workspace-Aware Build Execution

A Satria workspace begins with:

use workspace

projects = rupamod, rupa, ruka
Enter fullscreen mode Exit fullscreen mode

Rather than treating the workspace as a collection of unrelated commands, Satria understands its projects and their output artifacts.

Its workspace execution architecture separates important build phases, allowing project libraries to be prepared before dependent executable linking.

Conceptually:

          Workspace
              │
              ▼
       Source Discovery
              │
              ▼
      Compile Project Objects
              │
              ▼
      Build Project Libraries
              │
              ▼
      Link Final Executables
              │
              ▼
       Package Releases
Enter fullscreen mode Exit fullscreen mode

This architecture helps Satria manage complex project relationships without requiring developers to manually flatten every project into individual object-level build rules.

It doesn't mean circular dependencies disappear.

The linker must still receive the correct objects and libraries.

The difference is that Satria takes responsibility for coordinating those operations.


3. Real Builds, Not Benchmark Shortcuts

One of Satria's design principles is simple:

A faster build is meaningful only when the required work is actually performed.

A workspace may need to:

  • Compile C and C++ sources.
  • Generate object files.
  • Create static and shared libraries.
  • Package resources into archives.
  • Embed archives into binaries.
  • Link executables.
  • Produce distribution packages and checksums.

Skipping necessary work isn't an optimization.

It's an incomplete build.

This principle matters when comparing Satria against tools such as Ninja.

A benchmark that measures only compilation cannot automatically be considered equivalent to a build that also creates libraries, embeds resources, and packages releases.

Both tools should be evaluated against the same required outputs whenever possible.

Satria's workspace architecture is designed around performing those real operations efficiently.


4. Incremental Builds: Content Matters

Traditional timestamp-based build systems often rebuild files when their modification times change.

But a modification timestamp doesn't necessarily mean the contents changed.

Consider:

touch src/main.c
Enter fullscreen mode Exit fullscreen mode

The file's timestamp changes.

Its contents do not.

A timestamp-based dependency check may decide that recompilation is necessary.

Satria uses content-based fingerprinting to distinguish actual content changes from timestamp-only updates.

When the relevant content remains unchanged, unnecessary compilation can be avoided.

Why This Matters

Imagine a workspace containing hundreds or thousands of source files.

A script updates timestamps across a directory.

A conventional timestamp-driven build may schedule substantial work.

Satria can recognize unchanged content and avoid recompiling it.

This is particularly useful for:

  • Generated files.
  • Source synchronization.
  • Large workspaces.
  • Repeated development builds.
  • Environments with unusual filesystem behavior.

Of course, hashing isn't free.

The build system still needs to determine whether the relevant inputs changed.

The challenge is making that verification cheaper than unnecessary compilation.

Satria optimizes for doing the minimum necessary work—not merely the minimum number of commands.


5. Portable Builds: GNU ld Isn't Everywhere

Portability is another area where build systems face practical challenges.

On Linux, GNU binutils provides tools such as GNU ld.

One common technique for embedding binary resources is:

ld -r -b binary -o resource.o resource.tar
Enter fullscreen mode Exit fullscreen mode

This creates a relocatable object containing the binary data.

However, not every platform's linker supports the same options.

For example, Android Termux commonly uses LLVM's linker infrastructure.

During a real CMake+Ninja workspace test on Termux, the build failed while attempting to embed an archive:

ld: error: target emulation unknown:
-m or at least one .o file required
Enter fullscreen mode Exit fullscreen mode

The C compilation itself was not the problem.

The embed command assumed GNU linker behavior.

Satria's Approach

Satria checks whether a suitable GNU ld is available.

When it is, Satria can use the GNU linker embedding strategy.

When it isn't, the normal embed pipeline can use a C-array fallback.

Conceptually:

        Embed Archive
              │
              ▼
       GNU ld available?
          /       \
        Yes        No
         │          │
         ▼          ▼
     GNU ld      C-array
      embed      fallback
         │          │
         └────┬─────┘
              ▼
       Build Continues
Enter fullscreen mode Exit fullscreen mode

Satria also caches the linker availability result to avoid repeating unnecessary detection work.

This behavior matters because developers shouldn't have to redesign their build process every time they switch environments.

Honest Tooling

There is an interesting distinction between normal builds and profiling.

On Termux, requesting:

satria profile embed=rupamod
Enter fullscreen mode Exit fullscreen mode

can report:

GNU ld tidak tersedia
(jalur fallback C-array tidak diprofilkan)
Enter fullscreen mode Exit fullscreen mode

In other words, the normal build supports the fallback, but that particular profiling operation does not yet measure it.

Instead of reporting a misleading measurement, Satria explicitly states the limitation.

Correctness and transparency matter more than pretending every feature is complete.


6. Performance: Numbers From Real Tests

Performance claims should come from measurements, not marketing.

Satria has been tested in several environments, including Linux, Android Termux, and proot-distro.

One particularly interesting test used a real multi-project workspace containing C sources, libraries, embedded resources, executables, and release packaging.

Termux Native — Eight Workers

The following results were obtained using Satria v0.2.3.109 on an eight-core Android device running Termux.

Each run started with a workspace clean:

satria -w clean
Enter fullscreen mode Exit fullscreen mode

Followed by:

satria -w -j8
Enter fullscreen mode Exit fullscreen mode

Build output was redirected to /dev/null to reduce terminal-output overhead.

Run Cold Build
1 25.807 s
2 24.599 s
3 23.796 s
4 25.018 s
5 25.884 s
Median 25.018 s

The fastest run completed in 23.796 seconds.

These builds included the workspace's release pipeline, producing .tar.gz and .deb packages along with SHA-256 checksums.

Comparing Against CMake+Ninja

In a separate Termux native cold-build test, CMake+Ninja completed its configured workspace pipeline in:

36.501 seconds

Compared with Satria's five-run median of 25.018 seconds, this is a substantial observed difference.

However, there are important qualifications.

The CMake configuration used for this comparison did not reproduce every Satria output, particularly the complete release pipeline.

Additionally, the CMake+Ninja result came from a single measured cold build rather than a five-run median.

Therefore, these measurements demonstrate promising performance, but they should not be presented as a perfectly controlled, equal-work comparison.

Scaling Across Workers

The same Termux device was also tested using different worker counts.

Workers Cold Build
2 65.790 s
4 50.890 s
8 27.739 s
12 27.016 s

These are individual measurements rather than medians.

The results show substantial improvement when increasing parallelism from two to eight workers.

Increasing the worker count beyond the device's eight cores produced only a small additional improvement in that particular run.

This is expected: more workers do not create more physical CPU capacity.

The best worker count depends on the hardware, compiler, workload, and system conditions.


7. Linux, Termux, and Toolchain Flexibility

Satria isn't designed around a single compiler or operating environment.

Its development bootstrap can adapt to the available toolchain.

For example:

./install.sh --dev
Enter fullscreen mode Exit fullscreen mode

On Termux native, Satria can be built using Clang.

On Linux systems with GCC available, GCC can be used.

This flexibility helps keep the development experience straightforward across different environments.

The build tool itself should not require users to install unnecessary platform-specific dependencies merely to support an optional optimization.

That principle also influenced Satria's development.

An experimental global compiler-pool design was explored, but the original scheduler remained attractive because it already demonstrated strong parallel performance without introducing an additional Android semaphore dependency.

Sometimes the better optimization is keeping the architecture simpler.


8. Profiling and Understanding Build Costs

A fast build system should also help developers understand where time is being spent.

Satria includes profiling functionality for selected build operations.

For example:

satria profile library=core
Enter fullscreen mode Exit fullscreen mode

This can expose the time spent in phases such as discovery, collection, and archiving.

That information is useful because optimizing the wrong operation rarely improves overall performance.

If a library archive takes hundreds of milliseconds while source discovery takes only a few milliseconds, optimizing a small loop in discovery probably won't produce a meaningful improvement.

The same principle applies to workspace scheduling.

The important question isn't simply how many operations the scheduler performs.

It's what those operations actually cost.

Satria's performance work focuses on measuring those costs and reducing unnecessary overhead without skipping required outputs.


9. Satria Is Not a Package Manager

Satria's purpose is intentionally focused.

It is a build system.

It isn't trying to become a general-purpose package registry or dependency marketplace.

Its responsibilities center on building projects, coordinating workspace artifacts, and producing the outputs developers need.

This distinction helps prevent feature growth from overwhelming the core build engine.

A tool doesn't need to do everything.

It needs to do its intended job well.


10. Benchmarking Without Illusions

Ninja is an excellent build executor.

CMake is a mature and capable project configuration system.

Satria doesn't need to pretend otherwise.

Different architectures have different strengths.

Ninja excels at executing a well-defined acyclic build graph.

CMake offers extensive platform and toolchain configuration capabilities.

Satria focuses on providing a compact, declarative build experience with integrated project and workspace orchestration.

The important benchmark questions are:

Did both systems perform the same work?

Did both produce the required artifacts?

Were compiler settings and parallelism comparable?

Were measurements repeated under controlled conditions?

A benchmark that ignores those questions may produce impressive numbers without meaningful conclusions.

For Satria, losing a benchmark is not a reason to hide the result.

It's an opportunity to investigate where the time went.

And winning a benchmark is only meaningful when the comparison is fair.


Conclusion: A Simple Builder for You

Satria started from a straightforward idea:

Building software should be simpler than configuring the tools used to build it.

That philosophy extends beyond minimal configuration files.

It influences how Satria handles workspaces, incremental compilation, toolchain compatibility, profiling, and performance optimization.

With Satria, developers can start from:

use project
Enter fullscreen mode Exit fullscreen mode

And grow into a multi-project workspace when necessary:

use workspace

projects = rupamod, rupa, ruka
Enter fullscreen mode Exit fullscreen mode

Without abandoning the same build system.

Satria is still evolving.

There are optimizations to explore, compatibility improvements to make, and features that need further development.

But its direction remains clear:

Keep configuration simple. Perform the real work. Measure performance honestly.

Because a build system should help you build software—not become another project you have to maintain.

Satria — A Simple Builder for You

GitHub: https://github.com/aidomx/satria.git

What matters most to you in a build system: simplicity, portability, incremental performance, or workspace support?

Top comments (0)