Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Porters Documentation

Welcome to the official documentation for Porters - a modern, universal package manager for C and C++ projects.

What is Porters?

Porters is a comprehensive project management tool designed specifically for C/C++ developers. It simplifies the entire development workflow from project creation to building, dependency management, and publishing.

Key Features

  • 🚀 Universal Build System Support - Works with CMake, XMake, Meson, Make, and custom build systems
  • 📦 Smart Dependency Management - Supports Git, local paths, and remote registries
  • 💾 Global Cache System - Share dependencies across projects with ~/.porters/cache/
  • 🔌 Offline Mode - Work without network access using cached dependencies
  • Remote Registry - Discover packages from GitHub-based package index
  • �🌍 Global and Local Dependencies - Install packages globally or isolate them per-project
  • 🔄 Lock File Support - Ensures reproducible builds across environments
  • 🎯 Auto-Detection - Automatically detects existing build systems and project structure
  • 🔧 Interactive Project Creation - Step-by-step project setup with customizable options
  • 🟣 Hybrid C/C++ Support - Seamlessly combine C and C++ code with extern "C" scaffolding
  • Zero-Config Single File Execution - Run any C/C++ file instantly with porters execute - no project needed!
  • 🪟 External Terminal Support - Open programs in new terminal windows with --external flag
  • 📄 Automatic License Generation - Creates LICENSE files from 9+ SPDX templates (MIT, Apache-2.0, GPL, BSD, etc.)
  • 📝 Comprehensive README Generation - Auto-creates README with badges, usage examples, and project structure
  • 🏗️ Application & Library Templates - Complete scaffolding with examples, tests, and documentation
  • 🔍 System Requirements Check - Automatic detection of compilers and build tools on first run
  • ⚙️ Global Configuration - User-wide settings and preferences in ~/.porters/config.toml
  • 🛤️ PATH Management - Built-in commands to add/remove Cargo bin from system PATH
  • 📚 GitHub Integration - Seamlessly publish releases and manage versions
  • 🔄 Self-Updating - Keep Porters up-to-date with a single command
  • 📦 Package Manager Integration - Works with Conan, vcpkg, and XMake package managers

Why Porters?

C and C++ projects have historically lacked a unified package management solution. Porters bridges this gap by providing:

  • Consistency: Manage all your C/C++ projects the same way
  • Simplicity: Intuitive commands that just work
  • Flexibility: Support for multiple build systems and workflows
  • Isolation: Virtual environments per project using the ports/ folder
  • Reliability: Lock files ensure your builds are reproducible

Quick Start

# Install Porters
cargo install porters

# Porters automatically checks system requirements on first run
# Detects compilers (gcc, g++, clang, MSVC) and build tools (CMake, Make, etc.)

# Add Cargo bin to PATH (optional, for convenience)
porters add-to-path

# Create a new project (interactive wizard)
porters create my-project
# Choose: Application or Library
# Select: License (MIT, Apache-2.0, GPL-3.0, BSD, etc.)
# Porters generates: LICENSE file, README, source structure

# Or initialize an existing project
cd my-existing-project
porters init
# Also generates LICENSE file based on your choice

# Add dependencies
porters add fmt --git https://github.com/fmtlib/fmt

# Build your project
porters build

# Execute single C/C++ files instantly (no project needed!)
porters execute hello.cpp
porters execute game.c --external  # Opens in new terminal window

# Publish to GitHub
porters publish

System Architecture

Porters uses a dual-layer dependency system:

  • Global Dependencies (~/.porters/): Centralized cache for packages used across projects
  • Local Dependencies (ports/): Project-specific isolated environments

This design provides:

  • Faster dependency resolution (cached globally)
  • Complete isolation between projects
  • Reproducible builds via lock files

Getting Help

License

Porters is licensed under the Apache License 2.0. See the LICENSE file for details.

Installation

This guide will help you install Porters on your system.

Prerequisites

Before installing Porters, ensure you have the following:

Required

  • Rust Toolchain (for building from source or installing via Cargo)
    • Install from rustup.rs
    • Minimum version: Rust 1.70+

C/C++ Compiler (At least one required)

Porters will check for these automatically on first run.

  • Windows:

    • MSVC (Visual Studio Build Tools)
    • MinGW-w64
    • Clang/LLVM
  • macOS:

    • Xcode Command Line Tools: xcode-select --install
    • Clang (included with Xcode)
  • Linux:

    • GCC: sudo apt install gcc g++ (Debian/Ubuntu)
    • Clang: sudo apt install clang (Debian/Ubuntu)

At least one build system is recommended for project management:

  • CMake (Recommended)

    • Download from cmake.org
    • Minimum version: 3.15+
    • Install:
      • Windows: choco install cmake
      • Linux: sudo apt install cmake
      • macOS: brew install cmake
  • XMake

    • Install from xmake.io
    • Cross-platform build utility
  • Meson

    • Install via pip: pip install meson ninja
    • Fast and user-friendly build system
  • Make

    • Usually pre-installed on Linux/macOS
    • Windows: Install via MinGW or Cygwin

Note: Build tools are only required for project-based workflows (porters build, porters create). The porters execute command works without any build system!

Installing Porters

The easiest way to install Porters is through Cargo:

cargo install porters

This will download, compile, and install the latest version of Porters.

First Run System Check

After installation, when you run Porters for the first time, it will automatically:

  1. Check for C/C++ compilers (gcc, g++, clang, MSVC, MinGW)
  2. Check for build systems (CMake, Make, XMake, Meson, Ninja)
  3. Display what's found with version numbers
  4. Show installation instructions for missing tools
  5. Save detected tools to ~/.porters/config.toml
  6. Block execution if no C/C++ compiler is found

Example First Run:

$ porters --version

╭──────────────────────────────────────────────────╮
│  System Requirements Check                       │
╰──────────────────────────────────────────────────╯

Compilers
─────────
✅ g++ (version 11.4.0)
✅ gcc (version 11.4.0)
❌ clang++ (not found)

Build Systems
─────────────
✅ cmake (version 3.22.1)
✅ make (version 4.3)
❌ xmake (not found)

Status: ✅ System ready!

Installation Instructions:
──────────────────────────

To install missing tools on Linux:
  sudo apt-get install clang xmake

Porters version 0.1.0

Manual System Check:

You can re-run the system check anytime:

porters --check-system

This is useful after installing new compilers or build tools to update the global configuration.

Automatic PATH Setup

Porters can automatically add the Cargo bin directory to your system PATH.

Using the built-in command:

# Automatically add ~/.cargo/bin to PATH
porters add-to-path

This command:

  • Windows: Modifies User PATH via registry (requires admin privileges)
  • Linux/macOS: Appends to your shell profile (~/.bashrc, ~/.zshrc, etc.)
  • Detects your shell automatically
  • Creates backup before modifying

To remove from PATH:

porters remove-from-path

Manual PATH Setup:

If you prefer to do it manually:

Windows PowerShell (Run as Administrator):

[Environment]::SetEnvironmentVariable(
  "Path",
  [Environment]::GetEnvironmentVariable("Path", "User") + ";$env:USERPROFILE\.cargo\bin",
  "User"
)

Linux/macOS (add to ~/.bashrc or ~/.zshrc):

export PATH="$HOME/.cargo/bin:$PATH"

Then restart your terminal or run:

source ~/.bashrc  # or source ~/.zshrc

Quick Setup (Current Session Only):

# Windows PowerShell
$env:Path += ";$env:USERPROFILE\.cargo\bin"
# Linux/macOS
export PATH="$HOME/.cargo/bin:$PATH"

Using Installation Script (Windows)

For Windows users, we provide an automated installation script:

# Download and run the installer
cd path\to\Porters
.\install.ps1

This script will:

  1. Check Rust installation
  2. Install Porters via cargo
  3. Automatically add to PATH (with your permission)
  4. Verify the installation

From GitHub Releases

Download pre-built binaries from the GitHub Releases page:

  1. Download the appropriate binary for your platform
  2. Extract the archive
  3. Move the porters binary to a directory in your PATH

For Linux/macOS:

sudo mv porters /usr/local/bin/
chmod +x /usr/local/bin/porters

For Windows: Move porters.exe to a directory in your PATH (e.g., C:\Program Files\Porters\)

Building from Source

If you want to build Porters from source (for development or custom builds):

Step 1: Clone the Repository

# Clone the repository
git clone https://github.com/muhammad-fiaz/Porters.git
cd Porters

Step 2: Build the Project

# Build in release mode for optimal performance
cargo build --release

Step 3: Install the Binary

After building, you need to install the binary to your system PATH:

Linux/macOS:

# Copy to system PATH
sudo cp target/release/porters /usr/local/bin/

# Make it executable
sudo chmod +x /usr/local/bin/porters

Windows (PowerShell as Administrator):

# Copy to a directory in PATH (e.g., Program Files)
cp target\release\porters.exe "C:\Program Files\Porters\porters.exe"

# Or add to user PATH
$portersPath = "$env:USERPROFILE\.porters\bin"
New-Item -ItemType Directory -Force -Path $portersPath
cp target\release\porters.exe "$portersPath\porters.exe"

# Add to PATH permanently
[Environment]::SetEnvironmentVariable(
  "Path",
  [Environment]::GetEnvironmentVariable("Path", "User") + ";$portersPath",
  "User"
)

Step 4: Verify Installation

porters --version

You should see output similar to:

porters 0.1.0

Verify Installation

Confirm Porters is installed correctly:

porters --version

You should see output similar to:

porters 0.1.0

Global Configuration

On first run, Porters will create a global configuration directory:

  • Linux/macOS: ~/.porters/
  • Windows: C:\Users\<username>\.porters\

This directory contains:

  • config.toml - Global settings
  • packages/ - Globally installed packages
  • cache/ - Download cache

Updating Porters

Keep Porters up-to-date with:

porters self-update

Or via Cargo:

cargo install porters --force

Next Steps

Now that Porters is installed, continue to the Getting Started guide to create your first project!

Getting Started

This guide will walk you through creating and managing your first C/C++ project with Porters.

First Run: System Requirements Check

When you first install Porters and run any command, it will automatically check your system for:

  • C/C++ Compilers: gcc, g++, clang, clang++, MSVC (Windows), MinGW (Windows)
  • Build Systems: CMake, Make, XMake, Meson, Ninja

Example First Run:

$ porters --version

╭──────────────────────────────────────────────────╮
│  System Requirements Check                       │
╰──────────────────────────────────────────────────╯

Compilers
─────────
✅ g++ (version 11.4.0)
✅ gcc (version 11.4.0)

Build Systems
─────────────
✅ cmake (version 3.22.1)
✅ make (version 4.3)

Status: ✅ System ready!

Porters version 0.1.0

If any required tools are missing, Porters will display installation instructions for your platform.

Manual System Check:

You can run the system check anytime:

porters --check-system

Creating a New Project

The easiest way to start is creating a new project:

porters create my-awesome-project

This launches an interactive wizard that asks:

  1. Project Type: Application or Library
  2. Language: C, C++, or Both
  3. Library Name: (If creating a library)
  4. Author: Your name (saved to global config for future use)
  5. Email: Your email (saved to global config)
  6. Repository URL: Git repository URL (optional)
  7. License: Choose from MIT, Apache-2.0, GPL-3.0, BSD, MPL-2.0, LGPL-3.0, Unlicense
  8. Build System: CMake, XMake, Meson, Make, or Custom

What Gets Created

Application Project:

  • src/main.cpp or src/main.c with "Hello, World!" starter code
  • Build system configuration (CMakeLists.txt, xmake.lua, etc.)
  • LICENSE file auto-generated with your name and current year
  • README.md with build instructions
  • porters.toml configuration
  • Git repository initialization

Library Project:

  • Complete library structure:
    • include/<libname>/<libname>.hpp - Public header with namespace/API
    • src/<libname>.cpp - Implementation
    • examples/example.cpp - Usage example
    • tests/test_<libname>.cpp - Test skeleton
  • Build system configuration with library target
  • LICENSE file auto-generated
  • README.md with library usage examples
  • porters.toml with project-type = "library"

License File Generation

When you select a license, Porters automatically generates a complete LICENSE file containing:

  • Full SPDX-compliant license text
  • Your name as the copyright holder
  • Current year in copyright notice

Supported Licenses:

  • MIT - Simple and permissive
  • Apache-2.0 - Permissive with patent grant
  • GPL-3.0 / GPL-2.0 - Strong copyleft
  • BSD-3-Clause / BSD-2-Clause - Permissive BSD variants
  • MPL-2.0 - Weak copyleft (Mozilla Public License)
  • LGPL-3.0 - Weak copyleft for libraries
  • Unlicense - Public domain dedication

Quick Create with Defaults

Skip the questions and use defaults:

porters create my-project --yes

This creates a C/C++ application with:

  • CMake as the build system
  • Apache-2.0 license
  • Basic project structure

Hybrid C/C++ Projects (Both Option)

When asked for language, you can choose "🟣 Both (Hybrid C/C++ with extern "C")" to create a project that seamlessly combines C and C++ code.

Example Creation:

porters create hybrid-project
# Select: 🟣 Both (Hybrid C/C++ with extern "C")

Generated Project Structure:

hybrid-project/
├── porters.toml
├── CMakeLists.txt
├── src/
│   ├── main.cpp        # C++ entry point
│   ├── c_module.c      # C implementation
│   ├── cpp_utils.cpp   # C++ utilities
├── include/
│   ├── c_module.h      # C header with extern "C"
│   └── cpp_utils.hpp   # C++ header
└── README.md

Why Use Hybrid Projects?

  1. Gradual Migration: Migrate legacy C code to C++ incrementally
  2. Best of Both Worlds: Use C for low-level operations, C++ for high-level features
  3. Library Integration: Integrate existing C libraries in C++ applications
  4. Performance: Keep performance-critical C code while using C++ for convenience

Example Code Generated:

C++ main calls C functions via extern "C":

// src/main.cpp
#include "c_module.h"     // C functions via extern "C"
#include "cpp_utils.hpp"  // C++ utilities

int main() {
    // Call C function
    const char* msg = get_c_message();
    
    // Use C++ class
    StringHelper helper;
    std::string upper = helper.to_upper(msg);
    
    return 0;
}

C module with extern "C" wrapper:

// include/c_module.h
#ifdef __cplusplus
extern "C" {
#endif

const char* get_c_message(void);

#ifdef __cplusplus
}
#endif

Build System Handling:

CMake automatically handles mixed C/C++ compilation:

  • C files compiled with gcc/clang
  • C++ files compiled with g++/clang++
  • Proper linking of both object files

When to Choose "Both" vs Pure C/C++:

Choose "Both" when:

  • Migrating from C to C++ gradually
  • Integrating existing C libraries
  • Need C ABI for library exports
  • Performance-critical C code with C++ convenience layer

Choose Pure C or Pure C++ when:

  • New project with no legacy code
  • No need for cross-language interoperability
  • Want to enforce single language standard

Initializing Existing Project

Already have a C/C++ project? Initialize it with Porters:

cd your-existing-project
porters init

The init command will:

  1. Detect your existing source files
  2. Auto-detect your build system (if present)
  3. Ask for project metadata interactively:
    • Project name (defaults to folder name)
    • Version (defaults to 0.1.0)
    • Author
    • Description
    • License
  4. Create a porters.toml configuration file

Non-Interactive Init

Use the defaults without questions:

porters init --yes

Project Structure

A typical Porters project looks like:

my-project/
├── porters.toml       # Project configuration
├── porters.lock       # Dependency lock file (auto-generated)
├── ports/             # Local dependencies (isolated)
├── src/               # Source files
│   └── main.cpp
├── include/           # Header files
├── CMakeLists.txt     # Build system files (e.g., CMake)
└── README.md

Key Files

porters.toml

The main configuration file for your project:

[project]
name = "my-project"
version = "0.1.0"
description = "My awesome C++ project"
license = "Apache-2.0"
authors = ["Your Name <you@example.com>"]

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }

[dev-dependencies]
# Test frameworks, etc.

[build]
system = "cmake"

porters.lock

Auto-generated lock file ensuring reproducible builds:

version = "1"
updated_at = "2024-01-15T10:30:00Z"

[dependencies.fmt]
name = "fmt"
version = "10.1.1"
source = { Git = { url = "https://github.com/fmtlib/fmt", rev = "abc123" } }

ports/ Directory

Contains project-specific dependencies, isolated from other projects:

ports/
├── fmt/               # fmt library clone
└── spdlog/           # spdlog library clone

Building Your Project

Once your project is set up:

# Build the project
porters build

# Build in release mode
porters build --release

Porters will:

  1. Resolve dependencies from porters.toml
  2. Clone missing dependencies to ports/
  3. Update porters.lock
  4. Run the configured build system

Quick Syntax Validation

Fast compilation checking without creating executables:

# Check all source files in the project
porters check

# Check a specific file
porters check src/main.c

# Check with verbose compiler output
porters check --verbose

Benefits:

  • Faster than full builds - No linking or executable generation
  • 🎯 Focused error messages - Only shows compilation issues
  • 🔍 File-level granularity - Check individual files or entire projects
  • 📊 Clear summaries - See pass/fail counts at a glance

Example Output:

✅ Checking compilation (syntax-only)
🔍 Discovering source files in project...
📦 Found 3 source file(s)

🔨 Checking: src/main.c (C)
✅ PASSED: src/main.c

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Compilation Check Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   Total files checked: 3
   ✅ Passed: 3
   ❌ Failed: 0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ All compilation checks passed! ✅

Quick Single-File Execution (Zero Configuration!)

No setup needed! Execute any C/C++ file instantly - even without a project or porters.toml:

# Execute any C/C++ file - works immediately!
porters execute hello.c

# All C/C++ extensions supported
porters execute app.cpp     # C++
porters execute main.cxx    # C++
porters execute prog.cc     # C++
porters execute code.c      # C

# With arguments
porters execute main.cpp arg1 arg2

100% Automatic - No Configuration Required:

  • Works Anywhere - No porters.toml needed
  • Auto Compiler Detection - Finds gcc/clang/g++/clang++ in PATH
  • All Extensions - .c, .cpp, .cxx, .cc, .c++, .cp, .C, .CPP
  • Dependency Resolution - Reads porters.toml if present
  • Include/Lib Injection - Automatic dependency paths
  • One Command - Compiles and runs instantly

Example - No Project Needed:

// hello.c (anywhere on your system)
#include <stdio.h>

int main(int argc, char** argv) {
    printf("Hello from Porters!\n");
    if (argc > 1) {
        printf("Arguments: ");
        for (int i = 1; i < argc; i++) {
            printf("%s ", argv[i]);
        }
        printf("\n");
    }
    return 0;
}
# Just execute - no setup!
$ porters execute hello.c
Compiling hello.c...
Hello from Porters!

$ porters execute hello.c foo bar baz
Compiling hello.c...
Hello from Porters!
Arguments: foo bar baz

With Dependencies (Automatic):

When porters.toml exists, dependencies are automatically resolved:

# porters.toml (optional)
[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }
// main.cpp
#include <fmt/core.h>

int main() {
    fmt::print("Formatted output: {}\n", 42);
    return 0;
}
$ porters sync      # Download dependencies once
$ porters execute main.cpp
# Automatically includes fmt - no configuration needed!
Formatted output: 42

No [run] Section Needed:

The [run] section in porters.toml is completely optional. Only add it if you need custom compiler flags or non-standard include paths. 99% of the time, it's not needed!

See porters execute documentation for more details.

Using Package Managers

NEW: Porters integrates with popular C/C++ package managers, giving you access to thousands of libraries!

Supported Package Managers

  • Conan - C/C++ package manager with CMake integration
  • vcpkg - Microsoft's C/C++ package manager
  • XMake - Lua-based build system with package management

Quick Start with Package Managers

Install a package locally (project-specific):

# Using Conan
porters conan add fmt
porters co add spdlog        # 'co' is a shortcut for 'conan'

# Using vcpkg
porters vcpkg add boost
porters vc add catch2        # 'vc' is a shortcut for 'vcpkg'

# Using XMake
porters xmake add imgui
porters xm add glfw          # 'xm' is a shortcut for 'xmake'

Install a package globally (shared across all projects):

# Global installation saves disk space
porters conan add --global fmt
porters vcpkg add -g boost   # -g is short for --global

Local vs Global Packages

Local Installation (ports/{manager}/):

  • ✅ Project-specific versions
  • ✅ Reproducible builds
  • ✅ No conflicts between projects
  • ✅ Tracked in version control

Global Installation (~/.porters/packages/{manager}/):

  • ✅ Shared across all projects
  • ✅ Saves disk space
  • ✅ Faster project setup
  • ✅ Perfect for common libraries

Example Workflow:

# One-time: Install common libraries globally
porters co add --global fmt
porters co add --global spdlog
porters vc add --global catch2

# In each new project, they're available immediately!
cd my-new-project
porters list --global       # See what's available

Removing Packages

With confirmation (safe):

porters conan remove fmt
# Prompts: ⚠️  Remove fmt from ports/conan? (y/N):

Force remove (no confirmation):

porters conan remove --force fmt
porters vc remove -f boost   # -f is short for --force

