Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rules and Wildcards

What is a Rule?

A rule declares a transformation: given these inputs, produce these outputs by running this command. OxyMake figures out what needs to run based on what you ask for.

[rule.process]
input = ["data/{sample}.csv"]
output = ["results/{sample}.txt"]
shell = "python process.py {input} {output}"

This single rule handles ANY sample. When you ask for results/A.txt, OxyMake matches the output pattern, extracts sample = "A", substitutes it into the input pattern to get data/A.csv, and runs the command.

Wildcards

Wildcards are placeholders in curly braces: {sample}, {cohort}, {model}. They appear in input and output file patterns.

How wildcards resolve

OxyMake uses backward chaining: start from the output you want, find which rule can produce it, extract wildcard values from the match.

You ask for: results/patient_42.txt
                     ↓
Pattern:     results/{sample}.txt
                     ↓
Extracted:   sample = "patient_42"
                     ↓
Input becomes: data/patient_42.csv

Multiple wildcards

Rules can have multiple wildcards:

[rule.analyze]
input = ["data/{cohort}/{region}.parquet"]
output = ["results/{cohort}/{region}/report.html"]
shell = "python analyze.py {input} {output}"

Wildcard expansion from config

When you have a list of known values, put them in [config]:

[config]
samples = ["A", "B", "C"]

[rule.all]
input = ["results/{sample}.txt"]

The all rule has {sample} in its inputs but no outputs — it's an aggregation target. OxyMake expands {sample} from config.samples to request results/A.txt, results/B.txt, results/C.txt.

Expansion modes

When multiple wildcards expand from config lists, the expansion can be:

[config]
samples = ["A", "B"]
conditions = ["treated", "control"]

[rule.experiment]
output = ["results/{sample}_{condition}.csv"]
expand = "product"    # default: A_treated, A_control, B_treated, B_control
ModeBehaviorCount
product (default)All combinations (Cartesian product)N × M
zipParallel pairs (lengths must match)N

Wildcard constraints

Restrict which values a wildcard can take:

[rule.process]
output = ["results/{sample}.txt"]

[rule.process.wildcard_constraints]
sample = "[A-Z][a-z0-9_]*"    # regex: starts with uppercase letter

Existing files and rule ownership

An existing file that matches a wildcard output belongs to that rule when its inputs can be resolved. For example, with raw/{x}.csv → data/{x}.csv, if both raw/manual.csv and a hand-written data/manual.csv exist, ox run can regenerate and overwrite data/manual.csv, just as with make pattern rules.

If raw/manual.csv is missing and has no producer, an existing hand-maintained data/manual.csv is instead treated as a source. The same applies to leading wildcards ({dir}/data.csv) and config-derived directories ({config.outdir}/{x}.csv). Missing intermediate outputs are still rebuilt when their source inputs are available. Cycles, ambiguous producers, and invalid configuration remain errors. A missing source inside another producer is also an error; an existing final output cannot hide a broken dependency chain. Fixed inputs of wildcard rules, such as cfg/settings.txt, are required for every instance even when sample-specific inputs are missing.

Use wildcard_constraints to reserve hand-maintained names, even when matching raw inputs exist. For example, this rule only owns numbered samples, leaving data/manual.csv as a source:

[rule.prepare]
input = ["raw/{x}.csv"]
output = ["data/{x}.csv"]
shell = "cp {input} {output}"

[rule.prepare.wildcard_constraints]
x = "sample[0-9]+"

Outputs recorded in the cache retain their provenance requirements. If their original inputs are missing, only verified cache-import adoption permits them as leaves. ox run --no-cache disables that shortcut and errors until the inputs are restored. A modified adopted output also requires its producer's inputs. An absent cache permits unrecorded manual files, while a corrupt or unreadable cache denies that fallback. CLI, MCP and the Rust API share these rules and check source existence and cache provenance relative to the Oxymakefile directory.

Conditional guards

Rules can apply only to certain wildcard values:

[config]
special_samples = ["X1", "X2"]

[rule.extra_analysis]
input = ["results/{sample}.txt"]
output = ["extra/{sample}_analysis.html"]
when = "sample in @special_samples"

This rule exists only for samples X1 and X2. Other samples don't get the extra analysis — no phantom nodes in the graph, no skipped jobs.

Guards support: in @list, not in @list, == 'value', != 'value', =~ 'regex'.

The Four Execution Modes

ModeKeywordWho manages I/OIn-memory possible
Shellshell = "..."YouNo
Inline scriptrun = "..."YouNo
External scriptscript = "path"YouNo
Pure functioncall = "mod:fn"OxyMakeYes

Start with shell or run for quick prototyping. Migrate to call when your function stabilizes and you want OxyMake to optimize I/O.

See Execution Modes for details.