Appearance
Why Native Package Management Is Different
Node or Swift packages usually enter one dominant ecosystem with a manifest, resolver, lockfile, and standardized build conventions. C and C++ libraries must span operating systems, compilers, ABIs, build systems, and distribution policies. Package tools can automate choices, but those dimensions remain.
Consuming a library has several phases
A dependency may supply:
- headers needed while compiling;
- static libraries or import libraries needed while linking;
- shared libraries needed while loading;
- generated code or build-time executables;
- transitive compiler definitions and link dependencies;
- licenses, data, plugins, and runtime configuration.
Installing a runtime package may not install headers. Adding a header path may compile but leave unresolved symbols. Linking may succeed while deployment omits a shared library.
Source and binary packages solve different problems
A source dependency can be built with the consumer's compiler, flags, runtime, and target, improving ABI alignment but increasing build time and requiring compatible build logic.
A binary package installs quickly but must match architecture, OS, compiler ABI, runtime, build configuration where relevant, and dependency variants. Package identities may include these settings as part of a binary package ID.
Header-only libraries avoid a separate binary artifact but increase compile time and expose implementation/ABI details through templates and inline code.
Common acquisition models
System packages integrate with the operating system, security updates, and shared-library conventions. Versions may lag application desires, and development metadata is often a separate package.
Vendoring copies source into the repository or source archive. It maximizes control and offline reproducibility but transfers update, patch, and license responsibilities to the project.
Git submodules pin another repository commit without copying history into the parent. They are source acquisition, not package modeling; builds and transitive requirements still need integration.
Build-time fetching such as CMake FetchContent downloads and adds source during configuration. It is convenient for small dependencies but network-dependent, can expose option collisions, and makes configure responsible for acquisition.
Dedicated package managers such as vcpkg and Conan model versions, variants, binary caches, and build-system integration. Teams must still select registries, lock/update policy, and supported profiles/triplets.
Transitive usage requirements matter
If public headers expose a dependency's type, consumers need its include paths and compatible definitions. Static libraries often require transitive link dependencies because an archive does not absorb all dependency code. CMake imported targets encode these requirements better than a bare path to libfoo.a.
Private implementation dependencies should not leak into public headers unless the API accepts the coupling.
Locking means more than one version string
Reproducibility can require exact source revisions, package recipes, toolchain version, options, target profile, patches, generated build files, and environment inputs. A lockfile helps within its package manager but cannot pin an undocumented system SDK or mutable download URL.
Applications often want a tightly locked graph. Published libraries declare compatible ranges and must test against consumer resolution. Security updates require a deliberate process for refreshing locks and binary caches.
Avoid mixing ownership systems accidentally
Do not install half a graph through a system manager and half through a package manager without understanding which ABI and files win discovery. CMake may find an unintended global package unless toolchain and prefix paths constrain search.
One project can intentionally combine models—for example, platform SDKs from the OS, third-party C++ dependencies from vcpkg, and one vendored single-file C library—but the ownership of each dependency should be documented.
Optional prompts
Explain: Why can a library be installed and runnable but unavailable to compile against?
Answer: Runtime packages may omit headers, unversioned link names, and build metadata supplied by a separate development package.
Explain: Why does a bare .a path underdescribe a dependency?
Answer: Consumers may also need include directories, macros, platform libraries, link ordering, and transitive dependencies.