Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 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.

# Required at repository root for root module mode (`bazel_dep(name = "fluss-cpp", ...)`).
# Consumer examples use `local_path_override(..., path = "/path/to/fluss-rust")`, so
# Bazel resolves the module from the repository root. This also matches the Rust
# workspace layout used by `bindings/cpp` during cargo-based Bazel/CMake builds.
# `0.0.0` is a local-development placeholder in this repository branch.
# Consumers should depend on a published release version.
module(
name = "fluss-cpp",
version = "0.0.0",
)

bazel_dep(name = "rules_cc", version = "0.0.17")
bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "rules_foreign_cc", version = "0.15.1")
bazel_dep(name = "rules_python", version = "1.2.0")

python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(python_version = "3.12")
use_repo(python, "python_3_12")

foreign_cc_tools = use_extension("@rules_foreign_cc//foreign_cc:extensions.bzl", "tools")
use_repo(
foreign_cc_tools,
"cmake_3.31.8_toolchains",
"cmake_src",
"ninja_1.13.0_toolchains",
"ninja_build_src",
"rules_foreign_cc_framework_toolchains",
)

register_toolchains(
"@rules_foreign_cc_framework_toolchains//:all",
"@cmake_3.31.8_toolchains//:all",
"@ninja_1.13.0_toolchains//:all",
"@python_3_12//:all",
"@rules_foreign_cc//toolchains:all",
)

cpp_sdk = use_extension("//bindings/cpp/bazel/cpp:deps.bzl", "cpp_sdk")
cpp_sdk.config(
mode = "build",
arrow_cpp_version = "19.0.1",
protobuf_version = "3.25.5",
ep_cmake_ranlib = "/usr/bin/ranlib",
ep_cmake_ar = "/usr/bin/ar",
ep_cmake_nm = "/usr/bin/nm",
)
use_repo(cpp_sdk, "apache_arrow_cpp")
10 changes: 10 additions & 0 deletions bindings/cpp/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ bazel-testlogs
bazel-cpp
bazel-*
MODULE.bazel.lock

# Keep versioned Bazel consumer examples (name starts with bazel-).
!examples/bazel-consumer/
!examples/bazel-consumer/**
# `build/` is ignored globally above; keep this fixture path visible.
!examples/bazel-consumer/build/
!examples/bazel-consumer/build/**
examples/bazel-consumer/**/MODULE.bazel.lock
examples/bazel-consumer/**/bazel-*
examples/bazel-consumer/**/tmp.log
46 changes: 37 additions & 9 deletions bindings/cpp/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

licenses(["notice"])

load("@rules_cc//cc:defs.bzl", "cc_library", "cc_binary")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_import", "cc_library")