Using Command Aliases

All package manager commands support shortcuts:

# Full commands
porters conan add fmt
porters vcpkg add boost
porters xmake add imgui

# Shortcuts (faster!)
porters co add fmt
porters vc add boost
porters xm add imgui

Directory Structure with Package Managers

my-project/
├── porters.toml
├── .porters/              # Generated files (GITIGNORED)
│   └── cache/            # Build cache, temp files
├── ports/                # Local packages
│   ├── conan/           # Conan local packages
│   │   └── conanfile.txt
│   ├── vcpkg/           # vcpkg local packages
│   │   └── vcpkg.json
│   └── xmake/           # XMake local packages
│       └── xmake.lua
└── build/                # Final build output

~/.porters/               # Global directory
└── packages/            # Global package installations
    ├── conan/
    ├── vcpkg/
    └── xmake/

For complete details, see the Package Managers Guide.

What's Next?

Dependency Management

Porters provides flexible dependency management for C/C++ projects with support for multiple sources and isolation strategies.

Adding Dependencies

Basic Usage

Add a dependency from a Git repository:

porters add fmt --git https://github.com/fmtlib/fmt

This adds fmt to your porters.toml:

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }

Adding Dev Dependencies

Development-only dependencies (tests, benchmarks):

porters add catch2 --git https://github.com/catchorg/Catch2 --dev

Adding Optional Dependencies

Optional features:

porters add zlib --git https://github.com/madler/zlib --optional

Global vs Local Dependencies

Porters supports two dependency scopes:

Local Dependencies (Default)

Dependencies added with porters add are installed to the project's ports/ folder:

porters add fmt --git https://github.com/fmtlib/fmt

Location: ./ports/fmt/

Characteristics:

  • Isolated per-project
  • Version-locked via porters.lock
  • Perfect for project-specific needs

Global Dependencies

Install packages globally to ~/.porters/packages/:

porters install fmt --git https://github.com/fmtlib/fmt

Location (Linux/macOS): ~/.porters/packages/fmt/
Location (Windows): C:\Users\<username>\.porters\packages\fmt\

Characteristics:

  • Shared across all projects
  • Faster setup for frequently-used libraries
  • Centralized cache

Git Dependencies

Porters supports both HTTPS and SSH Git URLs:

HTTPS URLs

porters add fmt --git https://github.com/fmtlib/fmt

SSH URLs

porters add fmt --git git@github.com:fmtlib/fmt.git

Specific Branch

porters add fmt --git https://github.com/fmtlib/fmt --branch stable

Specific Tag

porters add fmt --git https://github.com/fmtlib/fmt --tag 10.1.1

In porters.toml:

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "10.1.1" }

Syncing Dependencies

The sync command ensures all dependencies from porters.toml are installed:

porters sync

This will:

  1. Read dependencies from porters.toml
  2. Download missing packages to ports/
  3. Resolve version constraints
  4. Update porters.lock

Include Dev Dependencies

porters sync --dev

Syncs both regular and dev dependencies.

Include Optional Dependencies

porters sync --optional

Syncs both regular and optional dependencies.

Include Everything

porters sync --dev --optional

Lock File

The porters.lock file tracks resolved dependency versions.

Generating/Updating Lock File

porters lock

This updates porters.lock with current installed dependencies.

Lock File Format

version = "1"
updated_at = "2024-01-15T10:30:00Z"

[dependencies.fmt]
name = "fmt"
version = "10.1.1"
source = { Git = { url = "https://github.com/fmtlib/fmt", rev = "a1b2c3d" } }
checksum = "sha256:..."
dependencies = []

Why Use Lock Files?

  • Reproducibility: Same versions across all environments
  • Team Collaboration: Everyone gets identical dependencies
  • CI/CD: Reliable builds in pipelines

Dependency Sources

Porters supports multiple dependency sources:

Git Repository

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }
spdlog = { git = "git@github.com:gabime/spdlog.git", branch = "v1.x" }

Local Path

[dependencies]
mylib = { path = "../mylib" }

Registry (Future)

Support for package registries is planned:

[dependencies]
boost = { registry = "conan", version = "1.80" }

Dependency Resolution

Porters resolves dependencies in this order:

  1. Check porters.lock for existing resolution
  2. Fetch from Git/path source
  3. Verify constraints (version, branch, tag)
  4. Clone to ports/ directory
  5. Update porters.lock

Constraint Validation

Porters validates:

  • Version requirements
  • Branch/tag specifications
  • Dependency conflicts

Best Practices

Version Pinning

Always specify versions or tags for production:

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "10.1.1" }

Lock File in Version Control

Commit porters.lock to ensure reproducible builds:

git add porters.lock
git commit -m "Update dependencies"

Separate Dev Dependencies

Keep development tools separate:

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }

[dev-dependencies]
catch2 = { git = "https://github.com/catchorg/Catch2" }
benchmark = { git = "https://github.com/google/benchmark" }

Use Global Install for Common Libraries

Install frequently-used libraries globally:

porters install boost --git https://github.com/boostorg/boost
porters install gtest --git https://github.com/google/googletest

Then reference them in projects without re-downloading.

Troubleshooting

Dependency Not Found

Ensure the Git URL is correct and accessible:

git clone https://github.com/fmtlib/fmt  # Test manually

Version Conflicts

Check porters.lock and resolve conflicts:

porters lock  # Regenerate lock file

Sync Failures

Clear the cache and retry:

rm -rf ports/
porters sync

Next Steps

Package Managers Integration

Porters provides seamless integration with popular C/C++ package managers, allowing you to manage external dependencies directly from your Porters project.

Supported Package Managers

Porters currently supports three major package managers:

  • Conan - Cross-platform package manager for C/C++
  • vcpkg - Microsoft's C/C++ package manager
  • XMake - Modern build system with built-in package manager

Installation Scopes

All package managers support two installation scopes:

Local Installation (Default)

Local packages are installed in the ports/ directory of your project:

porters conan add fmt
# Installs to: ports/conan/

Local installations are project-specific and won't affect other projects.

Global Installation

Global packages are installed in ~/.porters/packages/ and can be shared across multiple projects:

porters conan add --global fmt
# Installs to: ~/.porters/packages/conan/

Global installations are useful for:

  • Common libraries used across multiple projects
  • Reducing disk space by sharing package installations
  • Faster setup for new projects

Conan Integration

Conan is a widely-used C/C++ package manager.

Prerequisites

Install Conan first:

pip install conan

Basic Usage

Add a package locally:

porters conan add fmt
porters conan add boost --version 1.82.0

Add a package globally:

porters conan add --global fmt
porters conan add --global boost --version 1.82.0

List installed packages:

# List local packages
porters conan list

# List global packages
porters conan list --global

Search for packages:

porters conan search json

Remove a package:

# Remove local package (with confirmation)
porters conan remove fmt

# Remove global package (with confirmation)
porters conan remove --global fmt

# Force removal without confirmation
porters conan remove --force fmt
porters conan remove --global --force fmt

How It Works

Porters creates a conanfile.txt in either:

  • ports/conan/conanfile.txt (local)
  • ~/.porters/packages/conan/conanfile.txt (global)

The conanfile uses:

  • CMakeDeps generator for CMake integration
  • CMakeToolchain generator for cross-compilation support
  • --build=missing to build packages from source if binaries unavailable

vcpkg Integration

vcpkg is Microsoft's cross-platform package manager.

Prerequisites

Install vcpkg first:

# Windows
git clone https://github.com/microsoft/vcpkg
.\vcpkg\bootstrap-vcpkg.bat

# Linux/macOS
git clone https://github.com/microsoft/vcpkg
./vcpkg/bootstrap-vcpkg.sh

# Add to PATH

Basic Usage

Add a package locally:

porters vcpkg add fmt
porters vcpkg add nlohmann-json --version 3.11.2

Add a package globally:

porters vcpkg add --global fmt

List installed packages:

# List local packages
porters vcpkg list

# List global packages
porters vcpkg list --global

Search for packages:

porters vcpkg search json

Remove a package:

# Remove local package (with confirmation)
porters vcpkg remove fmt

# Remove global package without confirmation
porters vcpkg remove --global --force fmt

How It Works

Porters creates a vcpkg.json manifest in either:

  • ports/vcpkg/vcpkg.json (local)
  • ~/.porters/packages/vcpkg/vcpkg.json (global)

The manifest is used with vcpkg install --x-manifest-root for dependency management.

XMake Integration

XMake is a modern build system with a built-in package manager (xrepo).

Prerequisites

Install XMake first:

# Windows (via installer or Scoop)
scoop install xmake

# Linux/macOS
curl -fsSL https://xmake.io/shget.text | bash

Basic Usage

Add a package locally:

porters xmake add fmt
porters xmake add boost 1.82.0

Add a package globally:

porters xmake add --global fmt

List installed packages:

# List local packages
porters xmake list

# List global packages
porters xmake list --global

Search for packages:

porters xmake search json

Remove a package:

# Remove local package (with confirmation)
porters xmake remove fmt

# Remove with force flag
porters xmake remove --force fmt

How It Works

Porters creates an xmake.lua file in either:

  • ports/xmake/xmake.lua (local)
  • ~/.porters/packages/xmake/xmake.lua (global)

The xmake.lua uses:

  • add_requires() to declare package dependencies
  • xrepo install to fetch and install packages
  • add_packages() to link packages to targets

Common Workflows

Starting a New Project with External Dependencies

# Create a new project
porters new myproject --lang cpp

# Add dependencies locally
cd myproject
porters conan add fmt
porters conan add spdlog
porters vcpkg add catch2

# Build the project
porters build

Using Global Packages

# Install common libraries globally once
porters conan add --global fmt
porters conan add --global spdlog
porters vcpkg add --global catch2

# In each new project, reference them in porters.toml
# (Future feature: automatic global package detection)

Removing Dependencies

# Remove with confirmation prompt
porters conan remove old-package

# Remove multiple packages with force (no prompts)
porters conan remove --force package1
porters conan remove --force package2
porters conan remove --force package3

Searching for Packages

# Search across different package managers
porters conan search json
porters vcpkg search json
porters xmake search json

# Compare results and choose the best option

Integration with porters.toml

Package manager installations are complementary to porters.toml dependencies.

Currently, you need to:

  1. Add the package via package manager: porters conan add fmt
  2. Reference it in your porters.toml if needed
  3. Use it in your build system (CMake, XMake, etc.)

Future versions will provide tighter integration between package managers and porters.toml.

Using with Non-Porters Projects

All package manager commands work even without a porters.toml file, making Porters useful as a general package management tool:

# In any C/C++ project (even without porters.toml)
cd my-cmake-project
porters conan add fmt
porters conan add boost

# The packages are installed to ports/conan/
# Update your CMakeLists.txt to use them

Best Practices

When to Use Local vs Global

Use Local Installation when:

  • Different projects need different versions of the same library
  • Project has specific configuration requirements
  • You want complete isolation between projects
  • Working in a team with shared build environments

Use Global Installation when:

  • Using the same library version across many projects
  • Want to save disk space and installation time
  • Working on personal projects with consistent dependencies
  • Library is stable and unlikely to have version conflicts

Choosing a Package Manager

Use Conan if:

  • You need the largest package ecosystem
  • Working on cross-platform projects
  • Need advanced features like custom profiles
  • Building for embedded systems or cross-compilation

Use vcpkg if:

  • Working primarily on Windows
  • Prefer Microsoft ecosystem integration
  • Need CMake integration
  • Want simple, straightforward manifest-based management

Use XMake if:

  • Using XMake as your build system
  • Want unified build + package management
  • Need fast package installation
  • Prefer Lua-based configuration

Package Manager Compatibility

You can use multiple package managers in the same project:

porters conan add fmt      # Use Conan for fmt
porters vcpkg add catch2   # Use vcpkg for Catch2
porters xmake add imgui    # Use XMake for imgui

Each package manager maintains its own directory under ports/.

Troubleshooting

Package Manager Not Found

If you get "not installed" errors:

  1. Verify the tool is installed: conan --version, vcpkg version, xmake --version
  2. Ensure the tool is in your PATH
  3. Restart your terminal after installation

Installation Fails

If package installation fails:

  1. Check network connectivity
  2. Try with --verbose flag (future feature)
  3. Check package manager logs in ports/{manager}/
  4. Verify the package name is correct using search: porters conan search package-name

Global Packages Not Found

If global packages aren't accessible:

  1. Verify installation: porters conan list --global
  2. Check ~/.porters/packages/ directory exists
  3. Ensure proper permissions on global directory

Version Conflicts

If you encounter version conflicts:

  1. Use local installations for conflicting packages
  2. Create separate global environments (future feature)
  3. Specify exact versions when adding packages

Advanced Topics

Custom Package Repositories (Future)

Future versions will support custom package repositories:

# Add custom Conan remote
porters conan remote add mycompany https://conan.mycompany.com

# Add private vcpkg registry
porters vcpkg registry add mycompany https://vcpkg.mycompany.com

Environment-Specific Packages (Future)

Future versions will support environment-specific package installations:

# porters.toml
[dependencies]
fmt = { version = "10.0", package-manager = "conan" }

[dev-dependencies]
catch2 = { version = "3.0", package-manager = "vcpkg" }

Package Locking (Future)

Future versions will include a lock file for reproducible builds:

# Generate lock file
porters lock generate

# Install from lock file
porters install --locked

See Also

Package Registry

Porters includes its own package registry system for discovering and installing C/C++ libraries. The registry is a curated collection of package definitions stored as JSON files.

📦 What is the Registry?

The Porters registry is a community-driven collection of package definitions for popular C/C++ libraries. Each package includes:

  • Repository URL - Where to find the source code
  • Version Information - Latest stable version
  • Dependencies - Required packages with version constraints
  • Build System - How to build the package (CMake, Meson, XMake, etc.)
  • Platform Support - Which operating systems are supported
  • Constraints - Compiler requirements, C++ standard, architecture, etc.

🌐 Registry Synchronization

The Porters registry is hosted on GitHub at muhammad-fiaz/porters and is automatically synchronized to your local machine.

How Registry Sync Works

  1. First Use: On first registry operation, Porters clones the registry from GitHub
  2. Local Index: Registry is stored in ~/.porters/registry-index/
  3. Fast Searches: All searches use the local index (no network needed)
  4. Auto-Update: Registry can auto-update based on configuration

Registry Locations

LocationPurposePath
RemoteOfficial registryhttps://github.com/muhammad-fiaz/porters
Local IndexCached registry~/.porters/registry-index/

Update Registry

Manually update the local registry index from GitHub:

porters registry update

This fetches the latest package definitions from the remote repository.

Auto-Update Configuration

Configure automatic registry updates in ~/.porters/config.toml:

[registry]
url = "https://github.com/muhammad-fiaz/porters"
auto_update = true  # Update registry automatically
index_path = "~/.porters/registry-index"

Auto-update behavior:

  • Checks for updates on first use each day
  • Updates in background (non-blocking)
  • Falls back to cached index if update fails

Offline Mode

The registry works offline using the local index:

# Search offline
porters search --offline fmt

# Get package info offline
porters info --offline spdlog

When offline:

  • ✅ Uses local registry index
  • ❌ No remote updates
  • ⚠️ Package info may be outdated

Tip: Run porters registry update periodically to keep the local index fresh.

Registry Structure

The local registry index is organized as:

~/.porters/registry-index/
└── packages/
    ├── fmt.json
    ├── spdlog.json
    ├── catch2.json
    ├── boost.json
    └── ...

Each JSON file contains the package definition with metadata, dependencies, and build instructions.

🚀 Using the Registry

Search for Packages

Search the registry by name, description, or tags:

porters registry search <query>
porters search <query>  # Shorter alias

Examples:

# Search for networking libraries
porters search networking

# Search for a specific library
porters search catch2

# Search by tag
porters search testing

Install from Registry

Add a package from the registry to your project:

porters registry add <package-name>
porters add <package-name>  # Will check registry if not in package managers

Examples:

# Install from registry
porters registry add catch2

# Install with automatic fallback
porters add asio  # Checks Conan, vcpkg, XMake, then Registry

List All Packages

View all available packages in the registry:

porters registry list

Package Information

View detailed information about a package:

porters info <package-name>

📖 Package Definition Format

Packages are defined in JSON files following this schema:

{
  "name": "package-name",
  "description": "Short description (max 120 characters)",
  "repository": "https://github.com/owner/repo",
  "version": "1.2.3",
  "license": "MIT",
  "build_system": "cmake",
  
  "dependencies": {
    "fmt": "^9.0.0",
    "spdlog": "^1.10.0"
  },
  
  "dev_dependencies": {
    "catch2": "^3.0.0"
  },
  
  "options": {
    "shared": false,
    "tests": true,
    "examples": false
  },
  
  "install": {
    "cmake": {
      "find_package": "PackageName",
      "targets": ["PackageName::PackageName"],
      "components": ["Core", "Extras"]
    }
  },
  
  "tags": ["networking", "async", "modern-cpp"],
  "homepage": "https://example.com",
  "documentation": "https://docs.example.com",
  "platforms": ["linux", "windows", "macos"],
  
  "constraints": {
    "min_cpp_standard": "17",
    "max_cpp_standard": "23",
    "compilers": {
      "gcc": ">=9.0",
      "clang": ">=10.0",
      "msvc": ">=19.20"
    },
    "arch": ["x86_64", "arm64"],
    "environment": {
      "SOME_VAR": "required_value"
    }
  },
  
  "features": {
    "ssl": {
      "description": "Enable SSL support",
      "default": false,
      "dependencies": {
        "openssl": "^3.0.0"
      }
    }
  }
}

