Skip to content

Latest commit

 

History

History
1261 lines (930 loc) · 28.7 KB

File metadata and controls

1261 lines (930 loc) · 28.7 KB

PasBuild Design Specification

Table of Contents

1. Overview

PasBuild is a build automation tool for Free Pascal projects, inspired by Apache Maven. It provides standardized project structure, declarative configuration, and repeatable builds through a goal-based execution model.

1.1. Design Goals

  • Standardization: Eliminate per-project build script variations

  • Simplicity: Zero configuration for conforming projects

  • Extensibility: Support platform-specific and debug/release builds via profiles

  • Cross-platform: Work identically on Linux, Windows, macOS

  • Self-hosting: Tool builds itself using its own project.xml

1.2. Non-Goals (MVP)

  • Incremental compilation (rebuild everything)

  • IDE integration (CLI-only)

  • Custom plugin system

2. Project Structure

2.1. Standard Directory Layout

All PasBuild projects follow the Maven Standard Directory Layout adapted for Pascal:

MyProject/
├── project.xml              # Project configuration (POM equivalent)
├── LICENSE                  # Project license (optional)
├── README.adoc              # Project documentation (optional)
├── src/
│   └── main/
│       └── pascal/          # Application source code (*.pas, *.pp, *.lpr)
│           ├── Main.pas     # Entry point
│           ├── core/        # Optional: subdirectories for organization
│           └── gui/         # Optional: more subdirectories
└── target/                  # Build output (created by pasbuild)
    ├── units/               # Compiled units (*.o, *.ppu)
    └── <executable>         # Final binary

Rules:

  • project.xml MUST exist in project root

  • src/main/pascal/ MUST exist

  • Subdirectories under pascal/ are automatically scanned for -Fu paths

  • target/ is generated; MUST NOT be committed to version control

2.2. Rationale

Why strict layout?

  • Eliminates "where should I put this file?" decisions

  • Tools can make assumptions (automation)

  • New contributors immediately understand structure

  • Cross-project consistency

Why src/main/pascal/ instead of just src/?

  • Future-proofs for src/test/pascal/ (see [future-test-phase])

  • Mirrors Maven exactly (easier mental model for Java developers)

  • Allows for src/main/resources/ later (embedded resources, help files)

3. Configuration Format

3.1. File: project.xml

Location: Project root

Format: XML 1.0

Encoding: UTF-8

3.2. Minimal Schema

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <!-- Metadata -->
  <name>MyPascalApp</name>                    <!-- REQUIRED: Project identifier -->
  <version>1.0.0</version>                    <!-- REQUIRED: Semantic version -->
  <author>Jane Doe</author>                   <!-- OPTIONAL: Creator -->
  <license>MIT</license>                      <!-- OPTIONAL: SPDX identifier or "Proprietary" -->

  <!-- Build Configuration -->
  <build>
    <mainSource>Main.pas</mainSource>         <!-- REQUIRED: Entry point relative to src/main/pascal/ -->
    <outputDirectory>target</outputDirectory> <!-- OPTIONAL: Default = "target" -->
    <executableName>myapp</executableName>    <!-- OPTIONAL: Default = <name> lowercased -->

    <!-- Global compiler defines (always active) -->
    <defines>
      <define>UseCThreads</define>
    </defines>

    <!-- Global compiler options (extends defaults: -Mobjfpc -O1) -->
    <compilerOptions>
      <option>-vh</option>  <!-- Show hints -->
    </compilerOptions>
  </build>

  <!-- Build Profiles (environment-specific settings) -->
  <profiles>
    <profile>
      <id>debug</id>
      <defines>
        <define>DEBUG</define>
        <define>VERBOSE_LOGGING</define>
      </defines>
      <compilerOptions>
        <option>-O1</option>
        <option>-g</option>
        <option>-gl</option>
      </compilerOptions>
    </profile>

    <profile>
      <id>release</id>
      <defines>
        <define>RELEASE</define>
        <define>INLINE_FUNCTIONS</define>
      </defines>
      <compilerOptions>
        <option>-O3</option>
        <option>-CX</option>
        <option>-XX</option>
      </compilerOptions>
    </profile>
  </profiles>
</project>

3.3. Field Specifications

3.3.1. Required Fields

Field Type Validation

name

String

Alphanumeric + hyphens/underscores only. Max 64 chars.

version

String

Semantic versioning: MAJOR.MINOR.PATCH (e.g., 1.0.0)

build/mainSource

Path

