Skip to content

Latest commit

 

History

History
1766 lines (1337 loc) · 44.8 KB

File metadata and controls

1766 lines (1337 loc) · 44.8 KB

PasBuild Quick Start Guide

Complete guide to getting started with PasBuild.

1. Prerequisites

1.1. Free Pascal Compiler

PasBuild requires the Free Pascal Compiler (FPC) version 3.2.2 or later.

Verify FPC Installation:

fpc -iV

This 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        # Windows

If FPC is not found:

2. Installing PasBuild

2.1. Option 1: Bootstrap from Source

Clone the repository:

git clone https://github.com/graemeg/pasbuild.git
cd pasbuild

Bootstrap 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 --version

Install to system (optional):

# Copy to /usr/local/bin (or ~/bin)
sudo cp target/PasBuild /usr/local/bin/pasbuild

2.2. Option 2: Pre-built Binary

Download the latest release from GitHub Releases and extract to your PATH.

3. Project Types

PasBuild supports two types of projects:

  1. Application Projects - Executable programs with a main entry point

  2. Library Projects - Frameworks/libraries consisting only of units (no executable)

3.1. Application vs Library

Aspect Application Library

Main Source

Required: must be program unit

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/

4. Creating Your First Project

4.1. Using pasbuild init

The fastest way to start a new project:

mkdir myapp
cd myapp
pasbuild init

Interactive 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-Clause

Press ENTER to accept defaults shown in square brackets.

Generated Structure (Application):

myapp/
├── project.xml
├── LICENSE
└── src/
    ├── main/
    │   └── pascal/
    │       └── Main.pas
    └── test/
        └── pascal/
            └── TestRunner.pas

Generated Structure (Library):

mylib/
├── project.xml
├── LICENSE
└── src/
    ├── main/
    │   └── pascal/
    │       (empty - add your library units here)
    └── test/
        └── pascal/
            └── TestRunner.pas

4.2. Manual Project Setup

If you prefer to create files manually:

1. Create directory structure:

mkdir -p myapp/src/main/pascal
cd myapp

2. 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.

4.3. Creating a Library Project

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.pas

3. Compile:

pasbuild compile

PasBuild will:

  1. Auto-discover all .pas files

  2. Generate target/bootstrap_program.pas with all units in uses clause

  3. Compile the bootstrap to verify all units compile successfully

4.4. Version Numbering

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

5. Building Your Project

5.1. Basic Compilation

Compile your project:

pasbuild compile

Output:

[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 successful

Run your program:

./target/myapp          # Linux/macOS/FreeBSD
target\myapp.exe        # Windows

5.2. Using Build Profiles

Build 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 build

Build 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 sequentially

This 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 + logging

5.3. Compiler Options Order

PasBuild applies compiler options in a specific additive order, allowing you to set defaults and override them as needed:

  1. Hardcoded defaults: -Mobjfpc -O1 (always applied)

  2. Global compiler options: From <build><compilerOptions> (extends defaults)

  3. 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.

5.4. Using Conditional Compilation

In your Pascal code:

{$IFDEF DEBUG}
  WriteLn('Debug mode enabled');
  WriteLn('Starting application...');
{$ENDIF}

{$IFDEF RELEASE}
  // Release-specific code
{$ENDIF}

6. Advanced Features

6.1. Using Alternate Project Files

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.xml

Use 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 successful

An absolute path works too:

# Build from anywhere on the system
cd /tmp
pasbuild compile -f /home/user/myproject/project.xml

Example: 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.xml

Example: 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.xml

Note: 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.

6.2. Custom FPC Executable

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):

  1. CLI flag: --fpc <path>

  2. Environment variable: PASBUILD_FPC

  3. 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/fpc

Using 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 package

Use 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.

6.3. Multi-Module Projects

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 -m flag

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.xml

Basic 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 dependencies

For detailed multi-module documentation, see Multi-Module Tutorial.

6.4. Dependency Management

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:

  1. Build and install a library project to the local repository

  2. Declare the dependency in the consumer project’s project.xml

  3. 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.2

Step 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 successful

PasBuild 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 module

For detailed dependency management documentation, see Dependency Management Design.

6.5. Platform-Specific Code with Conditional Paths

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.pas

Configure 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:

  1. PasBuild auto-scans all directories (core/, etc.)

  2. For conditional paths:

    • On Linux: includes core/x11/, excludes core/gdi/ and core/cocoa/

    • On Windows: includes core/gdi/, excludes core/x11/ and core/cocoa/

    • On macOS: includes core/cocoa/, excludes core/x11/ and core/gdi/

  3. 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 *.inc files

  • If <includePaths> is empty or not specified: Falls back to <unitPaths> conditions (most common case)

  • Auto-scan behavior: All directories are scanned for *.inc files, 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.

6.6. Manual Unit Path Control

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.

7. Working with Resources

7.1. Resource Files

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.json

7.2. Configuring Resource Copying

Add 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)

