CLI: Build

Tier 0 · Story 10Complete

The build action compiles your Tyhp source files into PHP code. It runs the full compilation pipeline — parse, bind, check, optional package.tyhpdef generation, and emit — then writes the compiled PHP output files to the configured output directory.

Usage

tyhp build [options]

Options

  • --tyhp-project= — Path to the tyhp.json project file.
  • --clean — Delete all .php and .php.map files from the output directory before building.
  • --verbose — Display detailed output for each compilation phase, including per-file status and cache statistics.
  • --dry-run — Run the full compilation pipeline (parse, bind, check, emit) but do not write any output files. Reports what would be written.
  • --strict — Treat warnings as errors. The build fails if any warnings are produced. Overlay stamp mismatches (TYHP8021) and a missing tyhp/compiler pin (TYHP7702) are among those warnings.
  • --fix — Write stale extra.tyhp.require pins onto root require-dev and Composer-update those packages. Same write path as tyhp composer sync. Ordinary tyhp build never auto-updates Composer. See CLI: Composer.
  • --quiet — Suppress the banner and non-diagnostic output.
  • --suppress-warnings=<TYHP####,…> — Hide the listed warning codes for this run (replaces suppressWarnings in tyhp.json). Errors are never hidden.
  • --watch — Planned. Prints that watch mode is not implemented; use a rebuild loop or external file watcher instead.

Build Process

The build action performs the following steps in order:

  1. Load and validate configuration from tyhp.json and CLI arguments.
  2. Check root composer.json plus vendor/composer/installed.json for stale extra.tyhp.require pins (TYHP7701) and a missing tyhp/compiler pin (TYHP7702). Default: explain; missing extras fail the compile; --fix writes require-dev and Composer-updates (never on an ordinary build). See CLI: Composer.
  3. Discover source files matching the configured include patterns (excluding files matching exclude patterns).
  4. Parse all source files into Abstract Syntax Trees (ASTs). Parsing is multi-threaded for performance.
  5. Load tyhpdef files and populate the global scope with built-in types, functions, constants, and external type definitions.
  6. Run the binder to build scope hierarchies and resolve all name references.
  7. Run the type checker to validate type compatibility, null safety, generic constraints, and other rules.
  8. If errors are found, display diagnostics and exit with code 4 (CompileError). If --strict is set and warnings are found, exit with code 5 (CompileWarning).
  9. Tyhpdef generation (after check, before optimize/emit):
    • "type": "library" — always write package.tyhpdef and additive-merge extra.tyhp.package into composer.json in the publish directory (output.publishPath, default: the project root next to tyhp.json). build.generateTyhpdef is ignored.
    • "type": "application" — if build.generateTyhpdef is true, write only package.tyhpdef (no extra.tyhp.package). If false or unset, write nothing.
    • Library projects with executable entrypoint files report TYHP7505 and skip writing those artifacts.
    • If extra.tyhp.package already exists on that composer.json, the build adds missing keys and array items only; it does not remove or change existing entries.
    • The build never writes to CLI tyhpdef/. See CLI: Tyhpdef Generation.
    • Generated package.tyhpdef keeps the library's Tyhp surface: attributes, class-level type, in-package global use, and #[\Tyhp\GenericRuntime] on every emitted generic class/method/function. Consumers use \Tyhp\Generic::bind at foreign sites rather than helper names. internal symbols are omitted.
  10. Run the optimizer (currently a no-op).
  11. Run the emitter to transform Tyhp-specific constructs into PHP equivalents.
  12. Write compiled PHP files to the output directory. When build.generateSourcemap is true, also write a sibling .php.map (Source Map v3) and append a //# sourceMappingURL= comment to each PHP file. When build.updateComposer is true, also update composer.json in output.publishPath with Tyhp runtime package requires. When output.publishContent is set, copy those extra files into output.publishPath.

Multi-target gated PHP output (--php-targets) is a generate_tyhpdef flag, not part of tyhp build. tyhp build evaluates declare(php=…) / #[\Tyhp\Php] in loaded tyhpdefs against output.phpVersion ("8.2" through "8.5" for tyhpdef/php). See CLI: Source Map Generation.

Configuration (tyhp.json)

The build action reads configuration from the tyhp.json project file. Key configuration sections include:

  • suppressWarnings — Warning codes to hide (for example "TYHP8027"). Errors are never hidden. CLI --suppress-warnings replaces the JSON array.

Output Configuration

  • output.path — Output directory for compiled PHP files (default: "build/"). Supports {name}-style interpolation.
  • output.publishPath — Published package root for composer.json (including library extra.tyhp.package) and package.tyhpdef (default: ".", the project root). Supports interpolation, for example "./publish/{version}".
  • output.publishClean — When true, delete and recreate output.publishPath before compiling (default: false). Refused when publishPath is the project root.
  • output.publishContent — Array of copy rules for extra files written into output.publishPath after emit. src / dst / exclude interpolate; dst may also use per-file variables such as {fileName}. See Project Options List.
  • output.phpVersion — Target PHP version: "8.0" through "8.5". If omitted, defaults to "8.2" and emits warning TYHP4306 once per compilation. This is the compiler check/emit target, not the managed PHP used by tyhp generate_tyhpdef. For tyhpdef/php / tyhpdef/php-ext-*, it also selects which version-gated APIs are visible (8.2–8.5 matrix). {phpVersion.id} in other keys is the Tyhp PHP id ("8.2" → 802). See CLI: Lint.
  • output.strictTypes — Add declare(strict_types=1) to all output files (default: true).
  • output.comments — Include comments from source in the output (default: true).
  • output.namespacePrefix — Optional prefix added to all namespaces in output.