Must exist in src/main/pascal/. Extension: .pas, .pp, or .lpr

3.3.2. Optional Fields with Defaults

Field Default Value Notes

author

"Unknown"

Included in package metadata

license

"Proprietary"

SPDX identifier (MIT, BSD-3-Clause, GPL-3.0, Apache-2.0, etc.) or "Proprietary"

build/outputDirectory

"target"

Relative to project root

build/executableName

Lowercased <name>

Platform suffix added automatically (.exe on Windows)

3.4. Design Rationale

Why XML over JSON?

  • Schema validation (XSD) for configuration correctness

  • Comments supported (users can document profiles)

  • Extensibility via namespaces (future custom goals)

  • Consistency with Maven (familiar to enterprise developers)

  • FPC’s DOM/XMLRead units are mature and stable

Why semantic versioning?

  • Industry standard

  • Clear upgrade impact (MAJOR = breaking changes)

  • Enables dependency management and version resolution

4. Build System Architecture

4.1. Core Components

pasbuild (executable)
   ├── PasBuild.CLI          → Argument parsing, goal dispatch
   ├── PasBuild.Config       → XML parsing, validation
   ├── PasBuild.Types        → Shared data structures
   ├── PasBuild.Cleaner      → "clean" goal implementation
   ├── PasBuild.Builder      → "compile" goal implementation
   ├── PasBuild.Archiver     → "package" goal implementation
   └── PasBuild.Utils        → File operations, process execution

4.2. Data Flow

User Command: pasbuild compile -p debug
              ↓
    1. CLI.ParseArguments
              ↓
    2. Config.LoadProjectXML
              ↓
    3. Config.ValidateSchema
              ↓
    4. Builder.Execute(config, profile="debug")
              ↓
    5. Utils.RunFPC(compiledCommand)
              ↓
    6. Display compiler output
              ↓
    7. Exit with FPC's exit code

5. Build Goals

5.1. Goal Execution Model

PasBuild follows Maven’s lifecycle model where goals can have dependencies on other goals.

Goal Dependencies:

Goal Depends On Execution Order

clean

None

clean

compile

None

compile

package

clean, compile

clean → compile → package

install

process-resources, compile

process-resources → compile → install

init

None

init

Behavior:

  • When a goal declares dependencies, those goals execute first in order

  • Each dependency goal runs once (no duplicate execution)

  • If any dependency fails (exit code ≠ 0), execution stops immediately

  • Goals can be run individually or as part of a dependency chain

Example:

pasbuild package
# Executes: clean → compile → package
# If compile fails, package never runs

5.2. Goal: clean

Purpose: Delete all build artifacts

Usage:

pasbuild clean

Behavior:

  1. Read <build><outputDirectory> from project.xml (default: target)

  2. If directory exists:

    • Recursively delete all contents

    • Delete directory itself

  3. Display: "[INFO] Cleaned target directory"

Implementation Notes:

  • Safety: Only delete if path == configured output directory

  • No confirmation: Matches Maven behavior (add --force later if needed)

  • Use FPC’s DeleteDirectory or manual recursive delete via FindFirst/FindNext

5.3. Goal: compile

Purpose: Build the executable

Usage:

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

Behavior:

  1. Validate src/main/pascal/ exists

  2. Scan subdirectories for automatic -Fu paths

  3. Construct FPC command:

    • Base: fpc -Mobjfpc -O1

    • Source: src/main/pascal/<mainSource>

    • Output: -FE<outputDirectory> -FU<outputDirectory>/units

    • Defines: -d<define> for each global + profile define

    • Unit paths: -Fu<dir> for each scanned subdirectory

  4. Execute via TProcess

  5. Stream compiler output to console in real-time

  6. Exit with FPC’s exit code (0 = success)

Example Generated Command:

fpc -Mobjfpc -O1 \
    src/main/pascal/Main.pas \
    -FEtarget \
    -FUtarget/units \
    -dUseCThreads \
    -dDEBUG \
    -Fusrc/main/pascal/core \
    -Fusrc/main/pascal/gui \
    -omyapp

Directory Scanning Algorithm:

procedure ScanForUnitPaths(BaseDir: string; var Paths: TStringList);
  // Recursively find all subdirectories under src/main/pascal/
  // Add each as -Fu<path>
  // Exclude: ., .., .git, .svn, backup~
end;

5.4. Goal: package

Purpose: Create release archive

Usage:

pasbuild package              # Automatically runs: clean → compile → package

