Codegen extends the paradigm: some information exists only after a modpack has been selected. For example, the final enum of network messages must contain the messages declared by all selected mods.
Patchwork should not know how to build that enum. It only needs a generic way to declare and run a generator.
A mod declares a codegen task in its metadata:
[[package.metadata.mod.codegen]]
crate = "network-messages"
version = "0.1.0"
dev_crate = "network-messages"
[package.metadata.mod.codegen.generator]
crate = "network-codegen-utils"
command = "generate"Fields:
crate: name of the generated crate;version: version written in the generated crate;dev_crate: name of a crate insidemods/that the generator updates for language server support;generator.crate: crate that contains the generator binary;generator.command: subcommand passed to the generator.
Patchwork resolves generator.crate as:
mods/<generator.crate>/Cargo.toml
and runs:
cargo run --manifest-path mods/network-codegen-utils/Cargo.toml -- generate ...A generator should:
- read the
Cargo.tomlof the composed project; - discover selected mods through path dependencies;
- read domain metadata from those mods;
- preserve the Cargo source of ordinary library dependencies used by emitted
types (
git, registryversion, or a rebasedpath); - generate a standalone crate;
- optionally update the dev crate in
mods/; - avoid changing the generic framework.
For networking, the generator reads:
[package.metadata.network.messages]
clientbound = ["some_types::MessageA"]
serverbound = ["some_types::MessageB"]This table is not known by Patchwork. It is known only by the network generator.
Contributor discovery and generated dependencies have different rules. The composed project contains selected Patchwork mods as paths, so a generator can open their manifests and inspect their metadata. A contributor may, however, refer to a type from a plain Cargo library:
[dependencies]
block-api = { git = "https://github.com/example/project.git" }The generator does not need to find that library in the downloaded mod cache. It should copy the dependency declaration into the generated crate. Local paths must be canonicalized relative to the contributor and then rebased relative to the generated crate; Git/version sources should remain distributable. Requiring all such dependencies to be paths incorrectly turns ordinary libraries into Patchwork registry artifacts.
A generated crate can exist in two places:
build/network-messages/
mods/network-messages/
The first one is the real composition output. The second one is a development copy that helps rust-analyzer and other mods while working.
Mods can depend on:
network-messages = "0.1.0"and use a local patch during development:
[patch.crates-io]
network-messages = { path = "../network-messages" }In the composed project, Patchwork inserts a patch to the generated crate inside the build/cache folder.
The substring generated is reserved in Patchwork mod IDs for crates produced
by codegen during Compose. A Cargo crate whose Patchwork mod ID contains that
substring is not a registry artifact:
- Upload reports the crate as a non-publishable error entry;
- Browse and profile/project navigation do not expose a project page for it;
- the desktop dependency resolver skips it instead of looking in local registries, the backend, or the download cache;
- Compose removes it from modpack selection and lifecycle dependency graphs before attempting to read mod manifests from the cache;
- a dependency reference may still be stored on the publishing mod version, but clients render it as Generated during compose.
This rule applies to Patchwork mod IDs, not to arbitrary Cargo dependency names or output directory names. The generated crate is supplied by the codegen task and patched into the composed Cargo project; it is never downloaded as source from the registry.
To avoid rewriting crate-generation boilerplate, a library like codegen-utils
can provide generic primitives:
GeneratedCrate;GeneratedDependency;GeneratedFile;write_generated_crate;- crate-name normalization;
- type-path to variant-name conversion.
This library stays generic. It should not know what networking is.