- 1. Prerequisites
- 2. Installing PasBuild
- 3. Project Types
- 4. Creating Your First Project
- 5. Building Your Project
- 6. Advanced Features
- 7. Working with Resources
- 8. Available Goals
- 9. Configuration Reference
- 10. Command-Line Reference
- 11. Directory Layout
- 12. Troubleshooting
- 13. Next Steps
- 14. Getting Help
Complete guide to getting started with PasBuild.
PasBuild requires the Free Pascal Compiler (FPC) version 3.2.2 or later.
Verify FPC Installation:
fpc -iVThis should output a version number like 3.2.2 or higher.
FPC Must Be in PATH:
PasBuild needs to find the fpc command. Verify it’s in your PATH:
which fpc # Linux/macOS/FreeBSD
where fpc # WindowsIf FPC is not found:
-
Linux/FreeBSD: Install via package manager (
apt install fpc,pkg install fpc) -
macOS: Install via Homebrew (
brew install fpc) or download from https://www.freepascal.org -
Windows: Download installer from https://www.freepascal.org and add to PATH
Clone the repository:
git clone https://github.com/graemeg/pasbuild.git
cd pasbuildBootstrap compilation:
Follow the instructions in BOOTSTRAP.txt:
# Linux/macOS/FreeBSD
mkdir -p target/units
echo '1.0.0' > target\version.inc
fpc -Mobjfpc -O1 -FEtarget -FUtarget/units -Fitarget -Fusrc/main/pascal src/main/pascal/PasBuild.pas
# Verify build
./target/PasBuild --versionInstall to system (optional):
# Copy to /usr/local/bin (or ~/bin)
sudo cp target/PasBuild /usr/local/bin/pasbuildPasBuild supports two types of projects:
-
Application Projects - Executable programs with a main entry point
-
Library Projects - Frameworks/libraries consisting only of units (no executable)
| Aspect | Application | Library |
|---|---|---|
Main Source |
Required: must be |
Optional: auto-generated bootstrap program |
Compilation Output |
Executable binary |
Compiled units (.ppu files) |
Package Contents |
Executable + LICENSE + README |
Source code or compiled units |
Bootstrap Program |
Not needed |
Auto-generated in target/ |
The fastest way to start a new project:
mkdir myapp
cd myapp
pasbuild initInteractive Prompts:
Project type (application/library) [application]:
Project name [myapp]: MyApplication
Version [1.0.0]:
Author [yourusername]: Your Name
License (MIT/BSD-3-Clause/GPL-3.0/Apache-2.0/Proprietary) [MIT]: BSD-3-ClausePress ENTER to accept defaults shown in square brackets.
Generated Structure (Application):
myapp/
├── project.xml
├── LICENSE
└── src/
├── main/
│ └── pascal/
│ └── Main.pas
└── test/
└── pascal/
└── TestRunner.pasGenerated Structure (Library):
mylib/
├── project.xml
├── LICENSE
└── src/
├── main/
│ └── pascal/
│ (empty - add your library units here)
└── test/
└── pascal/
└── TestRunner.pasIf you prefer to create files manually:
1. Create directory structure:
mkdir -p myapp/src/main/pascal
cd myapp2. Create project.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<name>MyApplication</name>
<version>1.0.0</version>
<author>Your Name</author>
<license>BSD-3-Clause</license>
<build>
<mainSource>Main.pas</mainSource>
<executableName>myapp</executableName>
<outputDirectory>target</outputDirectory>
</build>
</project>3. Create src/main/pascal/Main.pas:
program Main;
{$mode objfpc}{$H+}
uses
SysUtils;
begin
WriteLn('Hello from MyApplication!');
end.Library projects (frameworks, toolkits) don’t have a main program. PasBuild automatically generates a bootstrap program to compile all units.
1. Create project.xml for a library:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<name>MyLibrary</name>
<version>1.0.0</version>
<author>Your Name</author>
<license>BSD-3-Clause</license>
<build>
<projectType>library</projectType>
<outputDirectory>target</outputDirectory>
</build>
</project>2. Add your library units:
src/main/pascal/
├── MyLib.Core.pas
├── MyLib.Utils.pas
└── MyLib.Types.pas3. Compile:
pasbuild compilePasBuild will:
-
Auto-discover all .pas files
-
Generate
target/bootstrap_program.paswith all units in uses clause -
Compile the bootstrap to verify all units compile successfully
PasBuild supports Semantic Versioning 2.0.0 with optional pre-release identifiers.
Valid version formats:
-
Release versions:
MAJOR.MINOR.PATCH-
Examples:
1.0.0,2.1.0,3.4.5
-
-
Pre-release versions:
MAJOR.MINOR.PATCH-PRERELEASE-
Examples:
1.0.0-alpha,1.0.0-beta.2,1.0.0-rc.1,2.0.0-SNAPSHOT
-
Pre-release identifier rules:
-
Can contain alphanumeric characters, dots, and hyphens:
[a-zA-Z0-9.-]+ -
Common conventions:
-
-alpha,-alpha.1,-alpha.2- Alpha releases -
-beta,-beta.1,-beta.2- Beta releases -
-rc.1,-rc.2- Release candidates -
-SNAPSHOT- Development snapshots (Maven convention) -
-dev,-nightly- Development builds
-
Examples in project.xml:
<!-- Stable release -->
<version>1.0.0</version>
<!-- Beta release -->
<version>1.1.0-beta</version>
<!-- Release candidate -->
<version>2.0.0-rc.1</version>
<!-- Development snapshot -->
<version>3.0.0-SNAPSHOT</version>Package naming:
The version string is used in package filenames:
-
Release:
myapp-1.0.0-x86_64-linux.zip,myapp-1.0.0-src.zip -
Pre-release:
myapp-1.0.0-beta-x86_64-linux.zip,myapp-2.0.0-SNAPSHOT-src.zip
Compile your project:
pasbuild compileOutput:
[INFO] PasBuild 1.0.0
[INFO] Executing goal: compile
[INFO] Compiling project...
[INFO] Build command: fpc -Mobjfpc -O1 src/main/pascal/Main.pas -FEtarget ...
Free Pascal Compiler version 3.2.2 ...
Compiling src/main/pascal/Main.pas
Linking target/myapp
12 lines compiled, 0.1 sec
[INFO] Build successfulRun your program:
./target/myapp # Linux/macOS/FreeBSD
target\myapp.exe # WindowsBuild profiles allow different compiler settings without code changes.
Add profiles to project.xml:
<profiles>
<profile>
<id>debug</id>
<defines>
<define>DEBUG</define>
</defines>
<compilerOptions>
<option>-g</option> <!-- Debug symbols -->
<option>-gl</option> <!-- Line info -->
<option>-Criot</option> <!-- Range/overflow/I/O checks -->
<option>-gh</option> <!-- Heap trace -->
</compilerOptions>
</profile>
<profile>
<id>release</id>
<defines>
<define>RELEASE</define>
</defines>
<compilerOptions>
<option>-O3</option> <!-- Maximum optimization -->
<option>-CX</option> <!-- Smart linking -->
<option>-XX</option> <!-- Strip symbols -->
</compilerOptions>
</profile>
</profiles>Build with profile:
pasbuild compile -p debug # Debug build
pasbuild compile -p release # Release buildBuild with multiple profiles:
Profiles can be combined by specifying multiple profile IDs separated by commas. Profiles are applied in the order specified, with later profiles able to override settings from earlier ones.
# Activate multiple profiles at once
pasbuild compile -p base,debug,logging
# Profiles are applied in order (left to right)
# Each profile's defines and compiler options are added sequentiallyThis is useful for composing build configurations. For example:
<profiles>
<profile>
<id>base</id>
<defines>
<define>APP_NAME</define>
</defines>
</profile>
<profile>
<id>debug</id>
<defines>
<define>DEBUG</define>
</defines>
<compilerOptions>
<option>-g</option>
<option>-gl</option>
</compilerOptions>
</profile>
<profile>
<id>logging</id>
<defines>
<define>ENABLE_LOGGING</define>
</defines>
</profile>
</profiles>Then you can build with different combinations:
pasbuild compile -p base # Just base settings
pasbuild compile -p base,debug # Base + debug symbols
pasbuild compile -p base,debug,logging # Base + debug + loggingPasBuild applies compiler options in a specific additive order, allowing you to set defaults and override them as needed:
-
Hardcoded defaults:
-Mobjfpc -O1(always applied) -
Global compiler options: From
<build><compilerOptions>(extends defaults) -
Profile-specific options: From
<profile><compilerOptions>(extends further, can override)
Example:
<build>
<compilerOptions>
<option>-vh</option> <!-- Show hints globally -->
</compilerOptions>
</build>
<profiles>
<profile>
<id>debug</id>
<compilerOptions>
<option>-g</option> <!-- Add debug symbols -->
<option>-O-</option> <!-- Disable optimization (overrides -O1) -->
</compilerOptions>
</profile>
</profiles>Result with pasbuild compile -p debug:
fpc -Mobjfpc -O1 -vh -g -O- ...
The -O- flag overrides the default -O1 because FPC applies the last occurrence of conflicting flags.
By default, PasBuild reads project.xml from the current directory. You can specify an alternate project file using the -f or --file option, similar to Maven’s -f flag.
Basic usage:
pasbuild compile -f custom.xml
pasbuild compile --file myproject.xmlUse cases:
-
Building from a subdirectory: Run PasBuild from anywhere by pointing to the project file
-
Multiple build configurations: Maintain separate project files for different deployment targets
-
Testing configurations: Test changes without modifying main project.xml
-
Build system integration: Use dynamically generated project files
Example: Building from a subdirectory
When editing source files deep in the project tree, you can build without navigating back to the project root. PasBuild automatically changes to the directory containing the project file, so all relative paths in project.xml resolve correctly — just like Maven’s -f flag.
# You're editing files in src/main/pascal/
cd src/main/pascal
# Build from here using a relative path to project.xml
pasbuild compile -f ../../../project.xml
# Output:
# [INFO] Changing to project directory: /home/user/myproject
# [INFO] Executing goal: compile
# [INFO] Build successfulAn absolute path works too:
# Build from anywhere on the system
cd /tmp
pasbuild compile -f /home/user/myproject/project.xmlExample: Multiple configurations
# Development build with custom settings
pasbuild compile -f project-dev.xml
# Production build with optimizations
pasbuild compile -f project-prod.xml
# CI/CD build with specific options
pasbuild compile -f project-ci.xmlExample: Testing new features
# Copy and modify for testing
cp project.xml project-test.xml
# Edit project-test.xml with experimental changes
# Test without affecting main project
pasbuild compile -f project-test.xmlNote: The file path can be relative or absolute. When a path to a different directory is provided, PasBuild changes to that directory before executing, ensuring all paths in project.xml resolve correctly.
By default, PasBuild uses the fpc command from your PATH. You can override this to use a specific FPC binary, which is useful for side-by-side compiler versions, patched compilers, or CI environments.
Precedence (highest to lowest):
-
CLI flag:
--fpc <path> -
Environment variable:
PASBUILD_FPC -
Default:
fpc(PATH resolution)
Using the --fpc flag:
# Use an explicit path to a specific FPC version
pasbuild compile --fpc /opt/fpc-3.3.1/bin/fpc
# Use a custom-named FPC binary on PATH
pasbuild compile --fpc fpc-ootb
# Check version with custom FPC
pasbuild --version --fpc /opt/fpc-3.3.1/bin/fpcUsing the PASBUILD_FPC environment variable:
# Set for a single command
PASBUILD_FPC=/opt/fpc-3.3.1/bin/fpc pasbuild compile
# Set for the entire shell session
export PASBUILD_FPC=/opt/fpc-3.3.1/bin/fpc
pasbuild compile
pasbuild test
# CI environment example
export PASBUILD_FPC=/usr/local/fpc-3.2.2/bin/fpc
pasbuild compile -p release
pasbuild packageUse cases:
-
Testing against multiple FPC versions (e.g., stable vs trunk)
-
CI/CD pipelines with non-standard FPC installation paths
-
Using patched or custom-built compilers
-
Cross-compilation with platform-specific FPC binaries
Note: The --fpc flag always takes precedence over PASBUILD_FPC. When a custom FPC is configured, it is used for all operations including version detection, target CPU/OS detection, and compilation.
For large projects with multiple components, PasBuild supports Maven-style multi-module projects with:
-
Aggregator projects - Coordinate multiple child modules
-
Library modules - Reusable frameworks and utilities
-
Application modules - Executable projects depending on libraries
-
Automatic dependency resolution - Compile in correct order with no manual configuration
-
Module selection - Build specific modules using
-mflag
Example multi-module structure:
my-framework/ # Aggregator (packaging=pom)
├── project.xml
├── core/ # Library module
│ └── project.xml
├── ui/ # Library depending on core
│ └── project.xml
└── demo/ # Application depending on ui
└── project.xmlBasic usage:
cd my-framework
pasbuild compile # Build all modules in dependency order
pasbuild compile -m demo # Build only demo (and its dependencies)
pasbuild dependency-tree # Show dependency graph without compiling
pasbuild dependency-tree -m demo # Show only demo's dependenciesFor detailed multi-module documentation, see Multi-Module Tutorial.
PasBuild supports cross-project dependency management through a local repository. This allows one project to use compiled units from another project without manually managing -Fu paths.
Workflow:
-
Build and install a library project to the local repository
-
Declare the dependency in the consumer project’s
project.xml -
Compile the consumer project — dependencies are resolved automatically
Step 1: Install the library
cd my-library
pasbuild install
# [INFO] Installed my-library:1.0.0 [x86_64-linux-3.2.2]
# [INFO] -> /home/user/.pasbuild/repository/my-library/1.0.0/x86_64-linux-3.2.2Step 2: Declare the dependency
In the consumer project’s project.xml:
<project>
<name>my-app</name>
<version>1.0.0</version>
<build>
<mainSource>MyApp.pas</mainSource>
</build>
<dependencies>
<dependency>
<name>my-library</name>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>Step 3: Compile the consumer
cd my-app
pasbuild compile
# [INFO] Resolving 1 external dependency...
# [INFO] Dependencies resolved successfully
# [INFO] Compiling project...
# [INFO] Build successfulPasBuild automatically adds the installed library’s units to the compiler’s -Fu search path. Transitive dependencies (dependencies of dependencies) are resolved automatically through metadata stored in the repository.
Multi-module install:
For aggregator projects, pasbuild install installs all non-aggregator modules:
cd fpgui
pasbuild install # Installs framework module, then uidesigner moduleFor detailed dependency management documentation, see Dependency Management Design.
For cross-platform projects with platform-specific directories, use conditional unit paths.
Example: Cross-Platform GUI Library
src/main/pascal/
├── core/
│ ├── MyLib.Base.pas ← Always included
│ ├── MyLib.Common.pas ← Always included
│ ├── x11/ ← Linux/FreeBSD only
│ │ └── MyLib.X11.pas
│ ├── gdi/ ← Windows only
│ │ └── MyLib.GDI.pas
│ └── cocoa/ ← macOS only
│ └── MyLib.Cocoa.pasConfigure conditional paths in project.xml:
<build>
<projectType>library</projectType>
<outputDirectory>target</outputDirectory>
<unitPaths>
<!-- Only specify platform-specific paths -->
<!-- PasBuild auto-scans everything else -->
<path condition="UNIX">core/x11</path>
<path condition="WINDOWS">core/gdi</path>
<path condition="DARWIN">core/cocoa</path>
</unitPaths>
</build>How it works:
-
PasBuild auto-scans all directories (core/, etc.)
-
For conditional paths:
-
On Linux: includes
core/x11/, excludescore/gdi/andcore/cocoa/ -
On Windows: includes
core/gdi/, excludescore/x11/andcore/cocoa/ -
On macOS: includes
core/cocoa/, excludescore/x11/andcore/gdi/
-
-
Non-conditional directories always included
Supported platform conditions:
-
UNIX- All Unix-like systems (Linux, FreeBSD, macOS) -
LINUX- Linux specifically -
FREEBSD- FreeBSD specifically -
DARWIN- macOS / iOS -
WINDOWS- All Windows versions -
WIN32- 32-bit Windows -
WIN64- 64-bit Windows -
Custom defines from
<defines>or profiles
Include files (*.inc):
PasBuild automatically detects directories containing *.inc files and adds them as include paths (-Fi). By default, include path filtering uses the same conditional rules as <unitPaths>. If a platform-specific directory contains .inc files, it’s only added as an include path when that platform’s condition is met.
Advanced: Separate include path conditions
In most projects, include files are located in the same directories as units, so they naturally share the same conditional filtering. However, if you need different conditional rules for include paths, you can specify them explicitly:
<build>
<unitPaths>
<path condition="UNIX">core/x11</path>
<path condition="WINDOWS">core/gdi</path>
</unitPaths>
<!-- Optional: Separate conditions for include files -->
<includePaths>
<path condition="UNIX">templates/unix</path>
<path condition="WINDOWS">templates/windows</path>
</includePaths>
</build>How include path filtering works:
-
If
<includePaths>is specified: Uses those conditions for filtering*.incfiles -
If
<includePaths>is empty or not specified: Falls back to<unitPaths>conditions (most common case) -
Auto-scan behavior: All directories are scanned for
*.incfiles, then filtered based on the active conditions
This fallback behavior ensures that projects without explicit <includePaths> automatically get correct platform-specific filtering for their include files.
By default, PasBuild auto-scans all subdirectories. For explicit control, use manual mode:
<build>
<manualUnitPaths>true</manualUnitPaths>
<unitPaths>
<!-- Must list ALL paths explicitly -->
<path>core</path>
<path>gui</path>
<path>utils</path>
<!-- Platform-specific paths still support conditions -->
<path condition="UNIX">core/x11</path>
<path condition="WINDOWS">core/gdi</path>
</unitPaths>
</build>When to use manual mode:
-
Large projects where auto-scan is slow
-
Need explicit control over compilation order
-
Want to exclude certain directories from compilation
Note: Manual mode still respects conditional filtering for listed paths.
PasBuild can automatically copy resource files (images, configuration templates, help files, etc.) from your source directory to the output directory during the build process.
Directory structure:
src/
├── main/
│ ├── pascal/ # Pascal source code
│ └── resources/ # Resource files
│ ├── config/
│ │ └── app.conf
│ ├── images/
│ │ └── logo.png
│ └── help/
│ └── manual.html
└── test/
├── pascal/ # Test source code
└── resources/ # Test resource files
└── test-data.jsonAdd a <resources> section to your project.xml:
<build>
<mainSource>Main.pas</mainSource>
<executableName>myapp</executableName>
<!-- Resource configuration -->
<resources>
<directory>src/main/resources</directory> <!-- optional, default shown -->
<filtering>false</filtering> <!-- optional, default: false -->
</resources>
</build>Configuration options:
-
<directory>- Source directory for resources (default:src/main/resources) -
<filtering>- Enable variable substitution in resource files (default:false)
When <filtering> is set to true, PasBuild will substitute project metadata variables in your resource files.
Supported variables:
-
${project.name}- Project name from project.xml -
${project.version}- Project version -
${project.author}- Project author -
${project.license}- Project license
Example resource file (src/main/resources/version.txt):
Application: ${project.name}
Version: ${project.version}
Author: ${project.author}
License: ${project.license}With filtering enabled and <name>MyApp</name>, <version>1.0.0</version> in project.xml:
After processing, target/version.txt will contain:
Application: MyApp
Version: 1.0.0
Author: Your Name
License: BSD-3-ClauseUse cases for filtering:
-
Version manifests
-
Configuration templates with project metadata
-
About dialog content
-
Database migration scripts with version numbers
-
Help files with current version information
Test resources work the same way as main resources but are configured in the <test> section:
<test>
<testSource>TestRunner.pas</testSource>
<framework>auto</framework>
<!-- Test resource configuration -->
<resources>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</resources>
</test>Test resources are copied to target/ before test compilation, making them available to your test code.
Resource processing is automatically integrated into the build lifecycle:
-
compile goal → First runs
process-resources, then compiles -
test-compile goal → First runs
compileandprocess-test-resources, then compiles tests
You can also run resource processing independently:
pasbuild process-resources # Copy main resources only
pasbuild process-test-resources # Copy test resources onlyExample output:
[INFO] Executing goal: process-resources
[INFO] Processing resources from src/main/resources...
[INFO] Filtering enabled
[INFO] Filtered: version.txt
[INFO] Filtered: config/app.conf
[INFO] Copied: images/logo.png
[INFO] Resources processed successfullyRemoves all build artifacts.
pasbuild cleanDeletes the target/ directory and all its contents.
Copies resource files from src/main/resources to target/.
pasbuild process-resourcesWhat it does:
-
Checks if
src/main/resourcesexists (skips if not found) -
Recursively copies all files preserving directory structure
-
If filtering is enabled, substitutes project variables
-
Reports files copied/filtered
Note: This goal is automatically run by the compile goal.
Compiles the project.
pasbuild compile # Default build
pasbuild compile -p debug # With debug profile
pasbuild compile -p release # With release profileWhat it does:
-
Verifies directory layout
-
Checks if FPC is available
-
Scans for unit paths
-
Creates output directories
-
Builds FPC command with all flags
-
Executes FPC with real-time output
Generated compiler flags:
-
-Mobjfpc- Object Pascal mode -
-O1- Basic optimization (default) -
-FEtarget- Executable output directory -
-FUtarget/units- Unit output directory -
-Fusrc/main/pascal- Unit search path (auto-scanned) -
-d<define>- Global and profile defines -
Profile compiler options override defaults
Copies test resource files from src/test/resources to target/.
pasbuild process-test-resourcesWhat it does:
-
Checks if
src/test/resourcesexists (skips if not found) -
Recursively copies all files preserving directory structure
-
If filtering is enabled, substitutes project variables
-
Reports files copied/filtered
Note: This goal is automatically run by the test-compile goal.
Compiles test code without running tests.
pasbuild test-compile
pasbuild test-compile -p debugDependencies: Automatically runs compile → process-test-resources → test-compile
What it does:
-
Compiles main code (via
compilegoal) -
Scans
src/test/pascal/for test files -
Auto-detects test framework (FPCUnit or FPTest)
-
Compiles test runner executable to
target/TestRunner -
Links to already-compiled main units (Maven-style)
-
Outputs test units to
target/test-units(separate from main units)
Test framework auto-detection:
PasBuild automatically detects which testing framework you’re using:
-
FPCUnit: Detected by presence of
fpcunitin uses clause -
FPTest: Detected by presence of
TestFrameworkin uses clause -
Default: Falls back to FPCUnit if no framework detected
Compiles and runs tests.
pasbuild test
pasbuild test -p debugDependencies: Automatically runs compile → process-test-resources → test-compile → test
What it does:
-
Compiles main code
-
Compiles tests
-
Executes test runner with configured options
-
Reports test results
Example output:
[INFO] Running tests...
[INFO] Execute command: target/TestRunner --all --format=plain
Time:00.001 N:5 E:0 F:0 I:0
TCalculatorTests Time:00.001 N:5 E:0 F:0 I:0
00.000 TestAddition
00.000 TestSubtraction
00.000 TestMultiplication
00.000 TestDivision
00.000 TestDivisionByZero
Number of run tests: 5
Number of errors: 0
Number of failures: 0
[INFO] All tests passedCreates a release archive.
pasbuild packageDependencies: Automatically runs clean → compile → package
What it creates:
A ZIP archive at target/<name>-<version>-<cpu>-<os>.zip containing:
-
Compiled executable
-
LICENSEfile (if exists) -
READMEfile (if exists - checks .md, .adoc, .txt, .rst)
Example:
pasbuild package
# Creates: target/myapp-1.0.0-x86_64-linux.zip
# Contains:
# myapp (or myapp.exe on Windows)
# LICENSE
# README.mdCreates a source code archive for distribution.
pasbuild source-packageWhat it creates:
A ZIP archive at target/<name>-<version>-src.zip containing source code and documentation.
Default includes (automatic):
-
src/directory (all source code) -
project.xml -
LICENSE*files (also checksCOPYING*) -
README*files -
BOOTSTRAP*files -
INSTALL*files
Optional includes (via configuration):
<build>
<sourcePackage>
<include>docs</include>
<include>examples</include>
<include>scripts</include>
</sourcePackage>
</build>Security:
All included paths must be:
-
Relative (no absolute paths)
-
Within project root (no
..parent references) -
Violations result in build failure with clear error
Example:
pasbuild source-package
# Creates: target/myapp-1.0.0-src.zip
# Contains:
# myapp-1.0.0/src/main/pascal/...
# myapp-1.0.0/project.xml
# myapp-1.0.0/LICENSE
# myapp-1.0.0/README.md
# myapp-1.0.0/BOOTSTRAP.txt
# myapp-1.0.0/docs/... (if configured)Use cases:
-
Submitting to software repositories (Debian, FreeBSD ports)
-
Conference/journal paper submissions
-
Creating "official" release source archives
-
Users without Git access
-
Archival purposes
Installs compiled units to the local repository (~/.pasbuild/repository/).
pasbuild installDependencies: Automatically runs process-resources → compile → install
What it does:
-
Compiles the project (if not already compiled)
-
Detects the FPC target triplet (e.g.,
x86_64-linux-3.2.2) -
Creates the repository directory structure
-
Copies all
.ppuand.ofiles fromtarget/units/to the repository -
Generates
metadata.xmlwith artifact identity and dependency information
Example:
pasbuild install
# Output:
# [INFO] Executing goal: install
# [INFO] Installing to local repository...
# [INFO] Found 5 unit(s) and 6 object file(s)
# [INFO] Copied unit files to repository
# [INFO] Installed my-library:1.0.0 [x86_64-linux-3.2.2]
# [INFO] -> /home/user/.pasbuild/repository/my-library/1.0.0/x86_64-linux-3.2.2Note: For multi-module aggregator projects, install runs on each non-aggregator module in dependency order.
Displays the dependency graph of a project without invoking the compiler.
pasbuild dependency-tree # All modules (or external deps for single-module)
pasbuild dependency-tree -m demo # Dependencies for the named module onlyWhat it does:
For multi-module (aggregator) projects, modules are listed in topological order — dependencies before dependents. Each module entry shows:
-
Local module dependencies (
[module]) -
External repository dependencies (
[external]) -
The module’s packaging type (
[library],[application],[aggregator])
For single-module projects, only the declared <dependencies> (external) are shown.
Example output — multi-module project:
[INFO] ------------------------------------------------------------------------
[INFO] Dependency Tree
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] math-core 1.0.0 [library]
[INFO] (no dependencies)
[INFO]
[INFO] math-utils 1.0.0 [library]
[INFO] └─ math-core [module]
[INFO]
[INFO] calculator 1.0.0 [application]
[INFO] ├─ math-utils [module]
[INFO] └─ some-lib:2.0.0 [external]Example output — single-module project with external dependencies:
[INFO] ------------------------------------------------------------------------
[INFO] Dependency Tree
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] my-app 1.0.0
[INFO] ├─ lib-alpha:1.0.0 [external]
[INFO] └─ lib-beta:3.2.1 [external]Use cases:
-
Inspect the dependency graph before a build, without compiler output interfering
-
Verify that module dependency declarations are correct
-
Quickly audit which external libraries a module relies on
-
Diagnose unexpected transitive dependency relationships
Bootstraps a new project.
pasbuild initInteractive prompts for:
-
Project type (default: application)
-
Project name (default: current directory name)
-
Version (default: 1.0.0)
-
Author (default: $USER or $USERNAME)
-
License (default: MIT)
What it creates:
-
project.xml- Project configuration with test section -
src/main/pascal/Main.pas- Hello World template (application only) -
src/test/pascal/TestRunner.pas- FPCUnit test template -
LICENSE- Full license text (MIT, BSD-3-Clause, or Proprietary)
Project type behavior:
-
Application: Creates Main.pas, includes mainSource and executableName in XML
-
Library: No Main.pas created, minimal XML (bootstrap auto-generated at compile time)
Error handling:
If project.xml already exists, returns error: "Project already initialized"
<?xml version="1.0" encoding="UTF-8"?>
<project>
<name>MyApp</name>
<version>1.0.0</version>
<author>Your Name</author>
<license>BSD-3-Clause</license>
<build>
<mainSource>Main.pas</mainSource>
<executableName>myapp</executableName>
</build>
</project><?xml version="1.0" encoding="UTF-8"?>
<project>
<!-- Project Metadata -->
<name>MyApplication</name>
<version>2.1.0</version>
<author>Your Name</author>
<license>BSD-3-Clause</license>
<!-- Build Configuration -->
<build>
<!-- Project type (application or library, default: application) -->
<projectType>application</projectType>
<!-- Main source file in src/main/pascal/ -->
<mainSource>Main.pas</mainSource>
<!-- Executable name (without platform extension) -->
<executableName>myapp</executableName>
<!-- Output directory (default: target) -->
<outputDirectory>target</outputDirectory>
<!-- Global defines (available in all builds) -->
<defines>
<define>UseCThreads</define>
<define>MYAPP_FEATURE_X</define>
</defines>
<!-- Global compiler options (extends defaults: -Mobjfpc -O1) -->
<compilerOptions>
<option>-vh</option> <!-- Show hints -->
<option>-vn</option> <!-- Show notes -->
</compilerOptions>
<!-- Conditional unit paths (platform-specific directories) -->
<unitPaths>
<path condition="UNIX">platform/x11</path>
<path condition="WINDOWS">platform/gdi</path>
<path condition="DARWIN">platform/cocoa</path>
</unitPaths>
<!-- Optional: Conditional include paths (*.inc files) -->
<!-- If not specified, falls back to unitPaths conditions -->
<!-- <includePaths>
<path condition="UNIX">includes/unix</path>
<path condition="WINDOWS">includes/windows</path>
</includePaths> -->
<!-- Resources configuration -->
<resources>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- Enable variable substitution -->
</resources>
<!-- Source package optional includes -->
<sourcePackage>
<include>docs</include>
<include>examples</include>
<include>scripts</include>
</sourcePackage>
</build>
<!-- Test Configuration -->
<test>
<!-- Test framework: auto (default), fpcunit, or fptest -->
<framework>auto</framework>
<!-- Test source file in src/test/pascal/ -->
<testSource>TestRunner.pas</testSource>
<!-- Framework-specific options passed to test runner -->
<frameworkOptions>
<option>--all</option> <!-- Run all tests -->
<option>--format=plain</option> <!-- Output format -->
</frameworkOptions>
<!-- Test resources configuration -->
<resources>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</resources>
</test>
<!-- External Dependencies (from local repository) -->
<dependencies>
<dependency>
<name>fpgui-framework</name>
<version>1.0.0</version>
</dependency>
</dependencies>
<!-- Build Profiles -->
<profiles>
<profile>
<id>debug</id>
<defines>
<define>DEBUG</define>
<define>VERBOSE_LOGGING</define>
</defines>
<compilerOptions>
<option>-g</option> <!-- Debug info -->
<option>-gl</option> <!-- Line info -->
<option>-Criot</option> <!-- Runtime checks -->
<option>-gh</option> <!-- Heap trace -->
</compilerOptions>
</profile>
<profile>
<id>release</id>
<defines>
<define>RELEASE</define>
</defines>
<compilerOptions>
<option>-O3</option> <!-- Max optimization -->
<option>-CX</option> <!-- Smart linking -->
<option>-XX</option> <!-- Strip symbols -->
<option>-Xs</option> <!-- Strip all -->
</compilerOptions>
</profile>
</profiles>
</project><?xml version="1.0" encoding="UTF-8"?>
<project>
<!-- Project Metadata -->
<name>MyLibrary</name>
<version>1.0.0</version>
<author>Your Name</author>
<license>BSD-3-Clause</license>
<!-- Build Configuration -->
<build>
<!-- Library project - no mainSource needed -->
<projectType>library</projectType>
<outputDirectory>target</outputDirectory>
<!-- Global defines -->
<defines>
<define>UseCThreads</define>
</defines>
<!-- Platform-specific unit paths -->
<!-- Bootstrap program auto-generated with all discovered units -->
<unitPaths>
<path condition="UNIX">corelib/x11</path>
<path condition="WINDOWS">corelib/gdi</path>
<path condition="DARWIN">corelib/cocoa</path>
</unitPaths>
<!-- Optional: Conditional include paths (*.inc files) -->
<!-- If not specified, falls back to unitPaths conditions -->
<!-- <includePaths>
<path condition="UNIX">includes/unix</path>
<path condition="WINDOWS">includes/windows</path>
</includePaths> -->
</build>
<!-- Build Profiles -->
<profiles>
<profile>
<id>debug</id>
<defines>
<define>DEBUG</define>
</defines>
<compilerOptions>
<option>-g</option>
<option>-gl</option>
</compilerOptions>
</profile>
<profile>
<id>release</id>
<defines>
<define>RELEASE</define>
</defines>
<compilerOptions>
<option>-O3</option>
<option>-CX</option>
</compilerOptions>
</profile>
</profiles>
</project>pasbuild clean # Remove build artifacts
pasbuild process-resources # Copy resources to target
pasbuild compile # Compile project (runs: process-resources → compile)
pasbuild compile -p <profile> # Compile with profile
pasbuild process-test-resources # Copy test resources to target
pasbuild test-compile # Compile tests (runs: compile → process-test-resources → test-compile)
pasbuild test # Run tests (runs: compile → process-test-resources → test-compile → test)
pasbuild package # Create release archive
pasbuild source-package # Create source archive
pasbuild install # Install to local repository (~/.pasbuild/repository/)
pasbuild init # Bootstrap new project-p <profile[,profile...]> # Activate build profile(s)
--profile <profile-id> # Activate build profile (long form)
-f <file>, --file <file> # Use alternate project file (default: project.xml)
--fpc <path> # Use custom FPC executable (or set PASBUILD_FPC env var)
-v, --verbose # Show full compiler output
-h, --help # Show help message
--version # Show version information# Clean build with debug profile
pasbuild clean
pasbuild compile -p debug
# Release build and package
pasbuild compile -p release
pasbuild package
# Build with multiple profiles
pasbuild compile -p base,debug,logging
# Verbose build (show full FPC output)
pasbuild compile -v
# Use alternate project file
pasbuild compile -f custom.xml
# Build from a subdirectory using relative path
cd src/main/pascal
pasbuild compile -f ../../../project.xml
# Create source distribution
pasbuild source-package
# Get help
pasbuild --help
pasbuild compile --helpPasBuild follows Maven’s Standard Directory Layout:
project-root/
├── project.xml # Project configuration
├── LICENSE # License file
├── README.md # Project documentation
├── BOOTSTRAP.txt # Bootstrap instructions (optional)
│
├── src/
│ ├── main/
│ │ ├── pascal/ # Application source files
│ │ │ ├── Main.pas
│ │ │ ├── Unit1.pas
│ │ │ └── subdir/ # Subdirectories auto-scanned
│ │ │ └── Unit2.pas
│ │ └── resources/ # Resource files (optional)
│ │ ├── config/
│ │ │ └── app.conf
│ │ └── images/
│ │ └── logo.png
│ └── test/
│ ├── pascal/ # Test source files
│ │ ├── TestRunner.pas
│ │ └── subdir/ # Test subdirectories auto-scanned
│ │ └── MyTests.pas
│ └── resources/ # Test resource files (optional)
│ └── test-data.json
│
└── target/ # Build output (auto-created)
├── myapp # Executable
├── TestRunner # Test executable
├── myapp-1.0.0-x86_64-linux.zip # Package archive
├── config/ # Copied from src/main/resources/
│ └── app.conf # (filtered if enabled)
├── images/ # Copied from src/main/resources/
│ └── logo.png
├── units/ # Compiled main units
│ ├── Unit1.ppu
│ ├── Unit1.o
│ └── ...
└── test-units/ # Compiled test units (separate)
├── MyTests.ppu
└── ...Key Points:
-
Main source files go in
src/main/pascal/ -
Test source files go in
src/test/pascal/ -
Main resource files go in
src/main/resources/(optional) -
Test resource files go in
src/test/resources/(optional) -
Build output goes in
target/ -
target/is auto-created and cleaned -
Subdirectories are automatically scanned for units and include files
-
Resources are automatically copied to
target/(with optional filtering) -
Test units compiled separately to
target/test-units/(Maven-style)
Error: "Free Pascal Compiler (fpc) not found in PATH"
Solution:
-
Verify FPC is installed:
fpc -iV -
Add FPC to PATH
-
Restart terminal
Error: "Main source file not found: src/main/pascal/Main.pas"
Solution:
-
Check file exists in correct location
-
Verify
<mainSource>inproject.xmlmatches filename -
File must be in
src/main/pascal/directory
Error: "Project already initialized (project.xml exists)"
Solution:
-
Delete
project.xmlif you want to re-initialize -
Or manually edit the existing
project.xml
Error: "Fatal: Can’t find unit MyUnit used by Main"
Solution:
-
Ensure unit file exists in
src/main/pascal/or subdirectory -
PasBuild automatically scans subdirectories for units
-
Check unit filename matches unit name in
usesclause
-
Read Design Document for architecture details
-
Check Implementation Progress for feature roadmap
-
Join the community and contribute!
-
GitHub Issues: Report bugs and request features
-
FPC Documentation: https://www.freepascal.org/docs.html