Behavior:

  1. Execute clean goal (dependency)

  2. Execute compile goal (dependency)

  3. If both dependencies succeed:

    • Create zip: <name>-<version>.zip

    • Include:

      • Compiled executable from target/

      • LICENSE file (if exists)

      • README.adoc or README.md (if exists)

    • Exclude:

      • .o, .ppu files (intermediate artifacts)

  4. Save archive to target directory.

  5. Display: "[INFO] Created myapp-1.0.0.zip"

Archive Structure:

myapp-1.0.0.zip
├── myapp          (or myapp.exe on Windows)
├── LICENSE
└── README.adoc

Rationale:

  • "Clean then build" ensures reproducibility

  • Flat structure (no intermediate directory) for simplicity

  • Only user-facing files (no build artifacts)

6. Profile System

6.1. Purpose

Allow environment-specific compiler settings without modifying source code.

Use Cases:

  • Debug vs Release builds

  • Platform-specific backends (GTK2 vs Qt)

  • Feature flags (trial version vs full version)

6.2. Activation

Command Line:

pasbuild compile -p <profile-id>

Example:

pasbuild compile -p release

6.3. Semantics

Active Defines = Global Defines + Profile Defines

Given:

<build>
  <defines>
    <define>UseCThreads</define>
  </defines>
</build>

<profiles>
  <profile>
    <id>release</id>
    <defines>
      <define>RELEASE</define>
    </defines>
  </profile>
</profiles>

Result of pasbuild compile -p release:

fpc ... -dUseCThreads -dRELEASE ...

Rules:

  • Profiles are additive (not replacement)

  • Unknown profile ID → Warning + ignore profile

  • No profile specified → Only global defines used

  • Multiple profiles supported via comma-separated list: -p profile1,profile2

7. Compiler Integration

7.1. FPC Discovery

Strategy: Require fpc in system PATH

Validation:

  1. Execute: fpc -i (or fpc -iV for short version)

  2. If exit code ≠ 0 → Error: "FPC not found in PATH"

  3. Parse version from output (informational only)

Note: FPC 3.2.2+ uses -i for full info, -iV for short version string, -iW for full version string

Future Enhancement: <build><compilerPath> override

7.2. Default Compiler Flags

Always Applied:

Flag Purpose

-Mobjfpc

Free Pascal object model (required for modern code)

-O1

Level 1 optimization (debug-friendly, reasonable speed)

-FE<dir>

Executable output directory

-FU<dir>

Unit files output directory (keeps .ppu/.o out of source tree)

-o<name>

Executable name

Automatically Generated:

Flag Generated By

-Fu<path>

Scanned from all subdirectories under src/main/pascal/

-d<define>

From <build><defines> + active profile <defines>

7.3. Compiler Output Handling

Real-time Streaming:

  • Use TProcess.Options := [poUsePipes, poStderrToOutPut]

  • Read TProcess.Output during compilation

  • Display to console immediately (UX: user sees progress)

Exit Code Propagation:

  • If FPC returns non-zero → pasbuild returns same code

  • Allows CI systems to detect build failures

8. Error Handling Strategy

8.1. MVP Approach: Fail Fast

Philosophy: Clear error messages, no automatic recovery

Error Categories:

Category Detection Response

Missing project.xml

File not found

"ERROR: project.xml not found in current directory"

Invalid XML

Parse exception

"ERROR: Invalid XML in project.xml: <exception message>"

Missing required field

XPath check

"ERROR: Required field <name> missing in project.xml"

Invalid directory layout

DirectoryExists check

"ERROR: src/main/pascal/ not found. Run 'pasbuild init' to create structure."

FPC not in PATH

fpc -i fails

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

Compilation failure

FPC exit code ≠ 0

Display FPC output, exit with same code

Dependency goal failure

Goal exit code ≠ 0

"ERROR: Goal '<goal>' failed, stopping execution"

No Silent Failures:

  • Every error writes to stderr

  • Exit code always reflects success (0) or failure (≠0)

9. Template Generation (init Goal)

9.1. Goal: init

Purpose: Bootstrap new PasBuild project

Usage:

cd MyNewProject
pasbuild init

Interactive Prompts:

Project name [MyNewProject]:
Version [1.0.0]:
Author [Your Name]:
License (MIT/BSD-3-Clause/GPL-3.0/Apache-2.0/Proprietary) [MIT]:

Generated Structure:

MyNewProject/
├── project.xml
├── LICENSE              (Generated from SPDX template if MIT/GPL/Apache chosen)
├── src/
│   └── main/
│       └── pascal/
│           └── Main.pas

