Status: Mailing-list discussion draft. A review PR for this document is not intended to be merged; the mailing-list thread remains the canonical decision record.
This document defines the intended installation and packaging contract for the TsFile C++ library and command-line tools. It is the design proposed in the package-manager discussion. The contract must be agreed before public binary packages are produced because downstream packages make library names, paths, and compatibility promises costly to change.
The work proceeds in stages. First, make the shared library, headers, CMake package, pkg-config metadata, CLI, and notices install correctly without any platform package. Second, validate that installation with independent consumers. Third, build native Homebrew and Debian/Ubuntu packages from the same install rules. RPM packaging follows after that workflow is stable.
Native packages contain the shared library and use dependencies supplied by their package manager. Static libtsfile packages are not planned. A portable all-in-one archive with pinned bundled dependencies is a separate distribution profile, not a fallback for every platform without APT.
The ASF source release remains the official Apache release. A package in Homebrew, Debian, Fedora, or another downstream repository is a downstream distribution of that release, not a separate Apache release.
Public packages must:
LICENSE and NOTICE files that describe the exact artifact contents, including bundled third-party software when applicable; andCI may create packages from development revisions for validation. Those artifacts are development-only, must include a revision identifier, and must not be promoted as releases. Public testing channels and release-candidate handling require a separate community decision.
These rules follow the ASF Release Policy, the ASF third-party license policy, and the ASF trademark policy.
Installation uses GNUInstallDirs and respects CMAKE_INSTALL_PREFIX, DESTDIR, and platform-specific directory overrides.
| Artifact | Installation path |
|---|---|
| Shared library | ${CMAKE_INSTALL_LIBDIR}/libtsfile.* |
| Windows runtime library | ${CMAKE_INSTALL_BINDIR}/tsfile.dll |
| Windows import library | ${CMAKE_INSTALL_LIBDIR}/tsfile.lib |
| Public and required transitive headers | ${CMAKE_INSTALL_INCLUDEDIR}/tsfile/... |
| CMake package | ${CMAKE_INSTALL_LIBDIR}/cmake/TsFile/ |
| pkg-config metadata | ${CMAKE_INSTALL_LIBDIR}/pkgconfig/libtsfile.pc |
| CLI | ${CMAKE_INSTALL_BINDIR}/tsfile-cli |
| License and project notices | ${CMAKE_INSTALL_DATADIR}/doc/tsfile/ |
Headers are installed below ${CMAKE_INSTALL_INCLUDEDIR}/tsfile, while the include root exported to consumers is ${CMAKE_INSTALL_INCLUDEDIR}. With those two settings, consumer code uses:
#include <tsfile/reader/tsfile_reader.h> #include <tsfile/writer/tsfile_writer.h>
For example, the exported target may obtain its installed include root through the following rule or an equivalent INSTALL_INTERFACE declaration:
install(TARGETS tsfile EXPORT TsFileTargets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
The implementation must replace the current build-tree staging directory in install(TARGETS) with the standard destinations above. It must also install headers through an explicit reviewed manifest. Copying every source header is not a declaration that every internal class is public API.
The supported CMake interface is:
find_package(TsFile CONFIG REQUIRED) target_link_libraries(my_application PRIVATE TsFile::tsfile)
Consumers choose where CMake searches for the installed package through CMAKE_PREFIX_PATH or TsFile_DIR. They do not manually add the TsFile header directory with target_include_directories():
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/tsfile
The installation provides:
TsFileConfig.cmake;TsFileConfigVersion.cmake;TsFileTargets.cmake; andTsFile::tsfile.The same target name is available in the build tree as an alias. Its usage requirements include the installed header directory, the required C++ standard, platform libraries, and any feature definitions visible from public headers. Generated CMake files must be relocatable and must not contain source or build directory paths.
The module name is libtsfile:
pkg-config --cflags --libs libtsfile
The name follows the installed library basename, libtsfile, and is less likely to collide with a generic module called tsfile. There is no central pkg-config registry. Before the first public package is submitted, maintainers must recheck the target Homebrew and Linux package indexes, plus commonly used public .pc module names, for conflicts. Consumer CI must use an isolated installation prefix and verify that pkg-config resolves libtsfile.pc from that prefix rather than from an unrelated preinstalled package.
libtsfile.pc contains the same public include and link requirements as the CMake target. Dependencies used only inside the shared library are omitted. A dependency whose types appear in public headers belongs in Requires; it must not be hidden in Requires.private.
Its include declaration is conceptually:
includedir=${prefix}/include Cflags: -I${includedir}
The generated Cflags keeps ${includedir} as its root and does not append /tsfile. CMake and pkg-config consumers therefore obtain the same #include <tsfile/...> form from the package metadata.
The first release containing the completed installation metadata establishes the packaged ABI baseline. The public API consists of:
TsFile::tsfile CMake target and libtsfile pkg-config module.An implementation header may need to be installed because a public header includes it. That does not make undocumented declarations in the implementation header a supported API. The implementation work should add a small set of documented umbrella or entry-point headers and reduce exposed implementation details over time without breaking those entry points.
The compatibility rules are:
The C wrapper remains part of libtsfile for the first milestone; it does not become a second runtime library. Splitting it later would require its own ABI and SONAME decision.
The release version and ABI epoch are separate values:
set(TSFILE_VERSION <approved-source-release-version>) set(TSFILE_ABI_VERSION <initial-abi-epoch>) set_target_properties(tsfile PROPERTIES VERSION "${TSFILE_VERSION}" SOVERSION "${TSFILE_ABI_VERSION}")
TSFILE_VERSION identifies the source release. TSFILE_ABI_VERSION is a positive integer changed only for an incompatible ABI change. Before selecting the initial value, implementation work must inspect the SONAME or install name produced by recent releases, identify known binary consumers, and establish a baseline from the installed public headers and exported symbols. If no stable binary compatibility promise exists, ABI epoch 1 is the recommended initial value. If an existing binary contract must be preserved, the initial value and transition plan must reflect it rather than silently relabeling the library.
For example, if epoch 1 is selected, a 2.4.0 build installs libtsfile.so.2.4.0 with SONAME libtsfile.so.1 on ELF platforms. Development suffixes such as .dev must never appear in a SONAME.
This replaces the current behavior where the complete project version is also used as SOVERSION. Keeping the ABI epoch independent avoids changing the runtime package name for every feature or patch release.
Before the baseline is declared, CI must compare builds with supported feature profiles and remove configuration-dependent layouts from public headers where possible. In particular, build-only and test-only definitions must not alter the installed ABI. Required feature state should be expressed through an installed generated configuration header and propagated by the consumer metadata.
The packaging contract supports one shared target, tsfile. Public binary packages must use a fixed, documented feature profile so that packages with the same SONAME have the same public ABI.
There is no plan to publish a static libtsfile. Native packages and the portable all-in-one archive both expose a shared library. A future request for a public static library requires a separate community proposal covering a concrete use case, dependency availability, security updates, symbol visibility, and license aggregation.
Native Homebrew, Debian/Ubuntu, Fedora, and EPEL packages should use system dependencies where the platform provides a supported version. This allows the platform to deliver security updates and avoids shipping duplicate libraries. The build must therefore gain an explicit provider mode rather than selecting bundled code implicitly:
TSFILE_DEPENDENCY_PROVIDER=system|bundled
system is required for every native downstream package, including Homebrew, DEB, and RPM. A dependency unavailable on a target platform may be disabled or handled in that platform's packaging review; it must not silently fall back to a bundled copy.
bundled is intended for reproducible local builds and the portable all-in-one profile. Its dependency versions and complete licensing material must be pinned and audited. The two provider modes produce distinct artifacts and must not be mixed in one package build.
The package configuration must expose only dependencies required by consumers. Private compression and parser implementations linked into the shared library must not leak source-tree include paths or unnecessary link flags into TsFile::tsfile or libtsfile.pc.
CPack and CI use three logical components:
| Component | Contents | Example native package names |
|---|---|---|
runtime | Versioned shared library and required notices | Debian: libtsfile1; RPM: tsfile-libs |
development | Headers, unversioned linker name/import library, CMake config, pkg-config file | Debian: libtsfile-dev; RPM: tsfile-devel |
tools | tsfile-cli and required notices | tsfile-tools |
The numeric Debian runtime suffix follows the ABI epoch, not the source release major version. Exact names remain platform packaging decisions.
Homebrew should use one formula containing the runtime, development files, and CLI unless Homebrew review establishes a reason to split them. Official distribution repositories use their native packaging files. CPack-generated DEB or RPM output is a local validation aid and is not submitted in place of a formula, Debian packaging, or an RPM spec. The CPack TGZ profile described below has a different purpose: it produces the portable all-in-one archive.
An all-in-one archive serves systems where a suitable native package is not available or installing development dependencies is impractical. It is not selected merely because a platform does not use APT. DNF/YUM, Homebrew, and other capable native package managers still use the system dependency profile.
A portable archive contains:
apache-tsfile-cpp-<version>-<os>-<arch>/ ├── bin/tsfile-cli ├── lib/libtsfile.<shared-library-suffix> ├── include/tsfile/... ├── lib/cmake/TsFile/... ├── lib/pkgconfig/libtsfile.pc ├── LICENSE └── NOTICE
Pinned third-party dependencies may be linked privately into the shared libtsfile or placed in a private library directory. Third-party symbols must not leak into the public ABI, and any private shared libraries must use origin-relative runtime lookup paths. The archive must not bundle foundational system runtimes such as glibc.
Each archive is specific to an operating system, architecture, compiler/C++ runtime baseline, and minimum operating-system version. It must be relocatable, run without development packages installed, and include exact third-party LICENSE and NOTICE content. A dependency security update requires rebuilding and republishing the complete archive.
The all-in-one archive is a convenience binary built from an approved source release. It does not replace the ASF source release and does not contain a public static libtsfile.
CPack uses separate packaging profiles:
DEB uses TSFILE_DEPENDENCY_PROVIDER=system and the runtime, development, and tools components.TGZ produces one portable all-in-one archive with TSFILE_DEPENDENCY_PROVIDER=bundled after its relocation, symbol, and license checks are available.RPM uses TSFILE_DEPENDENCY_PROVIDER=system and is enabled after RPM CI and package conventions are ready.Package metadata includes the source release version, project URL, license identifier, architecture, component dependencies where applicable, and package maintainer contact agreed by the community.
CPack configuration must be owned by the TsFile top-level project. The bundled ANTLR build currently calls include(CPack), which can produce a top-level configuration containing ANTLR metadata. Dependency CPack configuration must be disabled or isolated before TsFile packages are generated.
Packaging changes are complete only when CI verifies:
DESTDIR, with both the minimum supported CMake version and cmake --install on newer CMake versions.find_package(TsFile CONFIG REQUIRED), links TsFile::tsfile without manually adding an include directory, verifies that the imported include root is <prefix>/include rather than <prefix>/include/tsfile, compiles with the resulting <tsfile/...> include form, and reads and writes a small TsFile.#include <tsfile/...> spelling, and compiles and runs the same smoke operation.tsfile-cli --version.LICENSE or NOTICE content.The initial native-package matrix covers macOS with Homebrew dependencies and a supported Debian/Ubuntu release. A Linux all-in-one job is added only after the bundled-dependency and license checks are ready. Fedora/RPM jobs are added before RPM artifacts or repositories are advertised.
The implementation should be split into reviewable changes:
tsfile/-namespaced public-header layout and manifest, versioned shared-library rules, CMake export files, and pkg-config metadata.DEB packaging and a Homebrew test formula using system dependencies, plus their lifecycle CI.TGZ profile using pinned bundled dependencies, plus relocation, symbol, license, and clean-runtime checks.