CMake helpers

Consume apiary from CMake via find_package(Apiary) (installed) or add_subdirectory (vendored). Both provide:

  • apiary::apiary — the codegen executable.

  • apiary::annotations — an INTERFACE target carrying the APIARY_* macro header; link it instead of hardcoding the include path.

…plus the helper functions below (lifted from their in-source documentation).

apiary_detect_toolchain

Probe ``CMAKE_CXX_COMPILER`` (or a conda clang++) for the include paths
libtooling needs and cache them as:
  APIARY_RESOURCE_DIR        - clang -resource-dir (builtin headers)
  APIARY_EXTRA_ISYSTEM       - active conda env's include dir (third-party)
  APIARY_CXX_INCLUDE_DIRS    - the compiler's C++ stdlib search dirs

Without these even ``#include <string>`` fails when the build compiler is
gcc (libstdc++) rather than the clang that backs the apiary binary.

apiary_collect_usage_requirements

Recursively collect BUILD_INTERFACE include directories AND
INTERFACE_COMPILE_DEFINITIONS reachable from <target> via
INTERFACE_LINK_LIBRARIES. CMake has no built-in for this; the walk is
reliable since usage requirements are set explicitly via
target_link_libraries / target_include_directories.

  apiary_collect_usage_requirements(<target> INCLUDES_OUT <v> DEFINES_OUT <v>)

Genex-wrapped entries are unwrapped for BUILD_INTERFACE and dropped
otherwise (config-conditional values can't be evaluated at configure time).

apiary_add_bindings

Emit the codegen custom command(s) that run apiary on one set of headers.

  apiary_add_bindings(
      HEADERS         <abs header...>      # parsed by apiary (positional)
      SOURCE_INCLUDES <relative name...>   # --source-include each
      REGISTER_FUNCTION <name>             # --register-function (binding TU)
      MODULE          <name>               # --module (docs json; default "module")
      DEPENDS_TARGETS <target...>          # usage requirements -> -I / -D
      OUTPUT_DIR      <dir>                # where generated files land
      OUTPUT_NAME     <stem>               # base filename for outputs
      CXX_STANDARD    <n>                  # default 17
      MAX_DEFS_PER_TU <n>                  # split binding into shard TUs of ~n .def's
      NUM_TU <n>                           # split binding into exactly n shard TUs
      EXTRA_FLAGS     <flag...>            # extra -I/-D/... after --
      EXTRA_DEPENDS   <file...>            # extra DEPENDS (e.g. Defines.hpp)
      BINDING                              # emit the pybind TU (+ stub)
      DOCS_JSON                            # emit the docs-json (Python surface)
      CPP_DOCS_JSON                        # emit the docs-json (C++ surface)
      OUT_BINDING <v>  OUT_STUB <v>  OUT_DOCS_JSON <v>  OUT_CPP_DOCS_JSON <v>
  )

MAX_DEFS_PER_TU and NUM_TU both split the generated binding into several
smaller TUs (``<name>_pybind.shard<k>.cpp``) so a heavily instantiated module
doesn't compile as one TU large enough to exhaust memory. They differ in who
picks the shard count, which decides whether the tool has to run before the
OUTPUTs can be named:

  MAX_DEFS_PER_TU <n>  sizes each shard at ~n binding statements and lets the
      count follow the headers. The count is discovered at configure time by
      running ``apiary --plan``, so the binary must ALREADY EXIST then — true
      under find_package(Apiary); in add_subdirectory mode the tool isn't
      built yet, so sharding is skipped (with a status message) and a single
      TU is emitted.
  NUM_TU <n>  fixes the count at n and balances the units across it. The
      filenames follow from n alone, so nothing runs at configure time and
      this works in add_subdirectory mode. n <= 1 means a single TU.

Set at most one. NUM_TU is the one to reach for when apiary is built as part
of the same build; MAX_DEFS_PER_TU auto-sizes but costs a configure-time run
of the tool over every header.

Computes flags = APIARY_SYSTEM_FLAGS + usage-requirements(DEPENDS_TARGETS) +
EXTRA_FLAGS + -std, and sets the requested OUT_* variables in the caller's
scope. Requires apiary_detect_toolchain() to have run.

apiary_add_python_docs

Emit a docs-JSON fragment for a package's hand-written pure-Python layer, via
the static Python frontend (apiary_py_extract.py — stdlib ``ast`` only, no
import). The fragment is in the same schema as apiary_add_bindings' DOCS_JSON,
so it folds into the same merge: pass it to apiary_aggregate_extension as
PY_DOCS_JSON alongside the C++ DOCS_JSON.

  apiary_add_python_docs(
      PACKAGE     <name>        # top package import name (-> document "module")
      PACKAGE_DIR <dir>         # the package directory (contains __init__.py)
      SOURCE_ROOT <dir>         # optional: record location.file relative to this
                                #           (e.g. the repo root, for clean source links)
      OUTPUT      <file>        # fragment path (default <bindir>/<package>.py.docs.json)
      OUT_PY_DOCS_JSON <var>    # set to the fragment path in the caller's scope
  )

apiary_aggregate_extension

Assemble per-module bindings into one Python extension.

  apiary_aggregate_extension(
      NAME <target>                # pybind11 module target to create
      MAIN <main.cpp>              # consumer's PYBIND11_MODULE source
      BINDINGS <generated tu...>   # per-module binding TUs (from apiary_add_bindings)
      MODULES <name...>            # modules to register
      REGISTER_PREFIX <p>          # default apiary_register_
      MODULES_HEADER <path>        # generated header declaring <prefix>all()
      MODULES_INCLUDE_DIR <dir>    # added to the target so MAIN finds the header
      # optional .pyi aggregation:
      STUBS <pyi...>  STUBS_TARGET <name>  FRAG_DIR <d>  PKG_DIR <d>
      PY_HELPERS_DIR <d>  PY_HELPER_DEPENDS <file...>
      # optional consumer-provided overlay for runtime-patched members the
      # codegen can't see (content + target-class regex are the caller's):
      STUB_OVERLAY <file.pyi>  STUB_OVERLAY_CLASS_REGEX <regex>
      # optional docs render (built on demand, not part of ALL):
      DOCS_JSON <json...>      # C++-frontend docs-JSON fragments
      PY_DOCS_JSON <json...>   # static-Python-frontend fragments (apiary_py_extract.py)
      DOCS_TARGET <name>  DOCS_OUTDIR <d>
      DOCS_CONTENT_DIR <d>     # optional authored Markdown: per-module
                               # curation (## Topics) + free-standing articles
  )

The docs pipeline merges all fragments (DOCS_JSON + PY_DOCS_JSON) into one
canonical docs.json (apiary_merge_docs_json.py) before rendering, so C++- and
Python-origin symbols land on the same submodule pages.

Generates the register header, creates the pybind11 module (the consumer
configures output name / linkage / packaging afterward), and — when the
optional groups are given — wires the stub-aggregation (ALL) and docs-render
(on-demand) targets using Apiary's bundled scripts (APIARY_SCRIPTS_DIR).