Generated project.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <name>MyNewProject</name>
  <version>1.0.0</version>
  <author>Your Name</author>
  <license>MIT</license>

  <build>
    <mainSource>Main.pas</mainSource>
    <executableName>mynewproject</executableName>
  </build>
</project>

Generated Main.pas:

program Main;

{$mode objfpc}{$H+}

uses
  SysUtils;

begin
  WriteLn('Hello from MyNewProject!');
  WriteLn('Build tool: PasBuild');
end.

Behavior:

  • If project.xml exists → Error: "Project already initialized"

  • Non-interactive mode (future): pasbuild init --name Foo --version 1.0.0

10. Implemented Features

The following features have been implemented and are available in PasBuild:

10.1. Test Phase

Status: ✅ Implemented

Goals: pasbuild test-compile, pasbuild test

Features:

  • Separate src/test/pascal/ directory support

  • Test runner compilation with dependency on main compilation

  • Separate test units directory (target/test-units/)

  • Test execution with configurable framework options

  • Dependency chain: compile → test-compile → test

Configuration:

<test>
  <testSource>TestRunner.pas</testSource>
  <frameworkOptions>
    <option>--all</option>
    <option>--format=plain</option>
  </frameworkOptions>
</test>

10.2. Source Distribution Packaging

Status: ✅ Implemented

Goal: pasbuild source-package

Features:

  • Creates source archive: target/<name>-<version>-src.zip

  • Default includes: src/, project.xml, LICENSE*, README*, BOOTSTRAP*, INSTALL*

  • Optional includes via configuration

  • Security: validates paths are within project root

  • Archive structure with version prefix (Maven-style)

Configuration:

<build>
  <sourcePackage>
    <include>docs</include>
    <include>examples</include>
    <include>scripts</include>
  </sourcePackage>
</build>

Use Cases:

  • Distribution to software repositories (Debian, FreeBSD ports)

  • Conference/journal paper submissions

  • Users without Git access

  • Official release archives

10.3. Custom Compiler Options

Status: ✅ Implemented

Features:

  • Global compiler options in <build><compilerOptions>

  • Profile-specific compiler options in <profile><compilerOptions>

  • Additive application: hardcoded defaults → global → profile

  • Later options override earlier ones (FPC behavior)

Configuration:

<build>
  <compilerOptions>
    <option>-vh</option>  <!-- Show hints globally -->
  </compilerOptions>
</build>

<profiles>
  <profile>
    <id>release</id>
    <compilerOptions>
      <option>-O3</option>  <!-- Override default -O1 -->
      <option>-CX</option>
      <option>-XX</option>
    </compilerOptions>
  </profile>
</profiles>

10.4. Multiple Profiles Activation

Status: ✅ Implemented

Features:

  • Activate multiple profiles via comma-separated list: -p base,debug,logging

  • Profiles applied in order (left to right)

  • Each profile’s defines and options added sequentially

  • Composable build configurations

10.5. Alternate Project Files

Status: ✅ Implemented

Features:

  • Specify alternate project file: -f custom.xml or --file custom.xml

  • Default: project.xml

  • Supports relative and absolute paths

  • Clear error messages for missing files

Use Cases:

  • Multiple build configurations (dev/prod/ci)

  • Testing configurations without modifying main project.xml

  • Build system integration

10.6. Verbose Output Control

Status: ✅ Implemented

Features:

  • -v or --verbose flag shows full FPC compiler output

  • Default mode: clean console with INFO/ERROR only, full log to target/pasbuild-status/<goal>/compiler.log

  • Build status tracking: Maven-style status directories

  • Input file tracking: inputUnits.lst, inputIncludeFiles.lst

10.7. Resource Copying

Status: ✅ Implemented

Goals: pasbuild process-resources, pasbuild process-test-resources

Features:

  • Copies resources from src/main/resources/target/

  • Copies test resources from src/test/resources/target/

  • Preserves directory structure during copy

  • Optional variable filtering for project metadata substitution

  • Integrated into build lifecycle:

    • compile depends on process-resources

    • test-compile depends on process-test-resources

Configuration:

<build>
  <resources>
    <directory>src/main/resources</directory>  <!-- optional, default shown -->
    <filtering>true</filtering>                <!-- optional, default: false -->
  </resources>
</build>

<test>
  <resources>
    <directory>src/test/resources</directory>
    <filtering>true</filtering>
  </resources>
</test>

Variable Filtering:

When filtering=true, the following variables are substituted in resource files:

  • ${project.name} → Project name

  • ${project.version} → Project version

  • ${project.author} → Project author

  • ${project.license} → Project license

Use Cases:

  • Help files (.html, .chm)

  • Icons, images

  • Configuration templates with version/author info

  • Translation files (.po, .mo)

  • Database configuration files

  • Version manifests

11. Version Injection (Achieved via Resource Filtering)

Version injection is already possible using the resource copying feature with filtering enabled.

Implementation:

Create src/main/resources/version.inc:

'${project.version}'

Enable filtering in project.xml:

<build>
  <resources>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
  </resources>
</build>

Usage in Code:

const
  APP_VERSION = {$I version.inc};

begin
  WriteLn('MyApp version: ', APP_VERSION);
end.

How it works:

  1. During compile, process-resources runs first

  2. version.inc is copied to target/ with ${project.version} replaced by actual version

  3. FPC automatically includes target/ in include path

  4. Code includes the filtered version constant

For more metadata:

Create src/main/resources/project.inc:

{ Auto-generated project metadata - DO NOT EDIT }
const
  PROJECT_NAME = '${project.name}';
  PROJECT_VERSION = '${project.version}';
  PROJECT_AUTHOR = '${project.author}';
  PROJECT_LICENSE = '${project.license}';

Then in your code:

{$I project.inc}

begin
  WriteLn(PROJECT_NAME, ' version ', PROJECT_VERSION);
  WriteLn('By ', PROJECT_AUTHOR);
end.

Note: FPC does not support compiler defines with values (e.g., -dVERSION='1.0.0' is invalid in FPC). Include files are the standard approach for compile-time constants.

12. Semantic Versioning 2.0.0 Support

Status: ✅ Implemented

PasBuild supports full Semantic Versioning 2.0.0 including 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:

  • Can contain alphanumeric characters, dots, and hyphens: [a-zA-Z0-9.-]+

  • Regex validation: ^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$

Use cases:

  • Beta/alpha releases

  • Release candidates

  • Development snapshots (e.g., -SNAPSHOT for Maven compatibility)

  • Nightly builds

  • Feature branch builds

Package naming:

Pre-release versions are included in package filenames:

  • myapp-1.0.0-beta.zip

  • myapp-2.0.0-SNAPSHOT-src.zip

13. Dependency Management

PasBuild supports Maven-style dependency management through a local repository. Projects can declare external dependencies in project.xml, and PasBuild resolves them at compile time by locating pre-compiled units in the local repository.

13.1. Local Repository

Installed artifacts are stored under ~/.pasbuild/repository/ with the following structure:

~/.pasbuild/repository/
  <name>/
    <version>/
      <cpu>-<os>-<fpcVersion>/     # Target triplet
        units/                      # Compiled .ppu and .o files
        metadata.xml                # Artifact metadata and dependency list

The target triplet (e.g., x86_64-linux-3.2.2) ensures compiled units are only used with a matching FPC version and target architecture. This prevents .ppu version mismatch errors.

Paths containing spaces in the user’s home directory (e.g., C:\Users\John Smith\) are handled correctly through path quoting.

13.2. Install Goal

The install goal compiles the project and copies compiled units to the local repository:

pasbuild install

This executes: process-resourcescompileinstall. The install step:

  1. Detects the target triplet via fpc -iTP, fpc -iTO, fpc -iV

  2. Creates the repository directory structure

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

  4. Generates metadata.xml with artifact identity, build info, and dependency list

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

13.3. Declaring Dependencies

Consumer projects declare external dependencies in project.xml:

<project>
  <name>my-app</name>
  <version>1.0.0</version>
  <build>
    <mainSource>MyApp.pas</mainSource>
  </build>
  <dependencies>
    <dependency>
      <name>fpgui-framework</name>
      <version>1.0.0</version>
    </dependency>
  </dependencies>
</project>

Validation rules:

  • Each <dependency> requires both <name> and <version>

  • Version must be valid semantic version format

  • Duplicate dependency names are rejected

13.4. Dependency Resolution

When a project with <dependencies> is compiled, PasBuild resolves each dependency before invoking the compiler:

  1. Detect the current target triplet

  2. For each declared dependency, locate the artifact in the local repository

  3. Read metadata.xml to discover transitive dependencies

  4. Recursively resolve transitive dependencies (with cycle detection)

  5. Add all resolved unit paths to the compiler’s -Fu search paths

