blob: f94ad70318e6a14e57e731b715e0ce36169a49fc [file]
# 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.
# Accumulate all dependencies to provide suitable static link parameters to the
# third party libraries.
set(ICEBERG_SYSTEM_DEPENDENCIES)
set(ICEBERG_ARROW_INSTALL_INTERFACE_LIBS)
set(ICEBERG_AWSSDK_BUNDLED FALSE)
if(ICEBERG_S3 AND ICEBERG_BUNDLE_AWSSDK)
if(NOT ICEBERG_BUILD_BUNDLE)
message(FATAL_ERROR "ICEBERG_BUNDLE_AWSSDK requires ICEBERG_BUILD_BUNDLE to be ON")
endif()
set(ICEBERG_AWSSDK_BUNDLED TRUE)
endif()
# Mirror the AWS SDK bundle/system policy for Thrift (used by the Hive catalog):
# ICEBERG_BUNDLE_THRIFT is the user's intent, ICEBERG_THRIFT_BUNDLED the resolved
# conclusion that the rest of the build keys off.
set(ICEBERG_THRIFT_BUNDLED FALSE)
if(ICEBERG_BUILD_HIVE AND ICEBERG_BUNDLE_THRIFT)
if(NOT ICEBERG_BUILD_BUNDLE)
message(FATAL_ERROR "ICEBERG_BUNDLE_THRIFT requires ICEBERG_BUILD_BUNDLE to be ON")
endif()
set(ICEBERG_THRIFT_BUNDLED TRUE)
endif()
set(ICEBERG_AWSSDK_COMPONENTS)
if(NOT ICEBERG_AWSSDK_BUNDLED)
if(ICEBERG_S3)
list(APPEND
ICEBERG_AWSSDK_COMPONENTS
core
config
s3
transfer
identity-management
sts)
elseif(ICEBERG_SIGV4)
list(APPEND ICEBERG_AWSSDK_COMPONENTS core)
endif()
endif()
# ----------------------------------------------------------------------
# AWS SDK for C++
function(resolve_aws_sdk_dependency)
if(NOT ICEBERG_AWSSDK_COMPONENTS)
return()
endif()
find_package(AWSSDK REQUIRED COMPONENTS ${ICEBERG_AWSSDK_COMPONENTS})
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES AWSSDK)
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
# Forwarded to find_dependency(AWSSDK ...) in iceberg-config.cmake.in so
# downstream installed builds load the same AWS SDK targets.
set(ICEBERG_FIND_EXTRA_ARGS_AWSSDK
"COMPONENTS;${ICEBERG_AWSSDK_COMPONENTS}"
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# Versions and URLs for toolchain builds
#
# The following environment variables can be set to customize dependency URLs:
#
# ICEBERG_ARROW_URL - Apache Arrow tarball URL
# ICEBERG_AVRO_URL - Apache Avro tarball URL
# ICEBERG_AVRO_GIT_URL - Apache Avro git repository URL
# ICEBERG_NANOARROW_URL - Nanoarrow tarball URL
# ICEBERG_CROARING_URL - CRoaring tarball URL
# ICEBERG_UTF8PROC_URL - utf8proc tarball URL
# ICEBERG_NLOHMANN_JSON_URL - nlohmann-json tarball URL
# ICEBERG_SPDLOG_URL - spdlog tarball URL
# ICEBERG_CPR_URL - cpr tarball URL
#
# Example usage:
# export ICEBERG_ARROW_URL="https://your-mirror.com/apache-arrow-25.0.0.tar.gz"
# cmake -S . -B build
#
set(ICEBERG_ARROW_BUILD_VERSION "25.0.0")
set(ICEBERG_ARROW_BUILD_SHA256_CHECKSUM
"12afc2dc8137bdd4a68876cec939f664c9d55cfc7b75f55b45163ebb4e344d81")
if(DEFINED ENV{ICEBERG_ARROW_URL})
set(ARROW_SOURCE_URL "$ENV{ICEBERG_ARROW_URL}")
else()
set(ARROW_SOURCE_URL
"https://www.apache.org/dyn/closer.lua?action=download&filename=/arrow/arrow-${ICEBERG_ARROW_BUILD_VERSION}/apache-arrow-${ICEBERG_ARROW_BUILD_VERSION}.tar.gz"
"https://downloads.apache.org/arrow/arrow-${ICEBERG_ARROW_BUILD_VERSION}/apache-arrow-${ICEBERG_ARROW_BUILD_VERSION}.tar.gz"
"https://archive.apache.org/dist/arrow/arrow-${ICEBERG_ARROW_BUILD_VERSION}/apache-arrow-${ICEBERG_ARROW_BUILD_VERSION}.tar.gz"
)
endif()
set(ICEBERG_NANOARROW_BUILD_VERSION "0.9.0")
set(ICEBERG_NANOARROW_BUILD_SHA256_CHECKSUM
"801200a0e95e869d5c4bdeb5b535dba58551482bb782b7dc8bd599c8b6e8cacf")
if(DEFINED ENV{ICEBERG_NANOARROW_URL})
set(NANOARROW_SOURCE_URL "$ENV{ICEBERG_NANOARROW_URL}")
else()
set(NANOARROW_SOURCE_URL
"https://www.apache.org/dyn/closer.lua?action=download&filename=/arrow/apache-arrow-nanoarrow-${ICEBERG_NANOARROW_BUILD_VERSION}/apache-arrow-nanoarrow-${ICEBERG_NANOARROW_BUILD_VERSION}.tar.gz"
"https://downloads.apache.org/arrow/apache-arrow-nanoarrow-${ICEBERG_NANOARROW_BUILD_VERSION}/apache-arrow-nanoarrow-${ICEBERG_NANOARROW_BUILD_VERSION}.tar.gz"
"https://archive.apache.org/dist/arrow/apache-arrow-nanoarrow-${ICEBERG_NANOARROW_BUILD_VERSION}/apache-arrow-nanoarrow-${ICEBERG_NANOARROW_BUILD_VERSION}.tar.gz"
)
endif()
set(ICEBERG_UTF8PROC_BUILD_VERSION "2.10.0")
set(ICEBERG_UTF8PROC_BUILD_SHA256_CHECKSUM
"276a37dc4d1dd24d7896826a579f4439d1e5fe33603add786bb083cab802e23e")
if(DEFINED ENV{ICEBERG_UTF8PROC_URL})
set(UTF8PROC_SOURCE_URL "$ENV{ICEBERG_UTF8PROC_URL}")
else()
# Use the release asset (stable bytes) rather than the auto-generated tag archive,
# whose contents GitHub does not guarantee.
set(UTF8PROC_SOURCE_URL
"https://github.com/JuliaStrings/utf8proc/releases/download/v${ICEBERG_UTF8PROC_BUILD_VERSION}/utf8proc-${ICEBERG_UTF8PROC_BUILD_VERSION}.tar.gz"
)
endif()
# ----------------------------------------------------------------------
# FetchContent
include(FetchContent)
set(FC_DECLARE_COMMON_OPTIONS)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
list(APPEND FC_DECLARE_COMMON_OPTIONS EXCLUDE_FROM_ALL TRUE)
endif()
macro(prepare_fetchcontent)
set(BUILD_SHARED_LIBS OFF)
set(BUILD_STATIC_LIBS ON)
set(CMAKE_COMPILE_WARNING_AS_ERROR FALSE)
set(CMAKE_EXPORT_NO_PACKAGE_REGISTRY TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Use "NEW" for CMP0077 by default.
#
# https://cmake.org/cmake/help/latest/policy/CMP0077.html
#
# option() honors normal variables.
set(CMAKE_POLICY_DEFAULT_CMP0077
NEW
CACHE STRING "")
endmacro()
# ----------------------------------------------------------------------
# Apache Arrow
# Find OpenSSL for table encryption. Called before Arrow is configured, so that the
# imported OpenSSL targets are visible to the vendored Arrow subdirectories.
macro(resolve_openssl_for_encryption)
if(APPLE
AND NOT DEFINED OPENSSL_ROOT_DIR
AND NOT DEFINED ENV{OPENSSL_ROOT_DIR})
# Homebrew's OpenSSL is keg-only and not on the default search path.
execute_process(COMMAND brew --prefix openssl@3
OUTPUT_VARIABLE ICEBERG_BREW_OPENSSL_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(ICEBERG_BREW_OPENSSL_PREFIX AND EXISTS "${ICEBERG_BREW_OPENSSL_PREFIX}")
set(OPENSSL_ROOT_DIR "${ICEBERG_BREW_OPENSSL_PREFIX}")
endif()
endif()
find_package(OpenSSL REQUIRED)
endmacro()
function(resolve_arrow_dependency)
if(ICEBERG_ENCRYPTION)
resolve_openssl_for_encryption()
endif()
prepare_fetchcontent()
# Prevent Arrow from injecting -Werror into CMAKE_CXX_FLAGS_DEBUG via
# arrow_add_werror_if_debug(). PRODUCTION level only adds standard warnings.
set(BUILD_WARNING_LEVEL PRODUCTION)
set(ARROW_BUILD_SHARED OFF)
set(ARROW_BUILD_STATIC ON)
# Work around undefined symbol: arrow::ipc::ReadSchema(arrow::io::InputStream*, arrow::ipc::DictionaryMemo*)
set(ARROW_IPC ON)
set(ARROW_FILESYSTEM ON)
set(ARROW_S3 ${ICEBERG_S3})
set(ARROW_JSON ON)
set(ARROW_PARQUET ON)
if(ICEBERG_ENCRYPTION)
# Parquet modular encryption, used for encrypted Iceberg data and delete files.
set(PARQUET_REQUIRE_ENCRYPTION ON)
endif()
set(ARROW_ENABLE_THREADING ON)
set(ARROW_SIMD_LEVEL "NONE")
set(ARROW_RUNTIME_SIMD_LEVEL "NONE")
set(ARROW_POSITION_INDEPENDENT_CODE ON)
set(ARROW_DEPENDENCY_SOURCE "BUNDLED")
set(ARROW_WITH_ZLIB ON)
# zstd is the default value of the write.parquet.compression-codec table property,
# so a vendored Arrow without it cannot write Iceberg's default Parquet files.
# Not required by modular encryption itself; parquet_encryption_test covers the
# encrypted-plus-zstd combination to keep the two features verified together.
set(ARROW_WITH_ZSTD ON)
if(ICEBERG_S3 AND NOT ICEBERG_AWSSDK_BUNDLED)
set(AWSSDK_SOURCE "SYSTEM")
endif()
set(ZLIB_SOURCE "SYSTEM")
set(ARROW_VERBOSE_THIRDPARTY_BUILD OFF)
set(CMAKE_CXX_STANDARD 20)
# Arrow's bundled Thrift download (Parquet requires Thrift, so this fires even
# for non-Hive builds) only lists the live Apache mirrors closer.lua / dlcdn,
# which drop older releases. Thrift 0.22.0 (Arrow 24.0.0's pinned version) has
# already been removed from them and now 404s, breaking every bundled build.
# Point Arrow at archive.apache.org, which retains all releases, mirroring the
# archive fallback already used for ARROW_SOURCE_URL / NANOARROW_SOURCE_URL.
# Keep the version in sync with Arrow's ARROW_THRIFT_BUILD_VERSION on upgrades.
if(NOT DEFINED ENV{ARROW_THRIFT_URL})
set(ENV{ARROW_THRIFT_URL}
"https://archive.apache.org/dist/thrift/0.22.0/thrift-0.22.0.tar.gz")
endif()
# FetchContent sets arrow_SOURCE_DIR to the tarball root (SOURCE_SUBDIR is cpp)
# and on CMake < 3.30 project(arrow) only sets it as a cache variable, so the
# normal variable takes precedence and Arrow looks for thirdparty/versions.txt
# outside cpp/ (see CMP0180). Patch Arrow to use ARROW_SOURCE_DIR instead.
# See https://github.com/apache/iceberg-cpp/issues/1028.
# TODO: remove once the upstream Arrow fix is released.
set(ARROW_PATCH_COMMAND)
find_program(PATCH patch)
if(PATCH)
set(ARROW_PATCH_COMMAND ${PATCH} -p1 -i)
else()
find_program(GIT git)
if(GIT)
set(ARROW_PATCH_COMMAND ${GIT} apply)
endif()
endif()
if(ARROW_PATCH_COMMAND)
list(APPEND ARROW_PATCH_COMMAND
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/arrow-thirdparty-dir.patch)
endif()
fetchcontent_declare(Arrow
${FC_DECLARE_COMMON_OPTIONS}
URL ${ARROW_SOURCE_URL}
URL_HASH "SHA256=${ICEBERG_ARROW_BUILD_SHA256_CHECKSUM}"
PATCH_COMMAND ${ARROW_PATCH_COMMAND} SOURCE_SUBDIR cpp
FIND_PACKAGE_ARGS CONFIG)
fetchcontent_makeavailable(Arrow)
if(arrow_SOURCE_DIR)
# Arrow's Parquet targets do not inherit the OpenSSL include directory when
# Arrow is vendored; add it for the Parquet encryption sources. Depending on the
# platform and build type, they are compiled in the object library or directly in
# the static/shared library. The bundle library links OpenSSL::Crypto.
#
# Only vendored targets are touched here. With system Arrow the same names can
# be ALIAS targets created by a parent project, and target_include_directories
# cannot modify an ALIAS.
if(ICEBERG_ENCRYPTION)
foreach(parquet_target parquet_objlib parquet_static parquet_shared)
if(TARGET ${parquet_target})
get_target_property(parquet_target_alias ${parquet_target} ALIASED_TARGET)
if(NOT parquet_target_alias)
target_include_directories(${parquet_target} PRIVATE ${OPENSSL_INCLUDE_DIR})
endif()
endif()
endforeach()
endif()
if(NOT TARGET Arrow::arrow_static)
add_library(Arrow::arrow_static INTERFACE IMPORTED)
target_link_libraries(Arrow::arrow_static INTERFACE arrow_static)
target_include_directories(Arrow::arrow_static
INTERFACE ${arrow_BINARY_DIR}/src
${arrow_SOURCE_DIR}/cpp/src)
endif()
if(NOT TARGET Parquet::parquet_static)
add_library(Parquet::parquet_static INTERFACE IMPORTED)
target_link_libraries(Parquet::parquet_static INTERFACE parquet_static)
target_include_directories(Parquet::parquet_static
INTERFACE ${arrow_BINARY_DIR}/src
${arrow_SOURCE_DIR}/cpp/src)
endif()
set(ARROW_VENDORED TRUE)
set_target_properties(arrow_static PROPERTIES OUTPUT_NAME "iceberg_vendored_arrow")
set_target_properties(parquet_static PROPERTIES OUTPUT_NAME
"iceberg_vendored_parquet")
install(TARGETS arrow_static parquet_static
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
if(TARGET arrow_bundled_dependencies)
message(STATUS "arrow_bundled_dependencies found")
# arrow_bundled_dependencies is only INSTALL_INTERFACE and will not be built by default.
# We need to add it as a dependency to arrow_static so that it will be built.
add_dependencies(arrow_static arrow_bundled_dependencies)
# We cannot install an IMPORTED target, so we need to install the library manually.
get_target_property(arrow_bundled_dependencies_location arrow_bundled_dependencies
IMPORTED_LOCATION)
install(FILES ${arrow_bundled_dependencies_location}
DESTINATION ${ICEBERG_INSTALL_LIBDIR})
endif()
# Arrow's exported static target interface may reference system libraries
# (e.g. Threads, OpenSSL, CURL, ZLIB) that consumers need to find.
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES Threads ZLIB)
if(ARROW_S3)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES OpenSSL CURL)
endif()
else()
set(ARROW_VENDORED FALSE)
find_package(Arrow CONFIG REQUIRED)
find_package(Parquet CONFIG REQUIRED)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES Arrow Parquet)
endif()
# The bundle exports OpenSSL::Crypto in its link interface for both vendored and
# system Arrow, so consumers must find OpenSSL in both modes.
if(ICEBERG_ENCRYPTION)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES OpenSSL)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(ARROW_VENDORED
${ARROW_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# Apache Avro
function(resolve_avro_dependency)
prepare_fetchcontent()
set(AVRO_USE_BOOST
OFF
CACHE BOOL "" FORCE)
set(AVRO_BUILD_EXECUTABLES
OFF
CACHE BOOL "" FORCE)
set(AVRO_BUILD_TESTS
OFF
CACHE BOOL "" FORCE)
if(DEFINED ENV{ICEBERG_AVRO_URL})
# Support custom tarball URL
fetchcontent_declare(avro-cpp
${FC_DECLARE_COMMON_OPTIONS}
URL $ENV{ICEBERG_AVRO_URL}
SOURCE_SUBDIR
lang/c++
FIND_PACKAGE_ARGS
NAMES
avro-cpp
CONFIG)
else()
if(DEFINED ENV{ICEBERG_AVRO_GIT_URL})
set(AVRO_GIT_REPOSITORY "$ENV{ICEBERG_AVRO_GIT_URL}")
else()
set(AVRO_GIT_REPOSITORY "https://github.com/apache/avro.git")
endif()
fetchcontent_declare(avro-cpp
${FC_DECLARE_COMMON_OPTIONS}
GIT_REPOSITORY ${AVRO_GIT_REPOSITORY}
GIT_TAG 3a207fa6e87c535b95eafca4cf5a6ef524d77df2
SOURCE_SUBDIR
lang/c++
FIND_PACKAGE_ARGS
NAMES
avro-cpp
CONFIG)
endif()
fetchcontent_makeavailable(avro-cpp)
if(avro-cpp_SOURCE_DIR)
if(NOT TARGET avro-cpp::avrocpp_static)
add_library(avro-cpp::avrocpp_static INTERFACE IMPORTED)
target_link_libraries(avro-cpp::avrocpp_static INTERFACE avrocpp_s)
target_include_directories(avro-cpp::avrocpp_static
INTERFACE ${avro-cpp_BINARY_DIR}
${avro-cpp_SOURCE_DIR}/lang/c++)
endif()
set(AVRO_VENDORED TRUE)
set_target_properties(avrocpp_s PROPERTIES OUTPUT_NAME "iceberg_vendored_avrocpp")
set_target_properties(avrocpp_s PROPERTIES POSITION_INDEPENDENT_CODE ON)
install(TARGETS avrocpp_s
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
# TODO: add vendored ZLIB and Snappy support
find_package(Snappy CONFIG)
if(Snappy_FOUND)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES Snappy)
endif()
else()
set(AVRO_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES avro-cpp)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(AVRO_VENDORED
${AVRO_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# Nanoarrow
# It is also possible to vendor nanoarrow using the bundled source code.
function(resolve_nanoarrow_dependency)
prepare_fetchcontent()
fetchcontent_declare(nanoarrow
${FC_DECLARE_COMMON_OPTIONS}
URL ${NANOARROW_SOURCE_URL}
URL_HASH "SHA256=${ICEBERG_NANOARROW_BUILD_SHA256_CHECKSUM}"
FIND_PACKAGE_ARGS
NAMES
nanoarrow
CONFIG)
fetchcontent_makeavailable(nanoarrow)
if(nanoarrow_SOURCE_DIR)
if(NOT TARGET nanoarrow::nanoarrow_static)
add_library(nanoarrow::nanoarrow_static INTERFACE IMPORTED)
target_link_libraries(nanoarrow::nanoarrow_static INTERFACE nanoarrow_static)
target_include_directories(nanoarrow::nanoarrow_static
INTERFACE ${nanoarrow_BINARY_DIR}
${nanoarrow_SOURCE_DIR})
endif()
set(NANOARROW_VENDORED TRUE)
set_target_properties(nanoarrow_static
PROPERTIES OUTPUT_NAME "iceberg_vendored_nanoarrow"
POSITION_INDEPENDENT_CODE ON)
install(TARGETS nanoarrow_static
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
else()
set(NANOARROW_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES nanoarrow)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(NANOARROW_VENDORED
${NANOARROW_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# CRoaring
function(resolve_croaring_dependency)
prepare_fetchcontent()
set(ENABLE_ROARING_TESTS OFF)
set(ENABLE_ROARING_MICROBENCHMARKS OFF)
if(DEFINED ENV{ICEBERG_CROARING_URL})
set(CROARING_URL "$ENV{ICEBERG_CROARING_URL}")
else()
set(CROARING_URL
"https://github.com/RoaringBitmap/CRoaring/archive/refs/tags/v4.7.1.tar.gz")
endif()
fetchcontent_declare(croaring
${FC_DECLARE_COMMON_OPTIONS}
URL ${CROARING_URL}
FIND_PACKAGE_ARGS
NAMES
roaring
CONFIG)
fetchcontent_makeavailable(croaring)
if(croaring_SOURCE_DIR)
if(NOT TARGET roaring::roaring)
add_library(roaring::roaring INTERFACE IMPORTED)
target_link_libraries(roaring::roaring INTERFACE roaring)
target_include_directories(roaring::roaring INTERFACE ${croaring_BINARY_DIR}
${croaring_SOURCE_DIR}/cpp)
endif()
set(CROARING_VENDORED TRUE)
set_target_properties(roaring PROPERTIES OUTPUT_NAME "iceberg_vendored_croaring"
POSITION_INDEPENDENT_CODE ON)
install(TARGETS roaring
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
else()
set(CROARING_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES roaring)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(CROARING_VENDORED
${CROARING_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# utf8proc
function(resolve_utf8proc_dependency)
prepare_fetchcontent()
# The vendored build needs no install rules; without this, CMake < 3.28 (where
# FetchContent has no EXCLUDE_FROM_ALL) would install utf8proc's headers and
# pkg-config file into the iceberg install prefix.
set(UTF8PROC_INSTALL OFF)
fetchcontent_declare(utf8proc
${FC_DECLARE_COMMON_OPTIONS}
URL ${UTF8PROC_SOURCE_URL}
URL_HASH "SHA256=${ICEBERG_UTF8PROC_BUILD_SHA256_CHECKSUM}"
FIND_PACKAGE_ARGS
NAMES
utf8proc
CONFIG)
fetchcontent_makeavailable(utf8proc)
if(utf8proc_SOURCE_DIR)
if(NOT TARGET utf8proc::utf8proc)
add_library(utf8proc::utf8proc INTERFACE IMPORTED)
target_link_libraries(utf8proc::utf8proc INTERFACE utf8proc)
target_include_directories(utf8proc::utf8proc INTERFACE ${utf8proc_SOURCE_DIR})
endif()
set(UTF8PROC_VENDORED TRUE)
# utf8proc's CMake puts a raw build-tree path in INTERFACE_INCLUDE_DIRECTORIES, which
# install(EXPORT) rejects. Wrap it in BUILD_INTERFACE so the export is valid; utf8proc
# is a private dependency, so installed consumers never need its headers.
set_target_properties(utf8proc
PROPERTIES OUTPUT_NAME "iceberg_vendored_utf8proc"
POSITION_INDEPENDENT_CODE ON
INTERFACE_INCLUDE_DIRECTORIES
"$<BUILD_INTERFACE:${utf8proc_SOURCE_DIR}>")
install(TARGETS utf8proc
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
else()
set(UTF8PROC_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES utf8proc)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(UTF8PROC_VENDORED
${UTF8PROC_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# nlohmann-json
function(resolve_nlohmann_json_dependency)
prepare_fetchcontent()
set(JSON_BuildTests
OFF
CACHE BOOL "" FORCE)
if(DEFINED ENV{ICEBERG_NLOHMANN_JSON_URL})
set(NLOHMANN_JSON_URL "$ENV{ICEBERG_NLOHMANN_JSON_URL}")
else()
set(NLOHMANN_JSON_URL
"https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz")
endif()
fetchcontent_declare(nlohmann_json
${FC_DECLARE_COMMON_OPTIONS}
URL ${NLOHMANN_JSON_URL}
FIND_PACKAGE_ARGS
NAMES
nlohmann_json
CONFIG)
fetchcontent_makeavailable(nlohmann_json)
if(nlohmann_json_SOURCE_DIR)
if(NOT TARGET nlohmann_json::nlohmann_json)
add_library(nlohmann_json::nlohmann_json INTERFACE IMPORTED)
target_link_libraries(nlohmann_json::nlohmann_json INTERFACE nlohmann_json)
target_include_directories(nlohmann_json::nlohmann_json
INTERFACE ${nlohmann_json_BINARY_DIR}
${nlohmann_json_SOURCE_DIR})
endif()
set(NLOHMANN_JSON_VENDORED TRUE)
set_target_properties(nlohmann_json
PROPERTIES OUTPUT_NAME "iceberg_vendored_nlohmann_json"
POSITION_INDEPENDENT_CODE ON)
if(MSVC_TOOLCHAIN)
set(NLOHMANN_NATVIS_FILE ${nlohmann_json_SOURCE_DIR}/nlohmann_json.natvis)
install(FILES ${NLOHMANN_NATVIS_FILE} DESTINATION .)
endif()
install(TARGETS nlohmann_json
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
else()
set(NLOHMANN_JSON_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES nlohmann_json)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(NLOHMANN_JSON_VENDORED
${NLOHMANN_JSON_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# spdlog
function(resolve_spdlog_dependency)
prepare_fetchcontent()
find_package(Threads REQUIRED)
set(SPDLOG_USE_STD_FORMAT
ON
CACHE BOOL "" FORCE)
set(SPDLOG_BUILD_PIC
ON
CACHE BOOL "" FORCE)
if(DEFINED ENV{ICEBERG_SPDLOG_URL})
set(SPDLOG_URL "$ENV{ICEBERG_SPDLOG_URL}")
else()
set(SPDLOG_URL "https://github.com/gabime/spdlog/archive/refs/tags/v1.15.3.tar.gz")
endif()
fetchcontent_declare(spdlog
${FC_DECLARE_COMMON_OPTIONS}
URL ${SPDLOG_URL}
FIND_PACKAGE_ARGS
NAMES
spdlog
CONFIG)
fetchcontent_makeavailable(spdlog)
if(spdlog_SOURCE_DIR)
set_target_properties(spdlog PROPERTIES OUTPUT_NAME "iceberg_vendored_spdlog"
POSITION_INDEPENDENT_CODE ON)
target_link_libraries(spdlog INTERFACE Threads::Threads)
install(TARGETS spdlog
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
set(SPDLOG_VENDORED TRUE)
else()
set(SPDLOG_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES spdlog)
endif()
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES Threads)
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(SPDLOG_VENDORED
${SPDLOG_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# zlib
function(resolve_zlib_dependency)
# use system zlib, zlib is required by arrow and avro
find_package(ZLIB REQUIRED)
if(ZLIB_FOUND)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES ZLIB)
message(STATUS "ZLIB_FOUND ZLIB_LIBRARIES:${ZLIB_LIBRARIES} ZLIB_INCLUDE_DIR:${ZLIB_INCLUDE_DIR}"
)
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
endif()
endfunction()
# ----------------------------------------------------------------------
# cpr (C++ Requests)
function(resolve_cpr_dependency)
prepare_fetchcontent()
set(CPR_BUILD_TESTS OFF)
set(CPR_ENABLE_CURL_HTTP_ONLY ON)
set(CPR_ENABLE_SSL ON)
set(CPR_USE_SYSTEM_CURL ON)
set(CPR_USE_EXISTING_CURL_TARGET ON)
if(DEFINED ENV{ICEBERG_CPR_URL})
set(CPR_URL "$ENV{ICEBERG_CPR_URL}")
else()
set(CPR_URL "https://github.com/libcpr/cpr/archive/refs/tags/1.14.1.tar.gz")
endif()
if(NOT TARGET CURL::libcurl)
find_package(CURL REQUIRED)
endif()
fetchcontent_declare(cpr
${FC_DECLARE_COMMON_OPTIONS}
URL ${CPR_URL}
FIND_PACKAGE_ARGS
NAMES
cpr
CONFIG)
fetchcontent_makeavailable(cpr)
if(cpr_SOURCE_DIR)
if(NOT TARGET cpr::cpr)
add_library(cpr::cpr INTERFACE IMPORTED)
target_link_libraries(cpr::cpr INTERFACE cpr)
target_include_directories(cpr::cpr INTERFACE ${cpr_BINARY_DIR}
${cpr_SOURCE_DIR}/include)
endif()
set(CPR_VENDORED TRUE)
set_target_properties(cpr PROPERTIES OUTPUT_NAME "iceberg_vendored_cpr"
POSITION_INDEPENDENT_CODE ON)
add_library(iceberg::cpr ALIAS cpr)
install(TARGETS cpr
EXPORT iceberg_targets
RUNTIME DESTINATION "${ICEBERG_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${ICEBERG_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${ICEBERG_INSTALL_LIBDIR}")
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES OpenSSL CURL)
else()
set(CPR_VENDORED FALSE)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES cpr)
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
set(CPR_VENDORED
${CPR_VENDORED}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# SQL catalog database connectors (sqlpp23)
#
# The SQL catalog talks to the database through a semantic `CatalogStore`
# interface (src/iceberg/catalog/sql/catalog_store.h). The built-in stores are
# implemented on top of sqlpp23, a header-only, compile-time type-safe SQL
# library. Each connector is opt-in and pulls in its native client library:
#
# ICEBERG_SQL_SQLITE -> sqlpp23::sqlite3 (SQLite::SQLite3)
# ICEBERG_SQL_POSTGRESQL -> sqlpp23::postgresql (PostgreSQL::PostgreSQL)
# ICEBERG_SQL_MYSQL -> sqlpp23::mysql (MySQL::MySQL)
#
# Users who inject their own `CatalogStore` do not need sqlpp23 or any connector.
function(resolve_sql_catalog_dependencies)
if(NOT ICEBERG_SQL_SQLITE
AND NOT ICEBERG_SQL_POSTGRESQL
AND NOT ICEBERG_SQL_MYSQL)
message(STATUS "SQL catalog: no built-in connectors enabled")
return()
endif()
if(CMAKE_VERSION VERSION_LESS 3.28)
message(FATAL_ERROR "Built-in SQL catalog connectors require CMake >= 3.28; disable "
"ICEBERG_SQL_SQLITE, ICEBERG_SQL_POSTGRESQL, and ICEBERG_SQL_MYSQL "
"or use CMake >= 3.28")
endif()
prepare_fetchcontent()
# sqlpp23 requires C++23 and CMake >= 3.28.
set(CMAKE_CXX_STANDARD 23)
# Header-only consumption; do not scan for C++20 modules.
set(BUILD_WITH_MODULES OFF)
# Let sqlpp23 verify and locate the native client libraries for the connectors
# we enable, exposing the sqlpp23::<connector> targets.
set(BUILD_SQLITE3_CONNECTOR ${ICEBERG_SQL_SQLITE})
set(BUILD_POSTGRESQL_CONNECTOR ${ICEBERG_SQL_POSTGRESQL})
set(BUILD_MYSQL_CONNECTOR ${ICEBERG_SQL_MYSQL})
if(DEFINED ENV{ICEBERG_SQLPP23_URL})
set(SQLPP23_URL "$ENV{ICEBERG_SQLPP23_URL}")
else()
set(SQLPP23_URL "https://github.com/rbock/sqlpp23/archive/refs/tags/0.69.tar.gz")
endif()
fetchcontent_declare(sqlpp23
${FC_DECLARE_COMMON_OPTIONS}
URL ${SQLPP23_URL}
FIND_PACKAGE_ARGS
NAMES
Sqlpp23
CONFIG)
fetchcontent_makeavailable(sqlpp23)
# sqlpp23 locates the native client libraries within its own subdirectory
# scope. Re-run find_package with GLOBAL so the imported targets are visible
# where the SQL catalog library is defined, and record them as downstream
# system dependencies for the installed interface.
if(ICEBERG_SQL_SQLITE)
find_package(SQLite3 REQUIRED GLOBAL)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES SQLite3)
message(STATUS "SQL catalog: SQLite connector enabled (sqlpp23::sqlite3)")
endif()
if(ICEBERG_SQL_POSTGRESQL)
find_package(PostgreSQL REQUIRED GLOBAL)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES PostgreSQL)
message(STATUS "SQL catalog: PostgreSQL connector enabled (sqlpp23::postgresql)")
endif()
if(ICEBERG_SQL_MYSQL)
# MySQL has no standard CMake module; reuse the one sqlpp23 ships.
if(sqlpp23_SOURCE_DIR)
list(APPEND CMAKE_MODULE_PATH "${sqlpp23_SOURCE_DIR}/cmake/modules")
endif()
find_package(MySQL REQUIRED GLOBAL)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES MySQL)
message(STATUS "SQL catalog: MySQL connector enabled (sqlpp23::mysql)")
endif()
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
endfunction()
# ----------------------------------------------------------------------
# Zstd
function(resolve_zstd_dependency)
find_package(zstd CONFIG)
if(zstd_FOUND)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES zstd)
message(STATUS "Found zstd, version: ${zstd_VERSION}")
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
endif()
endfunction()
# ----------------------------------------------------------------------
# GoogleTest (tests only)
#
# GTest is only consumed by the unit tests; it is neither installed nor exported
# as a system dependency, so it does not touch ICEBERG_SYSTEM_DEPENDENCIES.
function(resolve_gtest_dependency)
prepare_fetchcontent()
set(INSTALL_GTEST
OFF
CACHE BOOL "" FORCE)
fetchcontent_declare(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG b514bdc898e2951020cbdca1304b75f5950d1f59 # release-1.15.2
FIND_PACKAGE_ARGS
NAMES
GTest)
fetchcontent_makeavailable(googletest)
endfunction()
# ----------------------------------------------------------------------
# Google Benchmark (benchmarks only)
#
# Like GTest, benchmark is only consumed by the benchmark targets and is neither
# installed nor exported as a system dependency.
function(resolve_benchmark_dependency)
prepare_fetchcontent()
set(BENCHMARK_ENABLE_GTEST_TESTS
OFF
CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_INSTALL
OFF
CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_TESTING
OFF
CACHE BOOL "" FORCE)
fetchcontent_declare(google_benchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG a4cf155615c63e019ae549e31703bf367df5b471 # v1.8.4
FIND_PACKAGE_ARGS
NAMES
benchmark
CONFIG)
fetchcontent_makeavailable(google_benchmark)
endfunction()
resolve_zlib_dependency()
resolve_nanoarrow_dependency()
resolve_croaring_dependency()
resolve_utf8proc_dependency()
resolve_nlohmann_json_dependency()
if(ICEBERG_SPDLOG)
resolve_spdlog_dependency()
endif()
if(ICEBERG_S3 OR ICEBERG_SIGV4)
if(ICEBERG_SIGV4 AND NOT ICEBERG_BUILD_REST)
message(FATAL_ERROR "ICEBERG_SIGV4 requires ICEBERG_BUILD_REST to be ON")
endif()
resolve_aws_sdk_dependency()
endif()
if(ICEBERG_BUILD_BUNDLE)
resolve_arrow_dependency()
# An installed Arrow does not export its bundled AWS SDK, which the S3 FileIO
# uses directly. Use the system SDK, which must match Arrow's.
if(ICEBERG_AWSSDK_BUNDLED AND NOT ARROW_VENDORED)
message(STATUS "Using the system AWS SDK, since Arrow is provided")
set(ICEBERG_AWSSDK_BUNDLED FALSE)
set(ICEBERG_AWSSDK_COMPONENTS core)
resolve_aws_sdk_dependency()
endif()
resolve_avro_dependency()
resolve_zstd_dependency()
endif()
if(ICEBERG_BUILD_REST)
resolve_cpr_dependency()
endif()
if(ICEBERG_BUILD_SQL_CATALOG)
resolve_sql_catalog_dependencies()
endif()
# ----------------------------------------------------------------------
# Thrift (Hive catalog)
#
# Provide a `thrift::thrift` target for iceberg_hive's generated Hive Metastore
# bindings, either bundled (from Arrow's build) or from a system install. Must
# run after resolve_arrow_dependency() so the bundled `thrift` target exists.
function(resolve_thrift_dependency)
if(NOT ICEBERG_BUILD_HIVE)
return()
endif()
if(ICEBERG_THRIFT_BUNDLED)
# Expose bundled Thrift and its include paths to Hive.
if(TARGET thrift AND NOT TARGET thrift::thrift)
add_library(thrift::thrift INTERFACE IMPORTED GLOBAL)
target_link_libraries(thrift::thrift INTERFACE thrift)
endif()
else()
# System Thrift, located by cmake_modules/FindThriftAlt.cmake (MODULE mode),
# which provides the `thrift::thrift` target iceberg_hive expects. Record it
# as a system dependency so downstream find_package(Iceberg) re-finds it.
find_package(ThriftAlt MODULE REQUIRED GLOBAL)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES ThriftAlt)
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
endif()
endfunction()
if(ICEBERG_BUILD_HIVE)
resolve_thrift_dependency()
endif()
# ----------------------------------------------------------------------
# Test and benchmark dependencies
#
# These are build-only tools, pulled in after the library dependencies and only
# when the corresponding build option is enabled.
if(ICEBERG_BUILD_TESTS)
resolve_gtest_dependency()
endif()
if(ICEBERG_BUILD_BENCHMARKS)
resolve_benchmark_dependency()
endif()