Skip to content

Targets

Terminology: This specification uses RFC 2119 keywords (MUST, SHOULD, MAY, etc.) to indicate requirement levels.

This document describes the target system in Structyl.

Overview

A target is a buildable unit in a Structyl project. Targets are unified—both language implementations and auxiliary tools use the same configuration model.

Empty targets configuration: When targets is absent or empty ({}), Structyl uses auto-discovery mode to scan for toolchain marker files. See project-structure.md for details.

Target Name Constraints

Target names (the keys in the targets object) MUST follow these rules:

  • Pattern: ^[a-z][a-z0-9-]*$ (lowercase letters, digits, hyphens only)
  • Length: 1-64 characters (enforced both at runtime and by JSON Schema for IDE validation)

Target Name Flexibility

Target names allow trailing hyphens and consecutive hyphens (e.g., my-target-, my--target), unlike project names which have stricter rules for package registry compatibility. Target names are internal identifiers and don't flow to external systems.

Invalid target names cause exit code 2. CLI output: structyl: targets.{name}: target name must match pattern ^[a-z][a-z0-9-]*$ (lowercase letters, digits, hyphens)

Reserved names: The target name all is reserved and MUST NOT be used. This prevents ambiguity with commands that operate on all targets (e.g., structyl build all vs. structyl build for a target named "all"). Using a reserved name causes exit code 2. CLI output: structyl: targets.all: "all" is a reserved name

Target Types

TypeDescriptionIncluded In
languageProgramming language implementationbuild, test, demo
auxiliarySupporting tools (docs, images, etc.)build only

Language Targets

Language targets represent implementations of your library in different programming languages.

Characteristics:

  • Included in structyl test and structyl demo
  • Expected to have reference test integration

Auxiliary Targets

Auxiliary targets are supporting tools that aren't language implementations.

Examples:

  • Image generation (img)
  • PDF documentation (pdf)
  • Website (web)
  • Code generation (gen)

Characteristics:

  • Only included in structyl build
  • May have dependencies on other targets
  • No test/demo expectations
  • When structyl test or structyl demo runs on all targets, auxiliary targets are skipped (see commands.md for skip behavior)

Target Configuration

Minimal Configuration

With toolchain auto-detection:

json
{
  "targets": {
    "rs": {
      "type": "language",
      "title": "Rust"
    }
  }
}

Structyl detects Cargo.toml in rs/ and uses the cargo toolchain.

Explicit Toolchain

json
{
  "targets": {
    "rs": {
      "type": "language",
      "title": "Rust",
      "toolchain": "cargo"
    }
  }
}

With Command Overrides

json
{
  "targets": {
    "cs": {
      "type": "language",
      "title": "C#",
      "toolchain": "dotnet",
      "commands": {
        "test": "dotnet run --project Pragmastat.Tests",
        "demo": "dotnet run --project Pragmastat.Demo"
      }
    }
  }
}

Full Configuration

json
{
  "targets": {
    "cs": {
      "type": "language",
      "title": "C#",
      "toolchain": "dotnet",
      "directory": "cs",
      "cwd": "cs",
      "vars": {
        "test_project": "Pragmastat.Tests"
      },
      "env": {
        "DOTNET_CLI_TELEMETRY_OPTOUT": "1"
      },
      "commands": {
        "test": "dotnet run --project ${test_project}",
        "demo": "dotnet run --project Pragmastat.Demo"
      }
    },
    "pdf": {
      "type": "auxiliary",
      "title": "PDF Manual",
      "directory": "pdf",
      "depends_on": ["img"],
      "commands": {
        "build": "latexmk -pdf manual.tex",
        "clean": "latexmk -C"
      }
    }
  }
}

Configuration Fields

FieldTypeDefaultDescription
typestringRequired"language" or "auxiliary"
titlestringRequiredDisplay name (1-64 characters, non-empty)
toolchainstringAuto-detectToolchain preset (see toolchains.md)
toolchain_versionstringNoneOverride mise tool version for this target

Type Requirement:

  • In Explicit mode (targets defined in .structyl/config.json): type MUST be specified for all targets
  • In Auto-Discovery mode: type is inferred from the slug—known language slugs become language, others become auxiliary

The Default Language Slugs table defines which slugs map to which types during auto-discovery.

Title Validation:

Invalid title values cause exit code 2 with one of:

  • targets.{name}.title: required (empty string)
  • targets.{name}.title: must be 64 characters or less (exceeds maximum)
FieldTypeDefaultDescription
directorystringTarget keyDirectory path relative to root
cwdstringdirectoryWorking directory for commands
commandsobjectFrom toolchainCommand definitions/overrides
varsobject{}Custom variables for command interpolation
envobject{}Environment variables
depends_onarray[]Targets that must build first
demo_pathstringNonePath to demo source (for doc generation)