config_setting(
name = "debug_mode",
Expand All @@ -34,6 +34,37 @@ config_setting(
values = {"compilation_mode": "opt"},
)

_PROTOC_SETUP_SNIPPET = """
set -e
if [ -n "$${CARGO:-}" ]; then
if [ ! -x "$$CARGO" ]; then
echo "Error: CARGO is set but not executable: $$CARGO" >&2
exit 1
fi
CARGO_BIN="$$CARGO"
else
CARGO_BIN=$$(command -v cargo || true)
if [ -z "$$CARGO_BIN" ]; then
echo "Error: cargo not found in PATH and CARGO is not set" >&2
exit 1
fi
fi
if [ -n "$${PROTOC:-}" ]; then
if [ ! -x "$$PROTOC" ]; then
echo "Error: PROTOC is set but not executable: $$PROTOC" >&2
exit 1
fi
export PROTOC
else
PROTOC_BIN=$$(command -v protoc || true)
if [ -z "$$PROTOC_BIN" ]; then
echo "Error: protoc not found in PATH and PROTOC is not set" >&2
exit 1
fi
export PROTOC="$$PROTOC_BIN"
fi
"""

genrule(
name = "cargo_build_debug",
srcs = glob([
Expand All @@ -47,8 +78,7 @@ genrule(
"src/lib.rs_debug.h",
"cxxbridge/rust/cxx_debug.h",
],
cmd = """
set -e
cmd = _PROTOC_SETUP_SNIPPET + """
EXECROOT=$$(pwd)
OUTPUT_LIB=$(location rust_lib_debug.a)
OUTPUT_CC=$(location rust_bridge_cc_debug.cc)
Expand All @@ -66,7 +96,7 @@ genrule(
exit 1
fi
cd $$WORKSPACE_ROOT
cargo build --manifest-path $$CARGO_DIR/Cargo.toml
"$$CARGO_BIN" build --manifest-path $$CARGO_DIR/Cargo.toml
CARGO_TARGET_DIR=$$WORKSPACE_ROOT/target
# cxxbridge uses the Cargo package name (with hyphen): fluss-cpp
RUST_BRIDGE_DIR=$$CARGO_TARGET_DIR/cxxbridge/fluss-cpp/src
Expand Down Expand Up @@ -114,8 +144,7 @@ genrule(
"src/lib.rs_release.h",
"cxxbridge/rust/cxx_release.h",
],
cmd = """
set -e
cmd = _PROTOC_SETUP_SNIPPET + """
EXECROOT=$$(pwd)
OUTPUT_LIB=$(location rust_lib_release.a)
OUTPUT_CC=$(location rust_bridge_cc_release.cc)
Expand All @@ -133,7 +162,7 @@ genrule(
exit 1
fi
cd $$WORKSPACE_ROOT
cargo build --release --manifest-path $$CARGO_DIR/Cargo.toml
"$$CARGO_BIN" build --release --manifest-path $$CARGO_DIR/Cargo.toml
CARGO_TARGET_DIR=$$WORKSPACE_ROOT/target
# cxxbridge uses the Cargo package name (with hyphen): fluss-cpp
RUST_BRIDGE_DIR=$$CARGO_TARGET_DIR/cxxbridge/fluss-cpp/src
Expand Down Expand Up @@ -252,7 +281,6 @@ cc_library(
"src/admin.cpp",
"src/connection.cpp",
"src/table.cpp",
":rust_bridge_cc_unified",
],
hdrs = [
"include/fluss.hpp",
Expand Down Expand Up @@ -303,6 +331,7 @@ cc_library(
}),
deps = [
":rust_lib",
"//bindings/cpp/bazel/cpp:arrow_cpp_dep",
],
visibility = ["//visibility:public"],
)
Expand Down Expand Up @@ -405,4 +434,3 @@ cc_binary(
}),
visibility = ["//visibility:public"],
)

141 changes: 132 additions & 9 deletions bindings/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,21 @@ include(FetchContent)
set(FLUSS_GOOGLETEST_VERSION 1.15.2 CACHE STRING "version of GoogleTest")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_package(Threads REQUIRED)
set(FLUSS_CPP_DEP_MODE "system" CACHE STRING "Dependency provisioning mode for fluss-cpp (system|build)")
set_property(CACHE FLUSS_CPP_DEP_MODE PROPERTY STRINGS system build)
set(FLUSS_CPP_ARROW_VERSION "19.0.1" CACHE STRING "Arrow C++ version baseline for fluss-cpp")
set(FLUSS_CPP_PROTOBUF_VERSION "3.25.5" CACHE STRING "Protobuf/protoc version baseline for fluss-cpp")
set(FLUSS_CPP_ARROW_SYSTEM_ROOT "" CACHE PATH "Optional Arrow installation prefix for system mode")
set(FLUSS_CPP_ARROW_SOURCE_URL
"https://github.com/apache/arrow/archive/refs/tags/apache-arrow-19.0.1.tar.gz"
CACHE STRING
"Arrow source archive URL used in build mode")
set(FLUSS_CPP_ARROW_SOURCE_SHA256
"4c898504958841cc86b6f8710ecb2919f96b5e10fa8989ac10ac4fca8362d86a"
CACHE STRING
"SHA256 for the Arrow source archive used in build mode")

find_package(Arrow REQUIRED)
find_package(Threads REQUIRED)

if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
Expand All @@ -47,11 +59,117 @@ if (FLUSS_DEV)
set(FLUSS_ENABLE_TESTING ON)
endif()

if (NOT FLUSS_CPP_DEP_MODE STREQUAL "system" AND NOT FLUSS_CPP_DEP_MODE STREQUAL "build")
message(FATAL_ERROR "Unsupported FLUSS_CPP_DEP_MODE='${FLUSS_CPP_DEP_MODE}'. Expected 'system' or 'build'.")
endif()

find_program(FLUSS_PROTOC_EXECUTABLE NAMES protoc)
if (NOT FLUSS_PROTOC_EXECUTABLE)
message(FATAL_ERROR "protoc not found. Install protoc or set it in PATH. (Fluss baseline: ${FLUSS_CPP_PROTOBUF_VERSION})")
endif()

if (DEFINED ENV{CARGO} AND NOT "$ENV{CARGO}" STREQUAL "" AND EXISTS "$ENV{CARGO}")
set(FLUSS_CARGO_EXECUTABLE "$ENV{CARGO}")
else()
if (DEFINED ENV{CARGO} AND NOT "$ENV{CARGO}" STREQUAL "")
get_filename_component(_FLUSS_CARGO_HINT_DIR "$ENV{CARGO}" DIRECTORY)
endif()
find_program(FLUSS_CARGO_EXECUTABLE NAMES cargo HINTS "${_FLUSS_CARGO_HINT_DIR}")
endif()
if (NOT FLUSS_CARGO_EXECUTABLE)
message(FATAL_ERROR "cargo not found. Install Rust toolchain or set CARGO/PATH.")
endif()

execute_process(
COMMAND ${FLUSS_PROTOC_EXECUTABLE} --version
OUTPUT_VARIABLE FLUSS_PROTOC_VERSION_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)" FLUSS_PROTOC_VERSION "${FLUSS_PROTOC_VERSION_OUTPUT}")
set(FLUSS_PROTOC_VERSION_NORM "${FLUSS_PROTOC_VERSION}")
set(FLUSS_CPP_PROTOBUF_VERSION_NORM "${FLUSS_CPP_PROTOBUF_VERSION}")
string(REGEX REPLACE "^3\\." "" FLUSS_PROTOC_VERSION_NORM "${FLUSS_PROTOC_VERSION_NORM}")
string(REGEX REPLACE "^3\\." "" FLUSS_CPP_PROTOBUF_VERSION_NORM "${FLUSS_CPP_PROTOBUF_VERSION_NORM}")
if (FLUSS_PROTOC_VERSION AND
NOT FLUSS_PROTOC_VERSION VERSION_EQUAL FLUSS_CPP_PROTOBUF_VERSION AND
NOT FLUSS_PROTOC_VERSION_NORM VERSION_EQUAL FLUSS_CPP_PROTOBUF_VERSION_NORM)
message(WARNING
"protoc version (${FLUSS_PROTOC_VERSION}) does not match Fluss baseline "
"(${FLUSS_CPP_PROTOBUF_VERSION}). Build may still work, but this is outside the tested baseline.")
endif()

message(STATUS "Fluss C++ dependency mode: ${FLUSS_CPP_DEP_MODE}")
message(STATUS "Fluss C++ protoc executable: ${FLUSS_PROTOC_EXECUTABLE} (${FLUSS_PROTOC_VERSION_OUTPUT})")
message(STATUS "Fluss C++ cargo executable: ${FLUSS_CARGO_EXECUTABLE}")

if (FLUSS_CPP_DEP_MODE STREQUAL "system")
if (FLUSS_CPP_ARROW_SYSTEM_ROOT)
list(APPEND CMAKE_PREFIX_PATH "${FLUSS_CPP_ARROW_SYSTEM_ROOT}")
set(Arrow_ROOT "${FLUSS_CPP_ARROW_SYSTEM_ROOT}")
endif()

find_package(Arrow REQUIRED)

if (DEFINED Arrow_VERSION AND Arrow_VERSION AND NOT Arrow_VERSION VERSION_EQUAL FLUSS_CPP_ARROW_VERSION)
message(WARNING
"Arrow version (${Arrow_VERSION}) does not match Fluss baseline "
"(${FLUSS_CPP_ARROW_VERSION}). Build may still work, but this is outside the tested baseline.")
endif()
else()
# Build mode: provision Arrow C++ from source in-tree.
set(ARROW_BUILD_SHARED ON CACHE BOOL "" FORCE)
set(ARROW_BUILD_STATIC OFF CACHE BOOL "" FORCE)
set(ARROW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(ARROW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(ARROW_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
set(ARROW_BUILD_INTEGRATION OFF CACHE BOOL "" FORCE)
set(ARROW_BUILD_UTILITIES OFF CACHE BOOL "" FORCE)
set(ARROW_COMPUTE OFF CACHE BOOL "" FORCE)
set(ARROW_CSV OFF CACHE BOOL "" FORCE)
set(ARROW_DATASET OFF CACHE BOOL "" FORCE)
set(ARROW_FILESYSTEM OFF CACHE BOOL "" FORCE)
set(ARROW_JSON OFF CACHE BOOL "" FORCE)
set(ARROW_PARQUET OFF CACHE BOOL "" FORCE)
set(ARROW_IPC ON CACHE BOOL "" FORCE)
# Reduce third-party sub-build complexity in build mode.
set(ARROW_JEMALLOC OFF CACHE BOOL "" FORCE)
set(ARROW_MIMALLOC OFF CACHE BOOL "" FORCE)
set(ARROW_DEPENDENCY_SOURCE BUNDLED CACHE STRING "" FORCE)
set(ARROW_SIMD_LEVEL NONE CACHE STRING "" FORCE)
set(ARROW_RUNTIME_SIMD_LEVEL NONE CACHE STRING "" FORCE)

FetchContent_Declare(
apache_arrow_src
URL ${FLUSS_CPP_ARROW_SOURCE_URL}
URL_HASH SHA256=${FLUSS_CPP_ARROW_SOURCE_SHA256}
SOURCE_SUBDIR cpp
)
FetchContent_MakeAvailable(apache_arrow_src)
Comment on lines 142 to 148
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FetchContent_Declare(apache_arrow_src ...) pulls the Arrow C++ source from ${FLUSS_CPP_ARROW_SOURCE_URL} without specifying a hash or other integrity verification. If the download is tampered with (e.g., compromised mirror, DNS poisoning, or MITM), CMake will build and link against attacker-controlled Arrow code in your C++ bindings. Add an integrity check (such as URL_HASH with a pinned digest) for the Arrow archive to ensure the downloaded contents are exactly the expected release.

Copilot uses AI. Check for mistakes.
set(FLUSS_CPP_ARROW_EXTRA_INCLUDE_DIRS
"${apache_arrow_src_SOURCE_DIR}/cpp/src"
"${apache_arrow_src_BINARY_DIR}/src")

if (TARGET arrow_shared AND NOT TARGET Arrow::arrow_shared)
add_library(Arrow::arrow_shared ALIAS arrow_shared)
endif()
if (NOT TARGET Arrow::arrow_shared)
message(FATAL_ERROR "Arrow build mode did not produce target Arrow::arrow_shared (or arrow_shared).")
endif()
endif()

# Get cargo target dir
execute_process(COMMAND cargo locate-project --workspace --message-format plain
OUTPUT_VARIABLE CARGO_TARGET_DIR
execute_process(COMMAND ${FLUSS_CARGO_EXECUTABLE} locate-project --workspace --message-format plain
OUTPUT_VARIABLE CARGO_MANIFEST_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
string(REGEX REPLACE "/Cargo.toml\n$" "/target" CARGO_TARGET_DIR "${CARGO_TARGET_DIR}")
if (NOT CARGO_MANIFEST_PATH)
message(FATAL_ERROR
"Failed to resolve Cargo workspace target dir via '${FLUSS_CARGO_EXECUTABLE} locate-project'. "
"Check Rust toolchain installation and PATH/CARGO.")
endif()
get_filename_component(CARGO_WORKSPACE_DIR "${CARGO_MANIFEST_PATH}" DIRECTORY)
set(CARGO_TARGET_DIR "${CARGO_WORKSPACE_DIR}/target")

set(CARGO_MANIFEST ${PROJECT_SOURCE_DIR}/Cargo.toml)
set(RUST_SOURCE_FILE ${PROJECT_SOURCE_DIR}/src/lib.rs)
Expand All @@ -77,7 +195,7 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
endif()

add_custom_target(cargo_build
COMMAND cargo build --manifest-path ${CARGO_MANIFEST} ${CARGO_BUILD_FLAGS}
COMMAND ${CMAKE_COMMAND} -E env PROTOC=${FLUSS_PROTOC_EXECUTABLE} ${FLUSS_CARGO_EXECUTABLE} build --manifest-path ${CARGO_MANIFEST} ${CARGO_BUILD_FLAGS}
BYPRODUCTS ${RUST_BRIDGE_CPP} ${RUST_LIB} ${RUST_HEADER_FILE}
DEPENDS ${RUST_SOURCE_FILE}
USES_TERMINAL
Expand All @@ -88,6 +206,9 @@ add_library(fluss_cpp STATIC ${CPP_SOURCE_FILE} ${RUST_BRIDGE_CPP})
target_sources(fluss_cpp PUBLIC ${CPP_HEADER_FILE})
target_sources(fluss_cpp PRIVATE ${RUST_HEADER_FILE})
target_include_directories(fluss_cpp PUBLIC ${CPP_INCLUDE_DIR})
if (FLUSS_CPP_ARROW_EXTRA_INCLUDE_DIRS)
target_include_directories(fluss_cpp PUBLIC ${FLUSS_CPP_ARROW_EXTRA_INCLUDE_DIRS})
endif()
target_link_libraries(fluss_cpp PUBLIC ${RUST_LIB})
target_link_libraries(fluss_cpp PRIVATE ${CMAKE_DL_LIBS} Threads::Threads)
target_link_libraries(fluss_cpp PUBLIC Arrow::arrow_shared)
Expand All @@ -114,9 +235,11 @@ target_link_libraries(fluss_cpp_kv_example PRIVATE Arrow::arrow_shared)
target_compile_definitions(fluss_cpp_kv_example PRIVATE ARROW_FOUND)
target_include_directories(fluss_cpp_kv_example PUBLIC ${CPP_INCLUDE_DIR})

set_target_properties(fluss_cpp
PROPERTIES ADDITIONAL_CLEAN_FILES ${CARGO_TARGET_DIR}
)
if (CARGO_TARGET_DIR)
set_target_properties(fluss_cpp
PROPERTIES ADDITIONAL_CLEAN_FILES "${CARGO_TARGET_DIR}"
)
endif()
add_dependencies(fluss_cpp cargo_build)

if (FLUSS_ENABLE_ADDRESS_SANITIZER)
Expand Down
26 changes: 26 additions & 0 deletions bindings/cpp/bazel/cpp/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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.

package(default_visibility = ["//visibility:public"])

# Stable indirection target for the Arrow C++ dependency. The implementation
# repo name can change across modes (registry/build/system) without touching
# bindings/cpp/BUILD.bazel.
alias(
name = "arrow_cpp_dep",
actual = "@apache_arrow_cpp//:arrow_cpp",
)
Loading