Required Fields

  • name - Package name (lowercase, hyphenated, e.g., awesome-lib)
  • description - Short description (10-120 characters)
  • repository - Git repository URL (must be https://)
  • version - Latest stable version (SemVer format, e.g., 1.2.3)
  • license - License identifier (SPDX format, e.g., MIT, Apache-2.0)
  • build_system - One of: cmake, meson, xmake, autotools, bazel, custom

Optional Fields

  • dependencies - Runtime dependencies (map of name → version requirement)
  • dev_dependencies - Development-only dependencies
  • options - Build options (shared libraries, tests, examples, etc.)
  • install - Installation metadata for build systems
  • tags - Searchable tags for categorization
  • homepage - Project homepage URL
  • documentation - Documentation URL
  • platforms - Supported platforms (default: all)
  • constraints - Build constraints and requirements
  • features - Optional features with their own dependencies

🔄 Dependency Resolution

Porters automatically resolves nested dependencies with:

Version Constraints

Use semantic versioning constraints:

{
  "dependencies": {
    "fmt": "^9.0.0",      // Compatible with 9.x.x
    "spdlog": "~1.10.0",  // Compatible with 1.10.x
    "boost": ">=1.75.0",  // At least 1.75.0
    "zlib": "1.2.11"      // Exact version
  }
}

Constraint Syntax:

  • ^1.2.3 - Compatible (1.2.3 ≤ version < 2.0.0)
  • ~1.2.3 - Approximately (1.2.3 ≤ version < 1.3.0)
  • >=1.2.3 - Greater than or equal
  • >1.2.3 - Greater than
  • <=1.2.3 - Less than or equal
  • <1.2.3 - Less than
  • 1.2.3 - Exact version

Nested Dependencies

Porters resolves dependencies recursively:

your-project
├── awesome-lib ^1.0.0
│   ├── fmt ^9.0.0
│   └── spdlog ^1.10.0
│       └── fmt ^8.0.0  ← Conflict detected!

Conflict Resolution:

  • Porters detects version conflicts
  • Shows all conflicting requirements
  • Fails with clear error messages
  • Suggests resolution strategies

Platform Constraints

Packages can specify platform requirements:

{
  "platforms": ["linux", "windows", "macos"],
  "constraints": {
    "arch": ["x86_64", "arm64"]
  }
}

If a platform constraint fails, installation is prevented with a clear error message.

Compiler Requirements

Packages can specify compiler versions:

{
  "constraints": {
    "min_cpp_standard": "17",
    "compilers": {
      "gcc": ">=9.0",
      "clang": ">=10.0",
      "msvc": ">=19.20"
    }
  }
}

Porters validates constraints before installation and warns about incompatibilities.

Environment Variables

Packages can require specific environment variables:

{
  "constraints": {
    "environment": {
      "JAVA_HOME": "/usr/lib/jvm/java-11",
      "PYTHON_VERSION": "3.9"
    }
  }
}

Missing or mismatched environment variables cause installation to fail.

🤝 Contributing Packages

Want to add your library to the registry? Here's how:

1. Create Package Definition

Create a JSON file in the appropriate category:

registry/
├── networking/       # Network libraries
├── graphics/         # Graphics & rendering
├── testing/          # Testing frameworks
├── serialization/    # JSON, XML, etc.
├── compression/      # Compression libraries
├── crypto/           # Cryptography
├── databases/        # Database clients
├── gui/              # GUI frameworks
├── audio/            # Audio processing
└── utilities/        # General utilities

2. Validate Schema

Ensure your package follows the schema in registry/schema.json:

# JSON schema validator
jsonschema -i registry/your-category/your-package.json registry/schema.json

3. Test Package

Test that your package can be found and installed:

# Search for your package
porters search your-package

# Install and build
porters add your-package
porters build

4. Submit Pull Request

  1. Fork the Porters repository
  2. Add your package JSON file to registry/category/
  3. Create categories if needed (e.g., registry/ml/ for machine learning)
  4. Submit a Pull Request with:
    • Package JSON file
    • Brief description of the library
    • Build and test instructions
    • Special installation notes (if any)

📂 Registry Structure

The registry is organized into categories:

registry/
├── README.md              # Registry documentation
├── schema.json            # JSON schema for validation
├── audio/                 # Audio processing
│   └── portaudio.json
├── compression/           # Compression
│   ├── zlib.json
│   └── bzip2.json
├── crypto/                # Cryptography
│   ├── openssl.json
│   └── libsodium.json
├── databases/             # Databases
│   ├── sqlite.json
│   └── postgresql.json
├── graphics/              # Graphics
│   ├── sdl2.json
│   └── glfw.json
├── gui/                   # GUI frameworks
│   ├── qt.json
│   └── wxwidgets.json
├── networking/            # Networking
│   ├── asio.json
│   └── curl.json
├── serialization/         # Serialization
│   ├── json.json
│   └── protobuf.json
├── testing/               # Testing
│   ├── catch2.json
│   └── gtest.json
└── utilities/             # Utilities
    ├── fmt.json
    └── spdlog.json

🎯 Best Practices

Package Naming

  • Use lowercase letters
  • Separate words with hyphens: awesome-lib
  • Match the upstream project name when possible
  • Keep it short and descriptive

Descriptions

  • Maximum 120 characters
  • Focus on what the library does
  • Include key features or use cases
  • Example: "Modern C++ networking library with async I/O and coroutines"

Version Requirements

  • Use semantic versioning
  • Prefer compatible (^) or approximate (~) constraints
  • Avoid exact versions unless absolutely necessary
  • Test with multiple dependency versions

Tags

  • Use lowercase
  • Separate words with hyphens
  • Include relevant categories: networking, async, header-only
  • Add use cases: gamedev, web, embedded
  • Include C++ features: modern-cpp, cpp17, coroutines

Dependencies

  • Only list runtime dependencies in dependencies
  • Put testing/benchmarking deps in dev_dependencies
  • Always specify version constraints
  • Test dependency resolution

Build Options

  • Document all available options
  • Provide sensible defaults
  • Explain what each option does
  • Consider platform-specific defaults

🔍 How It Works

Registry Discovery

When you run porters add <package>, Porters searches in order:

  1. Conan - Check if available in Conan Center
  2. vcpkg - Check if available in vcpkg registry
  3. XMake - Check if available in XMake repo
  4. Porters Registry - Search local registry JSON files

Dependency Resolution Process

  1. Parse root dependencies from your porters.toml
  2. Fetch package metadata from registry
  3. Validate version constraints (SemVer)
  4. Check platform requirements (OS, architecture)
  5. Verify compiler constraints (version, C++ standard)
  6. Resolve nested dependencies recursively
  7. Detect circular dependencies using graph analysis
  8. Detect version conflicts across dependency tree
  9. Topological sort for installation order
  10. Install packages from source or cache

Caching

All dependencies are cached globally:

~/.porters/
└── packages/
    ├── conan/
    │   └── fmt/
    │       └── 9.1.0/
    ├── vcpkg/
    │   └── spdlog/
    │       └── 1.11.0/
    ├── xmake/
    │   └── boost/
    │       └── 1.80.0/
    └── registry/
        └── catch2/
            └── 3.5.0/

Packages are shared across all projects, avoiding redundant downloads.

📊 Examples

Example: Adding Catch2

# Search for Catch2
$ porters search catch2
📦 catch2
   Modern, C++-native, header-only test framework
   Version: 3.5.0
   License: BSL-1.0
   Build System: cmake
   Repository: https://github.com/catchorg/Catch2
   Tags: testing, unit-testing, tdd, bdd

# Add to project
$ porters add catch2
✅ Added catch2 ^3.5.0 to dependencies
📦 Resolving dependencies...
   catch2 ^3.5.0
   └── (no dependencies)
✅ All dependencies resolved (1 package)

# Build and test
$ porters build
$ porters test

Example: Nested Dependencies

# Add awesome-lib which depends on fmt and spdlog
$ porters add awesome-lib
📦 Resolving dependencies...
   awesome-lib ^1.0.0
   ├── fmt ^9.0.0
   └── spdlog ^1.10.0
       └── fmt ^8.0.0  ← Different version required

❌ Dependency Conflicts Detected:
  Package: fmt
  Requested versions:
    - awesome-lib requires ^9.0.0
    - spdlog requires ^8.0.0

💡 Suggestion: Update spdlog to a version compatible with fmt ^9.0.0

Example: Platform Constraints

# Try to install Windows-only library on Linux
$ porters add windows-only-lib
❌ Error: Package windows-only-lib is not available for platform 'linux'
   Supported platforms: windows

# Architecture warning
$ porters add x86-only-lib
⚠️  Warning: Package x86-only-lib may not support architecture 'x86_64'
   Supported architectures: x86

🆘 Troubleshooting

Package Not Found

$ porters add nonexistent
❌ Error: Package 'nonexistent' not found in registry
💡 Try: porters search nonexistent

Solution: Check spelling, search the registry, or add the package to the registry.

Version Conflicts

❌ Dependency Conflicts Detected:
  Package: fmt
  Requested versions:
    - lib-a requires ^9.0.0
    - lib-b requires ^8.0.0

Solutions:

  1. Update one library to a compatible version
  2. Use features to make dependencies optional
  3. Fork and update dependency versions
  4. Report conflict to library maintainers

Platform Incompatibility

❌ Error: Package only-linux is not available for platform 'windows'

Solution: Check platforms field, use alternatives, or contribute Windows support.

Compiler Requirements

⚠️  Warning: Package requires GCC >=11.0, you have 9.4.0

Solutions:

  1. Upgrade your compiler
  2. Use a different package version
  3. Check if constraint can be relaxed

📚 Reference

  • Registry Path: registry/ in Porters repository
  • Schema: registry/schema.json
  • GitHub: https://github.com/muhammad-fiaz/Porters
  • Documentation: https://muhammad-fiaz.github.io/Porters/
  • Issues: https://github.com/muhammad-fiaz/Porters/issues

Caching

Porters provides a powerful caching system to speed up dependency resolution and avoid redundant downloads. The caching system operates at two levels: global cache and local cache.

📦 Global Cache

The global cache is a centralized cache stored in ~/.porters/cache/ that is shared across all Porters projects on your system. This means that once a dependency is downloaded for any project, it can be reused by all other projects without re-downloading.

Benefits

  • Faster builds: Dependencies are downloaded only once
  • Reduced bandwidth: No redundant downloads
  • Offline support: Work without internet using cached dependencies
  • Disk space efficiency: Shared storage across projects

Cache Location

PlatformCache Directory
Linux/macOS~/.porters/cache/
WindowsC:\Users\<YourName>\.porters\cache\

Cache Structure

~/.porters/cache/
├── package_name/
│   ├── 1.0.0/
│   │   ├── include/
│   │   ├── src/
│   │   └── .checksum
│   ├── 1.1.0/
│   │   ├── include/
│   │   ├── src/
│   │   └── .checksum
│   └── 2.0.0/
│       ├── include/
│       ├── src/
│       └── .checksum
└── another_package/
    └── 3.0.0/
        ├── lib/
        └── .checksum

Each package version is stored in its own directory with a checksum file for integrity verification.

🗂️ Local Cache

The local cache is project-specific and stored in .porters/cache/ within your project directory. It contains:

  • Downloaded dependency source code
  • Build artifacts
  • Temporary files from dependency resolution

Local Cache Structure

.porters/
└── cache/
    ├── sources/           # Git clones and downloaded sources
    │   ├── lib_a-abc123/
    │   └── lib_b-def456/
    └── artifacts/         # Compiled binaries and libraries
        ├── lib_a.a
        └── lib_b.so

🔄 Cache Workflow

When resolving dependencies, Porters follows this workflow:

  1. Check Global Cache: First, check if the dependency exists in ~/.porters/cache/
  2. Use Cached Version: If found, copy from global cache to project (fast)
  3. Download: If not cached, download the dependency from source
  4. Store in Global Cache: After download, store in global cache for future use
  5. Local Project Use: Make dependency available to the current project
graph TD
    A[Resolve Dependency] --> B{In Global Cache?}
    B -->|Yes| C[Copy from Global Cache]
    B -->|No| D{Offline Mode?}
    D -->|Yes| E[Error: Not in Cache]
    D -->|No| F[Download from Source]
    F --> G[Store in Global Cache]
    G --> C
    C --> H[Use in Project]

🎯 Using the Cache

Automatic Caching

Caching is automatic and requires no configuration. When you add a dependency:

porters add https://github.com/user/library

Porters automatically:

  1. Checks global cache for existing version
  2. Downloads if not cached
  3. Stores in global cache
  4. Makes available to your project

Check Cache Statistics

View cache usage and statistics:

porters cache stats

Example Output:

📊 Global Cache Statistics
Location: /home/user/.porters/cache/
Packages: 15
Total Size: 245.3 MB

Recent Packages:
  ✓ fmt @ 10.1.1 (cached 2 days ago)
  ✓ spdlog @ 1.12.0 (cached 1 week ago)
  ✓ nlohmann_json @ 3.11.2 (cached 3 days ago)

List Cached Packages

See all packages in the global cache:

porters cache list

Example Output:

📦 Globally Cached Packages (15)

  fmt
    ├── 9.1.0
    ├── 10.0.0
    └── 10.1.1
  
  spdlog
    ├── 1.11.0
    └── 1.12.0
  
  nlohmann_json
    └── 3.11.2

Clear Cache

Remove all cached packages to free disk space:

# Clear global cache
porters cache clear

# Clear only local project cache
porters cache clear --local

Warning: This will remove all cached dependencies. They will need to be re-downloaded when next used.

Clear Specific Package

Remove a specific package from cache:

porters cache remove <package_name>

# Remove specific version
porters cache remove <package_name> --version 1.0.0

⚙️ Cache Configuration

Configure caching behavior in ~/.porters/config.toml:

[cache]
enabled = true           # Enable/disable global cache
max_size_mb = 2048      # Maximum cache size (MB)
auto_clean = true        # Automatically clean old packages
cache_dir = "~/.porters/cache"  # Custom cache directory (optional)

# Auto-clean settings
[cache.clean]
max_age_days = 90       # Remove packages not used in 90 days
min_free_space_mb = 500 # Keep at least 500MB free

Disable Caching

To disable global caching:

[cache]
enabled = false

With caching disabled, dependencies are downloaded directly to each project without global storage.

Custom Cache Directory

Use a custom cache directory (e.g., on a different drive):

[cache]
cache_dir = "/mnt/data/porters_cache"  # Linux/macOS
# cache_dir = "D:\\PortersCache"       # Windows

🔒 Offline Mode

Work entirely offline using only cached dependencies. See Configuration for details.

When offline mode is enabled:

  • No network requests are made
  • Only cached dependencies can be used
  • Attempting to download uncached dependencies fails with clear error

Enable offline mode:

# In ~/.porters/config.toml
offline = true

# Or in project porters.toml
[project]
offline = true

Use offline mode temporarily:

porters build --offline
porters add <dep> --offline

🛡️ Cache Integrity

Porters ensures cache integrity through:

Checksums

Every cached package has a SHA-256 checksum:

  • Calculated when package is first cached
  • Verified when retrieving from cache
  • Detects corrupted or modified cache entries

Automatic Cleanup

With auto_clean = true, Porters automatically:

  • Removes corrupted packages
  • Deletes incomplete downloads
  • Cleans up old versions to free space
  • Maintains cache health

Manual Verification

Verify cache integrity:

porters cache verify

This checks:

  • All checksums match stored values
  • Directory structures are intact
  • No corrupted files exist

📊 Cache Performance

Space Savings

Typical space savings with global cache:

ProjectsWithout CacheWith CacheSavings
5 projects500 MB150 MB70%
10 projects1.2 GB200 MB83%
20 projects2.5 GB300 MB88%

Time Savings

Typical time savings:

Dependency SizeDownloadFrom CacheSpeedup
Small (< 1 MB)2-5 sec< 1 sec3-5x
Medium (1-10 MB)10-30 sec1-2 sec10-15x
Large (> 10 MB)60+ sec3-5 sec12-20x

🔍 Troubleshooting

Cache Permission Errors

If you encounter permission errors:

# Linux/macOS
chmod -R 755 ~/.porters/cache

# Or change owner
sudo chown -R $USER:$USER ~/.porters/cache

Corrupted Cache

If cache is corrupted:

# Clear and rebuild cache
porters cache clear
porters install  # Reinstall dependencies

Disk Space Issues

If running out of disk space:

# Check cache size
porters cache stats

# Remove old packages
porters cache clean --max-age-days 30

# Or clear entirely
porters cache clear

Cache Not Working

Verify cache is enabled:

# Check configuration
cat ~/.porters/config.toml | grep -A3 "\[cache\]"

# Expected output:
# [cache]
# enabled = true
# max_size_mb = 1024
# auto_clean = true

🎓 Best Practices

1. Regular Maintenance

# Monthly cache cleanup
porters cache clean --max-age-days 60

# Check cache health
porters cache verify

2. Optimize Cache Size

[cache]
max_size_mb = 1024  # Adjust based on available disk space
auto_clean = true   # Enable automatic cleanup

3. Use Offline Mode for CI/CD

# .github/workflows/build.yml
- name: Build with cache
  run: |
    porters cache list
    porters build --offline  # Use only cached deps

4. Share Cache in Teams

For team development, consider:

  • Shared network cache directory
  • Pre-populated cache in Docker images
  • Cache backup/restore scripts

5. Monitor Cache Growth

# Weekly check
porters cache stats

# Set up alerts if cache > 2GB
du -sh ~/.porters/cache

📚 See Also

Building Projects

Learn how to build C/C++ projects with Porters.

Build Systems

Porters natively supports 14 build systems with full build execution and automatically detects which one your project uses.

Automatic Detection

Porters automatically detects build systems by looking for specific files:

Build SystemDetection FilesStatus
CMakeCMakeLists.txt✅ Full Support
XMakexmake.lua✅ Full Support
Mesonmeson.build✅ Full Support
MakeMakefile, makefile, GNUmakefile✅ Full Support
Ninjabuild.ninja✅ Full Support
Autotoolsconfigure, configure.ac✅ Full Support
SConsSConstruct, SConscript✅ Full Support
Conanconanfile.txt, conanfile.py✅ Full Support
vcpkgvcpkg.json✅ Full Support
BazelBUILD, BUILD.bazel, WORKSPACE✅ Full Support
Buck2BUCK, .buckconfig✅ Full Support
Premakepremake5.lua, premake4.lua✅ Full Support
QMake*.pro✅ Full Support
Customporters.toml with [build.custom]✅ Full Support

Manual Configuration

You can explicitly specify the build system in porters.toml:

[build]
system = "cmake"  # or "xmake", "meson", "make", "ninja", etc.

Supported Build Systems

Traditional Build Systems

Make
[build]
system = "make"

Runs: make (respects existing Makefile)

Ninja
[build]
system = "ninja"

Runs: ninja (fast parallel builds)

Autotools
[build]
system = "autotools"

Runs:

./configure
make
make install

Modern Build Systems

[build]
system = "cmake"

Porters runs:

cmake -B build
cmake --build build
XMake
[build]
system = "xmake"

Runs:

xmake config
xmake build
Meson
[build]
system = "meson"

Runs:

meson setup build
meson compile -C build
SCons
[build]
system = "scons"

Runs: scons

Bazel
[build]
system = "bazel"

Runs: bazel build //...

Buck2
[build]
system = "buck2"

Runs: buck2 build //...

Package Managers

Conan
[build]
system = "conan"

Integrates with Conan package manager:

conan install .
cmake --preset conan-default
cmake --build build
vcpkg
[build]
system = "vcpkg"

Integrates with vcpkg:

vcpkg install
cmake -DCMAKE_TOOLCHAIN_FILE=...
cmake --build build

Other Build Systems

Premake
[build]
system = "premake"
QMake
[build]
system = "qmake"
Gradle (C++)
[build]
system = "gradle-cpp"

Custom Build

For projects with unique build requirements:

[build]
system = "custom"

[build.custom]
configure = "./configure.sh"
build = "./build.sh"
install = "./install.sh"
test = "./test.sh"
clean = "./clean.sh"

Compiler Detection

Porters automatically detects available compilers:

  • GCC (gcc, g++)
  • Clang (clang, clang++)
  • MSVC (cl)
  • LLVM (llvm)
  • MinGW (Windows)
  • Emscripten (emcc, em++)
  • Intel C++ Compiler (icc, icpc)

Set preferred compiler:

[build.env]
CC = "clang"
CXX = "clang++"

Build Configuration

Enhanced Build Settings

Configure build flags, include paths, and linking options:

[build]
system = "cmake"

[build.flags]
cflags = ["-Wall", "-Wextra", "-O2"]
cxxflags = ["-std=c++17", "-Wall", "-Wextra"]
ldflags = ["-pthread"]
defines = ["DEBUG", "USE_FEATURE_X"]

[build.include]
include = [
    "include/",
    "src/",
    "/usr/local/include",
]

[build.linking]
libraries = ["pthread", "m", "dl"]
library_paths = ["/usr/local/lib"]
frameworks = ["CoreFoundation"]  # macOS only

Build Scripts

Run custom scripts before/after building:

[build.scripts]
pre-build = "scripts/pre_build.sh"
post-build = "scripts/post_build.sh"
pre-install = "scripts/pre_install.sh"
post-install = "scripts/post_install.sh"

Example pre-build script:

#!/bin/sh
echo "🔧 Running pre-build tasks..."

# Generate version header
echo "#define VERSION \"$(git describe --tags)\"" > src/version.h

# Check dependencies
command -v cmake >/dev/null || {
    echo "❌ CMake not found"
    exit 1
}

echo "✅ Pre-build complete"

Debug vs Release

# Debug build (default)
porters build

# Release build (optimized)
porters build --release

Override in porters.toml:

[build.flags]
cflags = ["-g", "-O0"]          # Debug
# cflags = ["-O3", "-DNDEBUG"]  # Release

Environment Variables

Porters resolves environment variables in build configuration:

[build.env]
CC = "clang"
CXX = "clang++"
CMAKE_PREFIX_PATH = "/usr/local"
PKG_CONFIG_PATH = "/usr/local/lib/pkgconfig"
CFLAGS = "-march=native"
CXXFLAGS = "-std=c++20"

Platform-specific:

[build.env.linux]
CC = "gcc-11"
CXX = "g++-11"

[build.env.macos]
CC = "clang"
CXX = "clang++"

[build.env.windows]
CC = "cl"
CXX = "cl"

Auto Source Discovery

Porters automatically discovers source files:

[build]
auto_discover = true  # Default

Detected file types:

  • .c, .cpp, .cc, .cxx - Source files
  • .h, .hpp, .hh, .hxx - Header files

Exclude patterns:

[build]
exclude = [
    "tests/",
    "examples/",
    "vendor/",
    "**/*.test.cpp",
]

Build Targets

Specify what to build:

[build]
targets = ["all"]  # Default

# Or specific targets
targets = ["main", "library"]

For CMake:

[build]
system = "cmake"
targets = ["MyApp", "MyLib"]

Parallel Builds

Control build parallelism:

[build]
jobs = 4  # Number of parallel jobs

# Or auto-detect CPU cores
jobs = "auto"

Command line:

porters build --jobs 8
porters build -j 8

Cross-Compilation

Configure for cross-compilation:

[build]
system = "cmake"
toolchain = "cmake/arm-toolchain.cmake"

[build.env]
CMAKE_TOOLCHAIN_FILE = "cmake/arm-toolchain.cmake"
TARGET_ARCH = "arm64"

Build Artifacts

Specify output locations:

[build]
output_dir = "dist/"
binary_name = "my-app"

Install artifacts:

porters build --install

With custom prefix:

porters build --install --prefix /usr/local

Next Steps

Porters Execute Guide

100% Automatic Single-File C/C++ Execution - Zero Configuration Required

Overview

porters execute lets you compile and run any C/C++ file instantly - no project setup, no configuration files, no build systems. Just point it at your code and go!

Key Features

  • Works Anywhere - No porters.toml required
  • Auto Compiler Detection - Finds gcc, clang, g++, clang++ automatically
  • All File Extensions - Supports .c, .cpp, .cxx, .cc, .c++, .cp, .C, .CPP
  • Dependency Resolution - Reads porters.toml if present, optional otherwise
  • Automatic Include/Lib Paths - Zero configuration dependency integration
  • One Command Execution - Compiles and runs in a single step
  • External Terminal Support - Open programs in new terminal windows with --external

Basic Usage

Simple Execution

# Execute any C file
porters execute hello.c

# Execute any C++ file
porters execute main.cpp
porters execute app.cxx
porters execute code.cc
porters execute prog.c++

# With command-line arguments
porters execute myprogram.c arg1 arg2 arg3

# Open in external terminal window (new console)
porters execute game.cpp --external
porters execute interactive.c --external arg1 arg2

External Terminal Mode

The --external flag opens your program in a new terminal window instead of running it in the current terminal. This is useful for:

  • Interactive programs that need user input
  • Games or graphical console applications
  • Long-running programs you want to monitor separately
  • Programs with special console requirements (colors, cursor control, etc.)

Platform-Specific Behavior:

Windows:

  • Opens in new cmd.exe window
  • Window stays open after program exits (shows "Press any key to close...")
  • Displays compilation status in original terminal

Linux:

  • Tries terminal emulators in order: gnome-terminal, konsole, xterm
  • Opens in new window with your default terminal emulator
  • Window stays open after program exits

macOS:

  • Opens in new Terminal.app window
  • Uses AppleScript to create new terminal tab/window
  • Window stays open after program exits

Example:

$ porters execute snake_game.cpp --external
➡️  Executing single file: snake_game.cpp
ℹ️  Using compiler: g++
ℹ️  Compiling...
✅  Compilation successful!
➡️  Opening in external terminal...
ℹ️  Program launched in external terminal

# A new terminal window opens and runs your game
# Original terminal returns to prompt immediately

Supported File Extensions

ExtensionLanguageCompiler Used
.c, .CCgcc → clang → cc
.cpp, .CPPC++g++ → clang++ → c++
.cxxC++g++ → clang++ → c++
.ccC++g++ → clang++ → c++
.c++C++g++ → clang++ → c++
.cpC++g++ → clang++ → c++

Note: Header files (.h, .hpp, .hxx) cannot be executed directly - they must be included in a .c or .cpp file.

How It Works

Automatic Compiler Detection

Porters automatically finds your C/C++ compiler:

For C files (.c, .C):

  1. Tries gcc first (most common)
  2. Falls back to clang
  3. Falls back to generic cc

For C++ files (.cpp, .cxx, .cc, .c++, .cp, .CPP):

  1. Tries g++ first (most common)
  2. Falls back to clang++
  3. Falls back to generic c++

No configuration needed - just have a compiler in your PATH!

Dependency Resolution (Automatic)

When you have a porters.toml in your directory, porters execute automatically:

  • Reads all dependencies
  • Finds their include directories
  • Finds their library directories
  • Adds all paths to the compiler command
  • Links all required libraries

You don't need to configure anything!

Examples

Example 1: Hello World (No Project)

Create a file anywhere on your system:

// hello.c
#include <stdio.h>

int main() {
    printf("Hello from Porters!\n");
    return 0;
}

Execute it:

$ porters execute hello.c
Compiling hello.c...
Hello from Porters!

No porters.toml needed! Works instantly.

Example 2: With Arguments

// args.cpp
#include <iostream>

int main(int argc, char** argv) {
    std::cout << "Arguments received: " << argc - 1 << "\n";
    for (int i = 1; i < argc; i++) {
        std::cout << "  [" << i << "] " << argv[i] << "\n";
    }
    return 0;
}

Execute with arguments:

$ porters execute args.cpp foo bar baz
Compiling args.cpp...
Arguments received: 3
  [1] foo
  [2] bar
  [3] baz

Example 3: With Dependencies (Automatic)

Create a project with dependencies:

# porters.toml
[package]
name = "my-app"
version = "0.1.0"

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }

Sync dependencies once:

$ porters sync
Syncing dependencies...
✓ fmt downloaded

Create your code:

// format_test.cpp
#include <fmt/core.h>
#include <fmt/color.h>

int main() {
    fmt::print("Plain text\n");
    fmt::print(fg(fmt::color::green), "Green text\n");
    fmt::print(fg(fmt::color::red) | fmt::emphasis::bold, "Bold red text\n");
    return 0;
}

Execute - dependencies are automatically included:

$ porters execute format_test.cpp
Compiling format_test.cpp...
Plain text
Green text
Bold red text

No configuration needed! Porters found fmt automatically.

Example 4: Math and System Libraries

// math_test.c
#include <stdio.h>
#include <math.h>

int main() {
    double x = 16.0;
    double result = sqrt(x);
    printf("sqrt(%.0f) = %.2f\n", x, result);
    printf("sin(PI/2) = %.2f\n", sin(M_PI / 2.0));
    return 0;
}

Execute (math library automatically linked on Linux):

$ porters execute math_test.c
Compiling math_test.c...
sqrt(16) = 4.00
sin(PI/2) = 1.00

Example 5: Multiple Source Files Pattern

When you need multiple files, use includes:

// utils.h
#ifndef UTILS_H
#define UTILS_H

int add(int a, int b);
int multiply(int a, int b);

#endif
// utils.c
#include "utils.h"

int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}
// main.c
#include <stdio.h>
#include "utils.h"

int main() {
    printf("5 + 3 = %d\n", add(5, 3));
    printf("5 * 3 = %d\n", multiply(5, 3));
    return 0;
}

For multiple .c files, compile them together:

# Compile utils.c into object file
gcc -c utils.c -o utils.o

# Execute main.c and link utils.o
gcc main.c utils.o -o main && ./main

Or use porters build for multi-file projects:

$ porters init     # Create project
$ porters build    # Build all sources
$ porters run      # Execute compiled binary

Note: porters execute is for single-file quick execution. For multi-file projects, use porters build + porters run.

Optional Configuration

When is Configuration Needed?

99% of the time: NEVER!

porters execute works automatically. You only need the [run] section if:

  • Using a non-standard compiler not in PATH
  • Need custom compiler/linker flags
  • Have include paths outside of dependency directories
  • Want to exclude specific patterns from dependency resolution

Optional [run] Section

# porters.toml - ALL FIELDS ARE OPTIONAL!

[run]
# Custom C compiler (default: auto-detect gcc/clang/cc)
c-compiler = "clang"

# Custom C++ compiler (default: auto-detect g++/clang++/c++)
cpp-compiler = "clang++"

# Additional include directories (default: auto-detect from deps)
include-dirs = ["./custom/include", "/usr/local/custom/include"]

# Additional library directories (default: auto-detect from deps)
library-dirs = ["./custom/lib"]

# Patterns to exclude from automatic include/lib detection (default: empty)
exclude-patterns = ["**/tests/**", "**/examples/**"]

# Custom compiler flags (default: none)
compiler-flags = ["-Wall", "-Wextra", "-O2", "-std=c++20"]

# Custom linker flags (default: none)
linker-flags = ["-lpthread", "-ldl"]

Example - Custom Compiler:

[run]
cpp-compiler = "clang++"
compiler-flags = ["-std=c++20", "-Wall"]
$ porters execute modern.cpp
# Uses clang++ with C++20 and warnings enabled

Example - Custom Include Path:

[run]
include-dirs = ["/opt/mylib/include"]
$ porters execute app.c
# Automatically adds -I/opt/mylib/include

Differences from porters run

Featureporters executeporters run
PurposeSingle-file quick executionRun compiled project binary
Requires ProjectNoYes (porters.toml)
CompilationCompiles on-the-flyUses pre-built binary
Build SystemNone (direct compiler)CMake/XMake/Meson/Make/etc.
Multi-file SupportSingle file onlyFull project
SpeedCompiles each timeFast (already built)
Best ForPrototyping, testing, scriptsProduction applications

When to use porters execute:

  • Quick testing/prototyping
  • Single-file programs
  • Code demonstrations
  • Scripting with C/C++
  • Learning/teaching

When to use porters build + porters run:

  • Multi-file projects
  • Production applications
  • Complex build configurations
  • Performance-critical builds
  • Cross-compilation

Command Reference

Syntax

porters execute <file> [args...]

Parameters

  • <file> - Path to C/C++ source file (required)
    • Supported extensions: .c, .cpp, .cxx, .cc, .c++, .cp, .C, .CPP
    • Can be relative or absolute path
  • [args...] - Arguments to pass to the compiled program (optional)

Examples

# Basic execution
porters execute hello.c

# With relative path
porters execute ./src/main.cpp

# With absolute path
porters execute /home/user/code/test.c

# With arguments
porters execute myprogram.c input.txt --verbose --count=5

# Different C++ extensions
porters execute app.cpp
porters execute app.cxx
porters execute app.cc
porters execute app.c++

Troubleshooting

"Compiler not found"

Solution: Install gcc/g++ or clang/clang++

# Linux
sudo apt install build-essential

# macOS
xcode-select --install

# Windows
# Install MinGW or Visual Studio with C++ tools

"Unsupported file extension"

Solution: Use a supported extension

# Supported
porters execute main.c      # ✅
porters execute main.cpp    # ✅
porters execute main.cxx    # ✅

# Not supported
porters execute main.h      # ❌ Header files
porters execute main.txt    # ❌ Not C/C++

"Compilation failed"

Solution: Check compiler error messages

# Add warnings for better diagnostics
[run]
compiler-flags = ["-Wall", "-Wextra"]

"Missing includes"

Solution: Ensure dependencies are synced

# If using porters.toml with dependencies
porters sync

# Then execute
porters execute main.cpp

Performance Tips

Cache Compilation Results

Porters caches compiled binaries in ~/.porters/cache/ (Linux/macOS) or %USERPROFILE%\.porters\cache\ (Windows).

The cache is automatically managed - binaries are recompiled when:

  • Source file changes
  • Dependencies change
  • Compiler flags change

For Repeated Execution

If you're running the same file many times:

Option 1: Keep using execute (cached after first run)

$ porters execute test.c  # Compiles
$ porters execute test.c  # Uses cache (fast!)

Option 2: Use a project (fastest for repeated use)

$ porters init
$ porters build
$ porters run    # Very fast!
$ porters run    # Very fast!

Advanced Usage

Custom Compiler Flags

[run]
compiler-flags = [
    "-std=c++20",      # Use C++20 standard
    "-O3",             # Maximum optimization
    "-march=native",   # CPU-specific optimizations
    "-Wall",           # All warnings
    "-Wextra",         # Extra warnings
    "-Werror",         # Treat warnings as errors
]

Cross-Compilation

For cross-compilation, use porters build instead:

# Build for different platforms
porters build --linux
porters build --windows
porters build --macos
porters build --all-platforms

porters execute uses your local compiler for the current platform only.

Integration with Scripts

Use porters execute in shell scripts:

#!/bin/bash
# compile_and_test.sh

# Execute C program
if porters execute test.c --self-test; then
    echo "✓ Tests passed"
else
    echo "✗ Tests failed"
    exit 1
fi

Summary

porters execute is the fastest way to run C/C++ code:

  1. No configuration - Works immediately
  2. No project setup - Execute files anywhere
  3. Automatic dependencies - Reads porters.toml if present
  4. All C/C++ extensions - .c, .cpp, .cxx, .cc, .c++, .cp
  5. Smart compiler detection - Finds gcc, g++, clang, clang++
  6. One command - Compiles and runs instantly

Perfect for:

  • Quick prototyping
  • Code testing
  • Single-file programs
  • Learning C/C++
  • Code demonstrations

For larger projects, use:

  • porters build - Compile entire project
  • porters run - Execute compiled binary

Publishing

Publish your C/C++ projects to GitHub releases.

Prerequisites

  1. GitHub repository configured in porters.toml:
[project]
repository = "https://github.com/username/project"
  1. GitHub Personal Access Token:

Publishing a Release

porters publish --version 1.0.0

With token:

porters publish --version 1.0.0 --token ghp_xxxxx

Or set environment variable:

export GITHUB_TOKEN=ghp_xxxxx
porters publish --version 1.0.0

What Gets Published

  1. Creates Git tag (e.g., v1.0.0)
  2. Builds release binaries
  3. Creates GitHub release
  4. Uploads artifacts:
    • Source tarball
    • Platform-specific binaries
    • porters.toml

Version Management

Semantic Versioning

Follow SemVer:

  • Major (1.0.0): Breaking changes
  • Minor (0.1.0): New features, backward-compatible
  • Patch (0.0.1): Bug fixes

Updating Version

Update in porters.toml:

[project]
version = "1.2.3"

Next Steps

Extension System

Porters features a powerful extension system that allows you to create custom functionality and share it with others.

What are Extensions?

Extensions are plugins that can:

  • Hook into build lifecycle events (pre-build, post-build, pre-install, post-install)
  • Add custom commands to Porters
  • Extend functionality for specific build systems
  • Automate project-specific tasks

Installing Extensions

Important: Extensions are user-installed and managed locally. Porters does not automatically download extensions from crates.io or any package registry. You install extensions manually and Porters loads them from your local installation.

Install extensions to ~/.porters/extensions/<extension-name>/:

# 1. Create extensions directory if it doesn't exist
mkdir -p ~/.porters/extensions

# 2. Clone extension repository
cd ~/.porters/extensions
git clone https://github.com/user/porters-ext-myext my-extension

# 3. Porters automatically loads all extensions from this directory

From Git (via Porters)

Porters can help you clone extensions:

porters extension install my-extension --git https://github.com/user/porters-ext-myext

This clones the repository to ~/.porters/extensions/my-extension/.

From Local Path

Link a local extension directory:

porters extension install my-extension --path ./path/to/extension

This creates a copy or symlink in ~/.porters/extensions/.

Auto-Load from Configuration

List extensions in porters.toml to ensure they're loaded when Porters runs:

# Extensions to auto-load (must be installed in ~/.porters/extensions/)
extensions = [
    "my-extension",
    "another-extension",
    "formatter"
]

Note: This does NOT install extensions. It only tells Porters which installed extensions to load. You must install extensions manually first.

Extension Installation Workflow

Typical workflow for using an extension:

  1. Find Extension: Browse GitHub, documentation, or community recommendations
  2. Install Manually: Clone to ~/.porters/extensions/<name>/
  3. Configure (Optional): Add to extensions array in porters.toml
  4. Use: Extension hooks and commands are now available

Example:

# 1. Install manually
cd ~/.porters/extensions
git clone https://github.com/user/porters-formatter formatter

# 2. Add to porters.toml (optional)
echo 'extensions = ["formatter"]' >> porters.toml

# 3. Use extension commands
porters format

Creating Extensions

Generate Extension Template

porters extension create my-awesome-extension

This creates a new extension directory with the following structure:

my-awesome-extension/
├── extension.toml      # Extension manifest
├── README.md          # Documentation
└── hooks/             # Hook scripts
    └── example.sh

Extension Manifest (extension.toml)

[package]
name = "my-awesome-extension"
version = "0.1.0"
description = "My awesome Porters extension"
authors = ["Your Name <you@example.com>"]
license = "MIT"
repository = "https://github.com/user/porters-ext-myext"
homepage = "https://example.com"

[hooks]
pre-build = "hooks/pre_build.sh"
post-build = "hooks/post_build.sh"
pre-install = "hooks/pre_install.sh"
post-install = "hooks/post_install.sh"

[[commands]]
name = "custom-command"
description = "My custom command"
script = "scripts/custom.sh"

Extension Hooks

Extensions can hook into various lifecycle events:

Build Hooks

[hooks]
pre-build = "hooks/pre_build.sh"   # Run before building
post-build = "hooks/post_build.sh" # Run after building

Example pre-build hook (hooks/pre_build.sh):

#!/bin/sh
echo "🔧 Running pre-build checks..."

# Check for required tools
command -v cmake >/dev/null 2>&1 || {
    echo "❌ CMake is required but not installed"
    exit 1
}

# Generate build files
cmake -B build -S .

echo "✅ Pre-build complete"

Install Hooks

[hooks]
pre-install = "hooks/pre_install.sh"   # Run before installing dependencies
post-install = "hooks/post_install.sh" # Run after installing dependencies

Example post-install hook:

#!/bin/sh
echo "📦 Post-install: Setting up environment..."

# Copy configuration files
cp config.template.toml config.toml

# Set permissions
chmod +x scripts/*.sh

echo "✅ Post-install complete"

Custom Commands

Extensions can add new commands to Porters:

[[commands]]
name = "lint"
description = "Run code linting"
script = "scripts/lint.sh"

[[commands]]
name = "format"
description = "Format code"
script = "scripts/format.sh"

Usage:

porters lint     # Runs the extension's lint command
porters format   # Runs the extension's format command

Extension Examples

Example 1: Code Formatter Extension

# extension.toml
name = "porters-format"
version = "0.1.0"
description = "Code formatting extension for C/C++"

[[commands]]
name = "format"
description = "Format C/C++ code with clang-format"
script = "scripts/format.sh"
# scripts/format.sh
#!/bin/sh
find src -name "*.c" -o -name "*.cpp" -o -name "*.h" | xargs clang-format -i
echo "✅ Code formatted!"

Example 2: Documentation Generator

name = "porters-docs"
version = "0.1.0"
description = "Generate documentation with Doxygen"

[hooks]
post-build = "hooks/generate_docs.sh"

[[commands]]
name = "docs"
description = "Generate documentation"
script = "scripts/docs.sh"

Example 3: Testing Extension

name = "porters-test"
version = "0.1.0"
description = "Enhanced testing utilities"

[[commands]]
name = "test-all"
description = "Run all tests with coverage"
script = "scripts/test_coverage.sh"

[[commands]]
name = "test-watch"
description = "Watch for changes and re-run tests"
script = "scripts/test_watch.sh"

Publishing Extensions

1. Prepare for Publishing

Ensure your extension has:

  • A complete extension.toml manifest
  • A README.md with usage instructions
  • Proper license file
  • Working hooks/scripts

2. Create a Rust Crate

Since extensions can be published to crates.io, create a minimal Rust wrapper:

cd my-extension
cargo init --lib

Edit Cargo.toml:

[package]
name = "porters-ext-myext"
version = "0.1.0"
description = "My Porters extension"
authors = ["Your Name <you@example.com>"]
license = "MIT"
repository = "https://github.com/user/porters-ext-myext"

[package.metadata.porters]
extension = true

Add extension files to the package:

[package]
include = [
    "extension.toml",
    "hooks/**/*",
    "scripts/**/*",
    "README.md",
]

3. Publish to crates.io

cargo publish

4. Users Can Now Install

porters extension install porters-ext-myext

Managing Extensions

List Installed Extensions

porters extension list

Output:

📦 Installed Extensions:

  porters-format v1.0.0
    Code formatting extension for C/C++
    Repository: https://github.com/user/porters-format

  porters-docs v0.5.0
    Generate documentation with Doxygen
    Repository: https://github.com/user/porters-docs

Uninstall Extension

porters extension uninstall my-extension

Extension Best Practices

1. Error Handling

Always provide clear error messages:

#!/bin/sh
if ! command -v tool >/dev/null 2>&1; then
    echo "❌ ERROR: 'tool' is required but not installed"
    echo "   Install with: apt install tool"
    exit 1
fi

2. Cross-Platform Support

Use POSIX-compliant shell scripts or provide platform-specific scripts:

[hooks.linux]
pre-build = "hooks/linux/pre_build.sh"

[hooks.windows]
pre-build = "hooks/windows/pre_build.bat"

[hooks.macos]
pre-build = "hooks/macos/pre_build.sh"

3. Environment Variables

Extensions have access to project context:

#!/bin/sh
# Available environment variables:
# PORTERS_PROJECT_DIR - Project root directory
# PORTERS_BUILD_DIR - Build directory
# PORTERS_PROJECT_NAME - Project name from porters.toml

echo "Building ${PORTERS_PROJECT_NAME}..."

4. Dependency Management

Document required tools in your README:

## Requirements

- clang-format >= 10.0
- cmake >= 3.15
- python3

## Installation

Ubuntu/Debian:
apt install clang-format cmake python3

macOS:
brew install clang-format cmake python3

5. Versioning

Follow semantic versioning:

  • MAJOR: Breaking changes
  • MINOR: New features (backward compatible)
  • PATCH: Bug fixes

Advanced Features

Extension Configuration

Extensions can have their own configuration in porters.toml:

[extensions.my-extension]
enabled = true
config-file = ".my-extension.yml"
options = { verbose = true, strict = false }

Access in scripts:

#!/bin/sh
# Check if extension is enabled
if [ "${MY_EXT_ENABLED}" = "true" ]; then
    # Run extension logic
fi

Chaining Extensions

Multiple extensions can hook into the same lifecycle event. They execute in installation order.

Extension Dependencies

Extensions can depend on other extensions:

[dependencies]
porters-base-utils = "1.0"
porters-cmake-helpers = "0.5"

Troubleshooting

Extension Not Found

❌ Extension 'my-ext' not found

Solution: Check the extension name and ensure it's published to crates.io or use full git URL.

Hook Script Not Executable

❌ Permission denied: hooks/pre_build.sh

Solution: Make scripts executable:

