Named documentation target families
Status: proposed. This is an independent prerequisite for
sharing project configuration with bundles. It changes naming and
target references without introducing shared configuration or changing what the
documentation builds contain.
Earlier approach: PR #648
PR #648 explored named
docs() invocations by deriving target names in one helper, updating internal
references, and distinguishing virtual environments and metrics outputs. It also
updated external-inventory parsing, non-Bazel discovery, and PlantUML runfiles
lookup. These are useful implementation touchpoints for this prerequisite.
The earlier approach kept default targets unprefixed and used underscore prefixes
for custom names. This proposal instead uses dotted canonical targets for every
invocation and aliases for old public labels. Its suffix-based inventory lookup
and target-derived path reconstruction do not solve alias-to-artifact resolution;
use the artifact metadata described below. Its former docs_sources and combo
targets also predate the current bundle composition model and are not restored.
The PR required custom names to be repeated as docs_target_name in conf.py for
IDE execution. This proposal keeps the invocation identity in Bazel and carries
it into generated runtime/IDE metadata. That identity must include the package
and repository, not just the target name, so subdirectory projects work as well.
The review discussion
distinguishes multiple independent documentation builds from publishing multiple
sites. This prerequisite enables those builds and provides stable invocation
identities. It does not change publishing policy or replace docs_bundle() for
composing one documentation site.
Goal and public API
docs() currently declares a collection of targets with unrelated names, such as
docs, docs_bundle, needs_json, and live_preview. Give the invocation a
stable identity and put its generated targets in one namespace:
load("@score_docs_as_code//:docs.bzl", "docs")
docs(
name = "docs",
source_dir = "docs",
project = "Example",
project_url = "https://example.org/example",
)
Add optional name = "docs" after the existing parameters to preserve positional
calls. name is the Bazel identity; project remains the human-readable Sphinx
project name. Use a single target-name component without / for name so the
namespace and its generated directories stay within the declaring package.
All generated implementation targets use either <name>.<role> for supported
entry points or <name>.__internal__.<role> for implementation details. The
default invocation uses the same dotted scheme as any custom invocation.
The unqualified :<name> is an alias to :<name>.incremental. It remains a convenient
run command and provides the public label identifying the abstract docs()
invocation for future APIs. Thus //project:site identifies
docs(name = "site", ...), while //project:site.bundle selects its mountable
source bundle. This naming prerequisite does not add an owner argument to
docs_bundle().
Run incremental documentation generation with bazel run //:docs.incremental.
Reserve <name>.build for a future, separate documentation target intended for
bazel build; this prerequisite does not introduce that target.
Canonical targets and compatibility aliases
For docs(name = "docs"), generate these canonical entry points and aliases:
| Role |
Canonical target |
Compatibility alias |
| Incremental documentation command |
:docs.incremental |
:docs |
| Root source bundle |
:docs.bundle |
:docs_bundle |
| Project-wide Needs inventory |
:docs.needs_json |
:needs_json |
| Single Needs JSON file |
:docs.needs_json_file |
:needs_json_file |
| Metrics JSON file |
:docs.metrics_json |
:metrics_json |
| Source links |
:docs.sourcelinks_json |
:sourcelinks_json |
| Documentation checks |
:docs.check |
:docs_check |
| Link checks |
:docs.link_check |
:docs_link_check |
| Live preview |
:docs.live_preview |
:live_preview |
| IDE environment |
:docs.ide_support |
:ide_support |
| Traceability gate |
:docs.traceability_gate |
:traceability_gate |
Use native.alias for compatibility; aliases forward to canonical targets and
do not declare duplicate build actions. Preserve the existing public visibility
and manual-tag policy on aliases and the corresponding canonical targets. Keep
the traceability gate's existing delegation to the shared tool under its new
canonical name.
A custom invocation, such as docs(name = "site"), produces :site.incremental,
:site.bundle, :site.needs_json, and the other roles from the same table. It
also produces the owner alias :site. Only the default name = "docs" invocation
creates the legacy aliases, so named invocations can coexist without claiming
the same compatibility labels. Keep those aliases for this migration without
adding a removal deadline.
Use canonical labels for all generated dependencies, $(location ...)
expressions, environment values, and examples added as part of the implementation.
Compatibility aliases serve existing callers; generated rules must not depend
on them.
Internal targets
Move fixed private helpers such as _docs_generated_config, _docs_mounts, and
_external_docs_runfiles beneath the invocation's .__internal__. namespace.
For example, use docs.__internal__.generated_config and
docs.__internal__.mounts_manifest. The build executable can itself be
docs.incremental; it no longer needs the fixed _score_docs_cli name to avoid a
filesystem collision with the docs/ source directory.
Bundle-local and Needs-build helpers retain their existing owner-relative naming
convention, now based on the canonical owning target. For example:
docs.bundle.__internal__.needs_local
docs.bundle.__internal__.needs_sources
docs.needs_json.__internal__.sphinx_build
Private helpers do not receive compatibility aliases. Existing direct references
to internal target names, including repository tests, migrate to the new names.
Standalone docs_bundle(name = ...) declarations retain their current naming;
only the root bundle generated by docs() receives the new canonical name.
Output paths and integration
Namespace every invocation-owned helper, action output, and runtime environment,
not just public labels. Two named invocations with distinct source directories
must coexist in one package without target, generated-file, or virtual-environment
collisions. Duplicate names or collisions with user-declared targets remain Bazel
errors; do not invent alternate names automatically.
There is no compatibility requirement for existing output paths, including
generated configuration, extracted JSON files, build directories, runfiles, or
IDE environments. Compatibility aliases preserve target entry points only.
Use the same invocation-scoped layout for default and custom names, without
legacy-path exceptions or copies created solely to retain old paths.
Put generated configuration and extracted files under each invocation's
<name>.__internal__/ output directory, use .venv_<name> for IDE support, and
use _build_<name> for interactive builds, including separate stale-build
sentinels. Cleaning one invocation must not remove another invocation's outputs.
Pass the selected directories to runtime and traceability tools explicitly.
Ensure generated configuration is still staged at the Sphinx source-root location
expected by the existing source adapters. A source-provided conf.py keeps its
existing precedence and location.
An alias preserves a target reference, but it does not recreate an old target's
physical output directory or runfiles tree. In particular, renaming the Needs
build changes its target-derived output directory. Inventory consumers currently
recognize fixed target names and reconstruct paths from those names; adding
aliases alone would leave these consumers broken.
As part of this prerequisite, resolve inventory artifact locations from Bazel
files and carry those locations into runtime input metadata. Preserve the
repository/runfiles identity and distinguish directory-valued Needs exports from
single-file exports. Do not reconstruct an artifact path from the supplied alias
name. Support canonical and legacy labels, including cross-repository producers
that still use the previous target naming scheme. Update the legacy inventory
classification used by docs(data = ...) as well, so a canonical Needs target
does not become mounted bundle data accidentally.
Update runtime discovery, IDE setup, traceability tooling, and test helpers that
assume fixed targets or output directories. Keep commands through compatibility
aliases working, but migrate internal path-based assumptions to canonical outputs
or artifact discovery. Update documentation and scripts that refer to output
paths to the new layout. Direct Sphinx execution and the content of
Needs exports remain unchanged.
For IDE/Esbonio and direct Sphinx execution in a generated IDE environment, emit
metadata alongside that environment identifying the full owner label, its
canonical incremental target, and the corresponding runfiles location. Runtime
discovery must use the selected environment's metadata when Bazel runfiles are
not already available, rather than assuming root-package ide_support.runfiles
or asking authors to repeat the name in conf.py. Update PlantUML's use of that
discovery path too.
Any remaining fallback query for documentation data must address the owner's
canonical incremental target in its actual package, rather than querying
labels(data, //:docs) or another alias without a data attribute. These runtime
changes belong to the naming prerequisite and must not rely on the later shared
configuration proposal.
Delivery and acceptance
Implement this prerequisite independently of concept.md: first centralize
target-name derivation, move targets and helper references, add compatibility
aliases, and update artifact resolution and affected consumers. Do not add
docs_config, shared deps, or new publication metadata in this change.
Validate these behaviors:
- An existing
docs() declaration still works through every legacy public label;
each alias resolves to its canonical counterpart and schedules no duplicate
documentation action.
- Canonical incremental/check commands, mounted root bundles, and both Needs export
forms work for default and custom names.
- The owner and legacy run aliases execute the incremental target. No
<name>.build target is introduced by this prerequisite.
- Two named invocations with distinct source directories in one package have
independent targets, generated configurations, exports, and IDE environments.
- Canonical and compatibility inventory labels resolve the same actual artifact,
including external repositories and producers using the previous naming scheme.
- IDE/Esbonio and direct Sphinx execution in a named project's IDE environment
locate that project's runfiles, PlantUML, and external inventories without a
duplicated conf.py name. Cover a project outside the repository root and a
fallback query against the canonical incremental target.
- Legacy
data inventory handling, generated sources, metamodel inputs, source
links, and traceability commands retain their behavior after names change.
- Default and custom invocations use the same output-layout rules. Runtime tools,
tests, and documentation use the new paths or artifact discovery without
requiring any old output path to remain available.
- Positional calls remain valid, and existing
conf.py precedence and standalone
docs_bundle() names remain unchanged.
Run the relevant public macro integration scenarios sequentially and focused
inventory/runtime tests when implementing this prerequisite. For this
document-only change, check links, example syntax, and whitespace.
Named documentation target families
Status: proposed. This is an independent prerequisite for
sharing project configuration with bundles. It changes naming and
target references without introducing shared configuration or changing what the
documentation builds contain.
Earlier approach: PR #648
PR #648 explored named
docs()invocations by deriving target names in one helper, updating internalreferences, and distinguishing virtual environments and metrics outputs. It also
updated external-inventory parsing, non-Bazel discovery, and PlantUML runfiles
lookup. These are useful implementation touchpoints for this prerequisite.
The earlier approach kept default targets unprefixed and used underscore prefixes
for custom names. This proposal instead uses dotted canonical targets for every
invocation and aliases for old public labels. Its suffix-based inventory lookup
and target-derived path reconstruction do not solve alias-to-artifact resolution;
use the artifact metadata described below. Its former
docs_sourcesand combotargets also predate the current bundle composition model and are not restored.
The PR required custom names to be repeated as
docs_target_nameinconf.pyforIDE execution. This proposal keeps the invocation identity in Bazel and carries
it into generated runtime/IDE metadata. That identity must include the package
and repository, not just the target name, so subdirectory projects work as well.
The review discussion
distinguishes multiple independent documentation builds from publishing multiple
sites. This prerequisite enables those builds and provides stable invocation
identities. It does not change publishing policy or replace
docs_bundle()forcomposing one documentation site.
Goal and public API
docs()currently declares a collection of targets with unrelated names, such asdocs,docs_bundle,needs_json, andlive_preview. Give the invocation astable identity and put its generated targets in one namespace:
Add optional
name = "docs"after the existing parameters to preserve positionalcalls.
nameis the Bazel identity;projectremains the human-readable Sphinxproject name. Use a single target-name component without
/fornameso thenamespace and its generated directories stay within the declaring package.
All generated implementation targets use either
<name>.<role>for supportedentry points or
<name>.__internal__.<role>for implementation details. Thedefault invocation uses the same dotted scheme as any custom invocation.
The unqualified
:<name>is an alias to:<name>.incremental. It remains a convenientrun command and provides the public label identifying the abstract
docs()invocation for future APIs. Thus
//project:siteidentifiesdocs(name = "site", ...), while//project:site.bundleselects its mountablesource bundle. This naming prerequisite does not add an owner argument to
docs_bundle().Run incremental documentation generation with
bazel run //:docs.incremental.Reserve
<name>.buildfor a future, separate documentation target intended forbazel build; this prerequisite does not introduce that target.Canonical targets and compatibility aliases
For
docs(name = "docs"), generate these canonical entry points and aliases::docs.incremental:docs:docs.bundle:docs_bundle:docs.needs_json:needs_json:docs.needs_json_file:needs_json_file:docs.metrics_json:metrics_json:docs.sourcelinks_json:sourcelinks_json:docs.check:docs_check:docs.link_check:docs_link_check:docs.live_preview:live_preview:docs.ide_support:ide_support:docs.traceability_gate:traceability_gateUse
native.aliasfor compatibility; aliases forward to canonical targets anddo not declare duplicate build actions. Preserve the existing public visibility
and manual-tag policy on aliases and the corresponding canonical targets. Keep
the traceability gate's existing delegation to the shared tool under its new
canonical name.
A custom invocation, such as
docs(name = "site"), produces:site.incremental,:site.bundle,:site.needs_json, and the other roles from the same table. Italso produces the owner alias
:site. Only the defaultname = "docs"invocationcreates the legacy aliases, so named invocations can coexist without claiming
the same compatibility labels. Keep those aliases for this migration without
adding a removal deadline.
Use canonical labels for all generated dependencies,
$(location ...)expressions, environment values, and examples added as part of the implementation.
Compatibility aliases serve existing callers; generated rules must not depend
on them.
Internal targets
Move fixed private helpers such as
_docs_generated_config,_docs_mounts, and_external_docs_runfilesbeneath the invocation's.__internal__.namespace.For example, use
docs.__internal__.generated_configanddocs.__internal__.mounts_manifest. The build executable can itself bedocs.incremental; it no longer needs the fixed_score_docs_cliname to avoid afilesystem collision with the
docs/source directory.Bundle-local and Needs-build helpers retain their existing owner-relative naming
convention, now based on the canonical owning target. For example:
Private helpers do not receive compatibility aliases. Existing direct references
to internal target names, including repository tests, migrate to the new names.
Standalone
docs_bundle(name = ...)declarations retain their current naming;only the root bundle generated by
docs()receives the new canonical name.Output paths and integration
Namespace every invocation-owned helper, action output, and runtime environment,
not just public labels. Two named invocations with distinct source directories
must coexist in one package without target, generated-file, or virtual-environment
collisions. Duplicate names or collisions with user-declared targets remain Bazel
errors; do not invent alternate names automatically.
There is no compatibility requirement for existing output paths, including
generated configuration, extracted JSON files, build directories, runfiles, or
IDE environments. Compatibility aliases preserve target entry points only.
Use the same invocation-scoped layout for default and custom names, without
legacy-path exceptions or copies created solely to retain old paths.
Put generated configuration and extracted files under each invocation's
<name>.__internal__/output directory, use.venv_<name>for IDE support, anduse
_build_<name>for interactive builds, including separate stale-buildsentinels. Cleaning one invocation must not remove another invocation's outputs.
Pass the selected directories to runtime and traceability tools explicitly.
Ensure generated configuration is still staged at the Sphinx source-root location
expected by the existing source adapters. A source-provided
conf.pykeeps itsexisting precedence and location.
An alias preserves a target reference, but it does not recreate an old target's
physical output directory or runfiles tree. In particular, renaming the Needs
build changes its target-derived output directory. Inventory consumers currently
recognize fixed target names and reconstruct paths from those names; adding
aliases alone would leave these consumers broken.
As part of this prerequisite, resolve inventory artifact locations from Bazel
files and carry those locations into runtime input metadata. Preserve the
repository/runfiles identity and distinguish directory-valued Needs exports from
single-file exports. Do not reconstruct an artifact path from the supplied alias
name. Support canonical and legacy labels, including cross-repository producers
that still use the previous target naming scheme. Update the legacy inventory
classification used by
docs(data = ...)as well, so a canonical Needs targetdoes not become mounted bundle data accidentally.
Update runtime discovery, IDE setup, traceability tooling, and test helpers that
assume fixed targets or output directories. Keep commands through compatibility
aliases working, but migrate internal path-based assumptions to canonical outputs
or artifact discovery. Update documentation and scripts that refer to output
paths to the new layout. Direct Sphinx execution and the content of
Needs exports remain unchanged.
For IDE/Esbonio and direct Sphinx execution in a generated IDE environment, emit
metadata alongside that environment identifying the full owner label, its
canonical incremental target, and the corresponding runfiles location. Runtime
discovery must use the selected environment's metadata when Bazel runfiles are
not already available, rather than assuming root-package
ide_support.runfilesor asking authors to repeat the name in
conf.py. Update PlantUML's use of thatdiscovery path too.
Any remaining fallback query for documentation data must address the owner's
canonical incremental target in its actual package, rather than querying
labels(data, //:docs)or another alias without adataattribute. These runtimechanges belong to the naming prerequisite and must not rely on the later shared
configuration proposal.
Delivery and acceptance
Implement this prerequisite independently of
concept.md: first centralizetarget-name derivation, move targets and helper references, add compatibility
aliases, and update artifact resolution and affected consumers. Do not add
docs_config, shareddeps, or new publication metadata in this change.Validate these behaviors:
docs()declaration still works through every legacy public label;each alias resolves to its canonical counterpart and schedules no duplicate
documentation action.
forms work for default and custom names.
<name>.buildtarget is introduced by this prerequisite.independent targets, generated configurations, exports, and IDE environments.
including external repositories and producers using the previous naming scheme.
locate that project's runfiles, PlantUML, and external inventories without a
duplicated
conf.pyname. Cover a project outside the repository root and afallback query against the canonical incremental target.
datainventory handling, generated sources, metamodel inputs, sourcelinks, and traceability commands retain their behavior after names change.
tests, and documentation use the new paths or artifact discovery without
requiring any old output path to remain available.
conf.pyprecedence and standalonedocs_bundle()names remain unchanged.Run the relevant public macro integration scenarios sequentially and focused
inventory/runtime tests when implementing this prerequisite. For this
document-only change, check links, example syntax, and whitespace.