Build with dependencies and CI

Once Publish your first API pages works for a source root, give extraction the same source and dependency inputs your Java compiler needs. The extension does not invoke Maven or Gradle, run annotation processors, or resolve dependencies for you. Prepare those inputs before running Sphinx.

Choose where extraction runs

  • During Sphinx: set java_sources and java_classpath in conf.py. This is the simplest path when JDK 25 is available on the documentation builder.

  • Before Sphinx: run sphinx-java-autodoc in the Java build job, then pass its XML to Sphinx with java_model. The Sphinx job needs no JDK.

For example, for a locally available dependency:

java_sources = ["../src/main/java"]
java_classpath = ["../lib/dependency.jar", "../other-module/target/classes"]
java_generate = "api"

Dependencies, generated sources and multiple modules

Pass dependency JARs or class directories through java_classpath (a list), --classpath (platform path separator), or --classpath-file. Wildcard classpath expansion is not supported: enumerate files explicitly. Dependencies are never downloaded implicitly. A missing dependency results in compiler diagnostics with the failing symbol and a classpath hint.

List several source roots to combine non-JPMS Maven/Gradle modules:

java_sources = [
    '../core/src/main/java',
    '../client/src/main/java',
    '../client/target/generated-sources/annotations',
]
java_classpath = ['../lib/dependency.jar']

Produce generated sources before running Sphinx. No annotation processors execute inside extraction; Lombok-like compiler AST transformations are not supported. Do not include test sources unless they are part of the API you intend to publish. JPMS module-info.java inputs are rejected with an actionable error in this release; use non-modular roots or explicitly enumerate declaration files. Cross-module JPMS identity, module paths and split packages are deferred.

A binary dependency is enough to resolve a reference or signature. To inherit its Javadoc, set java_sourcepath to directories containing dependency sources:

java_sources = ["../src/main/java"]
java_sourcepath = ["../dependency-sources"]

These directories supply compiler lookup and ancestor comments, but are not selected for API pages. The CLI accepts repeated --sourcepath DIRECTORY arguments. Lookup source contents and order participate in cache invalidation; changing an ancestor comment refreshes the consuming documentation. Published source roots take precedence over lookup-only roots. Supply non-JPMS directories.

Explicit extraction and rendering

Use this when Java extraction runs in a separate job from Sphinx. From your project root, with JDK 25 available:

sphinx-java-autodoc src/main/java --release 17 --warnings-as-errors \
  --cache build/java-cache -o build/api.xml

Add --classpath-file build/classpath.txt when dependencies are needed. --encoding and repeated --exclude-package arguments match the java_encoding and java_exclude_packages settings. CLI paths are relative to the working directory. Then configure docs/conf.py:

extensions = ['sphinx_java_autodoc']
java_model = '../build/api.xml'
java_generate = 'api'

Do not also set java_sources. Reading an existing model requires no JDK. The build pipeline must rerun extraction when sources or dependencies change; Sphinx can only detect changes in the supplied model. The extraction command preserves the last successful output if compilation or strict diagnostics fail.

Maven

The Maven and Gradle fixtures resolve Commons Lang and generate demo.BuildInfo before compilation. Both complete workflows have been executed; see Compatibility and limits for versions. Dependency resolution is an explicit build step.

examples/maven/pom.xml reuses the standalone source fixture. Resolve build plugins and dependencies in an explicit preparation step. The extension itself never invokes Maven:

mvn -f examples/maven/pom.xml -DskipTests compile \
  org.apache.maven.plugins:maven-dependency-plugin:3.11.0:build-classpath \
  -Dmdep.includeScope=compile -Dmdep.outputFile=target/classpath.txt
sphinx-java-autodoc examples/standalone/src examples/build-consumer/src \
  examples/maven/target/generated-sources/java-autodoc \
  --classpath-file examples/maven/target/classpath.txt \
  -o build/maven-api.xml --cache build/java-cache --warnings-as-errors

For your own project, replace the fixture root with src/main/java. If no external dependencies exist, omit --classpath-file. The Maven dependency plugin writes platform-separated classpaths, including provided dependencies when compile scope is selected. Compile/generate-sources are explicit project build steps and may run project build plugins; extraction does not execute application code.

