Skip to content

Latest commit

Β 

History

History
460 lines (330 loc) Β· 18.1 KB

File metadata and controls

460 lines (330 loc) Β· 18.1 KB

πŸ—οΈ LFS Bazel Bootstrap

Build Linux From Scratch using Bazel! This hybrid build system combines the dependency management and caching power of Bazel with the traditional shell/make-based LFS build process.

What makes this special? Instead of forcing LFS into pure Bazel semantics, we use Bazel as a smart orchestrator that respects the LFS book's traditional build patterns while adding dependency tracking, caching, and runnable validation targets.

🎯 What Is This Project?

This project implements the entire Linux From Scratch 13.0-systemd build process using Bazel build rules. The recipes contain x86_64 and aarch64 branches; the complete build and boot path is currently verified only for native arm64 (aarch64) on Apple Silicon using a rootless Podman worker.

  • Three-stage bootstrap from your host system to a fully independent OS
  • Container-isolated builds with explicit dependency tracking
  • Incremental compilation using Bazel's caching
  • A repeatable, inspectable LFS build pipeline

Perfect for learning about Linux internals, build systems, or just building your own custom Linux distribution!

πŸ“¦ Repository Layout

lfs-bzl/
β”œβ”€β”€ src/                    # Bazel workspace root
β”‚   β”œβ”€β”€ packages/           # LFS chapter implementations
β”‚   β”‚   β”œβ”€β”€ chapter_05/     # Cross-toolchain (5 packages)
β”‚   β”‚   β”œβ”€β”€ chapter_06/     # Temporary tools (17 packages)
β”‚   β”‚   β”œβ”€β”€ chapter_07/     # Chroot preparation (6 packages)
β”‚   β”‚   β”œβ”€β”€ chapter_08/     # Final system (79 packages)
β”‚   β”‚   β”œβ”€β”€ chapter_09/     # System configuration
β”‚   β”‚   β”œβ”€β”€ chapter_10/     # Linux kernel + GRUB config
β”‚   β”‚   β”œβ”€β”€ chapter_11/     # Release files (The End) πŸŽ‰
β”‚   β”‚   └── hello_world/    # Toolchain validation tests
β”‚   β”œβ”€β”€ tools/              # Custom Bazel rules (lfs_package.bzl, lfs_macros.bzl, etc.)
β”‚   β”œβ”€β”€ sysroot/            # 🎯 Build artifacts (your LFS system!)
β”‚   └── MODULE.bazel        # Source package definitions
└── docs/                   # Documentation and design notes

βš™οΈ Requirements