Demo Path Configuration

The demo_path field specifies a custom path to the demo source file used by the $DEMO$ placeholder during documentation generation. When not specified, Structyl looks for a demo file at the conventional location <target_directory>/demo.<extension>, where the extension is determined by the target's toolchain (e.g., .py for Python toolchains, .go for Go, .rs for Cargo).

Example — custom demo path:

json
{
  "targets": {
    "py": {
      "type": "language",
      "title": "Python",
      "toolchain": "uv",
      "demo_path": "py/examples/quickstart.py"
    }
  }
}

With this configuration, the $DEMO$ placeholder in README templates resolves to the content of py/examples/quickstart.py instead of the default py/demo.py.

Missing demo file: If no demo file exists at the default location and demo_path is not configured, the $DEMO$ placeholder in README templates is replaced with an empty string. This is not an error condition.

Demo file markers: If the demo file contains structyl:demo:begin and structyl:demo:end markers, only the content between these markers is extracted:

python
# Full example with setup code
import mylib

# structyl:demo:begin
result = mylib.calculate([1, 2, 3, 4, 5])
print(f"Result: {result}")
# structyl:demo:end

# Cleanup code...

Toolchains

Toolchains provide default command implementations. See toolchains.md for the full reference.

Available Toolchains

ToolchainEcosystemAuto-detect File
cargoRustCargo.toml
dotnet.NET (C#/F#)*.csproj, *.fsproj
goGogo.mod
npmNode.jspackage.json
pnpmNode.jspnpm-lock.yaml
yarnNode.jsyarn.lock
bunBunbun.lockb
pythonPythonpyproject.toml, setup.py
uvPython (uv)uv.lock
poetryPython (Poetry)poetry.lock
gradleJVMbuild.gradle, build.gradle.kts
mavenJVMpom.xml
makeGenericMakefile
cmakeC/C++CMakeLists.txt
swiftSwiftPackage.swift

Complete Toolchain Reference

This table shows commonly used toolchains. For the complete list of 27 built-in toolchains with command mappings and auto-detection markers, see toolchains.md.

Toolchain Auto-Detection

When toolchain is not specified, Structyl checks for marker files in the target directory:

rs/
├── Cargo.toml    ← detected as "cargo"
└── src/

Auto-detection is best-effort. Explicit toolchain declaration is RECOMMENDED.

Commands

Commands are defined via the commands field or inherited from the toolchain. See commands.md for full details.

Command Inheritance

  1. If toolchain specified → inherit all toolchain commands
  2. commands object → override specific commands
  3. Missing command → error at runtime

Command Forms

json
{
  "commands": {
    // String: shell command
    "build": "cargo build",

    // Variant: colon naming convention
    "build:release": "cargo build --release",

    // Array: sequential execution
    "check": ["lint", "format-check"],

    // Null: explicitly disabled
    "bench": null
  }
}

Per-command working directory and environment overrides are not supported. Use target-level cwd and env fields instead.

See commands.md for the variant naming convention.

Dependencies

Targets can declare dependencies on other targets:

json
{
  "targets": {
    "img": { "type": "auxiliary", "title": "Images" },
    "pdf": {
      "type": "auxiliary",
      "title": "PDF",
      "depends_on": ["img"]
    },
    "web": {
      "type": "auxiliary",
      "title": "Website",
      "depends_on": ["img", "pdf"]
    }
  }
}

Execution Order

When running structyl build:

  1. Build targets with no dependencies first
  2. Build targets whose dependencies are satisfied
  3. Language targets can build in parallel (no implicit dependencies)
  4. Auxiliary targets build in dependency order

For the example above:

1. img (no dependencies)
2. pdf (depends on img) + language targets (parallel)
3. web (depends on img, pdf)

Parallel Execution

Targets are scheduled for execution in dependency order. When STRUCTYL_PARALLEL=1, targets execute sequentially with strict dependency guarantees. When STRUCTYL_PARALLEL > 1, targets at the same dependency depth MAY execute concurrently (see Known Limitation below).

Execution model:

  • A target becomes eligible when all targets in its depends_on list have been scheduled
  • Multiple eligible targets execute in parallel (up to STRUCTYL_PARALLEL workers)
  • Language targets without explicit dependencies are immediately eligible
  • Note: Parallel mode does not guarantee dependency completion before dependent execution

Example:

json
{
  "targets": {
    "gen": { "type": "auxiliary" },
    "cs": { "type": "language", "depends_on": ["gen"] },
    "py": { "type": "language", "depends_on": ["gen"] },
    "rs": { "type": "language" }
  }
}

Execution order:

  1. gen and rs start immediately (no dependencies)
  2. When gen completes, cs and py become eligible and start in parallel

Known Limitation: Parallel Execution and Dependencies

When STRUCTYL_PARALLEL > 1, Structyl DOES NOT guarantee that targets in depends_on complete before the dependent target starts execution. Topological ordering ensures dependencies are scheduled first, but the semaphore-based worker pool MAY execute dependent targets before their dependencies finish.

Formal Statement: Implementations requiring strict dependency ordering MUST use STRUCTYL_PARALLEL=1 or external orchestration.

Workarounds:

  1. Use sequential execution (STRUCTYL_PARALLEL=1)
  2. Ensure dependency relationships are idempotent (safe to re-run)
  3. Use external orchestration (mise, make) for strict dependency-aware parallelism

This is a known limitation tracked for future improvement.

STRUCTYL_PARALLEL ValueBehavior
Unset or emptyDefault to number of CPU cores
1Serial execution (one target at a time)
2 to 256Parallel execution with N workers
0, negative, >256, or non-integerFalls back to CPU core count (with warning)

Output Handling:

  • Each target's stdout/stderr is buffered independently
  • Output is printed atomically when the target completes
  • Output order follows completion order, not start order

Failure Behavior:

  • Fail-fast: First failure cancels all pending targets; running targets continue to completion

Note: There is no continue-on-error mode. Structyl delegates to mise for task execution, and mise stops on first failure.

Dependency Validation

At project load time, Structyl validates all target dependencies:

ValidationError Message
Reference to undefined targettarget "{name}": depends on undefined target "{dep}"
Self-referencetarget "{name}": cannot depend on itself
Circular dependencycircular dependency detected: {cycle}

All dependency validation errors exit with code 2.

Circular Dependencies

Circular dependencies are detected and reported as configuration errors:

structyl: error: circular dependency detected: a -> b -> c -> a

Target Directory Validation

At project load time, Structyl validates that each target's directory exists:

ConditionError MessageExit Code
Directory does not existtarget "{name}": directory not found: {path}2
Directory is not a directorytarget "{name}": path is not a directory: {path}2

Note: {path} in directory validation errors is the resolved path (relative to project root), not the raw configured value. If directory is not specified, {path} equals the target key.

Default Language Slugs

Structyl recognizes these slugs as language targets during auto-discovery:

SlugLanguageCode FenceDefault Toolchain
csC#csharpdotnet
goGogogo
ktKotlinkotlingradle
pyPythonpythonpython
rRr
rsRustrustcargo
tsTypeScripttypescriptnpm
jsJavaScriptjavascriptnpm
javaJavajavagradle
cppC++cppcmake
cCccmake
rbRubyruby
swiftSwiftswiftswift
scalaScalascalagradle

Custom slugs default to auxiliary type unless explicitly configured.

Target Operations

Single Target

bash
structyl <command> <target> [args]

# Examples
structyl build cs
structyl test py
structyl build:release rs

All Targets

bash
structyl <command> [args]

# Examples
structyl build              # Build all targets
structyl test               # Test all language targets
structyl clean              # Clean all targets

Filtered Operations

bash
# Build specific targets
structyl build cs py rs

# Build only language targets (explicit)
structyl build --type=language

# Build only auxiliary targets
structyl build --type=auxiliary

Meta Commands vs Target Commands

CommandScopeNotes
structyl buildAll targetsRespects dependencies
structyl testLanguage targets onlyParallel execution
structyl demoLanguage targets onlyParallel execution
structyl cleanAll targetsNo dependency order
structyl ciAll targetsFull pipeline

Target Listing

bash
structyl targets

Languages:
  cs   C#         (dotnet)
  go   Go         (go)
  py   Python     (uv)
  rs   Rust       (cargo)
  ts   TypeScript (pnpm)

Auxiliary:
  img  Image Generation
  pdf  PDF Manual (depends: img)
  web  Website (depends: img, pdf)

Output Format Stability

The structyl targets output is designed for human readability and is explicitly unstable. Do not parse this output programmatically. See stability.md#unstable-may-change for the list of unstable interfaces.

Adding Custom Targets

  1. Create directory
  2. Either:
    • Let Structyl auto-discover toolchain
    • Add to targets in .structyl/config.json with explicit configuration

Example—adding an image generation target:

json
{
  "targets": {
    "img": {
      "type": "auxiliary",
      "title": "Image Generation",
      "commands": {
        "build": "python scripts/generate_images.py",
        "clean": "rm -rf output/images"
      }
    }
  }
}
bash
structyl build img
structyl clean img

© 2026 Andrey Akinshin MIT