Skip to content

aryamurray/harbour

Repository files navigation

Harbour

A Cargo-like package manager and build system for C.

Harbour brings modern dependency management to C projects with a familiar workflow inspired by Rust's Cargo.

Features

  • Simple manifest format - Harbour.toml defines your project and dependencies
  • Deterministic builds - Lockfile ensures reproducible builds across machines
  • Git dependencies - Pull dependencies directly from git repositories
  • Path dependencies - Use local packages during development
  • Parallel builds - Compile sources in parallel for faster builds
  • Surface contracts - Fine-grained control over what headers and flags propagate to dependents

Installation

cargo install --path .

Quick Start

Create a new project

# Create a new executable project
harbour new myapp
cd myapp

# Or create a library
harbour new mylib --lib

Project structure

myapp/
├── Harbour.toml     # Project manifest
├── src/
│   └── main.c       # Source files
└── include/         # Public headers (for libraries)

Harbour.toml

[package]
name = "myapp"
version = "0.1.0"

[targets.myapp]
kind = "exe"
sources = ["src/**/*.c"]

[dependencies]
# Git dependency
zlib = { git = "https://github.com/example/zlib-harbour", tag = "v1.3.1" }

# Local path dependency
myutil = { path = "../myutil" }

Build your project

# Debug build
harbour build

# Release build
harbour build --release

Run tests

harbour test

Test targets are automatically discovered by name pattern: *_test, *_tests, test_*, test, tests.

Example test target in Harbour.toml:

[targets.unit_test]
kind = "exe"
sources = ["tests/**/*.c"]

Commands

Command Description
harbour new <name> Create a new project
harbour init Initialize project in current directory
harbour build Build the project
harbour test Build and run test targets
harbour add <pkg> --git <url> Add a git dependency
harbour add <pkg> --path <path> Add a local dependency
harbour remove <pkg> Remove a dependency
harbour update Update dependencies and lockfile
harbour tree Show dependency tree
harbour flags <target> Show compile/link flags with provenance
harbour linkplan <target> Show link order and sources
harbour explain <pkg> Explain why a package is in the graph
harbour clean Remove build artifacts
harbour toolchain show Show compiler configuration
harbour backend list List available build backends
harbour backend show <name> Show backend capabilities
harbour ffi bundle Create portable FFI bundle
harbour completions <shell> Generate shell completions

Dependency Management

Adding dependencies

# From a git repository
harbour add zlib --git https://github.com/example/zlib-harbour

# With a specific tag
harbour add zlib --git https://github.com/example/zlib-harbour --tag v1.3.1

# With a specific branch
harbour add zlib --git https://github.com/example/zlib-harbour --branch main

# With a specific commit
harbour add zlib --git https://github.com/example/zlib-harbour --rev abc123

# From a local path
harbour add myutil --path ../myutil

# Vcpkg (auto-resolves when registry doesn't have the package)
harbour add glfw3

Understanding the dependency graph

# Show the full dependency tree
harbour tree

# Explain why a package is included
harbour explain zlib

Surface Contracts

Harbour uses "surfaces" to control what compile and link flags propagate between packages.

[targets.mylib]
kind = "staticlib"
sources = ["src/**/*.c"]

[targets.mylib.surface.compile.public]
include_dirs = ["include"]        # Propagates to dependents
defines = [{ name = "MYLIB_API" }]

[targets.mylib.surface.compile.private]
include_dirs = ["src"]            # Internal only
defines = [{ name = "MYLIB_INTERNAL" }]

[targets.mylib.surface.link.public]
libs = [{ kind = "system", name = "pthread" }]

Viewing resolved flags

# See all flags with their source
harbour flags myapp

# Output:
# Compile flags for `myapp`:
#   -I/path/to/zlib/include    # from: zlib 1.3.1 (surface.compile.public)
#   -DZLIB_CONST               # from: zlib 1.3.1 (surface.compile.public)

# See link order
harbour linkplan myapp

Build Profiles

[profile.debug]
opt_level = 0
debug = true

[profile.release]
opt_level = 3
debug = false
lto = true

Build Backends

Harbour supports multiple build backends for different use cases:

# List available backends
harbour backend list

# Show backend capabilities
harbour backend show cmake

# Build with a specific backend
harbour build --backend=cmake
Backend Description
native Built-in compiler driver (default)
cmake CMake-based builds for complex projects
meson Meson build system support
custom User-defined build commands

FFI Bundling

Create portable shared library bundles for FFI consumption by other languages:

# Build with FFI mode (shared libraries + runtime deps)
harbour build --ffi

# Create FFI bundle
harbour ffi bundle --output ./dist

# Preview what would be bundled
harbour ffi bundle --dry-run

The FFI bundle includes:

  • Primary shared library
  • Transitive runtime dependencies
  • RPATH rewriting for portability (Linux/macOS)
  • JSON manifest listing all bundled files

Build Options

# Library linkage preference
harbour build --linkage=static   # Static libraries only
harbour build --linkage=shared   # Shared libraries only
harbour build --linkage=auto     # Backend decides (default)

# Cross-compilation (requires cmake/meson backend)
harbour build --backend=cmake --target-triple=x86_64-unknown-linux-gnu

Configuration Files

Harbour supports configuration files for persistent settings:

  • Global config: ~/.harbour/config.toml - User-wide defaults
  • Project config: .harbour/config.toml - Project-specific overrides

Project config takes precedence over global config. CLI flags override both.

Vcpkg integration is optional. Set VCPKG_ROOT (and optionally VCPKG_DEFAULT_TRIPLET) or configure the [vcpkg] section to inject vcpkg include/lib paths into native builds.

Example Configuration

# ~/.harbour/config.toml or .harbour/config.toml

[build]
# Default build backend (native, cmake, meson, custom)
backend = "native"

# Default linkage preference (static, shared, auto)
linkage = "auto"

# Default parallel jobs (omit for auto-detect)
jobs = 8

# Always emit compile_commands.json
emit_compile_commands = true

# Default C++ standard
cpp_std = "17"

[ffi]
# Default FFI bundle output directory
bundle_dir = "./dist"

# Include transitive runtime dependencies
include_transitive = true

# Rewrite RPATH for portability
rpath_rewrite = true

[net]
# Offline mode (don't fetch from network)
offline = false

[vcpkg]
# Enable vcpkg integration (defaults to VCPKG_ROOT if set)
enabled = true

# Optional overrides for vcpkg
root = "C:/vcpkg"
triplet = "x64-windows"

Shell Completions

Generate shell completions for tab-completion support:

# Bash (add to ~/.bashrc)
eval "$(harbour completions bash)"

# Zsh (add to ~/.zshrc)
eval "$(harbour completions zsh)"

# Fish (add to ~/.config/fish/config.fish)
harbour completions fish | source

# PowerShell (add to $PROFILE)
harbour completions powershell | Out-String | Invoke-Expression

Current Limitations

  • Registry support is experimental - The default registry is available, but harbour search expects a local registry clone today.
  • Workspace support is partial - Multi-package workspaces resolve and build, but some commands still assume a single root.

Troubleshooting

"could not find Harbour.toml"

You're not in a Harbour project directory. Run harbour init to create one, or cd to your project root.

"target not found"

Run harbour tree to see available targets, or check your Harbour.toml for typos.

"package not found in dependency graph"

The package isn't a dependency. Run harbour tree to see all dependencies, or harbour add to add it.

License

MIT

About

A Package Manger and Orchestration Layer for C/C++ Projects

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages