tomato·bazeldocs v0 · latest
Docs/Reference/Modules/rules_rdf

rules_rdf

Bazel rules for RDF — toolchain types for SPARQL, SHACL validation, format conversion, and reasoning. Concrete implementations live in sibling repos like rules_jena.

Latest0.4.0
Versions5
CategoryBazel rules
MaintainersMatt Marshall
Registryhttps://registry.tbzl.dev/modules/rules_rdf/
Sourcegithub.com/tomato-bazel/rules_rdf
MODULE.bazelstarlark
bazel_dep(name = "rules_rdf", version = "0.4.0")

View source & releases on GitHub ↗

Bazel rules for RDF. Defines abstract toolchain types for the four operations that show up in every production RDF/knowledge-graph pipeline — SPARQL query execution, SHACL validation, format conversion, and reasoning — and leaves the engine choice to a concrete toolchain registered by the consumer. The host repo only registers toolchains; the rules themselves are engine-agnostic.

rules_rdf also hosts the canonical RDF statement substrate//rdf:statement_proto (fastverk.rdf.v1): Jena-protobuf-shaped Term / Triple / Quad / Literal / PrefixDecl / RdfStatement, the L1 wire format graph data reduces to (keyed on stable IRIs). Schema-only; consumers generate their own language bindings.

Status: v0.2.0

What v0.2 adds on top of v0.1.0:

  • rdf_reason (//reason:defs.bzl) — build-action rule that runs the registered rdf_reasoner toolchain over a base dataset and emits the derived-triples graph (Turtle) as a file artifact. Provides a fresh RdfDatasetInfo so consumers can chain reason → validate → query. Supports built-in profiles (rdfs, owl-rl, owl-mini, owl-micro) + custom with a Jena rule file.
  • rdf_transform (//transform:defs.bzl) — converts a dataset between serializations via the registered rdf_serializer toolchain. Output extension follows the target format.
  • Binary RDF supportrdfthrift and rdfprotobuf added to RDF_FORMATS (Apache Jena’s binary serializations). Useful as cached intermediate forms; significantly faster to parse than Turtle for large datasets. Consumed by rdf_transform’s out_format + accepted by rdf_dataset’s srcs via .rt / .rpb / .bin extensions.
  • Four no-op smoke plugins (bash) covering every toolchain type. bazel test //... runs 14/14 — every public rule exercised end-to-end without needing rules_jena.

v0.1.0 status (still shipped):

What ships:

  • Four toolchain_type declarations in //rdf: (sparql_engine, rdf_validator, rdf_serializer, rdf_reasoner).
  • One rdf_*_toolchain rule per type for plugin registration (//rdf:toolchains.bzl), each carrying the binary + its runfiles so py_binary / java_binary plugins resolve cleanly inside the Bazel sandbox.
  • Five providers (SparqlEngineToolchainInfo, RdfValidatorToolchainInfo, RdfSerializerToolchainInfo, RdfReasonerToolchainInfo, RdfDatasetInfo).
  • rdf_dataset(name, srcs, in_format) — bundle RDF files into a format-tagged provider for downstream rules.
  • sparql_query_test(name, dataset, query) — zero-row SPARQL gate. The workhorse rule; resolves sparql_engine_toolchain_type.
  • rdf_validate_test(name, dataset, shapes, severity) — SHACL gate via rdf_validator_toolchain_type.
  • rdf_plugin_contract_test(name, plugin, toolchain_type) — runs the conformance driver (Python) against any plugin executable. Four scenarios: valid_minimal, malformed_input, unknown_flag, determinism.
  • Plugin contract finalized at rdf/plugin_contract.md (v1).
  • Stardoc-generated reference in docs/ for every public-API file.
  • End-to-end smoke test in examples/smoke/ — a no-op Python SPARQL engine registered as a toolchain, exercised through both sparql_query_test and rdf_plugin_contract_test. Both pass.

Deferred to v0.2:

  • sparql_query_run, rdf_transform, rdf_reason (need wider toolchain coverage; rules_jena unblocks them).
  • ShEx support in rdf_validate_test (the toolchain contract leaves room via a future --shapes-language flag).
  • Cross-format dataset support (mixed in_format in one dataset).

Architecture

Plugins implement a minimal stdin/argv/stdout contract; per-operation Bazel rules wrap them. Concrete RDF engines (Apache Jena, RDF4J, Oxigraph, …) live in sibling repos and register toolchains against the abstract types defined here.

//rdf:                        operation-neutral core
  - toolchain_type definitions per RDF operation
  - provider definitions (RdfDatasetInfo, …)
  - rdf_*_toolchain rules (register a plugin)
  - rdf_plugin_contract_test rule (verify a plugin conforms)
  - plugin_contract.md (authoritative spec)

//sparql:                     SPARQL query rules
  - sparql_query_test, sparql_query_run

//shacl:                      SHACL validation rules
  - rdf_validate_test

//convert:                    format conversion rules
  - rdf_transform

//reason:                     inference rules
  - rdf_reason

Adding a new RDF engine is:

  1. Write four plugin binaries (one per toolchain type) — or a single multi-tool binary dispatched on a subcommand flag. Each conforms to the plugin contract.
  2. Register one rdf_*_toolchain per operation pointing at the appropriate plugin.
  3. Gate registration with rdf_plugin_contract_test.

Planned toolchain types

TypeOperationInputsOutput
sparql_engine_toolchain_typeRun a SPARQL query against an RDF datasetdataset (one or more graph files) + .rq query filequery results (SRX / JSON / TSV / CSV / Turtle for CONSTRUCT)
rdf_validator_toolchain_typeValidate a dataset against a SHACL shapes graphdataset + shapes.ttlvalidation report (Turtle, sh:ValidationReport)
rdf_serializer_toolchain_typeConvert RDF between serializationsdataset in format Asame graph in format B (Turtle ↔ N-Triples ↔ JSON-LD ↔ RDF/XML ↔ TriG ↔ N-Quads)
rdf_reasoner_toolchain_typeMaterialise inferred triplesdataset + reasoning profile (rdfs, owl-rl, custom rules)derived-triples graph (Turtle)

Each type resolves a single plugin executable per consumer; an engine ships up to four plugins (or one binary with four subcommands) and registers each independently so a consumer can mix-and-match — e.g. Jena for SPARQL and Oxigraph for reasoning — without rebuilding the toolchain.

Planned user-facing rules

RuleToolchainPurpose
rdf_dataset(none)Bundle one or more graph files + format hints into an RdfDatasetInfo provider consumed by every downstream rule.
sparql_query_testsparql_engine_toolchain_typeZero-row gate — run a .rq query and fail the build if the result set is non-empty. The canonical SPARQL gate idiom.
sparql_query_runsparql_engine_toolchain_typeRun a query and emit the result set as a build artifact.
rdf_validate_testrdf_validator_toolchain_typeRun SHACL validation; fail the build on any sh:Violation.
rdf_transformrdf_serializer_toolchain_typeConvert a dataset between serializations. Idempotent on the same format.
rdf_reasonrdf_reasoner_toolchain_typeEmit the inferred triples for a dataset under a reasoning profile.

sparql_query_test is the workhorse — the production graph at kg/java/ uses the same “non-empty result set means violation” idiom for every PR gate, and rules_rdf lifts that idiom into a first-class Bazel rule.

The plugin contract

A plugin is any executable that conforms to:

INPUT
  stdin              the RDF document bytes (concatenated dataset, format declared via --in-format)
  argv               --key=value pairs

OUTPUT
  stdout             the generated output (query results, validation report, converted graph, inferred triples)
  stderr             diagnostics

EXIT
  0                  success
  non-zero           failure

Standard argv flags every plugin receives: --rule-name=NAME and --in-format=FORMAT. Per-toolchain flags (--query=PATH, --shapes=PATH, --out-format=FORMAT, --profile=NAME) are passed through by the calling rule. See rdf/plugin_contract.md for the authoritative spec (currently v0.1 draft).

Concrete implementations

  • fastverk/rules_jena — Apache Jena backend. Ships plugins for all four toolchain types (jena_sparql, jena_shacl, jena_riot, jena_reasoner) and a Maven-pinned JENA_DEPS set so consumers don’t re-declare it.

Others (RDF4J, Oxigraph) are not blocked by anything in this repo — the contract is the integration point. PRs to list third-party implementations here are welcome.

Install

.bazelrc:

common --registry=https://registry.fastverk.com/
common --registry=https://bcr.bazel.build/

MODULE.bazel:

bazel_dep(name = "rules_rdf", version = "0.2.0")

No toolchains are registered by default — pull in a concrete implementation (e.g. rules_jena) and register its toolchains in your MODULE.bazel.

Compatibility

  • Bazel: 7.4+, bzlmod required (tested on 9.1).

License

MIT.

Usage#

Real usage, taken from the module’s examples/.

examples/smoke/BUILD.bazel

load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
load("@rules_rdf//rdf:contract_test.bzl", "rdf_plugin_contract_test")
load("@rules_rdf//rdf:dataset.bzl", "rdf_dataset")
load(
    "@rules_rdf//rdf:toolchains.bzl",
    "rdf_reasoner_toolchain",
    "rdf_serializer_toolchain",
    "rdf_validator_toolchain",
    "sparql_engine_toolchain",
)
load("@rules_rdf//rdf:namespace.bzl", "rdf_namespace_manifest")
load("@rules_rdf//reason:defs.bzl", "rdf_reason")
load("@rules_rdf//sparql:defs.bzl", "sparql_query", "sparql_query_test")
load("@rules_rdf//transform:defs.bzl", "rdf_transform")
load("@rules_rdf//validate:defs.bzl", "rdf_validate_test")

package(default_visibility = ["//visibility:public"])

# No-op plugins for every rules_rdf toolchain type. Bash scripts —
# not py_binary — so they have no stage2 bootstrap to stage in
# action runfiles trees. Real implementations (rules_jena's jena_*)
# replace these for production.

sh_binary(name = "no_op_sparql", srcs = ["no_op_sparql.sh"])
sh_binary(name = "no_op_validator", srcs = ["no_op_validator.sh"])
sh_binary(name = "no_op_serializer", srcs = ["no_op_serializer.sh"])
sh_binary(name = "no_op_reasoner", srcs = ["no_op_reasoner.sh"])

sparql_engine_toolchain(name = "no_op_sparql_toolchain", binary = ":no_op_sparql")
rdf_validator_toolchain(name = "no_op_validator_toolchain", binary = ":no_op_validator")
rdf_serializer_toolchain(name = "no_op_serializer_toolchain", binary = ":no_op_serializer")
rdf_reasoner_toolchain(name = "no_op_reasoner_toolchain", binary = ":no_op_reasoner")

toolchain(
    name = "no_op_sparql_toolchain_def",
    toolchain = ":no_op_sparql_toolchain",
    toolchain_type = "@rules_rdf//rdf:sparql_engine_toolchain_type",
)

toolchain(
    name = "no_op_validator_toolchain_def",
    toolchain = ":no_op_validator_toolchain",
    toolchain_type = "@rules_rdf//rdf:rdf_validator_toolchain_type",
)

toolchain(
    name = "no_op_serializer_toolchain_def",
    toolchain = ":no_op_serializer_toolchain",
    toolchain_type = "@rules_rdf//rdf:rdf_serializer_toolchain_type",
)

toolchain(
    name = "no_op_reasoner_toolchain_def",
    toolchain = ":no_op_reasoner_toolchain",
    toolchain_type = "@rules_rdf//rdf:rdf_reasoner_toolchain_type",
)

# Source data — a 1-triple Turtle file every smoke target chains off.
rdf_dataset(
    name = "sample",
    srcs = ["sample.ttl"],
    in_format = "turtle",
)

# A linked vocabulary + a dataset that deps on it. `composed`'s
# transitive_files closure = sample.ttl + linked.ttl, so every consuming
# rule (query/reason/validate/transform) sees both graphs — the
# import-closure model used to ground schema.org + SKOS + DC.
rdf_dataset(
    name = "linked",
    srcs = ["linked.ttl"],
    in_format = "turtle",
)

rdf_dataset(
    name = "composed",
    srcs = ["sample.ttl"],
    in_format = "turtle",
    deps = [":linked"],
)

# -- End-to-end smokes ---------------------------------------------------------

sparql_query_test(
    name = "zero_row_gate_smoke",
    dataset = ":sample",
    query = "query.rq",
)

# Exercises the transitive closure through a consumer: the no-op engine
# receives both sample.ttl and linked.ttl.
sparql_query_test(
    name = "closure_smoke",
    dataset = ":composed",
    query = "query.rq",
)

# Import-closure aspect: parent owl:imports child; with child in deps the
# closure is complete, so the strict manifest builds. Drop the dep and
# `strict = True` would fail the build (the completeness gate).
rdf_dataset(
    name = "ont_child",
    srcs = ["ont_child.ttl"],
    in_format = "turtle",
)

rdf_dataset(
    name = "ont_parent",
    srcs = ["ont_parent.ttl"],
    in_format = "turtle",
    deps = [":ont_child"],
)

rdf_namespace_manifest(
    name = "closure_manifest",
    dataset = ":ont_parent",
    strict = True,
)

rdf_validate_test(
    name = "validate_smoke",
    dataset = ":sample",
    shapes = "shapes.ttl",
)

rdf_transform(
    name = "transform_smoke",
    dataset = ":sample",
    out_format = "ntriples",
)

# Producer: emit SELECT results as a build artifact (vs the zero-row
# gate). Turns a reasoned graph into downstream-consumable data.
sparql_query(
    name = "select_smoke",
    dataset = ":sample",
    out_format = "tsv",
    query = "query.rq",
)

rdf_reason(
    name = "reason_smoke",
    base = ":sample",
    profile = "rdfs",
)

# -- Conformance gates ---------------------------------------------------------

rdf_plugin_contract_test(
    name = "no_op_sparql_conforms",
    plugin = ":no_op_sparql",
    toolchain_type = "sparql_engine",
)

rdf_plugin_contract_test(
    name = "no_op_validator_conforms",
    plugin = ":no_op_validator",
    toolchain_type = "rdf_validator",
)

# … truncated — see the repo for the full example

Rules & providers#

Generated with Stardoc from the module's .bzl sources.

from docs/ROADMAP.md

rules_rdf roadmap

Two waypoints between today’s scaffold and a usable abstract RDF toolchain layer. Each waypoint is one published bazel-registry release.

v0.1 — toolchain types + plugin contract + placeholder rules

The goal is for a consumer to be able to declare every planned target type (rdf_dataset, sparql_query_test, rdf_validate_test, rdf_transform, rdf_reason) today, against a no-op default toolchain, then swap in a real implementation (e.g. rules_jena) without touching their BUILD files. This makes rules_rdf adoptable incrementally — consumers can wire their build graph before any engine is integrated.

Deliverables:

  • Plugin contract document at rdf/plugin_contract.md (draft already in tree). Same shape as rules_jsonschema’s plugin_contract.md, adjusted for RDF semantics:
    • stdin = the RDF document bytes (the dataset; format declared via --in-format), not a JSON schema.
    • argv = --key=value pairs (same as jsonschema). Standard flags: --rule-name, --in-format. Per-toolchain flags: --query, --shapes, --out-format, --profile.
    • stdout = generated output (query results / validation report / converted graph / inferred triples). Same single-file-per- invocation discipline.
    • stderr = diagnostics.
    • exit = 0 / non-zero.
  • All four toolchain types defined in //rdf:BUILD.bazel: sparql_engine_toolchain_type, rdf_validator_toolchain_type, rdf_serializer_toolchain_type, rdf_reasoner_toolchain_type.
  • Providers: RdfDatasetInfo, RdfEngineToolchainInfo, RdfValidatorToolchainInfo, RdfSerializerToolchainInfo, RdfReasonerToolchainInfo. Each toolchain info wraps a single binary File, matching the jsonschema pattern.
  • Default user-facing rules implemented as _no_op placeholders:
    • rdf_dataset — real (returns RdfDatasetInfo; no toolchain needed).
    • sparql_query_test, sparql_query_run, rdf_validate_test, rdf_transform, rdf_reason — declare their toolchain dependency and accept all their final attrs, but the in-repo default toolchain points at a _no_op binary that writes an empty stdout and exits 0. Consumers can declare targets and they build; swapping in rules_jena makes them actually run.
  • Conformance test driver rdf_plugin_contract_test covering the same scenarios as the jsonschema driver — valid_minimal (small dataset round-trips), malformed_input (garbage on stdin → exit non-zero, empty stdout), unknown_flag (rejects unknown argv), determinism (byte-identical stdout on identical invocations). One driver, parameterised by toolchain type.
  • stardoc for the public surface, with diff_test freshness.

Out of scope for v0.1: chained pipelines, real-engine examples, result-set diff helpers.

v0.2 — cross-toolchain wiring + real-engine examples

Once rules_jena is published and registered, rules_rdf grows the glue that ties multiple toolchains together in one pipeline.

Deliverables:

  • Chained pipelinesrdf_validate_test and sparql_query_test accept the output of rdf_reason as their dataset, so a consumer can express “materialise inferences, then run shape validation on the closure” as a typed build graph. The intermediate inferred graph is a real RdfDatasetInfo-bearing target, not a hidden side effect.
  • Result-set helpers — a small Starlark helper for the common zero-row-CSV gate pattern, plus an rdf_results_diff_test for golden SPARQL result sets (SRX/JSON normalisation).
  • Examples directory using a real RDF corpus:
    • W3C example datasets fetched via http_file with a pinned sha256 (the same fetch-and-pin discipline rules_docker_compose uses for the compose-spec schema).
    • One end-to-end smoke target per toolchain type, registered against rules_jena.
  • CI matrix running the conformance test driver against every registered concrete implementation we know about, gating rules_rdf releases on at least one concrete backend passing.

After v0.2 the abstract layer is feature-complete; further work moves into the concrete-implementation repos.

from docs/contract_test.md

rdf_plugin_contract_test(name, plugin, toolchain_type) runs the rules_rdf conformance test driver against any executable claiming to implement the plugin contract for the named toolchain type. See plugin_contract.md for what the driver asserts.

Plugin authors gate toolchain registration on it:

load("@rules_rdf//rdf:contract_test.bzl", "rdf_plugin_contract_test")

rdf_plugin_contract_test(
    name = "jena_sparql_conforms",
    plugin = "//jena:jena_sparql",
    toolchain_type = "sparql_engine",
)

The four toolchain types each have their own minimum-valid input inside the driver; pass the bare name (without the _toolchain_type suffix or @rules_rdf//rdf: prefix).

rdf_plugin_contract_test

load("@rules_rdf//rdf:contract_test.bzl", "rdf_plugin_contract_test")

rdf_plugin_contract_test(name, plugin, toolchain_type)

Run the rules_rdf conformance test driver against a plugin binary. See plugin_contract.md.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
pluginThe plugin binary to test. Any executable that claims to implement the rules_rdf plugin contract.Labelrequired
toolchain_typeWhich toolchain type’s scenarios to run: one of sparql_engine, rdf_validator, rdf_serializer, rdf_reasoner.Stringrequired

from docs/dataset.md

rdf_dataset(name, srcs, in_format) — declare a labeled collection of RDF files.

This is the single source of “what triples are in this graph?” that every other rule consumes. Carrying both the file depset and the format string up-front lets sparql_query_test / rdf_validate_test / … avoid sniffing extensions at action time and lets consumers mix datasets with declared formats in one BUILD target without ambiguity.

Multi-file datasets are concatenated by the consuming rule in lexicographic order before being piped to the plugin’s stdin (see rdf/plugin_contract.md). Consumers that care about ordering should name files to sort accordingly.

rdf_dataset

load("@rules_rdf//rdf:dataset.bzl", "rdf_dataset")

rdf_dataset(name, deps, srcs, in_format)

A labeled collection of RDF source files + linked-graph deps.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
depsOther rdf_datasets this graph links to (imported ontologies, vocabulary modules). Their files are folded into this dataset’s transitive_files closure, so reasoning/query over the linked vocabularies resolves. Deps should share in_format (normalize otherwise).List of labelsoptional[]
srcsRDF source files. Concatenated in lexicographic order by consuming rules before being piped to the plugin’s stdin.List of labelsrequired
in_formatSerialization of every file in srcs. Mixed-format datasets aren’t supported in v0.1 — use rdf_transform first.Stringoptional"turtle"

from docs/providers.md

Providers for the four rules_rdf toolchain types.

Each provider wraps both the executable and the runfiles needed to invoke it. Carrying runfiles in the provider matters for plugin implementations that aren’t a single self-contained binary — py_binary, java_binary, sh_binary all stage helper files via runfiles. Consuming rules merge the provider’s runfiles into their own to make the plugin actually executable inside a Bazel sandbox.

RdfDatasetInfo

load("@rules_rdf//rdf:providers.bzl", "RdfDatasetInfo")

RdfDatasetInfo(files, transitive_files, in_format)

A declared RDF dataset.

FIELDS

NameDescription
filesdepset[File]: this dataset’s own source files (excludes deps).
transitive_filesdepset[File]: the full graph closure — this dataset’s files plus the transitive closure of every deps dataset. Consumers needing all linked triples (sparql_query, rdf_reason, rdf_validate) operate over this; the subclass/import closure of a grounding ontology (schema.org + SKOS + DC + modules) is assembled here.
in_formatstr: serialization of the dataset files. One of turtle, ntriples, nquads, trig, jsonld, rdfxml. The whole closure must share this format (normalize a differing dep with rdf_transform first).

RdfReasonerToolchainInfo

load("@rules_rdf//rdf:providers.bzl", "RdfReasonerToolchainInfo")

RdfReasonerToolchainInfo(binary, runfiles, files_to_run)

An RDF inference engine. Resolved by rdf_reason.

FIELDS

NameDescription
binaryFile: an executable that runs RDFS / OWL / custom-rule inference and emits derived triples.
runfilesrunfiles: the plugin binary’s runfiles bundle.
files_to_runFilesToRunProvider: pass in an action’s tools= to materialize the plugin’s runfiles tree.

RdfSerializerToolchainInfo

load("@rules_rdf//rdf:providers.bzl", "RdfSerializerToolchainInfo")

RdfSerializerToolchainInfo(binary, runfiles, files_to_run)

An RDF format converter. Resolved by rdf_transform.

FIELDS

NameDescription
binaryFile: an executable that converts between RDF serializations (Turtle / N-Triples / N-Quads / JSON-LD / RDF/XML / TriG).
runfilesrunfiles: the plugin binary’s runfiles bundle.
files_to_runFilesToRunProvider: pass in an action’s tools= to materialize the plugin’s runfiles tree.

RdfValidatorToolchainInfo

load("@rules_rdf//rdf:providers.bzl", "RdfValidatorToolchainInfo")

RdfValidatorToolchainInfo(binary, runfiles, files_to_run)

An RDF validator (SHACL today; ShEx in scope for v0.2). Resolved by rdf_validate_test.

FIELDS

NameDescription
binaryFile: an executable that validates an RDF dataset against a shapes graph per the contract.
runfilesrunfiles: the plugin binary’s runfiles bundle.
files_to_runFilesToRunProvider: pass in an action’s tools= to materialize the plugin’s runfiles tree.

SparqlEngineToolchainInfo

load("@rules_rdf//rdf:providers.bzl", "SparqlEngineToolchainInfo")

SparqlEngineToolchainInfo(binary, runfiles, files_to_run)

A SPARQL query engine. Resolved by sparql_query_test and sparql_query_run.

FIELDS

NameDescription
binaryFile: an executable that runs SPARQL queries per the rules_rdf plugin contract.
runfilesrunfiles: the plugin binary’s runfiles bundle.
files_to_runFilesToRunProvider: pass in an action’s tools= so Bazel materializes the plugin’s runfiles tree (java_binary / py_binary plugins fail to locate runfiles otherwise).

from docs/reason.md

User-facing inference rules.

rdf_reason runs the registered rdf_reasoner toolchain over an RDF dataset and emits the derived-triples graph (Turtle) as a build artifact. Unlike sparql_query_test / rdf_validate_test, this is a regular rule — its output is a file that downstream rules can declare as a src or data dependency.

load("@rules_rdf//rdf:dataset.bzl", "rdf_dataset")
load("@rules_rdf//reason:defs.bzl", "rdf_reason")

rdf_dataset(name = "ontology", srcs = glob(["*.ttl"]))

rdf_reason(
    name = "inferred",
    base = ":ontology",
    profile = "rdfs",
)

For custom rule sets (Jena RETE rules):

rdf_reason(
    name = "inferred",
    base = ":ontology",
    profile = "custom",
    rules = "rules/transitive.rule",
)

The reasoner toolchain implementation decides which profiles are supported; the abstract layer only validates that profile = "custom" is paired with rules and vice versa.

rdf_reason

load("@rules_rdf//reason:defs.bzl", "rdf_reason")

rdf_reason(name, base, include_base, profile, rules)

Run inference over an RDF dataset; emit the derived-triples graph (Turtle).

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
baseRDF dataset to run inference over.Labelrequired
include_baseIf True, emit base + derived triples; otherwise only the derived (default).BooleanoptionalFalse
profileReasoning profile. custom requires rules.Stringoptional"rdfs"
rulesCustom rule file (Jena RETE syntax). Required iff profile = ‘custom’.LabeloptionalNone

from docs/sparql.md

User-facing SPARQL rules.

sparql_query_test is the zero-row gate idiom: declare an invariant as a SPARQL query whose result set is empty when the graph satisfies the invariant. CI runs it as a Bazel test; any non-empty row triggers a failure.

It’s the rules_rdf analog of the production GateZeroRows.java pattern in the Aion RFC repo’s kg/java/. v0.1 wires the rule through sparql_engine_toolchain_type; the actual SPARQL execution comes from whichever concrete toolchain the consumer registered (rules_jena, a future rules_rdflib, etc.).

load("@rules_rdf//rdf:dataset.bzl", "rdf_dataset")
load("@rules_rdf//sparql:defs.bzl", "sparql_query_test")

rdf_dataset(name = "corpus", srcs = glob(["*.ttl"]))

sparql_query_test(
    name = "no_dangling_refs",
    dataset = ":corpus",
    query = "queries/dangling.rq",
)

sparql_query

load("@rules_rdf//sparql:defs.bzl", "sparql_query")

sparql_query(name, dataset, out_format, query)

Run a SPARQL query and emit the results as a build artifact (the producer counterpart to sparql_query_test’s gate). Turns a reasoned graph into queryable, downstream-consumable data — e.g. grounding tuples for training-data generation.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
datasetThe rdf_dataset (closure) to query.Labelrequired
out_formatResult serialization. Tabular (tsv/csv/json/xml) for SELECT/ASK; RDF (turtle/ntriples/…) for CONSTRUCT/DESCRIBE (also yields an rdf_dataset).Stringrequired
queryThe SPARQL query file (SELECT/ASK → tabular; CONSTRUCT/DESCRIBE → graph).Labelrequired

sparql_query_smoke_test

load("@rules_rdf//sparql:defs.bzl", "sparql_query_smoke_test")

sparql_query_smoke_test(name, dataset, queries)

Assert that a set of SPARQL queries all parse + execute against a dataset. The query-smoke gate idiom — catches syntax errors and reference rot after schema changes.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
datasetAn rdf_dataset the queries run against.Labelrequired
queriesSPARQL query files. The test passes iff every one parses and executes without error (no row-count assertion — that’s sparql_query_test).List of labelsrequired

sparql_query_test

load("@rules_rdf//sparql:defs.bzl", "sparql_query_test")

sparql_query_test(name, dataset, query)

Run a SPARQL query against an RDF dataset; fail if the result set is non-empty. The zero-row gate idiom.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
datasetAn rdf_dataset whose triples the query runs against.Labelrequired
queryThe SPARQL query file. Result set must be empty for the test to pass (per --fail-on-nonempty).Labelrequired

from docs/toolchains.md

Toolchain registration rules for rules_rdf.

One rule per toolchain type. Each takes the plugin binary as a mandatory exec-config label and exposes the matching *ToolchainInfo provider with both the binary File and its runfiles bundle.

Concrete plugins (rules_jena, rules_rdflib, …) register via:

sparql_engine_toolchain(
    name = "jena_arq_sparql_toolchain",
    binary = ":jena_sparql",
)

toolchain(
    name = "jena_arq_sparql",
    toolchain = ":jena_arq_sparql_toolchain",
    toolchain_type = "@rules_rdf//rdf:sparql_engine_toolchain_type",
)

rdf_reasoner_toolchain

load("@rules_rdf//rdf:toolchains.bzl", "rdf_reasoner_toolchain")

rdf_reasoner_toolchain(name, binary)

Declare an RDF reasoner (inference) toolchain.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
binaryThe plugin executable. Must conform to the contract in rdf/plugin_contract.md.Labelrequired

rdf_serializer_toolchain

load("@rules_rdf//rdf:toolchains.bzl", "rdf_serializer_toolchain")

rdf_serializer_toolchain(name, binary)

Declare an RDF serializer (format-converter) toolchain.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
binaryThe plugin executable. Must conform to the contract in rdf/plugin_contract.md.Labelrequired

rdf_validator_toolchain

load("@rules_rdf//rdf:toolchains.bzl", "rdf_validator_toolchain")

rdf_validator_toolchain(name, binary)

Declare an RDF validator toolchain.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
binaryThe plugin executable. Must conform to the contract in rdf/plugin_contract.md.Labelrequired

sparql_engine_toolchain

load("@rules_rdf//rdf:toolchains.bzl", "sparql_engine_toolchain")

sparql_engine_toolchain(name, binary)

Declare a SPARQL engine toolchain.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
binaryThe plugin executable. Must conform to the contract in rdf/plugin_contract.md.Labelrequired

from docs/transform.md

User-facing format-conversion rule.

rdf_transform re-serializes an RDF dataset into a different format via the registered rdf_serializer toolchain. The output is a regular build artifact.

load("@rules_rdf//rdf:dataset.bzl", "rdf_dataset")
load("@rules_rdf//transform:defs.bzl", "rdf_transform")

rdf_dataset(name = "src_turtle", srcs = ["data.ttl"], in_format = "turtle")

rdf_transform(
    name = "data_ntriples",
    dataset = ":src_turtle",
    out_format = "ntriples",
)

Output filename = <name>.<ext> where <ext> is the canonical extension for out_format (.ttl, .nt, .nq, .trig, .jsonld, .rdf).

rdf_transform

load("@rules_rdf//transform:defs.bzl", "rdf_transform")

rdf_transform(name, dataset, out_format)

Convert an RDF dataset between serializations.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
datasetRDF dataset to convert.Labelrequired
out_formatTarget serialization.Stringrequired

from docs/validate.md

User-facing RDF validation rules.

rdf_validate_test runs a SHACL shapes graph against an RDF dataset and fails the build if any violations are reported. Resolves through rdf_validator_toolchain_type so the actual SHACL engine is pluggable (rules_jena’s org.apache.jena.shacl.ShaclValidator, a future rules_pyshacl, …).

load("@rules_rdf//rdf:dataset.bzl", "rdf_dataset")
load("@rules_rdf//validate:defs.bzl", "rdf_validate_test")

rdf_dataset(name = "ontology", srcs = glob(["ontology/*.ttl"]))

rdf_validate_test(
    name = "ontology_conforms",
    dataset = ":ontology",
    shapes = "shapes.ttl",
)

ShEx support is in scope for v0.2 (the toolchain contract leaves room for it via the --shapes-language arg, but for v0.1 the shapes file is assumed Turtle-encoded SHACL).

rdf_validate_test

load("@rules_rdf//validate:defs.bzl", "rdf_validate_test")

rdf_validate_test(name, dataset, severity, shapes)

Validate an RDF dataset against a SHACL shapes graph.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
datasetAn rdf_dataset to validate.Labelrequired
severityMinimum severity that fails the build.Stringoptional"violation"
shapesSHACL shapes graph (Turtle).Labelrequired

Conformance#

No gate findings. 10 contested atoms. See how gating works or the full report.

Contested atoms

Third-party modules where this module resolves a different version than others do. Not a violation of anything this module did — it is the actionable form of a registry-level convergence finding, and the sentence a maintainer can act on.

AtomResolved hereElsewhere
apple_support 1.24.2 2.2.0 ×1
bazel_skylib 1.8.2 1.9.0 ×2
gazelle 0.36.0 0.30.0 ×50.44.0 ×10.51.0 ×3
nlohmann_json 3.6.1 3.12.0.bcr.1 ×1
protobuf 33.4 34.0.bcr.1 ×2
rules_go 0.60.0 0.39.1 ×5
rules_jvm_external 6.7 6.8 ×4
rules_python 1.7.0 2.0.1 ×1
rules_swift 3.1.2 3.6.1 ×1
upb 0.0.0-20220923-a547704 0.0.0-20230516-61a97ef ×1

Dependencies#

rules_rdf in the registry graph — what it depends on (left) and what depends on it (right).

Depends on

platforms1.0.0bazel_skylib1.8.2protobuf33.4rules_python1.7.0rules_shell0.6.1devstardoc0.7.2devrules_jsonschema0.2.0dev

Used by (6 in the registry)

Versions#

5 published versions, newest first. Each resolves to an immutable, integrity-checked archive.

VersionIntegrity (sha256)Source archive
0.4.0 latest w3Rmkz8M2kjVm0tl… tag archive ↗
0.3.0 5Fgb2PtEFsWkm8y3… tag archive ↗
0.2.0 NECrbS8zRPEUjADr… tag archive ↗
0.1.0 NzaQNK5SZ9AZXTbo… tag archive ↗
0.0.1 PWU1ZHKEXMrLgABL… tag archive ↗

Changelog#

All notable changes to rules_rdf. The format is loosely Keep a Changelog — version headers mirror the published bazel-registry entries.

0.4.0 — canonical RDF statement substrate proto

  • //rdf:statement_proto (fastverk.rdf.v1, dev.fastverk.rdf.v1): the canonical RDF statement substrate — Term (IRI / literal / bnode), Triple / Quad, Literal (lexical + language/datatype), PrefixDecl, and RdfStatement (the stream unit). The message shapes mirror Apache Jena’s RDF Protobuf wire format, so a stream converts mechanically into a Jena Model / Dataset and round-trips through the rdfprotobuf serialization rules_rdf already speaks. This is the L1 wire format every graph reduces to, keyed on stable IRIs; domain vocabularies + SHACL shapes layer on top. Schema-only — consumers generate their own bindings (java_proto_library, …), so the only new dependency is protobuf (for proto_library). Provenance is carried out of band by the domain (e.g. as the key in KV<Context, RdfStatement>), keeping the substrate neutral.

0.3.0 — hermetic RDF-resource fetch + linked-graph closure

  • rdf_resource_repository repository rule + rdf bzlmod module extension (rdf.resource tag class). Sha-pinned fetch of a single RDF document (TTL / JSON-LD / N-Triples / …) from a URL (with mirror fallback) into a repo whose default BUILD overlay exports the raw file and declares a ready rdf_dataset(:dataset). The pin-an-ontology primitive for grounding vendored vocabularies (schema.org, SKOS, DC) into the build graph for rdf_transform / sparql_query / rdf_reason.
  • rdf_dataset gains deps (other rdf_datasets it links to) and RdfDatasetInfo gains transitive_files — the linked-graph closure (own files + the transitive closure of every dep). sparql_query / rdf_reason / rdf_validate / rdf_transform now operate over the closure, so cross-vocabulary subclass/subproperty chains resolve (a schema.org type whose superclass lives in an imported module). Fully backward compatible: with no deps, transitive_files == files.
  • rdf_namespace_aspect (+ rdf_namespace_manifest): traverses a dataset’s deps graph, extracting per-node namespaces + owl:imports (via the stdlib ns_tool), and folds them into a manifest with the harvested namespace set (the grounding vocabulary) + an import-completeness check. strict = True fails the build if any owl:imports is unprovided in the closure.
  • sparql_query producer rule: runs a SELECT/ASK (→ tsv/csv/json/xml) or CONSTRUCT/DESCRIBE (→ turtle/… , and yields an rdf_dataset) over a dataset’s closure, emitting results as a build artifact — the producer counterpart to sparql_query_test’s gate. Turns a reasoned graph into downstream-consumable data (e.g. grounding tuples for training-data generation).

0.2.0 — build-action rules + binary RDF formats

  • rdf_reason (//reason:defs.bzl) — build-action rule that runs the registered rdf_reasoner toolchain over a base dataset; emits the derived-triples Turtle file. Provides a fresh RdfDatasetInfo so consumers can chain reason → validate → query.
  • rdf_transform (//transform:defs.bzl) — convert between serializations via the registered rdf_serializer toolchain. Output extension auto-selected from out_format.
  • Binary RDF formats added to RDF_FORMATS: rdfthrift (.rt) and rdfprotobuf (.rpb / .bin). Apache Jena’s binary serializations — useful as cached intermediate forms.
  • Smoke fixtures rewritten as bash plugins (no py_binary bootstrap to stage in action runfiles trees). Four no-op plugins cover every toolchain type; bazel test //... runs 14/14 (8 stardoc diff_tests + 4 conformance + 4 rule smokes).
  • Stardoc for reason/defs.bzl + transform/defs.bzl.

0.1.0 — first usable surface

  • Four toolchain_types under //rdf:: sparql_engine, rdf_validator, rdf_serializer, rdf_reasoner.
  • Toolchain registration rules + providers carrying both the plugin binary and its runfiles (so py_binary / java_binary plugins resolve in the sandbox).
  • rdf_dataset + RdfDatasetInfo provider.
  • sparql_query_test — zero-row SPARQL gate, the workhorse rule.
  • rdf_validate_test — SHACL gate.
  • rdf_plugin_contract_test rule + Python driver. Four scenarios: valid_minimal, malformed_input, unknown_flag, determinism.
  • Plugin contract finalized at rdf/plugin_contract.md (v1).
  • Stardoc reference for all six public-API .bzl files.
  • End-to-end smoke (examples/smoke/) using a no-op Python SPARQL engine. Validates the contract pipeline without depending on a concrete RDF backend.

0.0.1 — scaffold

  • Initial scaffold via rels scaffold. No public API yet.

← All modules