blob: 4b2946822dfd11952c8a29031d53604f5c926164 [file] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# TsFile C++ Packaging and Installation Design
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][package-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.
## Goals
- Provide a conventional, relocatable CMake installation.
- Give downstream consumers stable CMake and pkg-config interfaces.
- Define the public ABI, library version, and SONAME policy.
- Define logical runtime, development, and tools components while allowing
each package manager to follow its own conventions.
- Generate local test packages and validate their complete lifecycle in CI.
- Keep every published package traceable to a community-approved Apache
source release.
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.
## Release and distribution boundary
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:
- be built from a source release approved by the project community;
- use a version that maps unambiguously to that source release;
- retain provenance such as the source URL and source checksum in the
platform-specific packaging metadata;
- carry `LICENSE` and `NOTICE` files that describe the exact artifact
contents, including bundled third-party software when applicable; and
- avoid presenting snapshots, nightly builds, or release candidates as a
general-purpose installation channel.
CI 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][asf-release-policy], the
[ASF third-party license policy][asf-third-party-policy], and the
[ASF trademark policy][asf-trademark-policy].
## Installation contract
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:
```cpp
#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:
```cmake
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.
### CMake consumer interface
The supported CMake interface is:
```cmake
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()`:
```bash
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/tsfile
```
The installation provides:
- `TsFileConfig.cmake`;
- `TsFileConfigVersion.cmake`;
- `TsFileTargets.cmake`; and
- the imported target `TsFile::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.
### pkg-config consumer interface
The module name is `libtsfile`:
```bash
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:
```pkg-config
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.
## Public API and ABI policy
The first release containing the completed installation metadata establishes
the packaged ABI baseline. The public API consists of:
- headers listed in the installation manifest as public entry points;
- public declarations reachable from those headers and required to use them;
- the C wrapper declarations installed for external consumers; and
- the `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:
- Adding API without changing existing binary layouts is compatible.
- Removing or changing an exported function, symbol, class layout, virtual
interface, enum value, or public data-member type is incompatible.
- Changing compiler flags or feature macros in a way that changes a public
type layout is incompatible.
- An incompatible change requires a new ABI epoch and therefore a new
SONAME. Source deprecation alone does not permit an ABI break within an
epoch.
- Platform toolchain ABI boundaries still apply. Portable C++ archives must
identify their operating system, architecture, compiler/runtime baseline,
and minimum supported operating-system version.
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.
### Library version and SONAME
The release version and ABI epoch are separate values:
```cmake
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.
## Shared-library scope
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.
## Dependency policy
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:
```text
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`.
## Native package components
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.
## 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:
```text
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 scope
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.
## CI acceptance criteria
Packaging changes are complete only when CI verifies:
1. A clean release build with tests and examples excluded from installed
artifacts.
2. Installation to a temporary, non-default prefix using `DESTDIR`, with both
the minimum supported CMake version and `cmake --install` on newer CMake
versions.
3. An independent CMake consumer that calls `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.
4. An independent pkg-config consumer that verifies the same include root,
uses the same `#include <tsfile/...>` spelling, and compiles and runs the
same smoke operation.
5. Each public entry-point header compiles in an isolated translation unit
using only the usage requirements provided by the installed package.
6. CLI execution, including `tsfile-cli --version`.
7. Package creation, installation on a clean image, upgrade from the previous
compatible release when one exists, removal, and a check for unexpected
remaining files.
8. Runtime/development/tools component dependencies and file ownership in
native packages.
9. Native packages link to package-manager dependencies and do not install
private copies of them.
10. The portable archive runs in a clean environment without development
dependencies, remains relocatable, and resolves any bundled libraries only
from inside the archive.
11. Absence of source/build paths, leaked third-party symbols, and unaccounted
`LICENSE` or `NOTICE` content.
12. SONAME, symlink, exported-symbol, and ABI checks against the previous
release in the same ABI epoch.
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.
## Incremental implementation
The implementation should be split into reviewable changes:
1. Agree on this installation, naming, ABI, and component contract.
2. Add standard install destinations, the `tsfile/`-namespaced public-header
layout and manifest, versioned shared-library rules, CMake export files,
and pkg-config metadata.
3. Add independent CMake and pkg-config consumers plus install/uninstall and
ABI checks.
4. Add explicit, isolated system/bundled dependency provider selection.
5. Add native `DEB` packaging and a Homebrew test formula using system
dependencies, plus their lifecycle CI.
6. Add the portable all-in-one `TGZ` profile using pinned bundled dependencies,
plus relocation, symbol, license, and clean-runtime checks.
7. Add native RPM packaging with system dependencies when maintainers and user
demand justify it.
8. Discuss ownership, credentials, retention, and release-candidate policy
before creating any public testing repository.
[package-discussion]: https://lists.apache.org/thread/cjd67hv8n1kwy2023r0906wjbgn0mfkb
[asf-release-policy]: https://www.apache.org/legal/release-policy.html
[asf-third-party-policy]: https://www.apache.org/legal/resolved.html
[asf-trademark-policy]: https://www.apache.org/foundation/marks/