This is an automated email from the ASF dual-hosted git repository.

ColinLeeo pushed a commit to branch feature/cpp-packaging
in repository https://gitbox.apache.org/repos/asf/tsfile.git

commit 0edf0cfdecd207d43de23dd8548eaf706cdfeb28
Author: ColinLee <[email protected]>
AuthorDate: Thu Aug 20 15:54:16 2026 +0800

    feat(cpp): add native installation packages
---
 .github/workflows/cpp-packaging.yml                | 130 ++++++++++++++++
 cpp/CMakeLists.txt                                 | 171 ++++++++++++++++++++-
 cpp/README.md                                      |   9 +-
 cpp/cmake/ANTLR4Dependency.cmake                   |   6 +-
 cpp/cmake/TsFileConfig.cmake.in                    |  29 ++++
 cpp/cmake/TsFilePublicHeaders.cmake                |  47 ++++++
 cpp/cmake/libtsfile.pc.in                          |  31 ++++
 cpp/cmake/tests/ANTLR4DependencyTest.cmake         |   2 +-
 .../tests/projects/ANTLR4Dependency/CMakeLists.txt |   2 +-
 .../projects/InstalledConsumer/CMakeLists.txt      |  42 +++++
 cpp/cmake/tests/projects/InstalledConsumer/main.cc |  24 +++
 .../projects/PkgConfigConsumer/CMakeLists.txt      |  32 ++++
 cpp/cmake/tests/projects/PkgConfigConsumer/main.c  |  21 +++
 cpp/cmake/tests/projects/PkgConfigConsumer/main.cc |  21 +++
 cpp/src/CMakeLists.txt                             | 118 ++++++++++++--
 cpp/third_party/CMakeLists.txt                     |  23 +++
 cpp/third_party/README.md                          |   2 +-
 cpp/tools/CMakeLists.txt                           |  17 +-
 packaging/README.md                                |  92 +++++++++++
 packaging/homebrew/tsfile.rb                       |  71 +++++++++
 20 files changed, 868 insertions(+), 22 deletions(-)