chmod +x hooks/*.sh

Extension Conflicts

⚠️  Warning: Multiple extensions define command 'build'

Solution: Uninstall conflicting extensions or rename commands.

Community Extensions

Popular Porters extensions:

  • porters-format: Code formatting with clang-format
  • porters-lint: Static analysis with clang-tidy
  • porters-docs: Documentation generation
  • porters-test: Enhanced testing utilities
  • porters-cmake: CMake project helpers
  • porters-conan: Conan integration
  • porters-vcpkg: vcpkg integration

Contributing

Create useful extensions and share them with the community! Submit your extension to awesome-porters to be featured.

Resources

Configuration

Complete reference for Porters configuration files.

Global Configuration

Porters maintains a global configuration file at ~/.porters/config.toml for user-wide settings and preferences.

Location:

  • Windows: C:\Users\<YourName>\.porters\config.toml
  • Linux/macOS: ~/.porters/config.toml

Automatic Creation:

The global config is automatically created on first run. Porters will:

  1. Create the ~/.porters/ directory
  2. Generate config.toml with default settings
  3. Run system requirements check
  4. Save detected compilers and build tools

Global Config Structure

[porters]
version = "0.1.0"
config_version = "1"

[user]
# Default author information for new projects
name = "Your Name"
email = "you@example.com"
default_license = "MIT"  # Default license for new projects

[cache]
# Global cache directory for compiled executables
dir = "~/.porters/cache"
max_size_mb = 1024
auto_clean = true

[system]
# Last system requirements check
last_check = "2024-01-15T10:30:00Z"

[system.compilers]
# Detected C/C++ compilers (auto-populated)
gcc = "/usr/bin/gcc"
gpp = "/usr/bin/g++"
clang = "/usr/bin/clang"
clangpp = "/usr/bin/clang++"

[system.build_tools]
# Detected build systems (auto-populated)
cmake = "/usr/bin/cmake"
make = "/usr/bin/make"
xmake = "/usr/local/bin/xmake"
meson = "/usr/bin/meson"
ninja = "/usr/bin/ninja"

[preferences]
# User preferences
default_build_system = "cmake"
default_language = "cpp"
auto_add_to_path = true
check_updates = true

Configuration Fields

[porters] Section

  • version - Porters version that created this config
  • config_version - Configuration file format version

[user] Section

  • name - Default author name for new projects
  • email - Default email for new projects
  • default_license - Default license (MIT, Apache-2.0, GPL-3.0, etc.)

[cache] Section

  • dir - Directory for temporary build files and executables
  • max_size_mb - Maximum cache size in megabytes
  • auto_clean - Automatically clean old cache files
  • cache_dir - Custom global cache directory path (optional)

Example:

[cache]
enabled = true
max_size_mb = 2048
auto_clean = true
cache_dir = "~/.porters/cache"  # Optional custom path

[registry] Section

  • url - Registry repository URL (default: https://github.com/muhammad-fiaz/porters)
  • auto_update - Automatically update registry index
  • index_path - Local registry index directory
  • last_update - Timestamp of last registry update

Example:

[registry]
url = "https://github.com/muhammad-fiaz/porters"
auto_update = true
index_path = "~/.porters/registry-index"
last_update = "2024-01-15T10:30:00Z"

Global Offline Mode

Enable offline mode globally to prevent all network operations:

# In ~/.porters/config.toml
offline = true

When offline mode is enabled:

  • ✅ Uses only cached dependencies
  • ✅ Uses local registry index
  • ❌ No network requests
  • ❌ Cannot download new packages

Use case: Working in environments without internet access or wanting to ensure reproducible builds.

[system] Section

  • last_check - Timestamp of last system requirements check
  • compilers - Detected compiler paths (auto-populated)
  • build_tools - Detected build system paths (auto-populated)

[preferences] Section

  • default_build_system - Preferred build system (cmake, xmake, meson, make)
  • default_language - Preferred language (c, cpp, both)
  • auto_add_to_path - Automatically add Cargo bin to PATH on first run
  • check_updates - Check for Porters updates automatically

System Requirements Check

On first run (or when running porters --check-system), Porters will:

  1. Check for C/C++ Compilers:

    • gcc, g++
    • clang, clang++
    • MSVC (Windows)
    • MinGW (Windows)
  2. Check for Build Systems:

    • CMake
    • Make
    • XMake
    • Meson
    • Ninja
  3. Display Results:

    • ✅ Found tools with version numbers
    • ❌ Missing tools with installation instructions
  4. Save to Config:

    • Detected tool paths saved to ~/.porters/config.toml
    • Used for faster detection in future runs

Example Output:

╭──────────────────────────────────────────────────╮
│  System Requirements Check                       │
╰──────────────────────────────────────────────────╯

Compilers
─────────
✅ g++ (version 11.4.0)
✅ gcc (version 11.4.0)
❌ clang++ (not found)

Build Systems
─────────────
✅ cmake (version 3.22.1)
✅ make (version 4.3)

Status: ✅ System ready!

Installation Instructions:

If tools are missing, Porters displays platform-specific installation commands:

Windows:

Install C/C++ compiler:
  - MSVC: Install Visual Studio Build Tools
  - MinGW: choco install mingw
  - Clang: choco install llvm

Install build tools:
  - CMake: choco install cmake

Linux (Debian/Ubuntu):

sudo apt-get update
sudo apt-get install gcc g++ clang cmake make

macOS:

xcode-select --install
brew install cmake

Manual Configuration

You can manually edit ~/.porters/config.toml to customize settings:

[user]
name = "Jane Developer"
email = "jane@example.com"
default_license = "Apache-2.0"

[preferences]
default_build_system = "xmake"
default_language = "cpp"
check_updates = true

[cache]
max_size_mb = 2048
auto_clean = true

After editing, run any Porters command to apply changes.


porters.toml

Main project configuration file.

Complete Example

[project]
name = "my-project"
version = "1.0.0"
description = "My awesome C++ project"
license = "Apache-2.0"
authors = ["Your Name <you@example.com>"]
repository = "https://github.com/username/my-project"
project-type = "application"  # or "library"
entry_point = "src/main"
platforms = ["windows", "macos", "linux"]
keywords = ["application", "c", "cpp"]
readme = "README.md"
offline = false  # Enable offline mode for this project

# Tool version requirements (like Python's requirements.txt)
[requires]
cpp = ">=17"           # C++ standard version
cmake = ">=3.20"       # CMake version
gcc = ">=9.0"          # GCC version
clang = ">=12.0"       # Clang version (alternative to gcc)
ninja = ">=1.10"       # Ninja build tool version
make = ">=4.0"         # Make version
meson = ">=0.60"       # Meson version
bazel = ">=5.0"        # Bazel version
conan = ">=1.50"       # Conan version
vcpkg = "*"            # Any version of vcpkg

# Extensions to auto-install from crates.io
extensions = [
    "porters-format",    # Code formatter extension
    "porters-lint",      # Linting extension
    "porters-doc"        # Documentation generator
]

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "10.1.1" }
spdlog = { git = "https://github.com/gabime/spdlog", branch = "v1.x" }
mylib = { path = "../mylib" }

[dev-dependencies]
catch2 = { git = "https://github.com/catchorg/Catch2" }
benchmark = { git = "https://github.com/google/benchmark" }

[optional-dependencies]
zlib = { git = "https://github.com/madler/zlib" }

[build]
system = "cmake"
options = ["-DBUILD_SHARED_LIBS=ON"]

[build.env]
CC = "clang"
CXX = "clang++"

# Build lifecycle scripts
[build.scripts]
pre-build = "echo Building..."
post-build = "strip build/myapp"
pre-install = "echo Installing..."
post-install = "echo Done!"

# Custom CLI commands
[[commands]]
name = "format"
description = "Format source code"
script = "clang-format -i src/**/*.cpp"

[[commands]]
name = "docs"
description = "Generate documentation"
script = "doxygen Doxyfile"
[commands.env]
DOXYGEN_OUTPUT = "docs/html"

# Named script shortcuts
[scripts]
test-all = "cargo build && cargo test"
deploy = "./deploy.sh production"

Project Section

Required fields:

  • name - Project name
  • version - Semantic version (e.g., "1.0.0")

Optional fields:

  • description - Project description
  • license - SPDX license identifier
  • authors - List of authors
  • repository - Git repository URL
  • project-type - "application" or "library"
  • entry_point - Main entry point
  • platforms - Target platforms
  • keywords - Search keywords
  • readme - README file path

Dependencies Section

Dependency sources:

Git (HTTPS):

fmt = { git = "https://github.com/fmtlib/fmt" }

Git (SSH):

spdlog = { git = "git@github.com:gabime/spdlog.git" }

With version constraints:

fmt = { git = "https://github.com/fmtlib/fmt", tag = "10.1.1" }
boost = { git = "https://github.com/boostorg/boost", branch = "boost-1.80.0" }

Local path:

mylib = { path = "../mylib" }

Build Section

[build]
system = "cmake"  # cmake, xmake, meson, make, custom
options = ["-DCMAKE_BUILD_TYPE=Release"]

[build.env]
CMAKE_PREFIX_PATH = "/usr/local"

Run Section - Direct File Execution (COMPLETELY OPTIONAL)

⚠️ YOU DON'T NEED THIS SECTION!

The porters execute command works 100% automatically without any configuration. This section only exists for advanced manual overrides.

What Works Automatically (No [run] Section Needed):

  • Compiler Detection - Finds gcc/clang/g++/clang++ in your PATH
  • Dependency Resolution - Reads dependencies from [dependencies]
  • Include Paths - Automatically adds -I for all dependency includes
  • Library Paths - Automatically adds -L for all dependency libraries
  • File Type Detection - .c → C compiler, .cpp → C++ compiler
  • Supported Extensions - .c, .cpp, .cxx, .cc, .c++, .cp, .C, .CPP

Zero Configuration Example:

# No porters.toml needed at all!
porters execute hello.c

# With dependencies - automatically resolved
porters execute my_app.cpp

Optional Manual Overrides (Only If You Need Custom Behavior):

[run]
# Extra include directories (beyond automatic dependency includes)
include-dirs = ["./include", "./extra/include"]

# Exclude patterns (rarely needed)
exclude-patterns = ["test_*", "*_backup.c"]

# Compiler flags (only if you want warnings, optimizations, etc.)
compiler-flags = ["-Wall", "-O2", "-std=c17"]

# Linker flags (only if you need extra libraries)
linker-flags = ["-lm", "-lpthread"]

# Override compiler (only if you need a specific one)
c-compiler = "clang"    # Default: auto-detect
cpp-compiler = "clang++"  # Default: auto-detect

# Execution mode settings (default: false for both)
use-external-terminal = false  # Open programs in new external terminal window
no-console = false             # Run without console window (Windows GUI apps)

Execution Mode Settings:

Configure how porters execute runs your programs:

  • use-external-terminal (default: false)

    • When true: Opens program in new external terminal window
    • Useful for: GUI applications, interactive programs needing separate window
    • CLI override: --external flag
    • Example use cases:
      • Interactive TUI applications that manage terminal state
      • Games that need full terminal control
      • Programs that should run independently of IDE/editor
  • no-console (default: false)

    • When true: Runs program without console window (Windows only)
    • Useful for: Pure GUI applications with no console output
    • CLI override: --no-console flag
    • Example use cases:
      • Windows GUI applications using Win32 API, SDL, SFML
      • Applications where console window would be distracting
      • Release builds of GUI apps

Example Configurations:

# For GUI game development
[run]
use-external-terminal = true  # Run in separate window
no-console = true            # No console clutter
linker-flags = ["-lSDL2"]    # Link SDL library

# For interactive terminal application
[run]
use-external-terminal = true  # Needs dedicated terminal
compiler-flags = ["-Wall", "-Wextra"]

# For standard CLI tool (defaults are fine, no [run] needed)
# Just use: porters execute main.c

Note: CLI flags (--external, --no-console) always override config settings.

When You Might Use [run]:

  • Adding project-specific include paths not in dependencies
  • Setting custom compiler warnings or optimizations
  • Linking additional system libraries
  • Using a specific compiler version
  • Configuring default execution mode for GUI apps
  • Setting up consistent behavior for team development

Tool Version Requirements

Specify minimum versions for build tools and compilers. Porters will validate these before building.

Supported version operators:

  • * - Any version
  • >=1.2.3 - Greater than or equal
  • <=1.2.3 - Less than or equal
  • >1.2.3 - Greater than
  • <1.2.3 - Less than
  • ^1.2.3 - Compatible (allows patch and minor updates, ~= caret in Cargo)
  • ~1.2.3 - Tilde (allows patch updates only)
  • 1.2.3 or ==1.2.3 - Exact version

Example:

[requires]
c = ">=11"              # C11 or later
cpp = ">=17"            # C++17 or later
cmake = ">=3.20"        # CMake 3.20+
gcc = ">=9.0"           # GCC 9.0+
clang = "^12.0"         # Clang 12.x (allows 12.1, 12.2, but not 13.0)
ninja = "~1.10.0"       # Ninja 1.10.x (allows 1.10.1, but not 1.11)
make = "*"              # Any Make version
meson = ">=0.60"        # Meson 0.60+
bazel = ">=5.0"         # Bazel 5.0+
conan = ">=1.50"        # Conan 1.50+
vcpkg = "*"             # Any vcpkg version
xmake = ">=2.7"         # XMake 2.7+
msvc = ">=19.30"        # MSVC 19.30+ (Visual Studio 2022)

Version check output:

$ porters build
✓ Checking tool version requirements...
  ✓ C++ Compiler: requires >=17, found 20
  ✓ CMake: requires >=3.20, found 3.25.1
  ✓ GCC: requires >=9.0, found 11.3.0
✓ All tool requirements satisfied ✓

If requirements not met:

$ porters build
✓ Checking tool version requirements...

🤔 Oops! Unsatisfied version requirements:
  ❌ C++: requires >=17, found 14
  ❌ CMake: requires >=3.20, found 3.15.0
  ❌ GCC: requires >=9.0, but tool not found in PATH

Please install or upgrade the required tools to continue.
See: https://github.com/muhammad-fiaz/porters#requirements

Extension Auto-Install

Automatically install extensions from crates.io when running porters sync.

Example:

extensions = [
    "porters-format",      # Code formatter
    "porters-lint",        # Linter
    "porters-doc",         # Documentation generator
    "porters-test-runner"  # Test runner
]

Extensions are always installed in the dev category and can be published to crates.io by anyone.

During sync:

$ porters sync
✓ Syncing dependencies from porters.toml
ℹ Auto-installing 3 extensions from config...
📦 Installing extension 'porters-format'...
✓ Extension 'porters-format' installed successfully! 🔌
📦 Extension 'porters-lint' already installed, skipping
📦 Installing extension 'porters-doc'...
✓ Extension 'porters-doc' installed successfully! 🔌

Custom Commands

Define custom CLI commands in your porters.toml for project-specific tasks.

Example:

[[commands]]
name = "format"
description = "Format all C++ source files"
script = "clang-format -i src/**/*.cpp include/**/*.hpp"

[[commands]]
name = "lint"
description = "Run clang-tidy linter"
script = "clang-tidy src/*.cpp -- -Iinclude"

[[commands]]
name = "docs"
description = "Generate Doxygen documentation"
script = "doxygen Doxyfile"

[commands.env]
DOXYGEN_OUTPUT = "docs/html"
OUTPUT_DIR = "build/docs"

Usage:

$ porters format       # Runs clang-format
$ porters lint         # Runs clang-tidy
$ porters docs         # Generates documentation

Named Scripts

Quick script shortcuts for common tasks.

Example:

[scripts]
test-all = "cargo build && cargo test --all"
deploy-prod = "./scripts/deploy.sh production"
clean-all = "rm -rf build ports .porters-cache"
bench = "cargo build --release && ./build/benchmark"

Usage:

$ porters run-script test-all
$ porters run-script deploy-prod
$ porters run-script bench

Dependency Checksums

Verify dependency integrity with SHA-256 checksums (automatically managed by lockfile).

Example:

[dependencies]
fmt = { 
    git = "https://github.com/fmtlib/fmt", 
    tag = "10.1.1",
    checksum = "sha256:a1b2c3d4e5f6..."  # Optional, auto-calculated if omitted
}

Porters automatically:

  1. Calculates checksums on first download
  2. Stores checksums in porters.lock
  3. Verifies checksums on subsequent builds

Checksum mismatch:

⚠️ Dependency hash mismatch for 'fmt':
   Expected: abc123...
   Found:    def456...

This may indicate tampering or corruption. Please verify the source.

Global Configuration

Location:

  • Linux/macOS: ~/.porters/config.toml
  • Windows: C:\Users\<username>\.porters\config.toml

Example

[settings]
parallel_jobs = 8
cache_enabled = true

[packages.fmt]
name = "fmt"
version = "10.1.1"
source = "https://github.com/fmtlib/fmt"
install_path = "/home/user/.porters/packages/fmt"
installed_at = "2024-01-15T10:30:00Z"

Lock File (porters.lock)

Auto-generated file ensuring reproducible builds.

Format

version = "1"
updated_at = "2024-01-15T10:30:00Z"

[dependencies.fmt]
name = "fmt"
version = "10.1.1"
source = { Git = { url = "https://github.com/fmtlib/fmt", rev = "a1b2c3d" } }
checksum = "sha256:..."
dependencies = []

[dependencies.spdlog]
name = "spdlog"
version = "1.12.0"
source = { Git = { url = "https://github.com/gabime/spdlog", rev = "e4f5678" } }
dependencies = ["fmt"]

Do not edit manually. Use porters lock to regenerate.


Transitive Dependencies

Porters automatically resolves dependencies-of-dependencies recursively.

How it works:

  1. When you add a dependency, Porters checks if it has its own porters.toml
  2. If found, Porters reads the dependency's dependencies
  3. This process repeats recursively to build a full dependency graph
  4. Circular dependencies are detected and reported as errors
  5. Version conflicts are detected and must be manually resolved

Example:

Your project's porters.toml:

[dependencies]
mylib = { path = "../mylib" }

mylib's porters.toml (transitive dependencies):

[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "10.1.1" }
spdlog = { git = "https://github.com/gabime/spdlog" }

When you run porters sync, Porters will:

  • Install mylib (your direct dependency)
  • Detect mylib has dependencies on fmt and spdlog
  • Automatically install fmt and spdlog (transitive dependencies)
  • Build them in the correct order: fmtspdlogmylibyour-project

Dependency graph visualization:

porters graph
# Output:
📦 Resolved dependency build order: fmt → spdlog → mylib

Circular dependency detection:

❌ Circular dependency detected involving: mylib

This means there's a circular dependency chain. Check your dependency tree.

Version conflict detection:

❌ Dependency conflicts detected:
  fmt requires versions: 10.1.1, 9.0.0

Two dependencies require different versions of 'fmt'. You must manually resolve this conflict.

Offline Mode

Porters supports working entirely offline using only cached dependencies and local registry index. This is useful for:

  • 🔒 Secure environments without internet access
  • ✈️ Air-gapped networks
  • 🏗️ Reproducible builds
  • 📦 CI/CD pipelines with pre-populated cache

Enabling Offline Mode

Global offline mode (affects all projects):

# ~/.porters/config.toml
offline = true

Project offline mode (project-specific):

# porters.toml
[project]
offline = true

Temporary offline mode (command-line):

porters build --offline
porters add <dep> --offline
porters sync --offline

How Offline Mode Works

When offline mode is enabled:

  1. Network Operations Blocked:

    • ❌ No Git clones/fetches
    • ❌ No registry updates from GitHub
    • ❌ No package downloads
  2. Cache-First Resolution:

    • ✅ Uses global cache (~/.porters/cache/)
    • ✅ Uses local cache (.porters/cache/)
    • ✅ Uses local registry index (~/.porters/registry-index/)
  3. Error Handling:

    • If dependency not in cache → clear error message
    • Suggests running without --offline to download

Preparing for Offline Mode

Before going offline, ensure all dependencies are cached:

# 1. Sync all dependencies (downloads if needed)
porters sync

# 2. Verify cache contents
porters cache list

# 3. Enable offline mode
porters config set offline true

# 4. Test that build works offline
porters build --offline

Offline Mode Example Workflow

Setup (with internet):

# Initialize project
porters init

# Add dependencies
porters add https://github.com/fmtlib/fmt
porters add https://github.com/gabime/spdlog

# Sync (downloads and caches)
porters sync

# Verify cache
porters cache stats
# Output: Packages: 2, Total Size: 15.2 MB

Work offline (no internet):

# Enable offline mode
export PORTERS_OFFLINE=1
# or add offline=true to porters.toml

# Build works entirely from cache
porters build --offline
# ✅ Using globally cached fmt
# ✅ Using globally cached spdlog
# 🔨 Building project...

Offline Mode with Registry

The registry can be used offline with a local index:

# 1. Update registry index (with internet)
porters registry update

# 2. Work offline
porters search fmt --offline
# Searches local registry index

porters info spdlog --offline
# Shows info from local index

Troubleshooting Offline Mode

"Package not found in cache":

# Temporarily disable offline to download
porters sync
# Then re-enable offline

"Registry index not found":

# Update registry index first
porters registry update
# Then work offline

Check what's cached:

# List all cached packages
porters cache list

# Show cache statistics
porters cache stats

Best Practices

  1. Pre-populate cache before going offline:

    porters sync
    porters cache verify
    
  2. Use lock file for reproducibility:

    porters lock
    # Commit porters.lock to version control
    
  3. Periodic cache updates:

    # Weekly: update registry and dependencies
    porters registry update
    porters update
    
  4. CI/CD offline builds:

    # .github/workflows/build.yml
    - name: Restore cache
      uses: actions/cache@v3
      with:
        path: ~/.porters/cache
        key: ${{ runner.os }}-porters-${{ hashFiles('**/porters.lock') }}
    
    - name: Build offline
      run: porters build --offline
    

