DEV Community

Tuntufye Mwakalasya
Tuntufye Mwakalasya

Posted on

Building a Mini Build System in Go: Understanding How Bazel Works Under the Hood

Imagine you're running a restaurant kitchen. You have recipes (build targets), ingredients (source files), and some dishes that need other dishes to be ready first (dependencies). How do you organize this chaos? That's exactly what build systems like Bazel, Make, and our Mini-Bazel do for software.

Why Build Systems Matter (The Busy Kitchen Problem)

Picture this: You're making a sandwich. You need:

  • Bread (must be toasted first)
  • Butter (must be softened)
  • Cheese (must be sliced)

If the bread is already toasted from earlier, why toast it again?
If someone changed the cheese type, you need to remake the sandwich.
This is exactly what build systems figure out for code!

In our Mini-Bazel example:

  • my_app is like the final sandwich
  • utils.a is like the toasted bread (a prerequisite)
  • The .go files are our raw ingredients // Let's break down our BUILD.mini file for everyone:
/*
Think of this as a recipe book where each recipe has:
- A name (what we're making)
- Instructions (the command to run)  
- Ingredients (source files)
- Other dishes needed first (dependencies)
*/

- name: "utils.a"          # Like "Toasted Bread"
  cmd: "go build..."       # "Put bread in toaster for 2 min"
  srcs: ["utils/greet.go"] # "You need: sliced bread"
  deps: []                 # "No other dishes needed first"

- name: "my_app"           # Like "Complete Sandwich"
  cmd: "go build..."       # "Assemble all parts"
  srcs: ["main.go"]        # "You need: plate, lettuce"
  deps: ["utils.a"]        # "First make: toasted bread"
Enter fullscreen mode Exit fullscreen mode

The Two Big Questions Every Build System Answers

Remember our kitchen? Every build system (chef) must answer:

Question 1: "What order should I cook things?" (The Scheduler)

Option A - The Prep Cook (Topological/Make-style):
"I'll list everything needed for dinner, sort them by dependencies,
then cook in that exact order."

  • Pro: Simple and predictable
  • Con: Can't handle surprises (dynamic dependencies)

Option B - The Adaptive Chef (Restarting/Excel-style):
"I'll start cooking, and if I realize I need something not ready,
I'll switch to making that first."

  • Pro: Handles surprises well
  • Con: Might restart dishes multiple times

Option C - The Multitasker (Suspending/Shake-style):
"I'll start multiple dishes, pause any that need something else,
and resume when ready."

  • Pro: Very efficient
  • Con: More complex to implement

Question 2: "How do I know if something needs remaking?" (The Rebuilder)

Check the Timer (Timestamp/Make-style):
"If ingredients arrived after the dish was made, remake it."

Taste Test (Verifying Traces/Shake-style):
"I remember exactly what went into this dish. If any of those
ingredients changed, remake it."

Recipe Card System (Constructive Traces/Bazel-style):
"I keep cards saying 'these exact ingredients make this exact dish'.
If someone else made it already, just use theirs!"

Here's how our Mini-Bazel starts (Part 1: Loading recipes)

func main() {
    // Step 1: Read the recipe book
    file, _ := os.ReadFile("BUILD.mini")

    // Step 2: Understand what each recipe means
    var targets []Target  // Our list of recipes
    yaml.Unmarshal(file, &targets)  // Parse the recipes

    // Step 3: Now we know what we CAN cook
    // (Part 2 will figure out what we SHOULD cook)
}
Enter fullscreen mode Exit fullscreen mode

Coming in Part 2: The Cooking Phase

Now that our Mini-Bazel can read recipes, we need to teach it to:

  1. Build a dependency graph (figure out cooking order)
  2. Detect what needs rebuilding (check if ingredients changed)
  3. Execute builds in the right order (actually cook!)

We'll implement a simple scheduler (topological sort) and a basic
rebuilder (timestamp checking), creating a Make-like build system
in Go!

Top comments (0)