If a dependency is not found, PasBuild reports a descriptive error indicating whether the artifact is missing entirely or available for a different target.

13.5. Metadata Format

Each installed artifact includes a metadata.xml file:

<artifact>
  <name>fpgui-framework</name>
  <version>1.0.0</version>
  <packaging>library</packaging>
  <build>
    <fpcVersion>3.2.2</fpcVersion>
    <targetCPU>x86_64</targetCPU>
    <targetOS>linux</targetOS>
    <timestamp>2026-02-13T00:24:26</timestamp>
  </build>
  <dependencies>
    <dependency>
      <name>some-base-lib</name>
      <version>1.0.0</version>
    </dependency>
  </dependencies>
</artifact>

The <dependencies> section enables transitive dependency resolution. When a consumer depends on fpgui-framework, PasBuild automatically resolves some-base-lib as well.

14. Future Enhancements

Future enhancements will be driven by user feedback and community requests. Potential areas include:

  • Remote artifact repository (similar to Maven Central)

  • Dependency version range resolution

15. Implementation Phases

See implementation-progress.adoc for detailed task breakdown and current status.

16. Multi-Module Projects

PasBuild supports Maven-style multi-module projects with aggregators, libraries, and applications.

16.1. Packaging Types

Three packaging types organize multi-module projects:

Type Purpose Example

pom (aggregator)

Coordinates multiple modules

Root project with <modules> list

library

Provides reusable components

Framework, utilities, UI toolkit

application

Final executable

Demo, tool, end-user application

16.2. Project Structure

my-framework/                      # Aggregator (packaging=pom)
├── project.xml
├── core/                          # Library (packaging=library)
│   ├── project.xml
│   └── src/main/pascal/
├── ui/                            # Library (depends on core)
│   ├── project.xml
│   └── src/main/pascal/
└── demo/                          # Application (depends on ui)
    ├── project.xml
    └── src/main/pascal/

16.3. Module Declaration

Aggregators list child modules:

<project>
  <build>
    <packaging>pom</packaging>
  </build>
  <modules>
    <module>core</module>
    <module>ui</module>
    <module>demo</module>
  </modules>
</project>

Modules declare dependencies:

<project>
  <!-- UI depends on core -->
  <modules>
    <module>../core</module>
  </modules>
</project>

16.4. Dependency Resolution

PasBuild automatically:

  1. Discovers all modules from aggregator

  2. Resolves dependencies between modules

  3. Detects circular dependencies

  4. Computes topological build order

  5. Adds resolved module paths to -Fu flags

16.5. Build Order Calculation

Uses depth-first topological sort to determine order:

demo (application)
  └── ui (library)
      └── core (library)

Build order: core → ui → demo

Cycle detection prevents:

A → B → A (cycle detected, build fails)

16.6. Validation Rules

All configurations are validated:

  • Aggregators (pom) MUST have <modules>, forbid <mainSource>

  • Libraries can optionally have <mainSource>

  • Applications MUST have <mainSource>

  • Only libraries and applications produce artifacts

  • Module dependencies cannot reference aggregators

  • Paths must remain within project tree (security)

16.7. CLI Support

Build specific modules:

pasbuild compile -m demo              # Build demo and dependencies
pasbuild test -m core                 # Test core only
pasbuild package -m ui -p release     # Package with profile

Display dependency graph in verbose mode:

pasbuild compile -v                   # Show dependency graph
pasbuild compile -v -m demo           # Show graph and build demo

Example output:

[INFO] Dependency Graph:
[INFO]
[INFO] core (no dependencies)
[INFO] ui (depends on: core)
[INFO]   └─ core
[INFO] demo (depends on: ui)
[INFO]   └─ ui

For details and examples, see multi-module-tutorial.adoc.

17. Appendix: Technology Choices

17.1. Programming Language

Free Pascal 3.2.2 in -Mobjfpc mode

Justification:

  • Self-hosting (tool builds itself)

  • Demonstrates best practices to users

  • Cross-platform without dependencies

17.2. Required FPC Units

Unit Purpose

DOM, XMLRead

Parse project.xml

SysUtils, Classes

File/directory operations

Process

Execute FPC compiler

Zipper

Create release archives

17.3. Unit Namespace Convention

Pattern: PasBuild.<Module>

Examples:

  • PasBuild.Config

  • PasBuild.Builder

  • PasBuild.Types

Rationale:

  • Avoids name collisions with user code

  • Professional organization

  • Demonstrates namespace usage to community