• SwiftPM command plugin
  • Xcode & command line
  • Swift 6.2
  • MIT

A Package.swift that writes itself.

Package Generator reads the imports in every target of a heavily modularized Swift project and writes the targets, test targets, products and dependencies of your Package.swift. You keep a short YAML file; the plugin keeps the manifest in step with the code.

packageGenerator.yaml
pragmaMark: true
spaces: 2

packageDirectoryTargets:
  - path: Sources/Core
    targets:
      - name: Core
      - name: CoreTests
        type: test

  - path: Sources/Features
    targets:
      - name: Auth
        parameters:
          - 'resources: [.process("Resources")]'
      - name: AuthTests
        type: test

Why

Modularizing is easy.
Keeping the manifest right is not.

Splitting an app into dozens or hundreds of SwiftPM targets pays off in build times, previews and boundaries. Then every new import means editing a dependencies: array by hand, and the manifest drifts from the code.

1 import

is one more line to add to Package.swift, in the right target, with the right .product(name:package:) identity.

0 drift

The plugin derives dependencies from the source, so a removed import disappears from the manifest on the next run.

1 file

to review: a YAML or JSON config listing folders and targets. Paths, test pairing and product names are inferred.

How it works

Two tools, one command.

Package Generator is a pair: this plugin, which you add to your package, and Package Generator CLI, the binary it runs to read your sources. The plugin downloads the CLI for you as a SwiftPM binary target.

This repository

Package Generator plugin

  • Finds packageGenerator.yaml, .yml or .json and resolves each target's folder.
  • Maps every import to a local target, an external product from your dependencies, or an Apple SDK.
  • Renders products and targets, sorted and grouped, and writes Package.swift.

Package Generator CLI

  • Parses every Swift file of every target with swift-syntax.
  • Reports each target's imports to the plugin as JSON.
  • Ships as an arm64 macOS artifact bundle; you never install or call it yourself.

Describe

List your folders and targets in packageGenerator.yaml. Add per-target parameters, exclusions or product names where you need them.

Run

Right-click your package in Xcode and choose Package Generator, or run the command below in a terminal or on CI.

Review

Set dryRun: true to write Package_generated.swift and diff it first. Output is deterministic, so diffs stay small.

Sources/Features/Auth/Login.swift

Login.swift
import Core
import ComposableArchitecture
import SwiftUI

// …

Terminal

zsh
swift package plugin \
  --allow-writing-to-package-directory \
  package-generator

Generated Package.swift (excerpt)

Package.swift
// MARK: - Generated 2 packages

// MARK: - Targets
package.products.append(contentsOf: [
  .library(name: "Auth", targets: ["Auth"]),
  .library(name: "Core", targets: ["Core"])
])

// MARK: - Products
package.targets.append(contentsOf: [
// … the Core group: Core, CoreTests …
// MARK: -
// MARK: Features
  .target(
    name: "Auth",
    dependencies: [
      "Core",
      .product(name: "ComposableArchitecture", package: "swift-composable-architecture")
    ],
    path: "Sources/Features/Auth",
    resources: [.process("Resources")]
  ),
  .testTarget(
    name: "AuthTests",
    // …
  )
])

Features

Built for apps with hundreds of modules.

Imports become dependencies

Local targets become "Name"; products from your root dependencies become .product(name:package:) with the URL-derived package identity SwiftPM expects.

Apple SDKs ignored, safely

A built-in list of about 270 Apple frameworks is skipped, but only after external products are checked, so a package product named Charts is never lost.

Your parameters, verbatim

parameters and additionalDependencies go into the manifest as written, for resources, Swift settings, macros and plugins that no import reveals.

Products your way

Set libraryType to dynamic or static globally or per target, and rename a product with productName when Xcode needs the two to differ.

Readable output

Sorted products and targets, optional // MARK: groups per folder, a custom header file, and your indentation.

Architecture insight

leafInfo annotates each target with its dependency counts and flags the heaviest; unusedThreshold warns about modules nobody imports.

Tests paired automatically

FeatureTests is linked to Feature by name, with regularTargetName for the exceptions.

Old configs keep working

The legacy targetsParameters map and boolean verbose are migrated in memory. See the migration guide.

Fast on big graphs

Rendering a 1,000-target manifest takes under 10 ms, measured with package-benchmark. See performance.

Install

Add the plugin, write the config, run it.

1. Add the dependency

In the Package.swift you want generated. Requires Swift 6.2 (Xcode 26) on an Apple silicon Mac.

Package.swift
dependencies: [
  .package(url: "https://github.com/mackoj/PackageGeneratorPlugin.git", from: "1.0.6"),
]

2. Describe your targets

Create packageGenerator.yaml next to it, like the one at the top of this page. If none exists, the first run writes a packageGenerator.json template.

3. Generate

In Xcode, right-click the package and choose Package Generator. From a terminal or CI:

zsh
swift package plugin --allow-writing-to-package-directory package-generator
# or with an explicit config
swift package plugin --allow-writing-to-package-directory package-generator --confFile myconfig.yaml