Next Steps

Command Reference

Complete reference for all Porters commands.

porters init

Initialize a Porters project in the current directory.

Usage:

porters init [OPTIONS]

Options:

  • --yes, -y - Use default values without prompting

Interactive Prompts:

  • Project name (defaults to folder name)
  • Version (default: 0.1.0)
  • Author name
  • Description
  • License (MIT, Apache-2.0, GPL-3.0, GPL-2.0, BSD-3-Clause, BSD-2-Clause, MPL-2.0, LGPL-3.0, Unlicense)

Behavior:

  • Detects existing C/C++ source files
  • Auto-detects build system (CMake, XMake, Meson, Make)
  • Creates porters.toml configuration
  • Automatically generates LICENSE file based on your selection
  • Initializes cache directories

License File Generation:

When you select a license, Porters automatically creates a LICENSE file with:

  • Full license text (SPDX-compliant)
  • Your name as the copyright holder
  • Current year in copyright notice

Supported Licenses:

  • MIT - Permissive, simple license
  • Apache-2.0 - Permissive with patent grant
  • GPL-3.0 - Strong copyleft
  • GPL-2.0 - Classic copyleft
  • BSD-3-Clause - Permissive, 3-clause variant
  • BSD-2-Clause - Permissive, simplified
  • MPL-2.0 - Weak copyleft (Mozilla)
  • LGPL-3.0 - Weak copyleft for libraries
  • Unlicense - Public domain dedication

Example:

cd my-existing-project
porters init

Example Flow:

$ porters init
? Project name: my-project
? Version: 0.1.0
? Author: John Doe
? Description: My awesome C++ library
? License:
  > MIT
    Apache-2.0
    GPL-3.0
    BSD-3-Clause

✅ Created porters.toml
✅ Generated LICENSE file (MIT License)
✅ Project initialized!

porters create

Create a new Porters project with automatic scaffolding.

Usage:

porters create <NAME> [OPTIONS]

Arguments:

  • <NAME> - Project name

Options:

  • --yes, -y - Use default values without prompting

Interactive Prompts:

  • Project type: 🚀 Application (executable) or 📦 Library (static/shared)
  • Language: 🔵 C (Pure C), 🔴 C++ (Pure C++), or 🟣 Both (Hybrid C/C++)
  • 📚 Library name (for libraries, optional)
  • 👤 Author name (optional)
  • 📧 Email (optional)
  • 🔗 Repository URL (optional)
  • 📝 License with descriptions:
    • ⚖️ Apache-2.0 (Permissive, with patent protection)
    • 📄 MIT (Very permissive, simple)
    • 🔓 GPL-3.0 (Copyleft, strong protection)
    • 🔓 GPL-2.0 (Copyleft, older version)
    • 📋 BSD-3-Clause (Permissive, with attribution)
    • 📋 BSD-2-Clause (Permissive, simpler)
    • 🔧 MPL-2.0 (Weak copyleft, file-level)
    • 📚 LGPL-3.0 (For libraries, weak copyleft)
    • 🆓 Unlicense (Public domain)
    • ✏️ Custom (Create your own)
    • ❌ None
  • ⚙️ Build system:
    • 🔨 CMake (Industry standard, most popular)
    • ⚡ XMake (Modern, fast, Lua-based)
    • 🏗️ Meson (Fast, Python-based)
    • 🔧 Make (Traditional, simple)
    • ✨ Custom (Manual configuration)

Project Types:

Application Projects

Creates an executable application with:

  • src/main.c or src/main.cpp (based on language choice)
  • Basic "Hello, World!" starter code
  • Build system configuration (CMakeLists.txt, xmake.lua, etc.)
  • porters.toml with project-type = "application"
  • LICENSE file (auto-generated from your selection)
  • README.md with build instructions
  • Git repository initialization

Directory Structure (Application):

my-app/
├── porters.toml
├── LICENSE             # Auto-generated
├── README.md
├── .gitignore
├── CMakeLists.txt      # or xmake.lua, meson.build, etc.
└── src/
    └── main.cpp        # or main.c

main.cpp Template:

#include <iostream>

int main() {
    std::cout << "Hello from my-app!" << std::endl;
    return 0;
}

Library Projects

Creates a reusable library with complete structure:

Directory Structure (Library):

my-lib/
├── porters.toml
├── LICENSE             # Auto-generated
├── README.md
├── .gitignore
├── CMakeLists.txt      # or xmake.lua, meson.build, etc.
├── include/
│   └── my_lib/
│       └── my_lib.hpp  # or my_lib.h
├── src/
│   └── my_lib.cpp      # or my_lib.c
├── examples/
│   └── example.cpp     # or example.c
└── tests/
    └── test_my_lib.cpp # or test_my_lib.c

Library Components:

1. Header File (include/my_lib/my_lib.hpp):

#ifndef MY_LIB_HPP
#define MY_LIB_HPP

namespace my_lib {
    void greet();
}

#endif // MY_LIB_HPP

2. Implementation (src/my_lib.cpp):

#include "my_lib/my_lib.hpp"
#include <iostream>

namespace my_lib {
    void greet() {
        std::cout << "Hello from my_lib library!" << std::endl;
    }
}

3. Example (examples/example.cpp):

#include "my_lib/my_lib.hpp"

int main() {
    my_lib::greet();
    return 0;
}

4. Test (tests/test_my_lib.cpp):

#include "my_lib/my_lib.hpp"

int main() {
    // Add your tests here
    my_lib::greet();
    return 0;
}

For C Libraries:

When creating a C library, Porters generates C-style code:

Header (include/my_lib/my_lib.h):

#ifndef MY_LIB_H
#define MY_LIB_H

#ifdef __cplusplus
extern "C" {
#endif

void my_lib_greet(void);

#ifdef __cplusplus
}
#endif

#endif // MY_LIB_H

Implementation (src/my_lib.c):

#include "my_lib/my_lib.h"
#include <stdio.h>

void my_lib_greet(void) {
    printf("Hello from my_lib library!\n");
}

Hybrid C/C++ Projects (Both Option)

When you select "🟣 Both (Hybrid C/C++)", Porters creates a project that seamlessly integrates C and C++ code with proper extern "C" usage.

Why Use Hybrid Projects?

  • Gradually migrate from C to C++ (or vice versa)
  • Use C libraries from C++ code
  • Leverage both C's low-level control and C++'s high-level features
  • Integrate legacy C code with modern C++

Directory Structure (Hybrid Application):

my-hybrid-app/
├── porters.toml
├── LICENSE
├── README.md
├── .gitignore
├── CMakeLists.txt
├── include/
│   ├── c_module.h       # C header with extern "C"
│   └── cpp_utils.hpp    # C++ header
└── src/
    ├── main.cpp         # C++ entry point
    ├── c_module.c       # C implementation
    └── cpp_utils.cpp    # C++ implementation

Generated Files:

1. Main Entry Point (src/main.cpp):

#include <iostream>
#include "c_module.h"

int main(int argc, char *argv[]) {
    std::cout << "🚀 Hello from C++ (Porters Hybrid Project)!" << std::endl;
    
    // Call C function from C++ code
    const char* c_message = get_c_message();
    std::cout << "📦 Message from C module: " << c_message << std::endl;
    
    return 0;
}

2. C Module Header (include/c_module.h):

#ifndef C_MODULE_H
#define C_MODULE_H

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Get a message from the C module
 * This function can be called from both C and C++ code
 */
const char* get_c_message(void);

/**
 * Process a number using C code
 */
int c_process_number(int value);

#ifdef __cplusplus
}
#endif

#endif /* C_MODULE_H */

3. C Module Implementation (src/c_module.c):

#include "c_module.h"
#include <stdio.h>

const char* get_c_message(void) {
    return "This is a C function callable from C++!";
}

int c_process_number(int value) {
    printf("Processing %d in C code\n", value);
    return value * 2;
}

4. C++ Utilities Header (include/cpp_utils.hpp):

#ifndef CPP_UTILS_HPP
#define CPP_UTILS_HPP

#include <string>
#include <vector>

namespace utils {

class StringHelper {
public:
    static std::string to_upper(const std::string& str);
    static std::vector<std::string> split(const std::string& str, char delimiter);
};

} // namespace utils

#endif /* CPP_UTILS_HPP */

5. C++ Utilities Implementation (src/cpp_utils.cpp):

#include "cpp_utils.hpp"
#include <algorithm>
#include <sstream>

namespace utils {

std::string StringHelper::to_upper(const std::string& str) {
    std::string result = str;
    std::transform(result.begin(), result.end(), result.begin(), ::toupper);
    return result;
}

std::vector<std::string> StringHelper::split(const std::string& str, char delimiter) {
    std::vector<std::string> tokens;
    std::stringstream ss(str);
    std::string token;
    
    while (std::getline(ss, token, delimiter)) {
        tokens.push_back(token);
    }
    
    return tokens;
}

} // namespace utils

Key Features of Hybrid Projects:

  1. Automatic extern "C" Setup: C headers properly wrapped for C++ compatibility
  2. Mixed Compilation: Build system automatically handles both .c and .cpp files
  3. Namespace Separation: C++ code uses namespaces, C code uses prefixes
  4. Documentation Comments: Clear examples of cross-language usage
  5. Best Practices: Follows industry standards for C/C++ interoperability

Build System Support:

All build systems (CMake, XMake, Meson) are configured to handle mixed C/C++ compilation:

CMake Configuration:

# Automatically generated - handles both C and C++ files
project(my-hybrid-app C CXX)

# C files compiled with C compiler
# C++ files compiled with C++ compiler
# Linking works automatically
add_executable(my-hybrid-app
    src/main.cpp
    src/c_module.c
    src/cpp_utils.cpp
)

When to Use Hybrid Projects:

Use Hybrid When:

  • Migrating existing C code to C++
  • Integrating with C libraries
  • Need C's performance + C++'s features
  • Team has expertise in both languages
  • Gradual modernization of legacy code

Use Pure C or C++ When:

  • Starting fresh project
  • Team specializes in one language
  • Project doesn't need cross-language features

Behavior:

  • Creates project directory
  • Generates appropriate source structure based on project type
  • Creates build system files (CMake, XMake, Meson, or Make)
  • Automatically generates LICENSE file with:
    • Full license text
    • Your name as copyright holder
    • Current year
  • Creates comprehensive README.md with:
    • Project description
    • Build instructions
    • Usage examples (for libraries)
    • License information
  • Initializes Git repository
  • Creates porters.toml and .gitignore

Examples:

# Interactive creation (recommended)
porters create my-app

# Quick create with defaults (application)
porters create my-project --yes

# The wizard will ask:
# 1. Project type → Application or Library
# 2. Language → C, C++, or Both
# 3. License → MIT, Apache-2.0, GPL-3.0, etc.

Example Interactive Flow:

$ porters create awesome-lib

? Project type:
  Application
  > Library

? Language:
  C
  > C++
  Both

? Library name: awesome_lib
? Author: Jane Developer
? Email: jane@example.com
? Repository URL: https://github.com/jane/awesome-lib
? License:
  > MIT
    Apache-2.0
    GPL-3.0
    BSD-3-Clause

? Build system:
  > CMake
    XMake
    Meson
    Make

✅ Created project: awesome-lib
✅ Generated LICENSE file (MIT License)
✅ Created library structure:
   - include/awesome_lib/awesome_lib.hpp
   - src/awesome_lib.cpp
   - examples/example.cpp
   - tests/test_awesome_lib.cpp
✅ Initialized Git repository
✅ Project ready!

Next steps:
  cd awesome-lib
  porters build

Generated README (Library):

The README includes:

  • Project title and description
  • Build instructions
  • Usage examples showing how to use the library
  • Installation guide
  • License badge and information
  • Contributing guidelines

Generated README (Application):

For applications, the README includes:

  • Project title and description
  • Build and run instructions
  • Command-line usage examples
  • Configuration information
  • License information

porters add

Add a dependency to the current project.

Usage:

porters add <PACKAGE> [OPTIONS]

Arguments:

  • <PACKAGE> - Package name

Options:

  • --git <URL> - Git repository URL (HTTPS or SSH)
  • --branch <BRANCH> - Specific Git branch
  • --tag <TAG> - Specific Git tag
  • --dev - Add as development dependency
  • --optional - Add as optional dependency

Behavior:

  • Adds dependency to porters.toml
  • Clones package to ports/<package>/
  • Updates porters.lock

Examples:

# Add from Git (HTTPS)
porters add fmt --git https://github.com/fmtlib/fmt

# Add from Git (SSH)
porters add spdlog --git git@github.com:gabime/spdlog.git

# Add specific tag
porters add fmt --git https://github.com/fmtlib/fmt --tag 10.1.1

# Add as dev dependency
porters add catch2 --git https://github.com/catchorg/Catch2 --dev

# Add optional dependency
porters add zlib --git https://github.com/madler/zlib --optional

porters install

Install a package globally to ~/.porters/packages/.

Usage:

porters install <PACKAGE> [OPTIONS]

Arguments:

  • <PACKAGE> - Package name

Options:

  • --git <URL> - Git repository URL
  • --branch <BRANCH> - Specific Git branch
  • --tag <TAG> - Specific Git tag

Behavior:

  • Creates ~/.porters/ directory structure
  • Clones package to ~/.porters/packages/<package>/
  • Updates global config (~/.porters/config.toml)

Examples:

porters install fmt --git https://github.com/fmtlib/fmt
porters install boost --git https://github.com/boostorg/boost --tag boost-1.80.0

porters sync

Synchronize dependencies from porters.toml with global cache support.

Usage:

porters sync [OPTIONS]

Options:

  • --dev - Include development dependencies
  • --optional - Include optional dependencies
  • --no-cache - Disable cache, force re-download all dependencies

Behavior:

  • Reads porters.toml
  • Checks global cache first (~/.porters/cache/)
  • Downloads missing dependencies to ports/
  • Stores new dependencies in global cache for future reuse
  • Resolves version constraints
  • Updates porters.lock
  • Supports offline mode (uses only cached dependencies)

Examples:

# Sync regular dependencies (cache-first)
porters sync

# Sync with dev dependencies
porters sync --dev

# Sync everything
porters sync --dev --optional

# Force re-download (bypass cache)
porters sync --no-cache

Cache Behavior:

  • First checks ~/.porters/cache/<package>/<version>/
  • If found, copies from cache to ports/
  • If not found, downloads from Git and caches globally
  • In offline mode, only uses cached dependencies

porters lock

Update the lock file with installed dependencies.

Usage:

porters lock

Behavior:

  • Scans ports/ directory
  • Resolves current dependency versions
  • Updates porters.lock with checksums and metadata
  • Records timestamp

Example:

porters lock

porters registry

Manage Porters package registry operations.

Usage:

porters registry <SUBCOMMAND>

Subcommands:

Search for packages in the registry.

Usage:

porters registry search <QUERY> [OPTIONS]

Options:

  • --tag <TAG> - Filter by tag (e.g., graphics, networking)
  • --limit <N> - Limit results (default: 20)

Examples:

# Search for packages
porters registry search json

# Search by tag
porters registry search --tag graphics

# Limit results
porters registry search fmt --limit 5

porters registry list

List all available packages in the registry.

Usage:

porters registry list

Example:

porters registry list

porters registry update

Update the local registry index from remote source.

Usage:

porters registry update

Behavior:

  • Fetches latest package metadata from GitHub
  • Updates ~/.porters/registry-index/
  • Uses Git sparse checkout for efficiency
  • Respects offline mode setting

Example:

porters registry update

Note: Registry auto-updates are configurable in ~/.porters/config.toml:

[registry]
auto_update = true  # Auto-update when checking for packages

porters clean

Clean build artifacts and temporary files.

Usage:

porters clean

Behavior:

  • Removes build/ directory
  • Removes .porters/cache/ directory
  • Preserves ports/ dependencies
  • Preserves porters.lock

Example:

porters clean

porters clean-cache

Clean dependency cache (local and/or global).

Usage:

porters clean-cache [OPTIONS]

Options:

  • --force, -f - Clean global cache as well (default: local only)

Behavior:

  • Without --force: Cleans only .porters/cache/ (project-local)
  • With --force: Also cleans ~/.porters/cache/ (global cache)

Examples:

# Clean local cache only
porters clean-cache

# Clean both local and global cache
porters clean-cache --force

Warning: Cleaning global cache will require re-downloading dependencies for all projects.


porters update

Update dependencies to latest compatible versions.

Usage:

porters update

Behavior:

  • Checks for newer versions of dependencies
  • Respects version constraints in porters.toml
  • Updates porters.lock with new versions
  • Downloads updated dependencies

Example:

porters update

porters update-deps

Update all dependencies to latest versions (ignoring constraints).

Usage:

porters update-deps [OPTIONS]

Options:

  • --latest - Update to absolute latest versions (ignore semver constraints)

Behavior:

  • Updates all dependencies to latest compatible versions
  • With --latest: Ignores semver constraints in porters.toml
  • Updates porters.lock

Examples:

# Update to latest compatible
porters update-deps

# Update to absolute latest (may break compatibility)
porters update-deps --latest

porters run

Run the compiled project executable.

Usage:

porters run [ARGS...]

Arguments:

  • [ARGS...] - Arguments to pass to the executable

Behavior:

  • Locates the compiled executable from build directory
  • Executes the program with provided arguments
  • Displays program output

Examples:

# Run without arguments
porters run

# Run with arguments
porters run --verbose input.txt

# After building
porters build
porters run

porters execute

Execute a single C/C++ source file directly with zero configuration required.

Usage:

porters execute <FILE> [ARGS...] [OPTIONS]

Arguments:

  • <FILE> - C/C++ source file to compile and run
  • [ARGS...] - Arguments to pass to the compiled program

Options:

  • --external - Open the program in a new external terminal window (instead of current terminal)
  • --no-console - Run without a console window (useful for GUI applications on Windows)

Supported File Extensions:

  • C: .c
  • C++: .cpp, .cxx, .cc, .c++, .cp, .C, .CPP

Note: Header files (.h, .hpp, .hxx) cannot be compiled directly.

100% Automatic - No Configuration Needed:

  • Compiler Auto-Detection - Finds gcc/clang (C) or g++/clang++ (C++)
  • Dependency Resolution - Reads porters.toml and adds include/lib paths automatically
  • File Type Detection - Determines C vs C++ from file extension
  • Smart Compilation - Compiles to temporary executable and runs immediately
  • Works Without porters.toml - Can execute any C/C++ file even outside a project

[run] Section - Optional Manual Overrides:

You don't need this section! Everything works automatically. Only add if you need custom configuration.

[run]
# Additional include directories (dependencies auto-included)
include-dirs = ["./include", "./extra/include"]

# Exclude patterns for automatic includes
exclude-patterns = ["test_*", "*_backup.c"]

# Compiler flags (optional - compiles without these)
compiler-flags = ["-Wall", "-O2", "-std=c17"]

# Linker flags (optional)
linker-flags = ["-lm", "-lpthread"]

# Override default compilers (auto-detected by default)
c-compiler = "gcc"      # or "clang", "cc"
cpp-compiler = "g++"    # or "clang++", "c++"

# Execution mode settings
use-external-terminal = false  # Open programs in external terminal (GUI apps)
no-console = false             # Run without console window (Windows GUI apps)

Execution Mode Settings:

Configure default execution behavior in porters.toml:

  • use-external-terminal - Opens program in new terminal window

    • Useful for: GUI applications, interactive programs that need separate window
    • Default: false (runs in current terminal)
    • CLI override: --external flag
  • no-console - Runs program without console window (Windows only)

    • Useful for: Pure GUI applications (no console output expected)
    • Default: false (shows console window)
    • CLI override: --no-console flag

Example Configuration for GUI App:

[run]
use-external-terminal = true  # Always open in new window
no-console = true            # No console window needed

Note: CLI flags (--external, --no-console) always override config settings.

Examples:

# Execute C file - works immediately, no setup!
porters execute hello.c

# Execute C++ file with arguments
porters execute main.cpp arg1 arg2

# Open in external terminal window (new console window)
porters execute game.cpp --external

# Run GUI app without console window (Windows)
porters execute gui_app.cpp --no-console

# Both flags together for standalone GUI app
porters execute standalone_gui.cpp --external --no-console

# Pass arguments to the program
porters execute calculator.cpp 10 + 20

# Use config defaults (porters.toml [run] section)
# If use-external-terminal = true in config:
porters execute interactive.cpp  # Opens in external terminal automatically

# With dependencies (automatic)
# Porters reads porters.toml and adds all dependency includes/libs
porters execute my_program.c

# Debug build (with compiler flags in porters.toml)
porters execute --release my_app.cpp