Gradle

examples/gradle/build.gradle reuses the same sources and provides an explicit writeJavaAutodocClasspath task. With a Gradle distribution supporting JDK 25:

gradle -p examples/gradle classes writeJavaAutodocClasspath
sphinx-java-autodoc examples/standalone/src examples/build-consumer/src \
  examples/gradle/build/generated/sources/java-autodoc \
  --classpath-file examples/gradle/build/java-autodoc-classpath.txt \
  -o build/gradle-api.xml --cache build/java-cache --warnings-as-errors

For your project, the Java plugin provides sourceSets.main.compileClasspath. Write its asPath to a UTF-8 file; pass source directories separately. No Gradle wrapper or automatic distribution download is bundled here. To verify both complete examples, including extraction and strict rendering with Java unavailable on PATH, run:

uv run --locked python tests/build_tools_check.py

Pass --maven and --gradle with executable paths when needed. The same check runs in the required CI consumer job. These fixtures use an explicit template-copy step for generated sources; they do not demonstrate annotation processing.

Publish the site from CI

This GitHub Actions workflow builds strictly on every push to main and deploys to GitHub Pages. In the repository settings, set Pages → Source to GitHub Actions. Save it as .github/workflows/docs.yml:

name: Documentation
on:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "25"
      - uses: actions/setup-python@v7
        with:
          python-version: "3.12"
      - run: python -m pip install sphinx-java-autodoc
      # Prepare java_classpath inputs and generated sources here, for example
      # with the Maven or Gradle commands below.
      - run: sphinx-build -W --keep-going -n docs docs/_build/html
      - uses: actions/upload-pages-artifact@v5
        with:
          path: docs/_build/html

  deploy:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v5

Each run starts from a clean output directory, so removed types do not leave old pages behind. Install the extension from your own lockfile or requirements file if you have one, and pin actions to commit SHAs if your project does. This repository publishes its own site with the same JDK setup, strict build and Pages actions; this standalone workflow has been checked with actionlint but has not run in another repository.

Caching and parallelism

The Sphinx cache lives under <doctreedir>/java-cache. The CLI defaults to .java-autodoc-cache or accepts --cache. Fingerprints include source paths and contents, classpath order and contents, source release, tool identity/JDK modules, extension version and extractor code. Cache hits still hash inputs; this costs I/O but catches edits even when timestamps are preserved. A changed input triggers whole-project extraction and re-reading of all Sphinx documents, including pages containing only Java roles and directives for types that were previously missing.

Output contains no build timestamps or machine-specific source paths. Temporary work directories and atomic replacement protect simultaneous extraction into one cache. Sphinx -j 2 reading/writing and domain inventory merges are tested. Separate Sphinx processes must use separate source/output generation directories; sharing one generated-page directory across independent builds is not supported.

Generated skeletons are written only when changed. The ownership manifest records old and intended content hashes before page updates, so an interrupted generation can retry even if the Java model changes again. Edited pages still stop the build. Removed types leave the source toctree and inventory; Sphinx can retain old HTML files in an existing output directory, so publish from a clean output directory. After extracting, Sphinx deletes older models from <doctreedir>/java-cache. The CLI never evicts entries from its --cache directory; remove old ones when no extraction is using it.

Direct doclet invocation

For direct doclet invocation, locate and compile the bundled source:

python -c "from sphinx_java_autodoc.extractor import DOCLET; print(DOCLET)"
javac -proc:none -encoding UTF-8 -d build/doclet /printed/path/SphinxDoclet.java
javadoc -quiet -encoding UTF-8 -protected \
  -doclet sphinxjava.SphinxDoclet -docletpath build/doclet \
  --sphinx-output build/api.xml -sourcepath src/main/java \
  -classpath lib/dependency.jar com.example

The doclet’s --sphinx-exclude option takes excluded packages separated by commas. Prefer the Python CLI for content caching, atomic output and warning handling. The direct command takes ordinary Javadoc package or file inputs and does not provide the Python wrapper’s cache or publication safeguards.