Build System¶
Epsilon's dependency-tracking build system with incremental compilation and content-based change detection.
Overview¶
The Epsilon build system (epsilon.tool.build
) provides:
- Content-based change detection using SHA-256 hashing
- Incremental compilation - only rebuild what changed
- Dependency resolution with topological sorting
- Module-based organization with
package.yaml
configuration - Parallel builds where dependencies allow
Basic Usage¶
Command Line Interface¶
# Build a specific module
./run.sh build epsilon.core
# Build with verbose output
./run.sh build epsilon.core --verbose
# Force rebuild (ignore cache)
./run.sh build epsilon.core --force
# Clean build artifacts
./run.sh build epsilon.core --clean
Programmatic Interface¶
(defpackage #:build-example
(:use #:common-lisp)
(:local-nicknames
(#:build #:epsilon.tool.build)))
(in-package #:build-example)
;; Build a module
(build:build "epsilon.core")
;; Build with options
(build:build "my-module" :force t :verbose t)
;; Get build status
(build:build-status "epsilon.core")
Module Configuration¶
package.yaml Structure¶
Each module requires a package.yaml
file:
name: my-module
description: Example module
version: 1.0.0
# Source directories
sources:
- src
- extra-src
# Test directories
tests:
- tests
- integration-tests
# Module dependencies
dependencies:
- core
- another-module
# Build configuration
build:
target-dir: target
optimize: true
debug: false
Directory Structure¶
Standard module layout:
my-module/
├── package.yaml # Module configuration
├── src/ # Source files
│ ├── package.lisp # Package definition
│ └── implementation.lisp
├── tests/ # Test files
│ └── tests.lisp
└── target/ # Build artifacts
├── fasls/ # Compiled files
└── cache/ # Build cache
Dependency Management¶
Dependency Resolution¶
The build system automatically resolves dependencies:
Circular Dependency Detection¶
Module Graph Visualization¶
Change Detection¶
Content Hashing¶
Files are hashed to detect changes:
(defstruct source-file
path ; Full file path
content-hash ; SHA-256 of file content
last-modified ; File modification time
dependencies) ; Other files this depends on
Incremental Builds¶
Only changed files and their dependents are rebuilt:
File Change: src/utils.lisp
├── Rebuild: utils.lisp → utils.fasl
├── Rebuild: app.lisp (depends on utils)
└── Skip: other-module.lisp (no dependency)
Build Cache¶
;; Cache structure
(defstruct build-cache
module-name ; Module being built
source-hashes ; Hash of each source file
dependency-hashes ; Hash of each dependency
build-time ; When build completed
artifacts) ; Generated files
Advanced Features¶
Parallel Compilation¶
;; Configure parallel builds
(build:set-build-options :parallel-jobs 4)
;; Dependencies are built in parallel when possible
(build:build "large-project")
Custom Build Steps¶
# package.yaml
build:
pre-build:
- generate-version-file
- copy-resources
post-build:
- run-tests
- create-documentation
;; Define custom build step
(build:define-build-step generate-version-file (module)
(with-open-file (stream "src/version.lisp"
:direction :output
:if-exists :supersede)
(format stream "(defconstant +version+ \"~A\")~%"
(module-version module))))
Build Profiles¶
# Different configurations for different environments
profiles:
development:
build:
optimize: false
debug: true
features: [debugging, profiling]
production:
build:
optimize: true
debug: false
features: [release]
Build Hooks¶
Pre/Post Build Actions¶
;; Register build hooks
(build:add-hook :pre-build
(lambda (module)
(format t "Starting build of ~A~%" (module-name module))))
(build:add-hook :post-build
(lambda (module success-p)
(if success-p
(format t "Successfully built ~A~%" (module-name module))
(format t "Build failed for ~A~%" (module-name module)))))
File Watchers¶
;; Watch for file changes and rebuild automatically
(build:watch "my-module"
:callback (lambda (changed-files)
(format t "Files changed: ~A~%" changed-files)
(build:build "my-module")))
Integration with Development Tools¶
REPL Integration¶
;; Hot reload during development
(build:enable-hot-reload "my-module")
;; Changes are automatically compiled and loaded
;; when files are modified
Editor Integration¶
;; Generate compilation database for IDE support
(build:generate-compile-commands "my-module")
;; Creates compile_commands.json for editors
Test Integration¶
;; Build and run tests
(build:build-and-test "my-module")
;; Run tests only if build succeeds
(when (build:build "my-module")
(test:run "my-module"))
Performance Optimization¶
Build Metrics¶
(build:build "my-module" :collect-metrics t)
;; View build performance
(build:show-metrics "my-module")
Sample output:
Build Metrics for my-module:
├── Total time: 2.5s
├── Compilation: 2.1s (84%)
├── Dependency resolution: 0.3s (12%)
├── Cache operations: 0.1s (4%)
└── Files processed: 15 (6 files/second)
Memory Usage¶
;; Monitor memory during build
(build:build "large-module" :monitor-memory t)
;; Configure memory limits
(build:set-memory-limit (* 2 1024 1024 1024)) ; 2GB limit
Distributed Builds¶
;; Build across multiple machines (future feature)
(build:build "huge-project"
:distributed t
:workers '("build-server-1" "build-server-2"))
Error Handling¶
Build Failures¶
(handler-case
(build:build "problematic-module")
(build:compilation-error (e)
(format t "Compilation failed in ~A:~%~A~%"
(build:error-file e)
(build:error-message e)))
(build:dependency-error (e)
(format t "Missing dependency: ~A~%"
(build:missing-dependency e))))
Recovery Strategies¶
;; Attempt to fix common issues automatically
(build:build "my-module" :auto-fix t)
;; Clean and rebuild on persistent errors
(build:clean-rebuild "my-module")
Configuration¶
Global Build Settings¶
;; Configure global build behavior
(build:configure
:default-optimize-level 2
:parallel-jobs (build:cpu-count)
:cache-directory "~/.epsilon/build-cache"
:max-cache-size (* 1024 1024 1024)) ; 1GB
Project-Specific Settings¶
# .epsilon-build.yaml (project root)
global:
cache-dir: .build-cache
parallel-jobs: 8
optimize: true
modules:
core:
optimize: false # Override for debugging
tests:
compile: false # Don't compile test files
Examples¶
Simple Library Build¶
# mylibrary/package.yaml
name: mylibrary
description: Simple utility library
version: 0.1.0
sources: [src]
tests: [tests]
dependencies: [core]
Complex Application Build¶
# myapp/package.yaml
name: myapp
description: Web application
version: 2.0.0
sources:
- src
- generated
dependencies:
- core
- web-framework
- database-driver
build:
pre-build:
- generate-api-client
- compile-assets
post-build:
- run-integration-tests
- package-distribution
Multi-Module Project¶
project/
├── core/package.yaml
├── utils/package.yaml
├── web/package.yaml
├── cli/package.yaml
└── tests/package.yaml
Next: Test Framework - Running and organizing tests