7.3. Variable Filtering

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-Clause

Use 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

7.4. Test Resources

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.

7.5. Build Lifecycle Integration

Resource processing is automatically integrated into the build lifecycle:

  • compile goal → First runs process-resources, then compiles

  • test-compile goal → First runs compile and process-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 only

Example 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 successfully

8. Available Goals

8.1. clean

Removes all build artifacts.

pasbuild clean

Deletes the target/ directory and all its contents.

8.2. process-resources

Copies resource files from src/main/resources to target/.

pasbuild process-resources

What it does:

  1. Checks if src/main/resources exists (skips if not found)

  2. Recursively copies all files preserving directory structure

  3. If filtering is enabled, substitutes project variables

  4. Reports files copied/filtered

Note: This goal is automatically run by the compile goal.

8.3. compile

Compiles the project.

pasbuild compile              # Default build
pasbuild compile -p debug     # With debug profile
pasbuild compile -p release   # With release profile

What it does:

  1. Verifies directory layout

  2. Checks if FPC is available

  3. Scans for unit paths

  4. Creates output directories

  5. Builds FPC command with all flags

  6. 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

8.4. process-test-resources

Copies test resource files from src/test/resources to target/.

pasbuild process-test-resources

What it does:

  1. Checks if src/test/resources exists (skips if not found)

  2. Recursively copies all files preserving directory structure

  3. If filtering is enabled, substitutes project variables

  4. Reports files copied/filtered

Note: This goal is automatically run by the test-compile goal.

8.5. test-compile

Compiles test code without running tests.

pasbuild test-compile
pasbuild test-compile -p debug

Dependencies: Automatically runs compileprocess-test-resourcestest-compile

What it does:

  1. Compiles main code (via compile goal)

  2. Scans src/test/pascal/ for test files

  3. Auto-detects test framework (FPCUnit or FPTest)

  4. Compiles test runner executable to target/TestRunner

  5. Links to already-compiled main units (Maven-style)

  6. 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 fpcunit in uses clause

  • FPTest: Detected by presence of TestFramework in uses clause

  • Default: Falls back to FPCUnit if no framework detected

8.6. test

Compiles and runs tests.

pasbuild test
pasbuild test -p debug

Dependencies: Automatically runs compileprocess-test-resourcestest-compiletest

What it does:

  1. Compiles main code

  2. Compiles tests

  3. Executes test runner with configured options

  4. 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 passed

8.7. package

Creates a release archive.

pasbuild package

Dependencies: Automatically runs cleancompilepackage

What it creates:

A ZIP archive at target/<name>-<version>-<cpu>-<os>.zip containing:

  • Compiled executable

  • LICENSE file (if exists)

  • README file (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.md

8.8. source-package

Creates a source code archive for distribution.

pasbuild source-package

What 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 checks COPYING*)

  • 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

8.9. install

Installs compiled units to the local repository (~/.pasbuild/repository/).

pasbuild install

Dependencies: Automatically runs process-resourcescompileinstall

What it does:

  1. Compiles the project (if not already compiled)

  2. Detects the FPC target triplet (e.g., x86_64-linux-3.2.2)

  3. Creates the repository directory structure

  4. Copies all .ppu and .o files from target/units/ to the repository

  5. Generates metadata.xml with 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.2

Note: For multi-module aggregator projects, install runs on each non-aggregator module in dependency order.

8.10. dependency-tree

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 only

What 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

8.11. init

Bootstraps a new project.

pasbuild init

Interactive 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"

9. Configuration Reference

9.1. Minimal Configuration

<?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>

9.2. Full Configuration (Application)

<?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>

9.3. Full Configuration (Library)

<?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>

10. Command-Line Reference

10.1. Goals

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

10.2. Options

-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

10.3. Examples

# 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 --help

11. Directory Layout

PasBuild 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)

12. Troubleshooting

12.1. FPC not found

Error: "Free Pascal Compiler (fpc) not found in PATH"

Solution:

  1. Verify FPC is installed: fpc -iV

  2. Add FPC to PATH

  3. Restart terminal

12.2. Main source file not found

Error: "Main source file not found: src/main/pascal/Main.pas"

Solution:

  1. Check file exists in correct location

  2. Verify <mainSource> in project.xml matches filename

  3. File must be in src/main/pascal/ directory

12.3. Project already initialized

Error: "Project already initialized (project.xml exists)"

Solution:

  • Delete project.xml if you want to re-initialize

  • Or manually edit the existing project.xml

12.4. Unit not found errors

Error: "Fatal: Can’t find unit MyUnit used by Main"

Solution:

  1. Ensure unit file exists in src/main/pascal/ or subdirectory

  2. PasBuild automatically scans subdirectories for units

  3. Check unit filename matches unit name in uses clause

12.5. Permission denied on executable

Error: Permission denied when running ./target/myapp

Solution:

chmod +x target/myapp

(PasBuild should set this automatically on Unix systems)

13. Next Steps

14. Getting Help