How It Works:

  1. Detect File Type: .c → C compiler, .cpp/.cxx/.cc → C++ compiler
  2. Find Compiler: Searches for gcc/clang/g++/clang++ in PATH
  3. Resolve Dependencies: Reads porters.toml [dependencies] section
  4. Add Include Paths: Automatically adds -I flags for all dependencies
  5. Add Library Paths: Automatically adds -L flags for dependency libs
  6. Compile: Compiles to temporary executable in .porters/cache/
  7. Execute: Runs compiled program with provided arguments

Error Handling:

If compilation fails, Porters displays the compiler output with errors highlighted. Common issues:

  • Missing compiler: Install gcc/g++ or clang/clang++
  • Dependency not found: Run porters sync to download dependencies
  • Compilation errors: Check source code and compiler output

porters build

Build the current project.

Usage:

porters build [OPTIONS]

Options:

  • --release, -r - Build in release mode (optimized)

Behavior:

  • Detects build system from porters.toml or auto-detects
  • Resolves dependencies
  • Runs build commands (e.g., cmake, xmake, meson)
  • Compiles source files

Examples:

# Debug build
porters build

# Release build
porters build --release

porters check

Check compilation of source files without creating executables (syntax-only check).

Usage:

porters check [FILE] [OPTIONS]

Arguments:

  • [FILE] - Optional path to a specific source file to check (e.g., src/main.c)
    • If omitted, checks all source files in the project

Options:

  • --verbose, -v - Display detailed compiler output including full error traces and warnings

Behavior:

  • Fast Compilation Check: Validates code syntax without generating executables
  • Smart Compiler Detection: Automatically selects appropriate compiler (GCC, Clang, MSVC)
  • Dependency Aware: Includes dependency paths from porters.toml configuration
  • Multi-Language Support: Handles both C and C++ files with correct standards
  • Detailed Error Reporting: Shows compilation errors with color-coded emoji indicators
  • Project or File Mode: Check entire project or single files

Compiler Flags Used:

  • GCC/Clang: -fsyntax-only (skips code generation and linking)
  • MSVC: /Zs (syntax check only)

Examples:

# Check all source files in the project
porters check

# Check a specific file
porters check src/main.c
porters check src/utils.cpp

# Check with verbose compiler output
porters check --verbose
porters check src/main.c --verbose

# Quick syntax validation before committing
porters check && git commit -m "Fixed compilation errors"

Output Example (Success):

✅ Checking compilation (syntax-only)
🔍 Discovering source files in project...
📦 Found 3 source file(s)

🔨 Checking: src/main.c (C)
✅ PASSED: src/main.c

🔨 Checking: src/utils.c (C)
✅ PASSED: src/utils.c

🔨 Checking: src/math.cpp (C++)
✅ PASSED: src/math.cpp

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Compilation Check Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   Total files checked: 3
   ✅ Passed: 3
   ❌ Failed: 0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ All compilation checks passed! ✅

Output Example (Error):

✅ Checking compilation (syntax-only)
🎯 Checking single file: src/main.c

🔨 Checking: src/main.c (C)
❌ FAILED: src/main.c

❌ Compilation errors:
  src/main.c:15:5: error: expected ';' before 'return'
  src/main.c:23:12: error: 'undeclared_var' undeclared
  ... (5 more lines, use --verbose for full output)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Compilation Check Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   Total files checked: 1
   ✅ Passed: 0
   ❌ Failed: 1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
❌ Compilation check failed: 1 error(s) found

Use Cases:

  • Rapid Feedback: Fast syntax validation during development
  • CI/CD Integration: Pre-build validation in pipelines
  • Code Review: Verify changes compile before creating pull requests
  • Debugging: Identify compilation errors without waiting for full builds
  • Learning: Students can quickly validate code syntax

Benefits:

  • Faster than full builds - No linking or executable generation
  • 🎯 Focused error messages - Only shows compilation issues
  • 🔍 File-level granularity - Check individual files or entire projects
  • 📊 Clear summaries - See pass/fail counts at a glance
  • 🛠️ Tool-agnostic - Works with any project structure

porters publish

Publish project to GitHub releases.

Usage:

porters publish [OPTIONS]

Options:

  • --version, -v <VERSION> - Version to publish (e.g., 1.0.0)
  • --token <TOKEN> - GitHub personal access token

Behavior:

  • Reads GitHub repository from porters.toml
  • Creates Git tag
  • Builds release binaries
  • Creates GitHub release
  • Uploads artifacts

Example:

porters publish --version 1.0.0

Prerequisites:

  • GitHub repository configured in porters.toml
  • GitHub token (via --token or GITHUB_TOKEN env variable)

porters self-update

Update Porters to the latest version.

Usage:

porters self-update

Behavior:

  • Fetches latest release from GitHub
  • Downloads platform-specific binary
  • Replaces current executable

Example:

porters self-update

porters add-to-path

Add the Cargo bin directory (~/.cargo/bin) to your system PATH environment variable.

Usage:

porters add-to-path

Behavior:

  • Windows: Adds %USERPROFILE%\.cargo\bin to User PATH via registry
  • Linux/macOS: Adds export PATH="$HOME/.cargo/bin:$PATH" to shell profile (~/.bashrc, ~/.zshrc, etc.)
  • Automatically detects your shell and configuration file
  • Creates backup before modifying shell profile
  • Requires administrator/elevated privileges on Windows

Platform-Specific Behavior:

Windows:

  • Modifies User environment variables in registry
  • Requires PowerShell with administrator privileges
  • Takes effect in new terminals after restart

Linux/macOS:

  • Appends to shell configuration file
  • Detects: bash, zsh, fish, or sh
  • Takes effect after: source ~/.bashrc (or restart terminal)

Example:

# Run with appropriate permissions
porters add-to-path

Output:

✅ Successfully added C:\Users\YourName\.cargo\bin to PATH
ℹ️  Please restart your terminal for changes to take effect

porters remove-from-path

Remove the Cargo bin directory from your system PATH environment variable.

Usage:

porters remove-from-path [OPTIONS]

Options:

  • --overwrite - Completely overwrite PATH with the new value (removes all Cargo bin references)

Behavior:

  • Without --overwrite: Removes first occurrence of Cargo bin directory
  • With --overwrite: Removes all occurrences and overwrites the entire PATH variable
  • Windows: Modifies User PATH via registry
  • Linux/macOS: Removes export statement from shell profile
  • Creates backup before modifications

Examples:

# Remove first occurrence of cargo bin from PATH
porters remove-from-path

# Remove all occurrences and overwrite PATH
porters remove-from-path --overwrite

Output:

✅ Successfully removed C:\Users\YourName\.cargo\bin from PATH
ℹ️  Please restart your terminal for changes to take effect

porters --check-system

Check system requirements and display installation status of C/C++ compilers and build tools.

Usage:

porters --check-system

Behavior:

  • Automatically runs on first launch after installation
  • Checks for C/C++ compilers: gcc, g++, clang, clang++, MSVC, MinGW
  • Checks for build systems: CMake, Make, XMake, Meson, Ninja
  • Displays installation instructions if requirements are missing
  • Saves check results to global config (~/.porters/config.toml)

Example:

porters --check-system

Example Output (Linux):

╭──────────────────────────────────────────────────╮
│  System Requirements Check                       │
╰──────────────────────────────────────────────────╯

Compilers
─────────
✅ g++ (version 11.4.0)
✅ gcc (version 11.4.0)
❌ clang++ (not found)
❌ clang (not found)

Build Systems
─────────────
✅ cmake (version 3.22.1)
✅ make (version 4.3)
❌ xmake (not found)

Status: ⚠️  Some tools are missing

Installation Instructions:
──────────────────────────

To install missing compilers and tools on Linux:

  sudo apt-get update
  sudo apt-get install clang build-essential cmake

For other distributions, use your package manager:
  - Fedora/RHEL: sudo dnf install clang cmake
  - Arch: sudo pacman -S clang cmake

First Run Behavior:

When you install Porters and run it for the first time (any command), it will:

  1. Automatically run the system check
  2. Display found compilers and build tools
  3. Show installation instructions if anything is missing
  4. Block execution if no C/C++ compiler is found

This ensures you have the necessary tools before using Porters.


porters run-script

Run a named script defined in porters.toml.

Usage:

porters run-script <NAME>

Arguments:

  • <NAME> - Script name defined in [scripts] section

Behavior:

  • Looks up script in porters.toml [scripts] section
  • Executes the script command in a shell
  • Shows available scripts if name not found
  • Cross-platform: Uses cmd.exe on Windows, sh on Unix

Configuration: Define scripts in porters.toml:

[scripts]
format = "clang-format -i src/**/*.{c,cpp,h,hpp}"
lint = "cppcheck --enable=all src/"
generate = "python scripts/codegen.py"

Examples:

# Run the 'format' script
porters run-script format

# Run the 'lint' script
porters run-script lint

# If script doesn't exist, shows available scripts
porters run-script unknown

Custom Commands

Execute custom commands defined in porters.toml.

Usage:

porters <command> [args...]

Behavior:

  • Searches for custom commands in [[commands]] array
  • Executes the matching command's script
  • Sets environment variables from command config
  • Falls back to showing available commands if not found

Configuration: Define custom commands in porters.toml:

[[commands]]
name = "docs"
script = "doxygen Doxyfile"

[commands.env]
DOXYGEN_OUTPUT = "docs/html"

[[commands]]
name = "benchmark"
script = "cmake --build build --target benchmark && ./build/benchmark"

[commands.env]
BENCHMARK_ITERATIONS = "1000"

Examples:

# Run custom 'docs' command
porters docs

# Run custom 'benchmark' command  
porters benchmark

# Custom commands are part of the main CLI
porters --help  # Shows all commands including custom ones

porters extension

Manage Porters extensions.

porters extension install

Install an extension.

Usage:

porters extension install <NAME> [OPTIONS]

Arguments:

  • <NAME> - Extension name

Options:

  • --git <URL> - Install from git repository
  • --path <PATH> - Install from local path

Examples:

# Install from crates.io
porters extension install porters-format

# Install from GitHub
porters extension install my-ext --git https://github.com/user/porters-ext-myext

# Install from local directory
porters extension install my-ext --path ./my-extension

porters extension uninstall

Uninstall an extension.

Usage:

porters extension uninstall <NAME>

Example:

porters extension uninstall porters-format

porters extension list

List installed extensions.

Usage:

porters extension list

Output:

📦 Installed Extensions:

  porters-format v1.0.0
    Code formatting extension for C/C++
    Repository: https://github.com/user/porters-format

  porters-docs v0.5.0
    Generate documentation with Doxygen

porters extension create

Create a new extension template.

Usage:

porters extension create <NAME>

Example:

porters extension create my-awesome-extension

Creates:

my-awesome-extension/
├── extension.toml
├── README.md
└── hooks/
    └── example.sh

Global Options

Available for all commands:

  • --help, -h - Show help message
  • --version, -V - Show version information

Examples:

porters --version
porters build --help

Environment Variables

Porters respects these environment variables:

  • GITHUB_TOKEN - GitHub personal access token for publishing
  • PORTERS_CONFIG - Path to global config (default: ~/.porters/config.toml)
  • PORTERS_CACHE - Path to global cache (default: ~/.porters/cache/)
  • PORTERS_OFFLINE - Enable offline mode (values: 1, true, yes)
  • HOME (Unix) / USERPROFILE (Windows) - User home directory

Example:

export GITHUB_TOKEN=ghp_xxxxxxxxxxxxx
porters publish --version 1.0.0

# Enable offline mode
export PORTERS_OFFLINE=1
porters sync  # Will use only cached dependencies

# Custom cache directory
export PORTERS_CACHE=/mnt/fast-ssd/porters-cache
porters sync

Offline Mode:

Enable offline mode to prevent all network access:

  1. Via Environment Variable:

    export PORTERS_OFFLINE=1
    
  2. Via Global Config (~/.porters/config.toml):

    offline = true
    
  3. Via Project Config (porters.toml):

    offline = true
    

When offline mode is enabled:

  • All dependencies must be cached
  • Registry searches use local cache only
  • No Git clones or fetches
  • Clear error messages for missing resources

Exit Codes

  • 0 - Success
  • 1 - Error (build failed, dependency not found, etc.)
  • 2 - Invalid arguments

Next Steps

Troubleshooting

Solutions to common issues when using Porters.

porters execute - Single File Execution Issues

"Execute: Compiler not found"

Cause: No C/C++ compiler installed or not in PATH

Solution:

# Install GCC (Linux)
sudo apt install build-essential

# Install Xcode Command Line Tools (macOS)
xcode-select --install

# Install MinGW (Windows)
# Download from mingw-w64.org
# Or install Visual Studio with C++ tools

# Verify installation
gcc --version        # For C
g++ --version        # For C++
clang --version      # Alternative compiler
clang++ --version    # Alternative C++ compiler

Note: porters execute automatically detects compilers in this order:

  • C files (.c): gcc → clang → cc
  • C++ files (.cpp, .cxx, .cc, .c++, .cp, .CPP): g++ → clang++ → c++

"Execute: Compilation failed"

Cause: Syntax errors or missing dependencies

Solution:

# 1. Check compiler output for specific errors
porters execute myfile.c

# 2. Verify dependencies are installed (if using porters.toml)
porters list

# 3. Sync dependencies if missing
porters sync

# 4. Add custom flags ONLY if needed (rarely required)
[run]  # This section is OPTIONAL!
compiler-flags = ["-Wall", "-Wextra"]  # See detailed warnings

# 5. Test compilation directly
gcc -I./include myfile.c -o test
./test

"Execute: Unsupported file extension"

Cause: File extension not recognized as C/C++

Solution:

Supported Extensions:

  • C files: .c, .C
  • C++ files: .cpp, .cxx, .cc, .c++, .cp, .CPP

Note: Header files (.h, .hpp, .hxx) cannot be executed directly - they must be included in a .c/.cpp file.

# Correct usage
porters execute main.c      # ✅ C file
porters execute app.cpp     # ✅ C++ file
porters execute code.cxx    # ✅ C++ file
porters execute prog.c++    # ✅ C++ file

# Incorrect - headers cannot be compiled
porters execute header.h    # ❌ Not supported
porters execute header.hpp  # ❌ Not supported

"Execute: Dependency includes not found"

Cause: Dependencies not resolved or incorrect paths

Solution:

# 1. Ensure dependencies are in porters.toml
[dependencies]
fmt = { git = "https://github.com/fmtlib/fmt" }

# 2. Sync dependencies
porters sync

# 3. Verify dependency structure
ls ports/fmt/include  # Should contain header files

# 4. Execute - includes are AUTOMATIC
porters execute main.cpp  # Dependencies auto-included!

# 5. ONLY add manual paths if automatic detection fails (rare)
[run]  # OPTIONAL section
include-dirs = ["./custom/path"]

Note: porters execute automatically finds and includes all dependency paths from porters.toml. Manual configuration is rarely needed.

"Execute: File not found"

Cause: Invalid file path or file doesn't exist

Solution:

# Use relative or absolute paths
porters execute ./src/main.c      # Relative
porters execute /full/path/file.c # Absolute

# Check file exists
ls ./src/main.c

# Ensure correct file extension
porters execute hello.c   # C file (uses gcc/clang)
porters execute hello.cpp # C++ file (uses g++/clang++)

"Execute: Permission denied"

Cause: Compiled executable lacks execute permissions

Solution:

# Linux/macOS: Porters handles this automatically
# If issues persist, check cache directory permissions
chmod -R u+rwx ~/.porters/cache

# Windows: Run as administrator if needed

"Execute: Custom compiler not used"

Cause: Compiler override in porters.toml not working

Solution:

# Ensure [run] section is correct (OPTIONAL - only if needed)
[run]
c-compiler = "clang"      # Must be in PATH
cpp-compiler = "clang++"

# Verify compiler is in PATH
which clang    # Linux/macOS
where clang    # Windows

# Test compiler directly
clang --version

Note: This is rarely needed! porters execute automatically finds compilers. Only use [run] section for custom compiler configurations.

porters run - Project Execution Issues

"Run: Build required before running"

Cause: Project not built or binary not found

Solution:

# Build project first
porters build

# Then run the compiled executable
porters run

# Or build and run in one go
porters build && porters run

"Run: Executable not found"

Cause: Build didn't produce expected binary

Solution:

# 1. Check build output directory
ls build/      # CMake default
ls .build/     # XMake default
ls builddir/   # Meson default

# 2. Verify build succeeded
porters build

# 3. Check porters.toml for correct output path
[build]
output-dir = "build"  # Should match your build system

# 4. Manually locate binary
find . -name "myproject" -type f

"Run: Arguments not passed to program"

Cause: Incorrect argument syntax

Solution:

# Correct usage
porters run -- arg1 arg2 arg3

# Everything after -- is passed to the program
porters run -- --flag=value input.txt

# Without -- for simple args (may work)
porters run arg1 arg2

Note: Use -- to separate porters args from program args if your program uses flags that might conflict with porters options.

Common Issues

"porters: command not found"

Cause: Porters not in PATH

Solution:

# Check if installed
cargo install --list | grep porters

# Add cargo bin to PATH (Linux/macOS)
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Windows: Add to PATH manually
# C:\Users\<username>\.cargo\bin

"porters.toml not found"

Cause: Running porters command outside project directory OR executing single file without project

Solution:

# For project commands (build, run, sync, etc.)
porters init  # Initialize project first
# OR
cd /path/to/your/project

# For single file execution - NO porters.toml needed!
porters execute hello.c  # Works anywhere!

Note: porters execute works without porters.toml. Other commands (build, run, sync) require a project. which clang # Linux/macOS where clang # Windows

Test compiler directly

clang --version


## Common Issues

### "porters: command not found"

**Cause**: Porters not in PATH