diff --git a/.github/workflows/cpp-packaging.yml 
b/.github/workflows/cpp-packaging.yml
new file mode 100644
index 000000000..ef5a457c2
--- /dev/null
+++ b/.github/workflows/cpp-packaging.yml
@@ -0,0 +1,130 @@
+#
+# 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
+#
+#     https://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.
+
+name: Cpp-Packaging
+
+on:
+  push:
+    branches:
+      - develop
+      - rc/**
+    paths:
+      - '.github/workflows/cpp-packaging.yml'
+      - 'cpp/**'
+      - 'packaging/**'
+      - 'LICENSE'
+      - 'NOTICE'
+  pull_request:
+    branches:
+      - develop
+      - rc/**
+    paths:
+      - '.github/workflows/cpp-packaging.yml'
+      - 'cpp/**'
+      - 'packaging/**'
+      - 'LICENSE'
+      - 'NOTICE'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  deb:
+    name: Debian package
+    runs-on: ubuntu-24.04
+    timeout-minutes: 30
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v7
+
+      - name: Install packaging tools
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y build-essential cmake ninja-build pkg-config 
dpkg-dev
+
+      - name: Configure and build
+        run: |
+          cmake -S cpp -B build/deb -G Ninja \
+            -DCMAKE_BUILD_TYPE=Release \
+            -DBUILD_TEST=OFF \
+            -DBUILD_TOOLS=ON \
+            -DTSFILE_ENABLE_CPACK=ON \
+            -DTSFILE_DEPENDENCY_SOURCE=AUTO \
+            -DTSFILE_ENABLE_NATIVE_ARCH=OFF
+          cmake --build build/deb --parallel
+          cmake --install build/deb
+
+      - name: Build and inspect DEB packages
+        run: |
+          cpack --config build/deb/CPackConfig.cmake -G DEB
+          ls -lh ./*.deb
+          for package in ./*.deb; do
+            dpkg-deb --info "$package"
+            dpkg-deb --contents "$package" | grep -E 
'(/libtsfile|/tsfile-cli|TsFileConfig|libtsfile.pc)' || true
+          done
+
+      - name: Upload DEB packages
+        uses: actions/upload-artifact@v4
+        with:
+          name: tsfile-deb
+          path: '*.deb'
+          if-no-files-found: error
+
+  rpm:
+    name: Fedora package
+    runs-on: ubuntu-24.04
+    container: fedora:latest
+    timeout-minutes: 30
+    steps:
+      - name: Install build and packaging tools
+        run: |
+          dnf install -y \
+            cmake gcc-c++ make ninja-build pkgconf-pkg-config rpm-build \
+            git curl tar xz unzip gzip
+
+      - name: Checkout repository
+        uses: actions/checkout@v7
+
+      - name: Configure and build
+        run: |
+          cmake -S cpp -B build/rpm -G Ninja \
+            -DCMAKE_BUILD_TYPE=Release \
+            -DBUILD_TEST=OFF \
+            -DBUILD_TOOLS=ON \
+            -DTSFILE_ENABLE_CPACK=ON \
+            -DTSFILE_DEPENDENCY_SOURCE=AUTO \
+            -DTSFILE_ENABLE_NATIVE_ARCH=OFF
+          cmake --build build/rpm --parallel
+          cmake --install build/rpm
+
+      - name: Build and inspect RPM packages
+        run: |
+          cpack --config build/rpm/CPackConfig.cmake -G RPM
+          ls -lh ./*.rpm
+          for package in ./*.rpm; do
+            rpm -qip "$package"
+            rpm -qlp "$package" | grep -E 
'(/libtsfile|/tsfile-cli|TsFileConfig|libtsfile.pc)' || true
+          done
+
+      - name: Upload RPM packages
+        uses: actions/upload-artifact@v4
+        with:
+          name: tsfile-rpm
+          path: '*.rpm'
+          if-no-files-found: error
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 7ff07a786..9da9a2d8c 100755
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -34,6 +34,25 @@ if (POLICY CMP0074)
 endif ()
 set(TsFile_CPP_VERSION 2.3.2.dev)
 
+include(GNUInstallDirs)
+include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/TsFilePublicHeaders.cmake)
+
+# The package version identifies the source release. Development suffixes are
+# intentionally removed from generated package metadata and never enter the
+# SONAME.
+string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" TSFILE_PACKAGE_VERSION
+        "${TsFile_CPP_VERSION}")
+if ("${TSFILE_PACKAGE_VERSION}" STREQUAL "")
+    message(FATAL_ERROR
+            "TsFile_CPP_VERSION must start with a semantic version: "
+            "${TsFile_CPP_VERSION}")
+endif ()
+set(TSFILE_ABI_VERSION "1" CACHE STRING
+        "TsFile shared-library ABI epoch (changes only on ABI breaks)")
+if (NOT TSFILE_ABI_VERSION MATCHES "^[0-9]+$")
+    message(FATAL_ERROR "TSFILE_ABI_VERSION must be a positive integer")
+endif ()
+
 include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/DependencySource.cmake)
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 
@@ -64,10 +83,18 @@ endif ()
 message("cmake using: USE_CPP11=${USE_CPP11}")
 # MSVC has no /std:c++11; CMake maps this to the closest supported standard
 # (C++14 default on MSVC), which compiles the C++11 codebase fine.
-set(CMAKE_CXX_STANDARD 11)
+set(TSFILE_CXX_STANDARD "11" CACHE STRING
+        "C++ language standard used to build TsFile (11, 14, or 17)")
+set_property(CACHE TSFILE_CXX_STANDARD PROPERTY STRINGS 11 14 17)
+if (NOT TSFILE_CXX_STANDARD MATCHES "^(11|14|17)$")
+    message(FATAL_ERROR
+            "TSFILE_CXX_STANDARD must be one of 11, 14, or 17")
+endif ()
+set(CMAKE_CXX_STANDARD ${TSFILE_CXX_STANDARD})
 set(CMAKE_CXX_STANDARD_REQUIRED OFF)
 if (NOT MSVC)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    set(CMAKE_CXX_FLAGS
+            "${CMAKE_CXX_FLAGS} -std=c++${TSFILE_CXX_STANDARD}")
 endif ()
 
 if (DEFINED ENV{CXX})
@@ -333,7 +360,8 @@ if (MSVC)
     # C++17 extensions), so we pin it explicitly for reproducibility.
     set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} /W3 /utf-8 /EHsc /bigobj 
/Zc:__cplusplus /std:c++14")
 else ()
-    set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -Wall -std=c++11")
+    set(CMAKE_CXX_FLAGS
+            "$ENV{CXXFLAGS} -Wall -std=c++${TSFILE_CXX_STANDARD}")
 endif ()
 add_subdirectory(third_party)
 
@@ -406,3 +434,140 @@ endif ()
 unset(_TSFILE_PROJECT_DEPENDENCIES)
 
 add_subdirectory(examples)
+
+# Install the compatibility header closure from the generated staging tree. The
+# staging tree is populated by the existing copy_* targets and contains only
+# headers selected by the reviewed closure manifest. The closure is retained
+# for source compatibility; it is not the final public API boundary.
+foreach (header_dir IN LISTS TSFILE_PUBLIC_HEADER_CLOSURE)
+    install(DIRECTORY "${LIBRARY_INCLUDE_DIR}/${header_dir}/"
+            DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/tsfile/${header_dir}"
+            COMPONENT development
+            FILES_MATCHING PATTERN "*.h")
+endforeach()
+if (ENABLE_SIMD AND TSFILE_SIMDE_INCLUDE_ROOT)
+    install(DIRECTORY "${TSFILE_SIMDE_INCLUDE_ROOT}/simde/"
+            DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/simde"
+            COMPONENT development
+            FILES_MATCHING PATTERN "*.h")
+endif ()
+if (ENABLE_ANTLR4 AND TSFILE_ANTLR4_INCLUDE_ROOT)
+    install(DIRECTORY "${TSFILE_ANTLR4_INCLUDE_ROOT}/"
+            DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+            COMPONENT development
+            FILES_MATCHING PATTERN "*.h")
+endif ()
+if (ENABLE_ANTLR4 AND TSFILE_UTF8CPP_INCLUDE_ROOT)
+    install(DIRECTORY "${TSFILE_UTF8CPP_INCLUDE_ROOT}/"
+            DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+            COMPONENT development
+            FILES_MATCHING PATTERN "*.h")
+endif ()
+
+set(TSFILE_LICENSE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE")
+set(TSFILE_NOTICE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../NOTICE")
+if (EXISTS "${TSFILE_LICENSE_FILE}")
+    install(FILES "${TSFILE_LICENSE_FILE}"
+            DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/tsfile"
+            COMPONENT runtime)
+endif()
+if (EXISTS "${TSFILE_NOTICE_FILE}")
+    install(FILES "${TSFILE_NOTICE_FILE}"
+            DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/tsfile"
+            COMPONENT runtime)
+endif()
+
+include(CMakePackageConfigHelpers)
+set(TSFILE_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/TsFile")
+configure_package_config_file(
+        "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TsFileConfig.cmake.in"
+        "${CMAKE_CURRENT_BINARY_DIR}/TsFileConfig.cmake"
+        INSTALL_DESTINATION "${TSFILE_CMAKE_INSTALL_DIR}")
+write_basic_package_version_file(
+        "${CMAKE_CURRENT_BINARY_DIR}/TsFileConfigVersion.cmake"
+        VERSION "${TSFILE_PACKAGE_VERSION}"
+        COMPATIBILITY SameMajorVersion)
+install(FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/TsFileConfig.cmake"
+        "${CMAKE_CURRENT_BINARY_DIR}/TsFileConfigVersion.cmake"
+        DESTINATION "${TSFILE_CMAKE_INSTALL_DIR}"
+        COMPONENT development)
+
+set(TSFILE_PKGCONFIG_CFLAGS "")
+foreach (_TSFILE_PUBLIC_FEATURE
+        ENABLE_ANTLR4
+        ENABLE_MEM_STAT
+        ENABLE_SNAPPY
+        ENABLE_LZ4
+        ENABLE_LZOKAY
+        ENABLE_ZLIB
+        ENABLE_GZIP
+        ENABLE_ZSTD
+        ENABLE_LZMA2
+        ENABLE_THREADS
+        ENABLE_SIMD)
+    if (${_TSFILE_PUBLIC_FEATURE})
+        string(APPEND TSFILE_PKGCONFIG_CFLAGS
+                " -D${_TSFILE_PUBLIC_FEATURE}")
+    endif ()
+endforeach ()
+configure_file(
+        "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libtsfile.pc.in"
+        "${CMAKE_CURRENT_BINARY_DIR}/libtsfile.pc"
+        @ONLY)
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libtsfile.pc"
+        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
+        COMPONENT development)
+unset(_TSFILE_PUBLIC_FEATURE)
+
+# CPack is an opt-in local/package-builder integration. Native release jobs
+# can configure the project with TSFILE_DEPENDENCY_SOURCE=SYSTEM when the
+# target image provides every compatible dependency; AUTO remains the
+# reproducible fallback for distribution images with older packages.
+option(TSFILE_ENABLE_CPACK
+        "Enable CPack DEB/RPM package metadata generation" OFF)
+if (TSFILE_ENABLE_CPACK)
+    set(CPACK_PACKAGE_NAME "tsfile")
+    set(CPACK_PACKAGE_VENDOR "Apache Software Foundation")
+    set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Apache TsFile C++ library")
+    set(CPACK_PACKAGE_DESCRIPTION
+            "Columnar storage library and command-line tools for time series 
data")
+    set(CPACK_PACKAGE_HOMEPAGE_URL "https://tsfile.apache.org/";)
+    set(CPACK_PACKAGE_CONTACT "[email protected]")
+    set(CPACK_RESOURCE_FILE_LICENSE "${TSFILE_LICENSE_FILE}")
+    set(CPACK_PACKAGE_VERSION "${TSFILE_PACKAGE_VERSION}")
+    string(REPLACE "." ";" _TSFILE_PACKAGE_VERSION_PARTS
+            "${TSFILE_PACKAGE_VERSION}")
+    list(GET _TSFILE_PACKAGE_VERSION_PARTS 0 CPACK_PACKAGE_VERSION_MAJOR)
+    list(GET _TSFILE_PACKAGE_VERSION_PARTS 1 CPACK_PACKAGE_VERSION_MINOR)
+    list(GET _TSFILE_PACKAGE_VERSION_PARTS 2 CPACK_PACKAGE_VERSION_PATCH)
+
+    set(CPACK_COMPONENTS_ALL runtime development tools)
+    set(CPACK_COMPONENT_DEVELOPMENT_DEPENDS runtime)
+    set(CPACK_COMPONENT_TOOLS_DEPENDS runtime)
+    set(CPACK_DEB_COMPONENT_INSTALL ON)
+    set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
+    set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+    set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME "tsfile")
+    set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_NAME "tsfile-dev")
+    set(CPACK_DEBIAN_TOOLS_PACKAGE_NAME "tsfile-tools")
+    set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_DEPENDS
+            "tsfile (= ${TSFILE_PACKAGE_VERSION})")
+    set(CPACK_DEBIAN_TOOLS_PACKAGE_DEPENDS
+            "tsfile (= ${TSFILE_PACKAGE_VERSION})")
+
+    set(CPACK_RPM_COMPONENT_INSTALL ON)
+    set(CPACK_RPM_PACKAGE_LICENSE "Apache-2.0")
+    set(CPACK_RPM_PACKAGE_RELEASE "1")
+    set(CPACK_RPM_PACKAGE_AUTOREQPROV ON)
+    set(CPACK_RPM_RUNTIME_PACKAGE_NAME "tsfile")
+    set(CPACK_RPM_DEVELOPMENT_PACKAGE_NAME "tsfile-devel")
+    set(CPACK_RPM_TOOLS_PACKAGE_NAME "tsfile-tools")
+    set(CPACK_RPM_DEVELOPMENT_PACKAGE_REQUIRES
+            "tsfile = ${TSFILE_PACKAGE_VERSION}-1")
+    set(CPACK_RPM_TOOLS_PACKAGE_REQUIRES
+            "tsfile = ${TSFILE_PACKAGE_VERSION}-1")
+
+    include(CPack)
+    unset(_TSFILE_PACKAGE_VERSION_PARTS)
+endif ()
diff --git a/cpp/README.md b/cpp/README.md
index 6341287e4..d14cf6ad6 100644
--- a/cpp/README.md
+++ b/cpp/README.md
@@ -80,6 +80,13 @@ TsFile C++ now supports:
 - **macOS**: Clang
 - **Windows**: MSVC 2017+ and MinGW
 
+### Installation and Packages
+
+The C++ install layout supports CMake consumers, pkg-config, a relocatable
+TGZ archive, native Linux DEB/RPM packages, and Homebrew on macOS. See
+[`packaging/README.md`](../packaging/README.md) for build commands and the
+platform-specific release rules.
+
 All code must compile without errors on all supported platforms before 
submission.
 
 We welcome any bug reports. You can open an issue with a title starting with 
[CPP] to describe the bug, like: https://github.com/apache/tsfile/issues/94
@@ -170,7 +177,7 @@ dependencies are resolved:
 
 ANTLR4, Snappy, LZ4, lzokay, SIMDe, zlib, Zstandard, and liblzma are currently
 resolved through this policy. A compatible system ANTLR4 must be version 4.9.3
-or newer and earlier than 5.0.0, and provide an `antlr4_static` or
+or newer and earlier than 4.10.0, and provide an `antlr4_static` or
 `antlr4_shared` target. A compatible system Snappy must be version 1.2.1 or
 newer in the 1.x release series and provide the `Snappy::snappy` CMake target.
 A compatible system LZ4 must be version 1.9.4 or newer in the 1.x release
diff --git a/cpp/cmake/ANTLR4Dependency.cmake b/cpp/cmake/ANTLR4Dependency.cmake
index 3d772c25c..b92483e51 100644
--- a/cpp/cmake/ANTLR4Dependency.cmake
+++ b/cpp/cmake/ANTLR4Dependency.cmake
@@ -19,7 +19,11 @@ under the License.
 
 set(TSFILE_ANTLR4_MIN_VERSION "4.9.3")
 set(TSFILE_ANTLR4_BUNDLED_VERSION "4.9.3")
-set(TSFILE_ANTLR4_NEXT_INCOMPATIBLE_VERSION "5.0.0")
+# The generated parser in this repository follows the 4.9 runtime API. ANTLR
+# 4.10 introduced breaking C++ runtime changes (and raised its language floor
+# to C++17), so newer system runtimes must use the verified bundled fallback
+# until the parser is regenerated and reviewed.
+set(TSFILE_ANTLR4_NEXT_INCOMPATIBLE_VERSION "4.10.0")
 set(TSFILE_ANTLR4_SYSTEM_INCLUDE_DIR "")
 set(_TSFILE_SYSTEM_ANTLR4_FOUND FALSE)
 
diff --git a/cpp/cmake/TsFileConfig.cmake.in b/cpp/cmake/TsFileConfig.cmake.in
new file mode 100644
index 000000000..eea711a9e
--- /dev/null
+++ b/cpp/cmake/TsFileConfig.cmake.in
@@ -0,0 +1,29 @@
+#[[
+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
+
+    https://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.
+]]
+
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+if (@ENABLE_THREADS@)
+    find_dependency(Threads)
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/TsFileTargets.cmake")
+
+check_required_components(TsFile)
diff --git a/cpp/cmake/TsFilePublicHeaders.cmake 
b/cpp/cmake/TsFilePublicHeaders.cmake
new file mode 100644
index 000000000..a517d3a9c
--- /dev/null
+++ b/cpp/cmake/TsFilePublicHeaders.cmake
@@ -0,0 +1,47 @@
+#[[
+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
+
+    https://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.
+]]
+
+# Public entry points are intentionally listed separately from the transitive
+# header closure. The current public headers include implementation-level
+# declarations, so the closure is installed until those dependencies can be
+# hidden behind a stable facade. Adding a new public entry point requires an
+# explicit review of this list.
+set(TSFILE_PUBLIC_ENTRYPOINT_HEADERS
+        cwrapper/tsfile_cwrapper.h
+        cwrapper/tsfile_cwrapper_expression.h
+        reader/tsfile_reader.h
+        writer/tsfile_writer.h
+        writer/tsfile_table_writer.h
+        writer/tsfile_tree_writer.h)
+
+# These directories form the reviewed transitive closure of the entry points
+# above. This is an installation compatibility closure, not a promise that
+# every header is a stable public API. Compression, encoding, parser, and
+# utility headers are implementation dependencies today, but are required for
+# consumers to parse the installed public declarations.
+set(TSFILE_PUBLIC_HEADER_CLOSURE
+        common
+        compress
+        cwrapper
+        encoding
+        file
+        parser
+        reader
+        utils
+        writer)
diff --git a/cpp/cmake/libtsfile.pc.in b/cpp/cmake/libtsfile.pc.in
new file mode 100644
index 000000000..ff820d0d8
--- /dev/null
+++ b/cpp/cmake/libtsfile.pc.in
@@ -0,0 +1,31 @@
+# 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
+#
+#     https://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.
+
+# The .pc file is installed below ${prefix}/@CMAKE_INSTALL_LIBDIR@/pkgconfig.
+# Deriving the prefix from its own location keeps DESTDIR and relocations
+# working without embedding the build-time install prefix.
+prefix=${pcfiledir}/../..
+exec_prefix=${prefix}
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+
+Name: libtsfile
+Description: Apache TsFile C++ library
+Version: @TSFILE_PACKAGE_VERSION@
+URL: https://tsfile.apache.org/
+Libs: -L${libdir} -ltsfile
+Cflags: -I${includedir} -I${includedir}/tsfile@TSFILE_PKGCONFIG_CFLAGS@
diff --git a/cpp/cmake/tests/ANTLR4DependencyTest.cmake 
b/cpp/cmake/tests/ANTLR4DependencyTest.cmake
index 2fc981712..e6f1f5565 100644
--- a/cpp/cmake/tests/ANTLR4DependencyTest.cmake
+++ b/cpp/cmake/tests/ANTLR4DependencyTest.cmake
@@ -84,7 +84,7 @@ endfunction()
 
 _tsfile_write_fake_antlr4("${_TSFILE_COMPATIBLE_ROOT}" 4.9.3 antlr4_static)
 _tsfile_write_fake_antlr4("${_TSFILE_TOO_OLD_ROOT}" 4.9.2 antlr4_static)
-_tsfile_write_fake_antlr4("${_TSFILE_INCOMPATIBLE_ROOT}" 5.0.0 antlr4_shared)
+_tsfile_write_fake_antlr4("${_TSFILE_INCOMPATIBLE_ROOT}" 4.10.0 antlr4_shared)
 file(MAKE_DIRECTORY "${_TSFILE_MISSING_ROOT}")
 
 _tsfile_run_antlr4_case(system-compatible SYSTEM SYSTEM TRUE
diff --git a/cpp/cmake/tests/projects/ANTLR4Dependency/CMakeLists.txt 
b/cpp/cmake/tests/projects/ANTLR4Dependency/CMakeLists.txt
index 61901e38a..551373afe 100644
--- a/cpp/cmake/tests/projects/ANTLR4Dependency/CMakeLists.txt
+++ b/cpp/cmake/tests/projects/ANTLR4Dependency/CMakeLists.txt
@@ -18,7 +18,7 @@ under the License.
 ]]
 
 cmake_minimum_required(VERSION 3.11)
-project(TsFileANTLR4DependencyTest NONE)
+project(TsFileANTLR4DependencyTest LANGUAGES CXX)
 
 get_filename_component(_TSFILE_CMAKE_DIR
         "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE)
diff --git a/cpp/cmake/tests/projects/InstalledConsumer/CMakeLists.txt 
b/cpp/cmake/tests/projects/InstalledConsumer/CMakeLists.txt
new file mode 100644
index 000000000..bf0f83e34
--- /dev/null
+++ b/cpp/cmake/tests/projects/InstalledConsumer/CMakeLists.txt
@@ -0,0 +1,42 @@
+#[[
+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
+
+    https://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.
+]]
+cmake_minimum_required(VERSION 3.11)
+project(TsFileInstalledConsumer LANGUAGES CXX)
+
+find_package(TsFile CONFIG REQUIRED)
+
+get_target_property(_TSFILE_INCLUDE_DIRS TsFile::tsfile
+        INTERFACE_INCLUDE_DIRECTORIES)
+if (NOT _TSFILE_INCLUDE_DIRS MATCHES "/include")
+    message(FATAL_ERROR "TsFile target does not expose the installed include 
root")
+endif ()
+if (_TSFILE_INCLUDE_DIRS MATCHES "cpp/src|build/")
+    message(FATAL_ERROR "TsFile target leaks a source or build include path: 
${_TSFILE_INCLUDE_DIRS}")
+endif ()
+
+get_target_property(_TSFILE_LINK_LIBRARIES TsFile::tsfile
+        INTERFACE_LINK_LIBRARIES)
+if (_TSFILE_LINK_LIBRARIES MATCHES "(^|;)[^;]*_obj($|;)|cpp/src|build/")
+    message(FATAL_ERROR
+            "TsFile target leaks private build link requirements: "
+            "${_TSFILE_LINK_LIBRARIES}")
+endif ()
+
+add_executable(tsfile_installed_consumer main.cc)
+target_link_libraries(tsfile_installed_consumer PRIVATE TsFile::tsfile)
diff --git a/cpp/cmake/tests/projects/InstalledConsumer/main.cc 
b/cpp/cmake/tests/projects/InstalledConsumer/main.cc
new file mode 100644
index 000000000..8d242f678
--- /dev/null
+++ b/cpp/cmake/tests/projects/InstalledConsumer/main.cc
@@ -0,0 +1,24 @@
+/*
+ * 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
+ *
+ *     https://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.
+ */
+#include <tsfile/cwrapper/tsfile_cwrapper.h>
+#include <tsfile/reader/tsfile_reader.h>
+#include <tsfile/writer/tsfile_table_writer.h>
+#include <tsfile/writer/tsfile_tree_writer.h>
+
+int main() { return TS_DATATYPE_INT32 == 1 ? 0 : 1; }
diff --git a/cpp/cmake/tests/projects/PkgConfigConsumer/CMakeLists.txt 
b/cpp/cmake/tests/projects/PkgConfigConsumer/CMakeLists.txt
new file mode 100644
index 000000000..1aaabc396
--- /dev/null
+++ b/cpp/cmake/tests/projects/PkgConfigConsumer/CMakeLists.txt
@@ -0,0 +1,32 @@
+#[[
+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
+
+    https://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.
+]]
+cmake_minimum_required(VERSION 3.11)
+project(TsFilePkgConfigConsumer LANGUAGES C CXX)
+
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(TsFile REQUIRED IMPORTED_TARGET libtsfile)
+if (NOT TsFile_INCLUDEDIR MATCHES "/include$")
+    message(FATAL_ERROR "pkg-config did not report the installed include root: 
${TsFile_INCLUDEDIR}")
+endif ()
+
+add_executable(tsfile_pkgconfig_consumer main.c)
+target_link_libraries(tsfile_pkgconfig_consumer PRIVATE PkgConfig::TsFile)
+
+add_executable(tsfile_pkgconfig_cpp_consumer main.cc)
+target_link_libraries(tsfile_pkgconfig_cpp_consumer PRIVATE PkgConfig::TsFile)
diff --git a/cpp/cmake/tests/projects/PkgConfigConsumer/main.c 
b/cpp/cmake/tests/projects/PkgConfigConsumer/main.c
new file mode 100644
index 000000000..f32e5a550
--- /dev/null
+++ b/cpp/cmake/tests/projects/PkgConfigConsumer/main.c
@@ -0,0 +1,21 @@
+/*
+ * 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
+ *
+ *     https://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.
+ */
+#include <tsfile/cwrapper/tsfile_cwrapper.h>
+
+int main(void) { return TS_DATATYPE_INT32 == 1 ? 0 : 1; }
diff --git a/cpp/cmake/tests/projects/PkgConfigConsumer/main.cc 
b/cpp/cmake/tests/projects/PkgConfigConsumer/main.cc
new file mode 100644
index 000000000..be94b8543
--- /dev/null
+++ b/cpp/cmake/tests/projects/PkgConfigConsumer/main.cc
@@ -0,0 +1,21 @@
+/*
+ * 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
+ *
+ *     https://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.
+ */
+#include <tsfile/reader/tsfile_reader.h>
+
+int main() { return 0; }
diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index 4e34031ed..e6b60d69c 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -78,6 +78,18 @@ add_subdirectory(reader)
 add_subdirectory(utils)
 add_subdirectory(writer)
 
+# Object libraries need the directory-level project dependencies on older
+# CMake versions, but those dependencies are implementation details of the
+# shared library and must not become exported consumer requirements. Keep the
+# platform thread target in the directory scope for its public link interface,
+# then link codecs explicitly as PRIVATE below.
+get_property(_TSFILE_SRC_LINK_LIBRARIES DIRECTORY PROPERTY LINK_LIBRARIES)
+if (ENABLE_THREADS)
+    set_property(DIRECTORY PROPERTY LINK_LIBRARIES Threads::Threads)
+else ()
+    set_property(DIRECTORY PROPERTY LINK_LIBRARIES "")
+endif ()
+
 set(_TSFILE_OBJECT_TARGETS
         common_obj
         compress_obj
@@ -123,35 +135,115 @@ if (${COV_ENABLED})
     else()
         set(COV_LINK_LIB -lgcov)
     endif()
-    if (CMAKE_VERSION VERSION_LESS "3.12")
-        target_link_libraries(tsfile ${COV_LINK_LIB})
-    else()
-        target_link_libraries(tsfile ${_TSFILE_OBJECT_TARGETS} ${COV_LINK_LIB})
-    endif()
+    target_link_libraries(tsfile PRIVATE ${COV_LINK_LIB})
 else()
     message("Disable code cov...")
-    if (NOT CMAKE_VERSION VERSION_LESS "3.12")
-        target_link_libraries(tsfile ${_TSFILE_OBJECT_TARGETS})
-    endif()
 endif()
 
+# Linking object libraries directly makes static-library exports depend on
+# private, non-exported object targets. Add their object files as sources
+# instead; this works on the CMake 3.11 baseline and keeps the install target
+# self-contained for both shared and static builds.
+foreach (_TSFILE_OBJECT_TARGET IN LISTS _TSFILE_OBJECT_TARGETS)
+    target_sources(tsfile PRIVATE
+            $<TARGET_OBJECTS:${_TSFILE_OBJECT_TARGET}>)
+endforeach ()
+
+if (NOT "${_TSFILE_PROJECT_DEPENDENCIES}" STREQUAL "")
+    target_link_libraries(tsfile PRIVATE ${_TSFILE_PROJECT_DEPENDENCIES})
+endif ()
+
 unset(_TSFILE_OBJECT_SOURCES)
 unset(_TSFILE_OBJECT_TARGET)
 unset(_TSFILE_OBJECT_TARGETS)
 
 add_dependencies(tsfile utils_obj encoding_obj)
 
+set_property(DIRECTORY PROPERTY LINK_LIBRARIES
+        "${_TSFILE_SRC_LINK_LIBRARIES}")
+unset(_TSFILE_SRC_LINK_LIBRARIES)
+
 if (TSFILE_BUILD_SHARED)
-    set(LIBTSFILE_PROJECT_VERSION ${TsFile_CPP_VERSION})
-    set(LIBTSFILE_SO_VERSION ${TsFile_CPP_VERSION})
+    # Keep development suffixes out of the install name and SONAME. The ABI
+    # epoch changes only for incompatible binary interface changes.
+    set(LIBTSFILE_PROJECT_VERSION ${TSFILE_PACKAGE_VERSION})
+    set(LIBTSFILE_SO_VERSION ${TSFILE_ABI_VERSION})
     set_target_properties(tsfile PROPERTIES VERSION 
${LIBTSFILE_PROJECT_VERSION})
     set_target_properties(tsfile PROPERTIES SOVERSION ${LIBTSFILE_SO_VERSION})
 endif()
 
+add_library(TsFile::tsfile ALIAS tsfile)
+
+target_include_directories(tsfile PUBLIC
+        $<BUILD_INTERFACE:${LIBRARY_INCLUDE_DIR}>
+        $<BUILD_INTERFACE:${PROJECT_INCLUDE_DIR}>
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/tsfile>)
+if (ENABLE_SIMD AND TSFILE_SIMDE_INCLUDE_ROOT)
+    # Public TsFile headers include SIMDe directly. Copy the resolved header
+    # tree into the install prefix so consumers do not need the build host's
+    # system or dependency-cache paths.
+    target_include_directories(tsfile PUBLIC
+            $<BUILD_INTERFACE:${TSFILE_SIMDE_INCLUDE_ROOT}>
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+endif ()
+if (ENABLE_ANTLR4 AND TSFILE_ANTLR4_INCLUDE_ROOT)
+    target_include_directories(tsfile PUBLIC
+            $<BUILD_INTERFACE:${TSFILE_ANTLR4_INCLUDE_ROOT}>
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+endif ()
+if (ENABLE_ANTLR4 AND TSFILE_UTF8CPP_INCLUDE_ROOT)
+    target_include_directories(tsfile PUBLIC
+            $<BUILD_INTERFACE:${TSFILE_UTF8CPP_INCLUDE_ROOT}>
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+endif ()
+target_compile_features(tsfile PUBLIC cxx_std_11)
+set(_TSFILE_PUBLIC_FEATURE_DEFINITIONS "")
+foreach (_TSFILE_PUBLIC_FEATURE
+        ENABLE_ANTLR4
+        ENABLE_MEM_STAT
+        ENABLE_SNAPPY
+        ENABLE_LZ4
+        ENABLE_LZOKAY
+        ENABLE_ZLIB
+        ENABLE_GZIP
+        ENABLE_ZSTD
+        ENABLE_LZMA2
+        ENABLE_THREADS
+        ENABLE_SIMD)
+    if (${_TSFILE_PUBLIC_FEATURE})
+        list(APPEND _TSFILE_PUBLIC_FEATURE_DEFINITIONS
+                ${_TSFILE_PUBLIC_FEATURE})
+    endif ()
+endforeach ()
+if (NOT "${_TSFILE_PUBLIC_FEATURE_DEFINITIONS}" STREQUAL "")
+    target_compile_definitions(tsfile INTERFACE
+            ${_TSFILE_PUBLIC_FEATURE_DEFINITIONS})
+endif ()
+unset(_TSFILE_PUBLIC_FEATURE)
+unset(_TSFILE_PUBLIC_FEATURE_DEFINITIONS)
+
+if (TSFILE_BUILD_SHARED)
+    # A shared library already resolves its private codec and object-library
+    # dependencies at link time. Do not export those build-only targets as
+    # consumer link requirements; they are not installed with the package.
+    set_property(TARGET tsfile PROPERTY INTERFACE_LINK_LIBRARIES "")
+endif ()
+
 # A shared library is a RUNTIME plus an import ARCHIVE on Windows and a LIBRARY
 # on Unix. A static library is an ARCHIVE on every platform. Cover all three so
 # the install step works for either library type.
 install(TARGETS tsfile
-        RUNTIME DESTINATION ${LIBRARY_OUTPUT_PATH}
-        LIBRARY DESTINATION ${LIBRARY_OUTPUT_PATH}
-        ARCHIVE DESTINATION ${LIBRARY_OUTPUT_PATH})
+        EXPORT TsFileTargets
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        COMPONENT runtime
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        COMPONENT runtime
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        COMPONENT development)
+
+install(EXPORT TsFileTargets
+        FILE TsFileTargets.cmake
+        NAMESPACE TsFile::
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TsFile
+        COMPONENT development)
diff --git a/cpp/third_party/CMakeLists.txt b/cpp/third_party/CMakeLists.txt
index f7a8851b5..203b93c34 100755
--- a/cpp/third_party/CMakeLists.txt
+++ b/cpp/third_party/CMakeLists.txt
@@ -35,6 +35,12 @@ if (ENABLE_ANTLR4)
                 "${TSFILE_UTF8CPP_SOURCE_DIR}/source")
 
         add_library(tsfile_antlr4_bundled STATIC ${_TSFILE_ANTLR4_SOURCES})
+        set(TSFILE_ANTLR4_INCLUDE_ROOT
+                "${TSFILE_ANTLR4_SOURCE_DIR}/runtime/Cpp/runtime/src"
+                CACHE INTERNAL "Resolved ANTLR4 include root" FORCE)
+        set(TSFILE_UTF8CPP_INCLUDE_ROOT
+                "${TSFILE_UTF8CPP_SOURCE_DIR}/source"
+                CACHE INTERNAL "Resolved utf8cpp include root" FORCE)
         set_target_properties(tsfile_antlr4_bundled PROPERTIES
                 POSITION_INDEPENDENT_CODE ON
                 CXX_STANDARD 11)
@@ -93,6 +99,17 @@ if (ENABLE_ANTLR4)
         target_link_libraries(tsfile_antlr4 INTERFACE
                 tsfile_antlr4_bundled)
     else ()
+        set(TSFILE_ANTLR4_INCLUDE_ROOT
+                "${TSFILE_ANTLR4_SYSTEM_INCLUDE_DIR}"
+                CACHE INTERNAL "Resolved ANTLR4 include root" FORCE)
+        if (TARGET utf8cpp)
+            get_target_property(_TSFILE_UTF8CPP_INCLUDE_ROOT utf8cpp
+                    INTERFACE_INCLUDE_DIRECTORIES)
+            set(TSFILE_UTF8CPP_INCLUDE_ROOT
+                    "${_TSFILE_UTF8CPP_INCLUDE_ROOT}"
+                    CACHE INTERNAL "Resolved utf8cpp include root" FORCE)
+            unset(_TSFILE_UTF8CPP_INCLUDE_ROOT)
+        endif ()
         target_link_libraries(tsfile_antlr4 INTERFACE
                 ${TSFILE_ANTLR4_SYSTEM_TARGET})
         if (TSFILE_ANTLR4_SYSTEM_INCLUDE_DIR)
@@ -449,9 +466,15 @@ if (ENABLE_SIMD)
 
     add_library(tsfile_simde INTERFACE)
     if (TSFILE_SIMDE_SOURCE STREQUAL "BUNDLED")
+        set(TSFILE_SIMDE_INCLUDE_ROOT "${TSFILE_SIMDE_SOURCE_DIR}"
+                CACHE INTERNAL "Resolved SIMDe include root" FORCE)
         target_include_directories(tsfile_simde INTERFACE
                 "${TSFILE_SIMDE_SOURCE_DIR}")
     else ()
+        get_target_property(TSFILE_SIMDE_INCLUDE_ROOT simde::simde
+                INTERFACE_INCLUDE_DIRECTORIES)
+        set(TSFILE_SIMDE_INCLUDE_ROOT "${TSFILE_SIMDE_INCLUDE_ROOT}"
+                CACHE INTERNAL "Resolved SIMDe include root" FORCE)
         target_link_libraries(tsfile_simde INTERFACE simde::simde)
     endif ()
     add_library(TsFile::SIMDe ALIAS tsfile_simde)
diff --git a/cpp/third_party/README.md b/cpp/third_party/README.md
index 6b782d8b5..ec860c0a0 100644
--- a/cpp/third_party/README.md
+++ b/cpp/third_party/README.md
@@ -86,7 +86,7 @@ dependency before compiling and linking TsFile.
   `Vocabulary.cpp`, `ATN.cpp`, `LL1Analyzer.cpp`, `LL1Analyzer.h`,
   `LexerATNSimulator.cpp`, `LexerATNSimulator.h`, `IntervalSet.cpp`, `Any.h`,
   and `CPPUtils.cpp`. utf8cpp is not modified.
-- Resolution: `SYSTEM` accepts ANTLR4 4.9.3 or newer and earlier than 5.0.0
+- Resolution: `SYSTEM` accepts ANTLR4 4.9.3 or newer and earlier than 4.10.0
   through an `antlr4_static` or `antlr4_shared` target; `BUNDLED` downloads or
   reuses both verified archives; and `AUTO` prefers a compatible system
   package before falling back to the verified archives.
diff --git a/cpp/tools/CMakeLists.txt b/cpp/tools/CMakeLists.txt
index 4448feab2..138e792ee 100644
--- a/cpp/tools/CMakeLists.txt
+++ b/cpp/tools/CMakeLists.txt
@@ -46,4 +46,19 @@ set_target_properties(tsfile_cli PROPERTIES
         OUTPUT_NAME tsfile-cli
         RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
 
-install(TARGETS tsfile_cli RUNTIME DESTINATION bin)
+# Keep the installed CLI self-contained with the sibling lib directory. This
+# is relative to the executable location and therefore remains valid when the
+# installation prefix is relocated.
+if (APPLE)
+    set_target_properties(tsfile_cli PROPERTIES
+            INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR}"
+            BUILD_WITH_INSTALL_RPATH TRUE)
+elseif (UNIX)
+    set_target_properties(tsfile_cli PROPERTIES
+            INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
+            BUILD_WITH_INSTALL_RPATH TRUE)
+endif ()
+
+install(TARGETS tsfile_cli
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        COMPONENT tools)
diff --git a/packaging/README.md b/packaging/README.md
new file mode 100644
index 000000000..ff77cf46b
--- /dev/null
+++ b/packaging/README.md
@@ -0,0 +1,92 @@
+<!--
+
+    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
+
+        https://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++ Packages
+
+The C++ installation contract is shared by all package formats. It installs
+the library, the current compatibility header closure, `TsFileConfig.cmake`,
+pkg-config metadata, the CLI, and Apache license files below one prefix.
+
+The header closure mirrors the include relationships used by the current C++
+implementation. It is intentionally broader than the long-term stable public
+API; a separate API cleanup will narrow it in a future version.
+
+## Portable archive
+
+Build a relocatable source archive on any host with CMake and CPack:
+
+```bash
+cmake -S cpp -B cpp/build/package \
+  -DCMAKE_BUILD_TYPE=Release \
+  -DBUILD_TEST=OFF \
+  -DBUILD_TOOLS=ON \
+  -DTSFILE_ENABLE_CPACK=ON \
+  -DTSFILE_DEPENDENCY_SOURCE=AUTO
+cmake --build cpp/build/package --parallel
+cmake --install cpp/build/package
+cpack --config cpp/build/package/CPackConfig.cmake -G TGZ
+```
+
+The resulting `tsfile-<version>-<platform>.tar.gz` contains a standard
+prefix layout and can be unpacked at `/usr/local`, a user directory, or a
+relocated application prefix.
+
+## Native Linux packages
+
+DEB and RPM packages must be built in the target distribution environment so
+CPack can run the native dependency scanner (`dpkg-shlibdeps` or
+`rpmbuild`). On Debian/Ubuntu use `-G DEB`; on Fedora/RHEL use `-G RPM`:
+
+```bash
+cmake -S cpp -B cpp/build/package \
+  -DCMAKE_BUILD_TYPE=Release \
+  -DBUILD_TEST=OFF \
+  -DBUILD_TOOLS=ON \
+  -DTSFILE_ENABLE_CPACK=ON \
+  -DTSFILE_DEPENDENCY_SOURCE=AUTO
+cmake --build cpp/build/package --parallel
+cmake --install cpp/build/package
+cpack --config cpp/build/package/CPackConfig.cmake -G DEB
+# or: cpack --config cpp/build/package/CPackConfig.cmake -G RPM
+```
+
+`SYSTEM` can be used instead of `AUTO` when the build image provides every
+compatible dependency, including ANTLR4 4.9.x. `AUTO` is the reproducible
+release default and uses the verified source fallback for unavailable or
+incompatible distro versions.
+
+The repository workflow `Cpp-Packaging` builds and uploads native DEB and RPM
+artifacts on packaging-related changes. The DEB job runs on Ubuntu and the RPM
+job runs in Fedora, so each package is generated with its native dependency
+metadata tool.
+
+## macOS
+
+Homebrew is the native macOS distribution path. The formula is
+`packaging/homebrew/tsfile.rb`; it builds the same CMake install layout and
+tests both a C++ consumer and `tsfile-cli --version` after installation.
+
+```bash
+brew install ./packaging/homebrew/tsfile.rb
+```
+
+The stable formula URL and checksum must be updated to the ASF source archive
+when the first release containing this packaging work is published.
diff --git a/packaging/homebrew/tsfile.rb b/packaging/homebrew/tsfile.rb
new file mode 100644
index 000000000..3e96f3dca
--- /dev/null
+++ b/packaging/homebrew/tsfile.rb
@@ -0,0 +1,71 @@
+# typed: strict
+# frozen_string_literal: true
+
+#
+# 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
+#
+#     https://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.
+
+# Formula for the Apache TsFile C++ library.
+class Tsfile < Formula
+  desc "Columnar storage library for time series data"
+  homepage "https://tsfile.apache.org/";
+  # Replace this development snapshot with the ASF source archive and its
+  # release checksum when the first native TsFile release is published.
+  url 
"https://github.com/apache/tsfile/archive/d4c3c94690cf9af819bc26bec7114a0de7900359.tar.gz";
+  version "2.3.2"
+  sha256 "369cf43742601fe6107b299647d2c01f705bfc9ff111402d7b5ddb9b50f40f16"
+  license "Apache-2.0"
+  head "https://github.com/apache/tsfile.git";, branch: "develop"
+
+  depends_on "cmake" => :build
+  depends_on "lz4"
+  depends_on "simde"
+  depends_on "snappy"
+  depends_on "utf8cpp"
+  depends_on "zstd"
+
+  uses_from_macos "zlib"
+
+  def install
+    args = %W[
+      -DBUILD_TEST=OFF
+      -DBUILD_TOOLS=ON
+      -DENABLE_LZMA2=OFF
+      -DTSFILE_DEPENDENCY_SOURCE=AUTO
+      -DTSFILE_ENABLE_NATIVE_ARCH=OFF
+      -DCMAKE_INSTALL_RPATH=#{rpath}
+    ]
+
+    system "cmake", "-S", "cpp", "-B", "build", *args, *std_cmake_args
+    system "cmake", "--build", "build"
+    system "cmake", "--install", "build"
+  end
+
+  test do
+    (testpath / "test.cpp").write <<~CPP
+      #include <tsfile/cwrapper/tsfile_cwrapper.h>
+
+      int main() {
+        return TS_DATATYPE_INT32 == 1 ? 0 : 1;
+      }
+    CPP
+
+    system ENV.cxx, "-std=c++11", "test.cpp", "-I#{include}", "-L#{lib}",
+           "-Wl,-rpath,#{lib}", "-ltsfile", "-o", "test"
+    system "./test"
+    system bin / "tsfile-cli", "--version"
+  end
+end

Reply via email to