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
--externalflag - 📄 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
- 📖 Browse the User Guide for detailed instructions
- 🔍 Check the Command Reference for all available commands
- 🐛 Visit the Troubleshooting section for common issues
- 💡 Read the Contributing guide to get involved
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)
- Xcode Command Line Tools:
-
Linux:
- GCC:
sudo apt install gcc g++(Debian/Ubuntu) - Clang:
sudo apt install clang(Debian/Ubuntu)
- GCC:
Build Tools (Optional but recommended)
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
- Windows:
-
XMake
- Install from xmake.io
- Cross-platform build utility
-
Meson
- Install via pip:
pip install meson ninja - Fast and user-friendly build system
- Install via pip:
-
Make
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
Via Cargo (Recommended)
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:
- Check for C/C++ compilers (gcc, g++, clang, MSVC, MinGW)
- Check for build systems (CMake, Make, XMake, Meson, Ninja)
- Display what's found with version numbers
- Show installation instructions for missing tools
- Save detected tools to
~/.porters/config.toml - 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:
- Check Rust installation
- Install Porters via cargo
- Automatically add to PATH (with your permission)
- Verify the installation
From GitHub Releases
Download pre-built binaries from the GitHub Releases page:
- Download the appropriate binary for your platform
- Extract the archive
- Move the
portersbinary 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 settingspackages/- Globally installed packagescache/- 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:
- Project Type: Application or Library
- Language: C, C++, or Both
- Library Name: (If creating a library)
- Author: Your name (saved to global config for future use)
- Email: Your email (saved to global config)
- Repository URL: Git repository URL (optional)
- License: Choose from MIT, Apache-2.0, GPL-3.0, BSD, MPL-2.0, LGPL-3.0, Unlicense
- Build System: CMake, XMake, Meson, Make, or Custom
What Gets Created
Application Project:
src/main.cpporsrc/main.cwith "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.tomlconfiguration- Git repository initialization
Library Project:
- Complete library structure:
include/<libname>/<libname>.hpp- Public header with namespace/APIsrc/<libname>.cpp- Implementationexamples/example.cpp- Usage exampletests/test_<libname>.cpp- Test skeleton
- Build system configuration with library target
- LICENSE file auto-generated
- README.md with library usage examples
porters.tomlwithproject-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?
- Gradual Migration: Migrate legacy C code to C++ incrementally
- Best of Both Worlds: Use C for low-level operations, C++ for high-level features
- Library Integration: Integrate existing C libraries in C++ applications
- 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:
- Detect your existing source files
- Auto-detect your build system (if present)
- Ask for project metadata interactively:
- Project name (defaults to folder name)
- Version (defaults to 0.1.0)
- Author
- Description
- License
- Create a
porters.tomlconfiguration 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:
- Resolve dependencies from
porters.toml - Clone missing dependencies to
ports/ - Update
porters.lock - 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.tomlneeded - ✅ Auto Compiler Detection - Finds gcc/clang/g++/clang++ in PATH
- ✅ All Extensions -
.c,.cpp,.cxx,.cc,.c++,.cp,.C,.CPP - ✅ Dependency Resolution - Reads
porters.tomlif 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?
- Learn about Dependency Management
- Explore Package Managers in detail
- Read about Build Configuration
- Check the Command Reference
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:
- Read dependencies from
porters.toml - Download missing packages to
ports/ - Resolve version constraints
- 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:
- Check
porters.lockfor existing resolution - Fetch from Git/path source
- Verify constraints (version, branch, tag)
- Clone to
ports/directory - 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
- Explore Build Configuration
- Review Command Reference
- Check Troubleshooting
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 dependenciesxrepo installto fetch and install packagesadd_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:
- Add the package via package manager:
porters conan add fmt - Reference it in your
porters.tomlif needed - 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:
- Verify the tool is installed:
conan --version,vcpkg version,xmake --version - Ensure the tool is in your PATH
- Restart your terminal after installation
Installation Fails
If package installation fails:
- Check network connectivity
- Try with
--verboseflag (future feature) - Check package manager logs in
ports/{manager}/ - Verify the package name is correct using search:
porters conan search package-name
Global Packages Not Found
If global packages aren't accessible:
- Verify installation:
porters conan list --global - Check
~/.porters/packages/directory exists - Ensure proper permissions on global directory
Version Conflicts
If you encounter version conflicts:
- Use local installations for conflicting packages
- Create separate global environments (future feature)
- 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
- Dependencies - Managing Porters dependencies
- Commands Reference - Complete command reference
- Build Configuration - Configuring builds
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
- First Use: On first registry operation, Porters clones the registry from GitHub
- Local Index: Registry is stored in
~/.porters/registry-index/ - Fast Searches: All searches use the local index (no network needed)
- Auto-Update: Registry can auto-update based on configuration
Registry Locations
| Location | Purpose | Path |
|---|---|---|
| Remote | Official registry | https://github.com/muhammad-fiaz/porters |
| Local Index | Cached 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 than1.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
- Fork the Porters repository
- Add your package JSON file to
registry/category/ - Create categories if needed (e.g.,
registry/ml/for machine learning) - 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:
- Conan - Check if available in Conan Center
- vcpkg - Check if available in vcpkg registry
- XMake - Check if available in XMake repo
- Porters Registry - Search local registry JSON files
Dependency Resolution Process
- Parse root dependencies from your
porters.toml - Fetch package metadata from registry
- Validate version constraints (SemVer)
- Check platform requirements (OS, architecture)
- Verify compiler constraints (version, C++ standard)
- Resolve nested dependencies recursively
- Detect circular dependencies using graph analysis
- Detect version conflicts across dependency tree
- Topological sort for installation order
- 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:
- Update one library to a compatible version
- Use features to make dependencies optional
- Fork and update dependency versions
- 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:
- Upgrade your compiler
- Use a different package version
- Check if constraint can be relaxed
🔗 Related
- Dependencies - Managing project dependencies
- Building - Building your project
- Configuration - Configuring Porters
- Contributing - Contributing to Porters
📚 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
| Platform | Cache Directory |
|---|---|
| Linux/macOS | ~/.porters/cache/ |
| Windows | C:\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:
- Check Global Cache: First, check if the dependency exists in
~/.porters/cache/ - Use Cached Version: If found, copy from global cache to project (fast)
- Download: If not cached, download the dependency from source
- Store in Global Cache: After download, store in global cache for future use
- 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:
- Checks global cache for existing version
- Downloads if not cached
- Stores in global cache
- 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:
| Projects | Without Cache | With Cache | Savings |
|---|---|---|---|
| 5 projects | 500 MB | 150 MB | 70% |
| 10 projects | 1.2 GB | 200 MB | 83% |
| 20 projects | 2.5 GB | 300 MB | 88% |
Time Savings
Typical time savings:
| Dependency Size | Download | From Cache | Speedup |
|---|---|---|---|
| Small (< 1 MB) | 2-5 sec | < 1 sec | 3-5x |
| Medium (1-10 MB) | 10-30 sec | 1-2 sec | 10-15x |
| Large (> 10 MB) | 60+ sec | 3-5 sec | 12-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
- Configuration - Global and project configuration
- Offline Mode - Working without internet
- Dependency Management - Managing dependencies
- Registry - Package registry system
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 System | Detection Files | Status |
|---|---|---|
| CMake | CMakeLists.txt | ✅ Full Support |
| XMake | xmake.lua | ✅ Full Support |
| Meson | meson.build | ✅ Full Support |
| Make | Makefile, makefile, GNUmakefile | ✅ Full Support |
| Ninja | build.ninja | ✅ Full Support |
| Autotools | configure, configure.ac | ✅ Full Support |
| SCons | SConstruct, SConscript | ✅ Full Support |
| Conan | conanfile.txt, conanfile.py | ✅ Full Support |
| vcpkg | vcpkg.json | ✅ Full Support |
| Bazel | BUILD, BUILD.bazel, WORKSPACE | ✅ Full Support |
| Buck2 | BUCK, .buckconfig | ✅ Full Support |
| Premake | premake5.lua, premake4.lua | ✅ Full Support |
| QMake | *.pro | ✅ Full Support |
| Custom | porters.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
CMake (Recommended)
[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.tomlrequired - ✅ 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.tomlif 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.exewindow - 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
| Extension | Language | Compiler Used |
|---|---|---|
.c, .C | C | gcc → clang → cc |
.cpp, .CPP | C++ | g++ → clang++ → c++ |
.cxx | C++ | g++ → clang++ → c++ |
.cc | C++ | g++ → clang++ → c++ |
.c++ | C++ | g++ → clang++ → c++ |
.cp | C++ | 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):
- Tries
gccfirst (most common) - Falls back to
clang - Falls back to generic
cc
For C++ files (.cpp, .cxx, .cc, .c++, .cp, .CPP):
- Tries
g++first (most common) - Falls back to
clang++ - 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
| Feature | porters execute | porters run |
|---|---|---|
| Purpose | Single-file quick execution | Run compiled project binary |
| Requires Project | No | Yes (porters.toml) |
| Compilation | Compiles on-the-fly | Uses pre-built binary |
| Build System | None (direct compiler) | CMake/XMake/Meson/Make/etc. |
| Multi-file Support | Single file only | Full project |
| Speed | Compiles each time | Fast (already built) |
| Best For | Prototyping, testing, scripts | Production 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
- Supported extensions:
[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:
- ✅ No configuration - Works immediately
- ✅ No project setup - Execute files anywhere
- ✅ Automatic dependencies - Reads
porters.tomlif present - ✅ All C/C++ extensions -
.c,.cpp,.cxx,.cc,.c++,.cp - ✅ Smart compiler detection - Finds gcc, g++, clang, clang++
- ✅ 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 projectporters run- Execute compiled binary
Publishing
Publish your C/C++ projects to GitHub releases.
Prerequisites
- GitHub repository configured in
porters.toml:
[project]
repository = "https://github.com/username/project"
- GitHub Personal Access Token:
- Create at github.com/settings/tokens
- Required scopes:
repo
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
- Creates Git tag (e.g.,
v1.0.0) - Builds release binaries
- Creates GitHub release
- 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.
Manual Installation (Recommended)
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:
- Find Extension: Browse GitHub, documentation, or community recommendations
- Install Manually: Clone to
~/.porters/extensions/<name>/ - Configure (Optional): Add to
extensionsarray inporters.toml - 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.tomlmanifest - A
README.mdwith 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:
- Create the
~/.porters/directory - Generate
config.tomlwith default settings - Run system requirements check
- 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 configconfig_version- Configuration file format version
[user] Section
name- Default author name for new projectsemail- Default email for new projectsdefault_license- Default license (MIT, Apache-2.0, GPL-3.0, etc.)
[cache] Section
dir- Directory for temporary build files and executablesmax_size_mb- Maximum cache size in megabytesauto_clean- Automatically clean old cache filescache_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 indexindex_path- Local registry index directorylast_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 checkcompilers- 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 runcheck_updates- Check for Porters updates automatically
System Requirements Check
On first run (or when running porters --check-system), Porters will:
-
Check for C/C++ Compilers:
- gcc, g++
- clang, clang++
- MSVC (Windows)
- MinGW (Windows)
-
Check for Build Systems:
- CMake
- Make
- XMake
- Meson
- Ninja
-
Display Results:
- ✅ Found tools with version numbers
- ❌ Missing tools with installation instructions
-
Save to Config:
- Detected tool paths saved to
~/.porters/config.toml - Used for faster detection in future runs
- Detected tool paths saved to
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 nameversion- Semantic version (e.g., "1.0.0")
Optional fields:
description- Project descriptionlicense- SPDX license identifierauthors- List of authorsrepository- Git repository URLproject-type- "application" or "library"entry_point- Main entry pointplatforms- Target platformskeywords- Search keywordsreadme- 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
-Ifor all dependency includes - ✅ Library Paths - Automatically adds
-Lfor 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:
--externalflag - Example use cases:
- Interactive TUI applications that manage terminal state
- Games that need full terminal control
- Programs that should run independently of IDE/editor
- When
-
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-consoleflag - Example use cases:
- Windows GUI applications using Win32 API, SDL, SFML
- Applications where console window would be distracting
- Release builds of GUI apps
- When
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.3or==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:
- Calculates checksums on first download
- Stores checksums in
porters.lock - 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:
- When you add a dependency, Porters checks if it has its own
porters.toml - If found, Porters reads the dependency's dependencies
- This process repeats recursively to build a full dependency graph
- Circular dependencies are detected and reported as errors
- 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
mylibhas dependencies onfmtandspdlog - Automatically install
fmtandspdlog(transitive dependencies) - Build them in the correct order:
fmt→spdlog→mylib→your-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:
-
Network Operations Blocked:
- ❌ No Git clones/fetches
- ❌ No registry updates from GitHub
- ❌ No package downloads
-
Cache-First Resolution:
- ✅ Uses global cache (
~/.porters/cache/) - ✅ Uses local cache (
.porters/cache/) - ✅ Uses local registry index (
~/.porters/registry-index/)
- ✅ Uses global cache (
-
Error Handling:
- If dependency not in cache → clear error message
- Suggests running without
--offlineto 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
-
Pre-populate cache before going offline:
porters sync porters cache verify -
Use lock file for reproducibility:
porters lock # Commit porters.lock to version control -
Periodic cache updates:
# Weekly: update registry and dependencies porters registry update porters update -
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.tomlconfiguration - 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.corsrc/main.cpp(based on language choice)- Basic "Hello, World!" starter code
- Build system configuration (CMakeLists.txt, xmake.lua, etc.)
porters.tomlwithproject-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:
- Automatic
extern "C"Setup: C headers properly wrapped for C++ compatibility - Mixed Compilation: Build system automatically handles both .c and .cpp files
- Namespace Separation: C++ code uses namespaces, C code uses prefixes
- Documentation Comments: Clear examples of cross-language usage
- 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.tomland.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.lockwith checksums and metadata - Records timestamp
Example:
porters lock
porters registry
Manage Porters package registry operations.
Usage:
porters registry <SUBCOMMAND>
Subcommands:
porters registry search
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.lockwith 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 inporters.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.tomland 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:
--externalflag
-
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-consoleflag
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:
- Detect File Type:
.c→ C compiler,.cpp/.cxx/.cc→ C++ compiler - Find Compiler: Searches for gcc/clang/g++/clang++ in PATH
- Resolve Dependencies: Reads
porters.toml[dependencies] section - Add Include Paths: Automatically adds
-Iflags for all dependencies - Add Library Paths: Automatically adds
-Lflags for dependency libs - Compile: Compiles to temporary executable in
.porters/cache/ - 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 syncto 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.tomlor 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.tomlconfiguration - 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
--tokenorGITHUB_TOKENenv 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\binto 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:
- Automatically run the system check
- Display found compilers and build tools
- Show installation instructions if anything is missing
- 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.exeon Windows,shon 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 publishingPORTERS_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:
-
Via Environment Variable:
export PORTERS_OFFLINE=1 -
Via Global Config (
~/.porters/config.toml):offline = true -
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- Success1- Error (build failed, dependency not found, etc.)2- Invalid arguments
Next Steps
- Review Configuration
- Read Troubleshooting
- Explore Getting Started
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:
- Check GitHub Issues: github.com/muhammad-fiaz/Porters/issues
- Open New Issue: Provide:
- Porters version (
porters --version) - Operating system
- Error message
- Steps to reproduce
- Porters version (
- 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:
-
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
-
Ensure all tests pass - Run
cargo testlocally before pushing -
Update documentation - Any new features or changes must update
docs/src/ -
Add tests for new features - Maintain or improve code coverage
-
Follow existing code style - Run
cargo fmtandcargo clippy -
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.,
rmforremove,lsforlist) - 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) - Createsconanfile.txt, CMakeDeps/CMakeToolchain - vcpkg (
vcpkg.rs) - Manifest mode withvcpkg.json - XMake (
xmake.rs) -xmake.luawith 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:
PackageManagertrait 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 databuild/: Final compiled binaries and librariesports/: 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:
- Check global cache (
~/.porters/cache/) - Check local cache (
.porters/cache/) - Download from network (if not offline)
- 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
- Create module in
src/build/ - Implement build detection logic
- Add build command execution
- Update
detect_existing_build_system() - Add tests in module
- Update documentation
Adding a New Package Manager
- Create module in
src/pkg_managers/ - Implement
PackageManagertrait - Add scope-aware install/remove methods
- Support local and global paths
- Add confirmation prompts
- Create comprehensive tests
- 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
- Extend
DependencySourceenum (deps/mod.rs) - Implement cloning/fetching logic
- Add constraint validation
- Update lock file format
- Add tests
- 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
PathBuffor all paths - Handles both
/and\separators - Respects
HOME(Unix) andUSERPROFILE(Windows)
Build System Detection
- Checks for executables with
whichcrate - 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