**Solution**:
```bash,no_run
# Check if installed
cargo install --list | grep porters

# Add cargo bin to PATH (Linux/macOS)
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Windows: Add to PATH manually
# C:\Users\<username>\.cargo\bin

"porters.toml not found"

Cause: Running porters command outside project directory

Solution:

# Initialize project first
porters init

# Or navigate to project directory
cd /path/to/your/project

Build Errors

"Build system not found"

Cause: Required build tool not installed

Solution:

# Install CMake
# macOS: brew install cmake
# Ubuntu: sudo apt install cmake
# Windows: Download from cmake.org

# Install XMake
# curl -fsSL https://xmake.io/shget.text | bash

# Install Meson
# pip install meson ninja

"Compiler not found"

Cause: No C/C++ compiler installed

Solution:

# Install GCC (Linux)
sudo apt install build-essential

# Install Clang (macOS)
xcode-select --install

# Install MSVC (Windows)
# Download Visual Studio with C++ tools

"Build failed with unknown error"

Cause: Various build issues

Solution:

# Clean build
rm -rf build/

# Rebuild
porters build

# Check build system files
# Ensure CMakeLists.txt, xmake.lua, etc. are valid

Dependency Resolution

"Failed to clone repository"

Cause: Git URL incorrect or authentication required

Solution:

# Test Git clone manually
git clone https://github.com/fmtlib/fmt

# For SSH URLs, ensure SSH keys are configured
ssh -T git@github.com

# Use HTTPS if SSH fails
porters add fmt --git https://github.com/fmtlib/fmt

"Dependency version conflict"

Cause: Multiple incompatible versions required

Solution:

# Check porters.lock
cat porters.lock

# Regenerate lock file
porters lock

# Manually resolve in porters.toml
# Specify compatible versions

"Dependency not found in ports/"

Cause: Dependencies not synchronized

Solution:

# Sync all dependencies
porters sync

# Force re-download
rm -rf ports/
porters sync

Platform-Specific Issues

Windows

Issue: "Permission denied" when running porters

Solution:

# Run as Administrator
# Or add exception to antivirus

Issue: Long path errors

Solution:

# Enable long paths
reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1

macOS

Issue: "xcrun: error: invalid active developer path"

Solution:

xcode-select --install

Issue: Library not found errors

Solution:

# Set library paths
export DYLD_LIBRARY_PATH=/usr/local/lib:$DYLD_LIBRARY_PATH

Linux

Issue: Missing system libraries

Solution:

# Install development tools
sudo apt install build-essential cmake git

# For specific libraries
sudo apt install libssl-dev libcurl4-openssl-dev

Performance Issues

Slow dependency downloads

Solution:

# Use global install for common libraries
porters install fmt --git https://github.com/fmtlib/fmt
porters install spdlog --git https://github.com/gabime/spdlog

# Enable caching (should be default)
# Check ~/.porters/config.toml

Slow builds

Solution:

# Increase parallel jobs in ~/.porters/config.toml
[settings]
parallel_jobs = 16  # Adjust based on CPU cores

Getting Help

If you encounter issues not covered here:

  1. Check GitHub Issues: github.com/muhammad-fiaz/Porters/issues
  2. Open New Issue: Provide:
    • Porters version (porters --version)
    • Operating system
    • Error message
    • Steps to reproduce
  3. Consult Documentation: Review relevant sections

Debug Mode

Enable verbose logging:

# Set environment variable
export RUST_LOG=debug
porters build

This provides detailed output for debugging.

Next Steps

Contributing

Thank you for your interest in contributing to Porters!

Ways to Contribute

  • 🐛 Report bugs
  • 💡 Suggest features
  • 📝 Improve documentation
  • 🔧 Submit code fixes
  • ✨ Add new features

Getting Started

1. Fork and Clone

git clone https://github.com/YOUR_USERNAME/Porters.git
cd Porters

2. Create a Branch

git checkout -b feature/my-awesome-feature

3. Build and Test

cargo build
cargo test
cargo run -- --version

Development Guidelines

Code Style

Follow Rust conventions:

cargo fmt
cargo clippy

Commit Messages

Use conventional commits:

feat: add support for Conan packages
fix: resolve dependency version conflicts
docs: update installation guide
test: add tests for lock file generation

Pull Requests

When submitting a pull request:

  1. Use the PR Template - GitHub automatically loads .github/pull_request_template.md

    • Complete all relevant sections (type of change, related issues, description)
    • Check off all applicable items in the checklists
    • Document breaking changes with migration guide if applicable
  2. Ensure all tests pass - Run cargo test locally before pushing

  3. Update documentation - Any new features or changes must update docs/src/

  4. Add tests for new features - Maintain or improve code coverage

  5. Follow existing code style - Run cargo fmt and cargo clippy

  6. Build system verification - Test with all supported build systems (CMake, XMake, Meson, Make)

PR Template Sections:

  • Type of change (bug fix, feature, breaking change, docs, refactor, etc.)
  • Related issues
  • Test configuration and steps
  • Code quality checklist (clippy, fmt, self-review)
  • Testing checklist (unit tests, integration tests, manual testing)
  • Documentation checklist (commands.md, configuration.md, README, CHANGELOG)
  • Breaking changes migration guide
  • Performance impact assessment

See .github/pull_request_template.md for the complete checklist.

Project Structure

Porters/
├── src/
│   ├── main.rs          # CLI entry point
│   ├── config.rs        # Configuration handling
│   ├── scan.rs          # Project scanning
│   ├── build/           # Build system integration
│   ├── deps/            # Dependency management
│   ├── global.rs        # Global configuration
│   ├── lockfile.rs      # Lock file management
│   └── util.rs          # Utilities
├── docs/                # Documentation (mdBook)
├── Cargo.toml           # Rust manifest
└── README.md

Testing

# Run all tests
cargo test

# Run specific test
cargo test test_name

# Integration tests
cargo test --test integration_tests

Documentation

Documentation uses mdBook:

# Install mdBook
cargo install mdbook

# Serve locally
cd docs
mdbook serve

# Build
mdbook build

Need Help?

  • Open an issue for questions
  • Join discussions on GitHub
  • Check existing issues before reporting

License

By contributing, you agree that your contributions will be licensed under the Apache License 2.0.

Architecture

Technical overview of Porters' architecture and design.

System Overview

Porters follows a modular architecture with clear separation of concerns:

┌─────────────────────────────────────────┐
│          CLI Interface (main.rs)        │
│         Command Parsing (clap)          │
└─────────────┬───────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────┐
│        Core Modules                     │
├─────────────────────────────────────────┤
│  • Config (config.rs)                   │
│  • Scanner (scan.rs)                    │
│  • Dependencies (deps/)                 │
│  • Build Systems (buildsystem.rs)       │
│  • Package Managers (pkg_managers/)     │
│  • Global Packages (global_packages.rs) │
│  • Global Config (global_config.rs)     │
│  • Lock File (lockfile.rs)              │
│  • Cache System (cache.rs)              │
│  • Registry (registry.rs)               │
└─────────────┬───────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────┐
│       External Tools                    │
├─────────────────────────────────────────┤
│  • Git (git2)                           │
│  • Build Tools (cmake, xmake, etc.)     │
│  • Package Managers (conan, vcpkg)      │
│  • GitHub API (octocrab)                │
└─────────────────────────────────────────┘

Module Breakdown

CLI Layer (main.rs)

Handles command-line interface using clap:

  • Command parsing with aliases (e.g., rm for remove, ls for list)
  • Argument validation
  • User interaction (via dialoguer)
  • Output formatting with emojis and colors

Configuration (config.rs)

Manages project configuration:

  • TOML parsing/writing (serde, toml)
  • Configuration validation
  • Schema enforcement
  • Default configuration generation
  • Config overrides from command line

Package Managers (pkg_managers/)

NEW: Integration with C/C++ package ecosystems:

Supported Managers:

  • Conan (conan.rs) - Creates conanfile.txt, CMakeDeps/CMakeToolchain
  • vcpkg (vcpkg.rs) - Manifest mode with vcpkg.json
  • XMake (xmake.rs) - xmake.lua with xrepo integration

Features:

  • Install Scopes: Local (ports/) vs Global (~/.porters/packages/)
  • Force Flags: Skip confirmation prompts with --force
  • Version Pinning: Specify exact package versions
  • Unified Interface: PackageManager trait for consistency

Scanner (scan.rs)

Project structure detection:

  • C/C++ file discovery
  • Build system detection
  • Directory traversal
  • Excluded directories (.porters, build, etc.)

Dependency Management (deps/)

Core dependency resolution:

  • Git repository cloning (git2)
  • Version constraint validation
  • Dependency graph resolution
  • Lock file management
  • Transitive dependency tracking

Build Systems (buildsystem.rs)

Integration with build tools:

  • CMake support
  • XMake support
  • Meson support
  • Make support
  • Ninja support
  • Custom commands
  • Automatic detection
  • Build system validation

Global Packages (global_packages.rs)

Manages global state and project paths:

  • ~/.porters/ directory structure
  • Project directory helpers
  • Lock file paths
  • Dependency directory paths
  • Settings persistence

Global Configuration (global_config.rs)

NEW: Enhanced global configuration system:

Features:

  • Offline Mode: Blocks all network activity when enabled
  • Cache Configuration: Configurable cache directory and size limits
  • Registry Configuration: Remote registry URL and auto-update settings
  • User Preferences: Default author, email, license, language
  • System Requirements Check: Automatic compiler and build tool detection

Configuration Location: ~/.porters/config.toml

Example:

porters_version = "0.0.1"
offline = false
auto_update_check = true

[preferences]
author = "Your Name"
email = "you@example.com"
license = "MIT"
default_language = "cpp"

[cache]
enabled = true
max_size_mb = 1024
auto_clean = true

[registry]
url = "https://github.com/muhammad-fiaz/porters"
auto_update = true

Cache System (cache.rs)

NEW: Dual-layer caching architecture:

GlobalCache

  • Centralized cache in ~/.porters/cache/
  • Shared across all projects
  • Cache-first dependency resolution
  • Offline mode support
  • Package storage, retrieval, and listing
  • Cache statistics and cleanup

DependencyCache

  • Project-local cache in .porters/cache/
  • Build artifacts and temporary files
  • Hash-based verification
  • Download caching

Features:

  • SHA-256 checksum validation
  • Automatic cache invalidation
  • Human-readable size reporting
  • .git directory exclusion during copy

Lock File (lockfile.rs)

Ensures reproducible builds:

  • Dependency version pinning
  • Checksum generation (SHA-256)
  • Transitive dependency tracking
  • Timestamp management
  • Git commit resolution
  • Offline mode compatibility

Registry (registry.rs)

NEW: Enhanced package registry with remote sync:

Features:

  • Package discovery and search
  • Version resolution
  • Metadata management
  • Remote Registry Sync: Git sparse checkout from GitHub
  • Offline Mode Support: Works without network when registry is cached
  • Auto-Update: Configurable automatic registry updates
  • Tag-based searching
  • Package listing

Registry Location: ~/.porters/registry-index/

Integration:

  • Syncs from configured GitHub repository
  • Falls back to local registry in offline mode
  • Caches registry metadata for performance

Data Flow

Adding a Dependency

User: porters add fmt --git https://...
  │
  ├─> Parse arguments (main.rs)
  │
  ├─> Validate Git URL (deps/mod.rs)
  │
  ├─> Clone repository (git2)
  │      └─> Download to .porters/cache/sources/
  │
  ├─> Update porters.toml (config.rs)
  │
  └─> Update porters.lock (lockfile.rs)

Adding a Package Manager Dependency

User: porters conan add fmt --global
  │
  ├─> Parse arguments (main.rs)
  │
  ├─> Check if Conan is installed
  │
  ├─> Determine scope (Global vs Local)
  │      ├─> Global: ~/.porters/packages/conan/
  │      └─> Local:  ports/conan/
  │
  ├─> Update conanfile.txt
  │
  └─> Run conan install (optional)

Building a Project

User: porters build
  │
  ├─> Read porters.toml (config.rs)
  │
  ├─> Resolve dependencies (deps/)
  │      ├─> Check porters.lock
  │      ├─> Sync missing deps to .porters/cache/
  │      └─> Validate constraints
  │
  ├─> Check binary cache (bin_cache.rs)
  │      └─> Return if cached build is valid
  │
  ├─> Detect build system (scan.rs, build/)
  │
  ├─> Run build in .porters/build/
  │
  └─> Copy output to build/
         └─> cmake/xmake/meson/make

Directory Structure

Global Directory (~/.porters/)

.porters/
├── config.toml           # Global configuration (offline, cache, registry)
├── cache/                # Global dependency cache (NEW)
│   ├── <package>/       # Cached packages by name
│   │   └── <version>/   # Version-specific cache
│   └── ...
├── registry-index/       # Remote registry cache (NEW)
│   ├── packages/        # Package metadata
│   └── .git/            # Git sparse checkout data
├── packages/             # Globally installed packages
│   ├── conan/           # Conan global packages
│   │   └── conanfile.txt
│   ├── vcpkg/           # vcpkg global packages
│   │   └── vcpkg.json
│   └── xmake/           # XMake global packages
│       └── xmake.lua
└── temp/                 # Temporary files

Project Directory

my-project/
├── porters.toml          # Project config
├── porters.lock          # Lock file (reproducible builds)
├── .porters/             # Project-specific cache (GITIGNORED)
│   ├── cache/           # Build artifacts, temp files
│   │   ├── sources/     # Cached downloads
│   │   └── build/       # Intermediate build files
│   └── temp/            # Temporary working directory
├── ports/                # Local dependencies
│   ├── fmt/             # Git dependency
│   ├── spdlog/          # Git dependency
│   ├── conan/           # Conan packages (local scope)
│   │   └── conanfile.txt
│   ├── vcpkg/           # vcpkg packages (local scope)
│   │   └── vcpkg.json
│   └── xmake/           # XMake packages (local scope)
│       └── xmake.lua
├── src/                  # Source files
├── include/              # Headers
└── build/                # Final build output (executables, libraries)

Cache Organization

Key Principle: .porters/ contains all generated/temporary files, build/ contains final artifacts

  • .porters/cache/: Intermediate build files, downloads, temporary data
  • build/: Final compiled binaries and libraries
  • ports/: Local dependency source code
  • ~/.porters/packages/: Global package manager installations

Key Design Decisions

1. Dual-Layer Dependencies

Global (~/.porters/packages/):

  • Shared across projects
  • Faster setup for common libraries
  • Centralized cache
  • Reduces disk space usage

Local (ports/):

  • Project-specific isolation
  • Version pinning per project
  • Reproducible builds
  • No conflicts between projects

Rationale: Balance between performance and isolation.

2. Separated Cache and Build Directories

.porters/ (cache):

  • All temporary and generated files
  • Safe to gitignore
  • Can be cleaned without losing source code

build/ (output):

  • Final build artifacts only
  • Clear separation of concerns
  • Matches developer expectations

Rationale: Clean separation between intermediate and final artifacts.

3. Lock File Format

Uses TOML for human readability and compatibility with porters.toml.

Alternative considered: Binary format (rejected for poor diff-ability in Git)

4. Build System Agnostic

Supports multiple build systems instead of enforcing one.

Rationale: Respects existing project structures and developer preferences.

5. Cache-First Dependency Model

NEW: Prioritizes global cache before network access.

Resolution Order:

  1. Check global cache (~/.porters/cache/)
  2. Check local cache (.porters/cache/)
  3. Download from network (if not offline)
  4. Store in global cache for reuse

Rationale:

  • Drastically reduces network usage
  • Enables true offline development
  • Shares dependencies across projects
  • Faster setup for common libraries

6. Offline Mode Support

NEW: Complete functionality without network access.

When Enabled:

  • Uses only cached dependencies
  • Blocks all network requests
  • Falls back to local registry
  • Clear error messages for missing resources

Rationale:

  • Supports air-gapped environments
  • Reliable builds without internet
  • Prevents unexpected network failures
  • Faster CI/CD pipelines

7. Git-First Dependency Model

Prioritizes Git repositories over package registries.

Rationale:

  • C/C++ ecosystem heavily Git-based
  • Simpler initial implementation
  • Direct source access
  • Registry support available via remote sync

8. Package Manager Integration

Integrates with existing C/C++ package managers rather than replacing them.

Rationale:

  • Leverages existing ecosystems
  • No need to repackage libraries
  • Familiar workflow for C/C++ developers
  • Unified interface across managers

7. InstallScope Enum

Type-safe distinction between local and global installations.

Rationale:

  • Prevents accidental scope mixing
  • Clear intent in code
  • Compile-time safety
  • Default to Local for project isolation

Technology Stack

Core Dependencies

  • clap (4.5): CLI framework with derive macros
  • serde (1.0): Serialization/deserialization
  • toml (0.9): TOML parsing and writing
  • tokio (1.48): Async runtime for concurrent operations
  • git2 (0.20): Git operations (cloning, fetching)
  • dialoguer (0.12): User prompts and confirmations
  • reqwest (0.12): HTTP client for downloads
  • self_update (0.42): Self-update mechanism
  • anyhow (1.0): Error handling
  • thiserror (2.0): Error derive macros
  • colored (3.0): Terminal colors
  • walkdir (2.5): Recursive directory traversal
  • regex (1.12): Pattern matching
  • petgraph (0.8): Dependency graph
  • sha2 (0.10): SHA-256 hashing
  • chrono (0.4): Timestamps
  • semver (1.0): Semantic versioning
  • which (8.0): Executable detection

Performance Characteristics

  • Async I/O: Concurrent downloads with tokio
  • Parallel builds: Configurable via parallel_jobs
  • Caching: Minimizes redundant downloads and builds
  • Binary cache: Reuses compiled artifacts
  • Incremental builds: Only rebuilds changed files

Extensibility

Adding a New Build System

  1. Create module in src/build/
  2. Implement build detection logic
  3. Add build command execution
  4. Update detect_existing_build_system()
  5. Add tests in module
  6. Update documentation

Adding a New Package Manager

  1. Create module in src/pkg_managers/
  2. Implement PackageManager trait
  3. Add scope-aware install/remove methods
  4. Support local and global paths
  5. Add confirmation prompts
  6. Create comprehensive tests
  7. Update package-managers.md

Example skeleton:

#![allow(unused)]
fn main() {
pub struct NewManager {
    ports_dir: String,
}

impl PackageManager for NewManager {
    fn install(&self, package: &str, version: Option<&str>, scope: InstallScope) -> Result<()> {
        // Implementation
    }
    
    fn remove(&self, package: &str, scope: InstallScope, force: bool) -> Result<()> {
        // With confirmation if !force
    }
    
    fn get_install_path(&self, scope: InstallScope) -> PathBuf {
        match scope {
            InstallScope::Local => PathBuf::from(&self.ports_dir).join("newmanager"),
            InstallScope::Global => {
                let home = std::env::var("HOME")
                    .or_else(|_| std::env::var("USERPROFILE"))
                    .unwrap_or_else(|_| ".".to_string());
                PathBuf::from(home).join(".porters").join("packages").join("newmanager")
            }
        }
    }
    
    // ... other methods
}
}

Adding a New Dependency Source

  1. Extend DependencySource enum (deps/mod.rs)
  2. Implement cloning/fetching logic
  3. Add constraint validation
  4. Update lock file format
  5. Add tests
  6. Update documentation

Configuration Override System

Automatic Behavior (default):

  • Detects build system automatically
  • Uses sensible defaults
  • Configures caching automatically

Manual Overrides (porters.toml):

  • [build] section overrides build system
  • [cache] section overrides cache settings
  • [dependencies] overrides auto-detection

Example:

[project]
name = "myproject"

[build]
system = "cmake"  # Overrides auto-detection
generator = "Ninja"  # Overrides default

[cache]
enabled = true
max_size = "5GB"  # Overrides default

[dependencies]
# Manual dependency specification overrides auto-scan

Cross-Platform Support

Path Handling

  • Uses PathBuf for all paths
  • Handles both / and \ separators
  • Respects HOME (Unix) and USERPROFILE (Windows)

Build System Detection

  • Checks for executables with which crate
  • Platform-specific defaults (MSVC on Windows, GCC on Unix)

Testing

  • All tests pass on Windows, Linux, macOS
  • Path assertions handle both separators
  • No hardcoded platform assumptions

Next Steps

Development Guide

Guide for developers working on Porters.

Development Setup

Prerequisites

  • Rust 1.70+ (install via rustup)
  • Git
  • At least one build system (CMake, XMake, Meson, or Make)
  • C/C++ compiler (for testing)

Clone and Build

git clone https://github.com/muhammad-fiaz/Porters.git
cd Porters

# Build
cargo build

# Run tests
cargo test

# Run development version
cargo run -- --version

Project Structure

Porters/
├── src/
│   ├── main.rs           # CLI entry point
│   ├── config.rs         # Configuration handling
│   ├── scan.rs           # Project scanning
│   ├── util.rs           # Utilities
│   ├── global.rs         # Global config management
│   ├── lockfile.rs       # Lock file management
│   ├── build/            # Build system modules
│   │   ├── mod.rs
│   │   ├── cmake.rs
│   │   ├── xmake.rs
│   │   └── ...
│   └── deps/             # Dependency management
│       ├── mod.rs
│       └── resolve.rs
├── tests/                # Integration tests
├── docs/                 # Documentation (mdBook)
├── Cargo.toml            # Rust manifest
└── README.md

Development Workflow

1. Create Feature Branch

git checkout -b feature/my-feature

2. Make Changes

Follow Rust best practices:

# Format code
cargo fmt

# Check for errors
cargo check

# Run clippy
cargo clippy

3. Add Tests

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_my_feature() {
        // Test code
    }
}
}

4. Run Tests

# All tests
cargo test

# Specific test
cargo test test_my_feature

# With output
cargo test -- --nocapture

5. Commit and Push

git add .
git commit -m "feat: add my feature"
git push origin feature/my-feature

6. Open Pull Request

Create PR on GitHub with:

  • Clear description
  • Test results
  • Documentation updates

Testing

Unit Tests

Located in each module:

#![allow(unused)]
fn main() {
// src/config.rs
#[cfg(test)]
mod tests {
    #[test]
    fn test_parse_config() {
        // ...
    }
}
}

Run:

cargo test --lib

Integration Tests

Located in tests/ directory:

#![allow(unused)]
fn main() {
// tests/integration_test.rs
#[tokio::test]
async fn test_project_creation() {
    // ...
}
}

Run:

cargo test --test integration_test

Test Coverage

# Install tarpaulin
cargo install cargo-tarpaulin

# Generate coverage
cargo tarpaulin --out Html

Debugging

Enable Logging

export RUST_LOG=debug
cargo run -- build

VS Code Debugging

.vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "lldb",
      "request": "launch",
      "name": "Debug Porters",
      "cargo": {
        "args": ["build", "--bin=porters"]
      },
      "args": ["build"],
      "cwd": "${workspaceFolder}"
    }
  ]
}

Documentation

Code Documentation

#![allow(unused)]
fn main() {
/// Adds a dependency to the project
///
/// # Arguments
///
/// * `name` - Dependency name
/// * `git` - Git repository URL
///
/// # Examples
///
/// ```no_run
/// add_dependency("fmt", Some("https://github.com/fmtlib/fmt")).await?;
/// ```
pub async fn add_dependency(name: &str, git: Option<String>) -> Result<()> {
    // ...
}
}

Generate docs:

cargo doc --open

User Documentation

Uses mdBook:

cd docs
mdbook serve --open

Release Process

1. Update Version

Cargo.toml:

[package]
version = "0.2.0"

2. Update CHANGELOG

## [0.2.0] - 2024-01-15

### Added
- Feature X
- Feature Y

### Fixed
- Bug Z

3. Create Git Tag

git tag -a v0.2.0 -m "Release v0.2.0"
git push origin v0.2.0

4. Publish to crates.io

cargo publish

5. GitHub Release

Create release on GitHub with:

  • Tag: v0.2.0
  • Title: "Porters v0.2.0"
  • Description: Copy from CHANGELOG
  • Attach binaries

Performance Profiling

CPU Profiling

# Install flamegraph
cargo install flamegraph

# Profile
cargo flamegraph --bin porters -- build

Memory Profiling

# Install valgrind (Linux)
sudo apt install valgrind

# Profile
valgrind --leak-check=full cargo run -- build

Benchmarking

#[bench]
fn bench_dependency_resolution(b: &mut Bencher) {
    b.iter(|| {
        // Code to benchmark
    });
}

Run:

cargo bench

Continuous Integration

GitHub Actions workflow (.github/workflows/ci.yml):

name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
      - run: cargo test
      - run: cargo clippy
      - run: cargo fmt --check

Next Steps