Understanding how hx builds projects.
Overview
hx wraps Cabal’s build system, providing:
- Unified interface for builds
- Enhanced error messages
- Progress reporting
- Multiple compiler backends
Build Process
What Happens During hx build
Configuration
- Read
hx.tomland.cabalfiles - Determine compiler and options
- Check lockfile
- Read
Dependency Resolution
- Verify dependencies are installed
- Install missing packages from lockfile
Compilation
- Compile modules in dependency order
- Generate interface files (
.hi) - Generate object files (
.o)
Linking
- Link object files into executables
- Apply optimizations
Build Order
Modules are compiled in dependency order:
MyProject.Types (no imports)
MyProject.Parser (imports Types)
MyProject (imports Parser, Types)
Main (imports MyProject)hx parallelizes independent modules.
Incremental Builds
How Incremental Builds Work
Only recompile what changed:
- Check timestamps - Has source file changed?
- Check interface hashes - Have dependencies changed?
- Recompile if needed - Only affected modules
# First build - compiles everything
$ hx build
Compiling MyProject.Types
Compiling MyProject.Parser
Compiling MyProject
Compiling Main
Linking my-project
# Second build - nothing changed
$ hx build
Up to date
# After editing MyProject.hs
$ hx build
Compiling MyProject # Only this module
Linking my-project # Must relinkWhen to Clean
Force full rebuild:
hx clean
hx buildReasons to clean:
- Corrupted build state
- Switching GHC versions
- Strange build errors
Build Artifacts
Location
Artifacts go to dist-newstyle/:
dist-newstyle/
├── build/
│ └── x86_64-osx/
│ └── ghc-9.8.2/
│ └── my-project-0.1.0/
│ ├── build/
│ │ ├── MyProject/ # Library objects
│ │ └── my-project/ # Executable
│ │ └── my-project # The binary
│ └── cache/
├── packagedb/
└── tmp/Interface Files (.hi)
GHC interface files contain:
- Type signatures
- Exports
- Inlining info
Used for:
- Incremental compilation
- Cross-module optimization
Object Files (.o)
Compiled machine code, linked into final executable.
Optimization Levels
Debug (Default)
hx build
# Equivalent to: -O0 or -O1Fast compilation, slower runtime.
Release
hx build --release
# Equivalent to: -O2Slower compilation, faster runtime.
Configuration
[build]
# Default to release builds
release = true
# Or customize optimization
ghc-options = ["-O1"]Optimization Flags
| Flag | Description |
|---|---|
-O0 | No optimization |
-O1 | Light optimization (default) |
-O2 | Full optimization |
-fllvm | Use LLVM backend |
-fspecialize | Specialize functions |
Parallelism
Parallel Compilation
hx build -j8 # 8 parallel jobs
hx build -j0 # Use all CPUsConfiguration
[build]
jobs = 4 # Default parallel jobsHow It Works
Independent modules compile in parallel:
Time →
Thread 1: [Types] → [Parser] → [Main]
Thread 2: [Utils] → [Config]
Thread 3: [Logging]
↓
[Linking]Compiler Backends
GHC (Default)
hx build --backend ghcUses cabal build under the hood.
BHC
hx build --backend bhcInvokes bhc build with translated options.
See Compiler Backends for details.
Build Modes
Full Build
hx buildCompiles all components.
Component-Specific
hx build --lib # Library only
hx build --exe app # Specific executable
hx build --test # Tests onlyCheck (No Codegen)
hx checkType checks without producing binaries. Faster for quick feedback.
GHC Options
Adding Options
[build]
ghc-options = ["-Wall", "-Wextra", "-Werror"]Or per-command:
hx build --ghc-options="-Werror"Common Options
| Option | Description |
|---|---|
-Wall | Enable common warnings |
-Wextra | Enable extra warnings |
-Werror | Treat warnings as errors |
-O2 | Full optimization |
-threaded | Enable multi-threading |
-rtsopts | Enable RTS options |
-with-rtsopts=-N | Use all CPUs at runtime |
Cross-Compilation
Build for different platforms:
hx build --target aarch64-linux-gnuSee Cross-Compilation for details.
CI Builds
Recommended CI Build
- name: Build
run: |
hx lock --frozen # Verify lockfile
hx build # Build project
hx test # Run testsCaching
Cache ~/.cabal/ and dist-newstyle/:
- uses: actions/cache@v3
with:
path: |
~/.cabal
dist-newstyle
key: ${{ runner.os }}-cabal-${{ hashFiles('hx.lock') }}Troubleshooting
Build Failures
Read the error message carefully:
src/MyLib.hs:10:5: error:
Not in scope: 'undefined'
Perhaps you meant 'Data.Undefined' (imported from Data)Incremental Build Issues
If incremental builds produce wrong results:
hx clean
hx buildMemory Issues
For large projects:
# Reduce parallelism
hx build -j2
# Or add GHC options
hx build --ghc-options="+RTS -M4G -RTS"