Before you begin, you'll need:

  • Bazel/Bazelisk with bzlmod support. The repository does not yet pin a tested Bazel release, so use of Bazelisk's moving default is a known gap.
  • Podman 3.0+ configured for rootless containers. All build phases execute through the Podman worker.
  • Host tools: Bash, Podman, and the normal Bazel prerequisites. Run bazel test //packages/chapter_02:podman_check_test and then bazel build //packages/chapter_02:version_check to validate the environment.
  • Disk space: allow at least 20GB. A completed local sysroot is currently about 13GB before Bazel's download and action caches.
  • Optional boot validation: QEMU 10+, matching aarch64 UEFI firmware, and Expect (used by //packages/chapter_11:boot_test).
  • No sudo required! Entire build runs as regular user with rootless Podman

πŸš€ Quickstart

# IMPORTANT: Bazel workspace root is `src/` (commands won't work from repo root)
cd src

# 1️⃣ Verify Podman and the bootstrap container meet LFS requirements
bazel test //packages/chapter_02:podman_check_test
bazel build //packages/chapter_02:version_check

# 2️⃣ Build the cross-toolchain (Chapter 5)
bazel build //packages/chapter_05

# 3️⃣ Build all temporary tools (Chapter 6)
bazel build //packages/chapter_06

# 4️⃣ Build Chapter 7 chroot base system (rootless Podman worker - no sudo!)
bazel build //packages/chapter_07

# 5️⃣ Build Chapter 8 final system (79 packages - rootless Podman worker)
bazel build //packages/chapter_08

# 6️⃣ Configure System (Chapter 9)
bazel build //packages/chapter_09

# 7️⃣ Make System Bootable (Chapter 10) - builds Linux kernel
bazel build //packages/chapter_10

# 8️⃣ Finalize System (Chapter 11) - creates release files
bazel build //packages/chapter_11

# 9️⃣ Create the self-booting UEFI disk image -> sysroot/lfs-uefi.img (optional)
bazel build //packages/chapter_11:bootable
# (fast-iteration raw image instead: //packages/chapter_11:bootable_quick)

# πŸ§ͺ Validate each toolchain stage:
bazel build //packages/hello_world:hello_cross  # Cross Toolchain (Ch 5) βœ…
bazel build //packages/hello_world:hello_chroot # Chroot Tools (Ch 7) βœ…
bazel build //packages/hello_world:hello_final  # Final System (Ch 8) βœ…

Build Artifacts Location: src/sysroot/

  • Chapter 5 cross-toolchain: src/sysroot/tools/bin/
  • Chapter 6 temporary tools: src/sysroot/usr/bin/, src/sysroot/usr/lib/
  • Chapter 7+ final system: src/sysroot/ (root filesystem)

πŸ”§ Toolchain Hierarchy (The Four-Stage Bootstrap)

This project builds four distinct toolchain stages in sequence, each more capable than the last. This mirrors the traditional LFS bootstrap process:

1️⃣ Bootstrap Toolchain (Worker Container)

  • Location: The Podman worker container (/usr/bin/gcc, etc.)
  • Purpose: Bootstrap the cross-toolchain (Chapter 5)
  • Verified by: bazel build //packages/chapter_02:version_check
  • Limitation: This first stage comes from the container image rather than the LFS build

2️⃣ Cross Toolchain (Chapter 5) 🎯

  • Bazel Target: //packages/chapter_05:cross_toolchain
  • Location: $LFS/tools/bin (e.g., aarch64-lfs-linux-gnu-gcc; the triplet is $(uname -m)-lfs-linux-gnu, derived from the build container's architecture)
  • Purpose: Build temporary tools (Chapter 6) for the LFS target
  • Key Components:
    • Binutils Pass 1 (assembler, linker)
    • GCC Pass 1 (C/C++ compiler, minimal libc)
    • Linux API headers
    • Glibc (C library)
    • Libstdc++ (C++ standard library)
  • Validation: bazel run //packages/hello_world:hello_cross
  • Why? Isolates from host system contamination

3️⃣ Temporary Tools Toolchain (Chapter 6) πŸš€

  • Bazel Target: //packages/chapter_06:temp_tools_toolchain
  • Location: $LFS/usr/bin (rebuilt gcc, binutils, etc.)
  • Purpose: Full-featured temporary toolchain with POSIX threads, ready for chroot
  • Key Components:
    • Binutils Pass 2 (rebuilt with complete utilities)
    • GCC Pass 2 (full compiler with threading support)
    • 17 core utilities (bash, coreutils, make, grep, etc.)
  • IMPORTANT: πŸ”’ This toolchain is cross-compiled to run ON the LFS target, not the host!
    • Cannot run directly on host system (binaries are linked against LFS glibc)
    • Requires chroot environment to execute
    • Validation happens in Chapter 7 when building inside chroot

4️⃣ Final System Toolchain (Chapter 8) πŸŽ‰

  • Bazel Target: //packages/chapter_08:toolchain
  • Location: $LFS/usr/bin (native GCC, built inside chroot)
  • Purpose: The complete, self-hosting toolchain for the final system
  • Key Components:
    • Native GCC 15.2.0 (built inside chroot, no host dependencies)
    • Native Binutils 2.46.0
    • Glibc 2.43
    • 79 total packages (compression, security, python, systemd, etc.)
  • Validation: bazel build //packages/hello_world:hello_final
  • Result: A fully independent, bootable Linux system!

🎯 How They Work Together

Container GCC β†’ builds β†’ Cross Toolchain (Ch 5)
                        ↓
          Cross Toolchain β†’ builds β†’ Temp Tools (Ch 6)
                                          ↓
                      Temp Tools β†’ builds β†’ Chroot Base (Ch 7)
                                                  ↓
                            Chroot Base β†’ builds β†’ Final System (Ch 8)
                                                        ↓
                                                  Bootable Linux! 🐧

Each stage removes dependency on the previous, creating a fully independent system.

Validation targets at each stage:

  • //packages/hello_world:hello_cross - Cross toolchain (runs on host)
  • //packages/hello_world:hello_chroot - Chroot tools (runs in container)
  • //packages/hello_world:hello_final - Final system GCC (builds deps if needed, cached after)

🐳 Hybrid Build Architecture

This project uses a unique hybrid approach across different LFS chapters:

Chapters 5-6: Container Builds

  • Run directly in the rootless Podman worker container (without chroot)
  • Write to staging sysroot (src/sysroot/)
  • No host sudo required
  • Fast, simple, cached by Bazel

πŸš€ Chapters 7-8+: Rootless Podman Worker (No Sudo!)

  • Persistent Bazel JSON worker running in rootless Podman container
  • Container mounts staging sysroot at /lfs
  • All builds run as regular user (no sudo required!)
  • Fast: container stays alive across builds, amortizing startup cost
  • Isolated: --network=none enforces offline builds
  • Mounts virtual filesystems (/dev, /proc, /sys, /run) inside container
  • Single worker instance to avoid race conditions (configured in .bazelrc)

The Result: Modern container-based workflow with zero sudo requirements for the entire build process.

πŸ—οΈ Build Progress

Current implementation status:

  • βœ… Chapter 5: Cross-toolchain (5 packages) - Podman worker container
  • βœ… Chapter 6: Temporary tools (17 packages) - Podman worker container
  • βœ… Chapter 7: Chroot base system (6 packages) - Rootless Podman worker
  • βœ… Chapter 8: Final system (79 packages) - Rootless Podman worker
  • βœ… Chapter 9: System Configuration (Systemd, Network, Shells)
  • βœ… Chapter 10: Linux Kernel + GRUB bootloader config
  • βœ… Chapter 11: Release files (lfs-release, os-release, lsb-release)

πŸŽ‰ LFS 13.0-systemd BUILD COMPLETE! The sysroot contains a bootable Linux system (native aarch64: glibc 2.43, gcc 15.2.0, binutils 2.46.0, kernel 6.18.10).

  • βœ… BLFS extras (//packages/blfs): sudo, openssh, curl, git, CA certificates, plus login/network config (root password lfs, sshd + systemd-resolved enabled) β€” the booted VM has working DHCP, DNS and ssh.

Warning

The generated image is a development image with the public root password lfs and password-based root SSH enabled. Do not expose it to an untrusted network; change the credentials and SSH policy before any non-local use.

πŸš€ Booting Your LFS System

The image now carries everything needed to boot on its own: the kernel, an initramfs (initramfs-6.18.10-lfs-13.0-systemd.img, built in-chroot by mkinitramfs + LFS-built cpio), and a real UEFI GRUB binary embedded in the image's EFI System Partition β€” no QEMU -kernel injection required. The root filesystem is found by LABEL=lfs-root, which the initramfs resolves.

Boot-verified 2026-07-07 on Apple Silicon (QEMU 10 + hvf + edk2-aarch64): UEFI β†’ GRUB 2.14 β†’ Linux 6.18.10-lfs-13.0-systemd β†’ systemd β†’ login prompt.

Primary: self-booting UEFI disk

# Build the real self-booting UEFI disk image -> sysroot/lfs-uefi.img
# (Bazel builds kernel + initramfs + GRUB EFI in-chroot; the container only
#  packages the GPT + FAT ESP via mtools/sgdisk/dd β€” no loop devices, no sudo)
bazel build //packages/chapter_11:bootable

# Boot in QEMU with aarch64 UEFI firmware (Homebrew qemu ships
# edk2-aarch64-code.fd). Firmware auto-boots the removable-media fallback
# \EFI\BOOT\BOOTAA64.EFI β€” no bootloader install, no NVRAM entry, no efibootmgr.
qemu-system-aarch64 -M virt -cpu host -accel hvf -m 4G \
  -drive if=pflash,format=raw,readonly=on,file="$(brew --prefix)/share/qemu/edk2-aarch64-code.fd" \
  -drive file=sysroot/lfs-uefi.img,format=raw,if=virtio \
  -netdev user,id=n0,hostfwd=tcp::2222-:22 \
  -device virtio-net-pci,netdev=n0 \
  -nographic

# Log in as root (password: lfs), or from another terminal:
#   ssh -p 2222 root@localhost
# Exit QEMU: Ctrl-a x

# Automated boot gate (boots a -snapshot copy, checks login + DHCP):
#   bazel test //packages/chapter_11:boot_test
# Note: the embedded GRUB config does not pin console=ttyAMA0 on the kernel
# command line; if no output appears after GRUB with -nographic, drop
# -nographic and add -device virtio-gpu-pci for a graphical console.

Fast-iteration fallback: raw ext4 + QEMU -kernel

The original raw-image path is kept for quick iteration β€” it skips the bootloader/ESP assembly and injects the kernel directly:

# Build the raw ext4 image (aliases: :bootable_quick or :create_disk_image)
bazel build //packages/chapter_11:bootable_quick

# Boot with QEMU (-kernel injection; no in-image bootloader)
qemu-system-aarch64 -M virt -cpu host -accel hvf -m 4G \
  -kernel sysroot/boot/vmlinuz-6.18.10-lfs-13.0-systemd \
  -initrd sysroot/boot/initramfs-6.18.10-lfs-13.0-systemd.img \
  -append "root=LABEL=lfs-root rw console=ttyAMA0" \
  -drive file=sysroot/lfs.img,format=raw,if=virtio \
  -nographic

# Exit QEMU: Ctrl-a x

Design Decisions:

  • Init System: systemd (not SysVinit)
  • Strip Command: Skipped (optional per LFS book)
  • Expected Test Failures: Some tests fail in chroot - this is documented and expected per LFS book

See docs/status.md for detailed progress tracking.

⚠️ Common Pitfalls

First-time Podman Setup

The rootless Podman worker (used for Chapter 7-8+) requires initial setup:

Symptom: Podman commands fail or container can't start

Solution: Ensure Podman is installed and configured for rootless mode:

# Check Podman version
podman --version  # Should be 3.0+

# Test rootless container
podman run --rm hello-world

Best practice: Build container image before starting chroot builds:

cd src
bazel run //tools/podman:container_image

See docs/troubleshooting.md for detailed setup and troubleshooting.

πŸ”„ Cleanup and Restart

Starting Fresh (Full Rebuild)

To completely restart the build from scratch:

cd src

# 1️⃣ Remove the entire sysroot directory
rm -rf sysroot/

# 2️⃣ Clean Bazel's cache
bazel clean --expunge

# 3️⃣ Rebuild the complete system and bootable image
# Build cross-toolchain (Chapter 5) - ~5-10 minutes
bazel build //packages/chapter_05:cross_toolchain

# Build temporary tools (Chapter 6) - ~30-45 minutes
bazel build //packages/chapter_06

# Build Chapter 7 chroot base system - ~5-10 minutes
bazel build //packages/chapter_07

# Build Chapters 8-11, BLFS extras, and the UEFI image
bazel build //packages/chapter_11:bootable

Build time varies substantially by hardware, cache state, and package test load. A clean build is a multi-hour operation.

Important Notes:

  • No sudo required! The Podman worker handles all containerization internally.
  • Always clean both sysroot AND Bazel cache together. The sysroot and Bazel cache must stay in sync. If you clean one without the other, install scripts may fail when encountering existing files.

Restarting from a Specific Chapter

You don't need to start from scratch if you want to iterate on a later chapter:

Restart Chapter 6 only:

# Remove Chapter 6 artifacts
rm -rf sysroot/usr/

# Rebuild Chapter 6
bazel clean  # Clear Bazel's action cache
bazel build //packages/chapter_06

Restart Chapter 7 only:

# Remove Chapter 7 artifacts
rm -rf sysroot/{bin,sbin,lib,lib64,etc,var}
rm -rf sysroot/usr/bin/{bison,perl,python3,makeinfo}

# Rebuild Chapter 7
bazel clean
bazel build //packages/chapter_07:chroot_cleanup

Clean Build vs Incremental Build

Clean build (start from scratch):

bazel run //tools/podman:cleanup_orphaned  # release any stuck containers/mounts
rm -rf sysroot/
bazel clean --expunge
bazel build //packages/chapter_06

Incremental build (preserve sysroot):

# Just rebuild a specific target
bazel build //packages/chapter_06:m4

# Or clean Bazel's cache but keep sysroot
bazel clean
bazel build //packages/chapter_06

Best practice: Use incremental builds during development. Only do clean builds when troubleshooting or starting fresh.

πŸ’» Development Notes

How Builds Work

  • Unsandboxed execution: Builds run outside Bazel's sandbox to write into src/sysroot/
  • Build logs: Written to bazel-out/lfs-logs/<target>.log in the Bazel execroot
  • Dependency tracking: Each package creates a .done marker file for Bazel

Project Structure

  • Chapter mapping: Each LFS chapter maps to a package directory (src/packages/chapter_XX/)
  • Custom rules: Build logic lives in src/tools/lfs_package.bzl and lfs_macros.bzl
  • Source definitions: Package URLs and checksums in src/MODULE.bazel

πŸ“š Documentation

Core Documentation

Chapter Guides

🀝 Contributing

This is a personal learning project, but feedback and suggestions are welcome! File issues or PRs on GitHub.

πŸ“„ License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Third-Party Licenses

This repository includes the Linux From Scratch book as a Git submodule in docs/lfs-book/ for reference purposes. That submodule is currently pinned to the 12.2 branch while the build recipes follow 13.0-systemd; use the linked online 13.0 book when checking recipe details. The LFS book has its own separate licensing:

See docs/lfs-book/appendices/license.xml for the full LFS license details.

πŸ“– Appendix: Resources

Official Guides

Community