# Install/export and dependency policy This project exposes install/export support through the CMake helpers in `cmake/cc_targets.cmake` and `cmake/cc_install.cmake`. Install/export is explicit: only targets marked with `INSTALL` or `cc_install()` are installed, and external packages are rediscovered by consumers rather than bundled into the install prefix. ## Install/export entry points Install/export is enabled when `CC_ENABLE_INSTALL` is true. The top-level project uses install/export by default; subprojects can disable it through the usual project configuration. Targets enter the export set in one of two ways: ```cmake cc_library(my_lib TYPE STATIC SRCS my_lib.cc ALIAS MyPackage::my_lib INSTALL ) cc_library(other_lib TYPE STATIC SRCS other_lib.cc ALIAS MyPackage::other_lib ) cc_install(other_lib) cc_finalize_install() ``` `cc_finalize_install()` is idempotent and normally runs at generate time. It creates install rules for marked targets, exports `Targets.cmake`, generates `Config.cmake`, generates `ConfigVersion.cmake`, and installs public headers from `include//` when that directory exists. Unmarked targets are not installed automatically. This is intentional: a private or internal build target stays out of the installed package unless it is explicitly marked for install. ## Required install metadata Every installed target needs a valid export identity: - Provide `ALIAS namespace::name` on `cc_library(... INSTALL)` or `cc_executable(... INSTALL)` targets. - For targets installed by direct `cc_install()`, set the `_CC_ALIAS` target property yourself if the target was not created by a helper that accepts `ALIAS`. - The alias must contain exactly one `::` separator. - Namespace and target name must both be non-empty. - All targets in one generated package must share the same namespace. Good: ```cmake cc_library(core TYPE STATIC SRCS core.cc ALIAS MyPackage::core INSTALL ) ``` Bad: ```cmake cc_library(core TYPE STATIC SRCS core.cc INSTALL) # missing ALIAS cc_library(core TYPE STATIC SRCS core.cc ALIAS core INSTALL) # not namespaced cc_library(core TYPE STATIC SRCS core.cc ALIAS a::b::c INSTALL) # too many separators ``` During finalization the alias leaf is used as `EXPORT_NAME`, so a raw target such as `core_impl` can be exported as `MyPackage::core` by setting `ALIAS MyPackage::core`. ## Header install policy `cc_finalize_install()` installs headers only from: ```text include// ``` The destination is: ```text /include// ``` Only `*.h` and `*.hpp` files under that directory are installed by this helper. Headers stored elsewhere are not automatically discovered or installed by `cc_finalize_install()`. If a package needs a different layout, add explicit install rules and keep the exported include directories consistent with the installed tree. The install-consumer fixture uses this convention with headers such as: ```cpp #include "ConsumerTestLib/math_utils.h" #include "ConsumerTestLib/str_utils.h" ``` Its package fixture also adds install-tree include usage requirements: ```cmake target_include_directories(consumer_test_math PUBLIC $ $ ) ``` Do not assume the helper rewrites arbitrary source-tree include directories into install-tree paths. Keep installed public headers under `include//` and set explicit `BUILD_INTERFACE` / `INSTALL_INTERFACE` include directories when consumer usage requires them. ## Dependency registry and generated `find_dependency()` External install-time dependencies are declared with `cc_register_dependency()` and referenced from `cc_install(... DEPS ...)`: ```cmake cc_register_dependency(fmt PACKAGE fmt VERSION 9.0 TARGETS fmt::fmt ) cc_register_dependency(spdlog PACKAGE spdlog VERSION 1.12 COMPONENTS fmt TARGETS spdlog::spdlog ) cc_library(dep_lib TYPE STATIC SRCS dep_lib.cc ALIAS MyPackage::dep_lib INSTALL ) cc_install(dep_lib DEPS fmt spdlog) ``` For every referenced logical dependency, `cc_finalize_install()` validates that the logical name was registered. It then writes `include(CMakeFindDependencyMacro)` and `find_dependency(...)` calls into the generated package config before including the exported targets file. The example above generates lines equivalent to: ```cmake include(CMakeFindDependencyMacro) find_dependency(fmt 9.0) find_dependency(spdlog 1.12 COMPONENTS fmt) include("${CMAKE_CURRENT_LIST_DIR}/MyPackageTargets.cmake") ``` Supported registration metadata: - `PACKAGE `: required. This is the name passed to `find_dependency()`. - `VERSION `: optional. - `EXACT`: optional exact version requirement. - `COMPONENTS ...`: optional required components. - `OPTIONAL_COMPONENTS ...`: optional components. - `TARGETS ...`: metadata used by static private dependency validation. Duplicate logical names are allowed only when every metadata field matches the previous registration exactly. External dependencies are not copied, vendored, bundled, or installed into this package's prefix by these helpers. Consumers must have those dependencies available to `find_package()` through their normal package manager, `CMAKE_PREFIX_PATH`, toolchain file, or other CMake package discovery mechanism. ## Static `PRIVATE` dependency policy Installed static libraries can leak `PRIVATE` link dependencies into their exported usage requirements as link-only dependencies. The helpers therefore validate static libraries marked for install. For a `PRIVATE` project-target dependency: ```cmake cc_library(private_dep TYPE STATIC SRCS private_dep.cc ALIAS MyPackage::private_dep INSTALL ) cc_library(installed_lib TYPE STATIC SRCS installed_lib.cc DEPS PRIVATE private_dep ALIAS MyPackage::installed_lib INSTALL ) ``` This is valid only because `private_dep` is also installed/exported and has a valid namespaced alias. If `private_dep` is not installed, finalization rejects the package. Fix one of these ways: - install/export the project dependency too and give it a valid namespaced alias; - make the dependency `PUBLIC` or `INTERFACE` if consumers are intended to link it; - for true external packages, use `cc_register_dependency()` plus `cc_install(... DEPS ...)`. For a `PRIVATE` imported dependency: ```cmake add_library(ExternalPkg::external INTERFACE IMPORTED) cc_register_dependency(external_pkg PACKAGE ExternalPkg TARGETS ExternalPkg::external ) cc_library(installed_lib TYPE STATIC SRCS installed_lib.cc DEPS PRIVATE ExternalPkg::external ALIAS MyPackage::installed_lib INSTALL ) cc_install(installed_lib DEPS external_pkg) ``` This is valid because the imported target is listed in the registered dependency's `TARGETS` metadata and the installed target declares that logical dependency with `cc_install(... DEPS external_pkg)`. Without that metadata, finalization rejects the installed static library. Raw link items, linker flags, and generator expressions that are not CMake targets are left untouched by this validation. ## Consumer verification The narrow install-consumer fixture validates the intended installed-package contract: 1. configure, build, and install a package fixture; 2. configure a separate consumer using only `find_package(ConsumerTestLib CONFIG REQUIRED)`; 3. link against imported targets `ConsumerTestLib::consumer_test_math` and `ConsumerTestLib::consumer_test_str`; 4. build and run the consumer executable. Run it with: ```sh python3 scripts/dev_check.py install-consumer ``` The fixture is intentionally narrow. It verifies package config generation, target export, installed headers, and separate consumer usage; it does not imply arbitrary transitive dependencies are automatically installed or bundled. ## Troubleshooting ### `target "..." is marked for install but has no ALIAS` The installed target has no export identity. Add a namespaced alias: ```cmake cc_library(my_lib TYPE STATIC SRCS my_lib.cc ALIAS MyPackage::my_lib INSTALL) ``` For manually created targets, set `_CC_ALIAS` before `cc_install()`: ```cmake add_library(tooling INTERFACE) set_target_properties(tooling PROPERTIES _CC_ALIAS "MyPackage::tooling") cc_install(tooling) ``` ### `ALIAS "..." is not namespaced` or `must contain exactly one "::" separator` Use exactly `namespace::name`. Do not use plain names (`my_lib`) or nested aliases (`a::b::c`). Keep one namespace for all installed targets in the package. ### `target(s) reference unknown install-time dependency "..."` A target declares `cc_install(target DEPS logical_name)` but no matching `cc_register_dependency(logical_name PACKAGE ...)` exists. Register the dependency before finalization. ### `cc_register_dependency(...): PACKAGE is required` Every dependency registration must name the package that consumers will find: ```cmake cc_register_dependency(fmt PACKAGE fmt TARGETS fmt::fmt) ``` ### `PRIVATE imported dependency "..." is not covered by a registered install dependency` An installed static library links a CMake imported target privately, but that imported target is not covered by dependency registry metadata. Register the external package with `TARGETS ` and declare the logical dependency on the installed target: ```cmake cc_register_dependency(fmt PACKAGE fmt TARGETS fmt::fmt) cc_install(my_static_lib DEPS fmt) ``` ### `PRIVATE project-target dependency "..." that is not installed in the same export set` An installed static library privately links another project target that is not itself installed/exported. Install/export that dependency with a valid alias, or change the link visibility if consumers should depend on it directly. ### Installed headers are missing Confirm the headers live under `include//` and use `.h` or `.hpp` extensions. Headers elsewhere are outside the helper's automatic header install policy. Also verify that exported targets expose install-tree include directories such as `$` when consumers include package headers.