LogoDart Code Generation

build.yaml Reference#

Source of truth: build/build_config/lib/src/ — package version 1.3.0

The build.yaml file lives at the root of your package and tells build_runner about your builders, their configuration, and how they apply to packages.


Top-Level Sections#

builders:              # Declare Builder implementations
post_process_builders: # Declare PostProcessBuilder implementations
targets:               # Configure which sources each builder runs on
global_options:        # Override builder options globally (in consuming packages)
additional_public_assets: []  # Extra assets to expose publicly from this package

builders: Section#

Each key under builders: is a builder name. The full builder key is package_name|builder_name.

builders:
  my_builder:
    # Required: Dart import for the builder factory
    import: "package:my_package/builder.dart"

    # Required: List of top-level function names matching BuilderFactory typedef
    builder_factories: ["myBuilder"]

    # Required: Map from input extension(s) to output extension(s)
    # See patterns below
    build_extensions: {".dart": [".g.dart"]}

    # When to apply this builder automatically.
    # Options: none | dependents | allPackages | rootPackage
    # Default: none
    auto_apply: dependents

    # Where generated files go.
    # cache: in build cache (invisible to user, default for source_gen part builders)
    # source: written to disk alongside source files (like dart_mappable's .mapper.dart)
    # Default: cache
    build_to: cache

    # Whether this builder is "lazy" — only runs if its output is needed
    # Default: false
    is_optional: false

    # Extensions that MUST exist before this builder runs (for ordering)
    # The builder still runs even if required_inputs aren't present —
    # it's purely for ordering, not filtering
    required_inputs: [".freezed.dart"]

    # Builder keys that should run AFTER this builder
    # Format: "package_name|builder_name"
    runs_before: ["other_package|other_builder"]

    # Builders that should be applied when this builder is applied
    # Most common: tells build_runner to also run source_gen's combining_builder
    # which merges .g.part files into a single .g.dart
    applies_builders: ["source_gen|combining_builder"]

    # Default configuration for consuming packages
    defaults:
      generate_for:
        - "lib/**"
        - "!lib/**/*.g.dart"
      options:
        some_option: value
      dev_options:
        verbose: true
      release_options:
        verbose: false

build_extensions Patterns#

# Simple suffix — matches any file with that extension
build_extensions: {".dart": [".g.dart"]}

# Multiple outputs from one input
build_extensions: {".dart": [".mapper.dart", ".init.dart"]}

# Multiple input extensions
build_extensions:
  ".dart": [".g.dart"]
  ".json": [".dart"]

# Path-anchored (^ prefix) — only this exact path
build_extensions: {"^pubspec.yaml": ["lib/src/pubspec.g.dart"]}

# Capture group ({{}}) — captures part of the path
build_extensions: {"^assets/{{}}.json": ["lib/generated/{{}}.dart"]}
# assets/users.json → lib/generated/users.dart

# Package-level ($package$) — runs exactly once per package
build_extensions: {r"$package$": ["lib/generated/all_mappers.dart"]}

auto_apply Values#

ValueBehavior
none Never auto-applied; must be explicitly enabled in targets:
dependentsApplied to packages that directly depend on this package
allPackagesApplied to all packages in the build
rootPackageApplied only to the root (currently being built) package

Most annotation-driven generators use dependents — they run on packages that depend on your annotations package.

build_to Values#

ValueBehavior
cache Files go to the build cache, not visible on disk. Used by source_gen part builders.
source Files written to disk next to source files. Used by dart_mappable (.mapper.dart).

Important: When build_to: source, the generated files appear in the source tree and should be committed. When build_to: cache, they're regenerated on each build.


post_process_builders: Section#

Post-process builders run after all regular builders and have limited access (only their primary input).

post_process_builders:
  my_post_builder:
    import: "package:my_package/builder.dart"

    # Only ONE factory function (not a list, unlike builders:)
    builder_factory: "myPostBuilder"

    # Extensions of files to process
    input_extensions: [".dart"]

    # cache or source
    build_to: cache

    defaults:
      generate_for: ["lib/**"]
      options: {}

targets: Section#

Used within packages to configure how builders apply to their own source files.