Build Configuration

  • build.generateSourcemap — When true, write a .php.map file next to each compiled .php file and append //# sourceMappingURL= (default: false).
  • build.sourcemapIncludeContent — When true with sourcemaps on, embed original .tyhp text in each map's sourcesContent (default: false).
  • build.generateTyhpdef — For applications, write package.tyhpdef in the publish directory when true. Libraries always emit package.tyhpdef and additive-merge extra.tyhp.package on publish-directory composer.json; this key is ignored for "type": "library".
  • build.updateComposer — Generate or update composer.json in output.publishPath with PSR-4 autoloading for the compiled PHP (default: false).
  • build.structBacking — How structs are backed in PHP: "array" (default: "array").
  • build.decimalBacking — Decimal math backend: "bcmath" or "gmp" (default: "bcmath").
  • build.decimalScale — Default scale for decimal operations (default: 28).
  • build.decimalRounding — Default rounding mode (default: "halfUp").
  • build.allowEval — Re-enable the eval() function, which is disabled by default in Tyhp (default: false).
  • build.experimentalReadonlyCloneWith — Allow clone ... with on readonly properties for PHP 8.2–8.4 (default: false). PHP 8.5+ does not need this flag.
  • build.runtimeGenericChecks — Emit runtime type checks at generic boundaries (default: false).
  • build.entryPointAutoloader — Optional map of named autoloader paths injected into entry points.
  • psr4 — PSR-4 namespace-to-directory mappings for the output.
  • psr4Includes — Additional PSR-4 autoload paths.

Checker Configuration

Null safety, required type annotations, and narrowing mixed before use are unconditional. The checker section only exposes resource limits and tooling knobs:

  • checker.templateStringMaxStates — Upper bound on template-string automaton complexity (default: 256).
  • checker.maxFixIterations — Maximum auto-fix re-run iterations for tyhp lint --fix (default: 10).

Example Configuration

{
    "include": ["./src/**/*.tyhp", "./src/**/*.php"],
    "exclude": ["./src/legacy/**"],
    "output": {
        "path": "./src",
        "publishPath": "./publish/{version}",
        "publishClean": false,
        "publishContent": [
            { "src": "README.md" },
            { "src": "LICENSE", "dst": "LICENSE.txt" },
            { "src": "docs/**/*.md", "dst": "docs", "exclude": ["docs/drafts/**"] }
        ],
        "phpVersion": "8.4",
        "strictTypes": true
    },
    "build": {
        "updateComposer": true
    },
    "checker": {
        "templateStringMaxStates": 256
    },
    "psr4": {
        "App\\": "src/"
    }
}

{version} comes from the project-root composer.json next to tyhp.json. Add a "version" field there if tyhp init did not. See String interpolation.

Incremental Builds

Tyhp supports incremental compilation using an AST cache. When you rebuild a project, files that have not changed since the last build are loaded from the cache rather than being re-parsed. This significantly speeds up rebuild times for large projects. The binder and checker still run on all files to ensure cross-file consistency.

Build state is persisted between runs. If no source files or configuration have changed, the build exits early with a "Nothing to build" message. Use --clean to force a full rebuild by clearing the output directory and build state.

Error Output

Errors and warnings are displayed in a format similar to other compiled languages:

src/Models/User.tyhp(42,5): error TYHP4003: Member modifier 'static' is not allowed on interface methods
src/Services/Auth.tyhp(15,10): error TYHP3003: Symbol 'InvalidUser' not found

Build failed with 2 errors.

  Files:     42 source files
  Duration:  0.91s (parse: 0.45s, bind: 0.12s, check: 0.34s)
  Errors:    2
  Warnings:  0

Tyhp Runtime Packages

Some Tyhp features require small runtime PHP packages to function. When build.updateComposer is enabled, the build action automatically adds the necessary Composer dependencies based on the features used in your code:

  • tyhpdef/php — PHP builtin tyhpdefs (always-present 8.2+ surface, gated to output.phpVersion). Optional extensions are tyhpdef/php-ext-*.
  • tyhp/core — Generic type helpers, property accessors, disposable support, named types, scalar extension methods.
  • tyhp/decimal — Decimal arithmetic operations (when the decimal type is used).
  • tyhp/async — Promise-based async/await support (when async functions are used).
  • tyhp/lambda — Expression-tree / PropertyPath runtime (when parsable lambdas are used).

Package MAJOR for compiled tyhp/* helpers is the target PHP (804.x for PHP 8.4), not the compiler ceiling. Each of those packages has its own X.Y. Applications pin 80N.X.Y; libraries or PHP majors with that package's X (803.0.* || 804.0.* || 805.0.* while it is on 0.y). tyhpdef/* packages pin their composer.json version (not 80N.X.Y). See the Composer Runtime Packages page.