targets:
  # $default is the standard target for a package's lib/ sources
  $default:
    # Glob patterns for files included in this target
    # Exclusions start with !
    sources:
      - "lib/**"
      - "test/**"
      - "!lib/**/*.g.dart"

    # Whether build_runner should auto-apply builders from dependencies
    # Default: true
    auto_apply_builders: true

    # Per-builder configuration overrides
    builders:
      # Key format: "package_name|builder_name"
      "source_gen|combining_builder":
        options:
          ignore_for_file:
            - "lines_longer_than_80_chars"

      "json_serializable|json_serializable":
        # Enable or disable this builder for this target
        enabled: true

        # Glob patterns (relative to package root) for which inputs this builder runs on
        generate_for:
          - "lib/src/**"
          - "test/**"
          # Exclusions:
          - "!lib/src/generated/**"

        # Options passed to the builder (builder reads from BuilderOptions.config)
        options:
          field_rename: snake
          explicit_to_json: true

        # Options only in dev builds (dart pub get vs dart pub get --no-precompile)
        dev_options:
          checked: true

        # Options only in release builds
        release_options:
          checked: false

  # You can define multiple targets (unusual but possible)
  my_tests:
    sources: ["test/**"]
    builders:
      "my_package|my_builder":
        enabled: true

global_options: Section#

Used in consuming packages to override builder options globally. This is what end-users put in their build.yaml to configure your generator:

global_options:
  "json_serializable|json_serializable":
    options:
      field_rename: snake
      explicit_to_json: true
    runs_before: []

Complete Real-World Examples#

json_serializable's build.yaml (generator package)#

builders:
  json_serializable:
    import: "package:json_serializable/builder.dart"
    builder_factories: ["jsonSerializable"]
    build_extensions: {".dart": ["json_serializable.g.part"]}
    auto_apply: dependents
    build_to: cache
    applies_builders: ["source_gen|combining_builder"]

Key points:

  • Output is .g.part (not .g.dart) — source_gen's combining_builder merges it
  • applies_builders: ["source_gen|combining_builder"] is required for the merge step
  • build_to: cache — the .g.part is invisible, the .g.dart is in cache too

retrofit's build.yaml (generator package)#

builders:
  retrofit_generator:
    import: "package:retrofit_generator/retrofit_generator.dart"
    builder_factories: ["retrofitBuilder"]
    build_extensions: { ".dart": [".retrofit.g.part"] }
    auto_apply: dependents
    build_to: cache
    applies_builders: ["source_gen|combining_builder"]

dart_mappable_builder's build.yaml (source output)#

builders:
  dart_mappable_builder:
    import: "package:dart_mappable_builder/dart_mappable_builder.dart"
    builder_factories: [ "buildMappable" ]
    build_extensions: { ".dart": [ ".mapper.dart", ".init.dart" ] }
    required_inputs: [".freezed.dart"]
    auto_apply: dependents
    build_to: source

Key points:

  • build_to: source — outputs are .mapper.dart and .init.dart files on disk
  • required_inputs: [".freezed.dart"] — ensures freezed runs first (ordering only)
  • No applies_builders needed — outputs directly to disk, no combining step

Consuming package build.yaml (example)#

# In the app that uses your generator
targets:
  $default:
    builders:
      "my_generator|my_builder":
        options:
          output_prefix: "_generated"
        generate_for:
          - "lib/models/**"

How build_runner Discovers and Orders Builders#

  1. Reads build.yaml from every package in the dependency graph
  2. Creates BuilderDefinition objects from each builders: entry
  3. Orders builders using a topological sort based on:
    • runs_before declarations
    • required_inputs (implies ordering)
    • applies_builders (implied dependency)
  4. For each source file matching build_extensions input, calls Builder.build()
  5. Post-process builders run in a final phase after all regular builders

Common Mistakes#

MistakeFix
Forgot `applies_builders: ["source_gencombining_builder"]`
Used builder_factory (singular) under builders: Use builder_factories (plural, a list) for regular builders
Used builder_factories under post_process_builders: Use builder_factory (singular) for post-process builders
required_inputs is not filtering It only controls ordering, not whether the builder runs
auto_apply: dependents but builder not running Check that the generator package is a dev_dependency in the consuming package
build_to: cache but file not found on disk Expected — cache files aren't on disk; use build_to: source if you need them visible