diff --git a/.github/workflows/ci_bindings_c.yml b/.github/workflows/ci_bindings_c.yml index 33b2ecbcc910..28cd852b6827 100644 --- a/.github/workflows/ci_bindings_c.yml +++ b/.github/workflows/ci_bindings_c.yml @@ -44,31 +44,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install gtest manually - run: | - sudo apt-get update - sudo apt-get install libgtest-dev valgrind - cd /usr/src/gtest - sudo cmake CMakeLists.txt - sudo make - sudo cp lib/*.a /usr/lib - sudo ln -s /usr/lib/libgtest.a /usr/local/lib/libgtest.a - sudo ln -s /usr/lib/libgtest_main.a /usr/local/lib/libgtest_main.a - name: Setup Rust toolchain uses: ./.github/actions/setup - name: Build C binding working-directory: "bindings/c" - run: make build + run: | + mkdir build && cd build + cmake .. -DTEST_ENABLE_ASAN=ON + make -j$(nproc) - name: Check diff run: git diff --exit-code - name: Build and Run tests working-directory: "bindings/c" - run: make test - - - name: Build and Run memory-leak tests - working-directory: "bindings/c" - run: make memory_leak + run: ./build/tests diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt new file mode 100644 index 000000000000..3c5a54019f4e --- /dev/null +++ b/bindings/c/CMakeLists.txt @@ -0,0 +1,90 @@ +# 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. + +cmake_minimum_required(VERSION 3.22) +project(opendal-c) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +option(TEST_ENABLE_ASAN "Enable AddressSanitizer for tests" OFF) +set(GOOGLETEST_VERSION 1.15.2) + +# force the compiler to support these standards +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# for GoogleTest, it should be no less than C++14 +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# fetch google test via GitHub +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v${GOOGLETEST_VERSION}.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +set(CARGO_DIST_DIR "${PROJECT_SOURCE_DIR}/target/debug") +if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CARGO_BUILD_TYPE "--release") + set(CARGO_DIST_DIR "${PROJECT_SOURCE_DIR}/target/release") +endif() + +set(OPENDAL_STATIC_LIB "${CARGO_DIST_DIR}/libopendal_c.a") +set(OPENDAL_SHARED_LIB "${CARGO_DIST_DIR}/libopendal_c.so") +message(NOTICE "-- OpenDAL C static lib: ${OPENDAL_STATIC_LIB}") +message(NOTICE "-- OpenDAL C shared lib: ${OPENDAL_SHARED_LIB}") + +# custom target for cargo build +add_custom_target(cargo_build + COMMAND sh -c "cargo build ${CARGO_BUILD_TYPE}" + BYPRODUCTS ${OPENDAL_STATIC_LIB} ${OPENDAL_SHARED_LIB} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} +) + +# cmake target for static lib +add_library(opendal_c_static INTERFACE) +target_link_libraries(opendal_c_static INTERFACE ${OPENDAL_STATIC_LIB}) +target_include_directories(opendal_c_static INTERFACE include) +add_dependencies(opendal_c_static cargo_build) + +# cmake target for shared lib +add_library(opendal_c_shared INTERFACE) +target_link_libraries(opendal_c_shared INTERFACE ${OPENDAL_SHARED_LIB}) +target_include_directories(opendal_c_shared INTERFACE include) +add_dependencies(opendal_c_shared cargo_build) + +# example targets +add_executable(basic examples/basic.c) +target_link_libraries(basic opendal_c_shared) + +add_executable(error_handle examples/error_handle.c) +target_link_libraries(error_handle opendal_c_shared) + +# test targets +file(GLOB TEST_SRCS tests/*.cpp) +add_executable(tests ${TEST_SRCS}) +target_link_libraries(tests opendal_c_shared gtest_main) +if (TEST_ENABLE_ASAN) + target_compile_options(tests PRIVATE -fsanitize=address) + target_link_options(tests PRIVATE -fsanitize=address) +endif() diff --git a/bindings/c/Makefile b/bindings/c/Makefile index a9b13d842e56..5acf1e908d22 100644 --- a/bindings/c/Makefile +++ b/bindings/c/Makefile @@ -15,75 +15,17 @@ # specific language governing permissions and limitations # under the License. -RPATH=$(PWD)/target/debug -OBJ_DIR=./build DOC_DIR=./docs -CCFLAGS=-I./include -CXXFLAGS=-I./include -std=c++14 -LDFLAGS=-L$(RPATH) -Wl,-rpath,$(RPATH) - -LIBS=-lopendal_c -lgtest -lpthread - -VALGRIND=valgrind --error-exitcode=1 --leak-check=full -- - -.PHONY: all -all: build test examples - .PHONY: format format: cargo fmt find . -name '*.cpp' -exec clang-format -i --style=WebKit --verbose {} \; find . -name '*.c' -exec clang-format -i --style=WebKit --verbose {} \; -.PHONY: build -build: - mkdir -p $(OBJ_DIR) - cargo build - -.PHONY: test -test: - $(CXX) tests/bdd.cpp -o $(OBJ_DIR)/bdd $(CXXFLAGS) $(LDFLAGS) $(LIBS) - $(CXX) tests/list.cpp -o $(OBJ_DIR)/list $(CXXFLAGS) $(LDFLAGS) $(LIBS) - $(CXX) tests/error_msg.cpp -o $(OBJ_DIR)/error_msg $(CXXFLAGS) $(LDFLAGS) $(LIBS) - $(CXX) tests/opinfo.cpp -o $(OBJ_DIR)/opinfo $(CXXFLAGS) $(LDFLAGS) $(LIBS) - $(OBJ_DIR)/bdd - $(OBJ_DIR)/list - $(OBJ_DIR)/error_msg - $(OBJ_DIR)/opinfo - -.PHONY: test_memory_leak -memory_leak: - $(VALGRIND) $(OBJ_DIR)/bdd - $(VALGRIND) $(OBJ_DIR)/list - $(VALGRIND) $(OBJ_DIR)/error_msg - .PHONY: doc doc: mkdir -p $(DOC_DIR) curl --proto '=https' --tlsv1.2 -sSf https://cdn.jsdelivr.net/gh/jothepro/doxygen-awesome-css@2.2.1/doxygen-awesome.min.css \ -o $(DOC_DIR)/doxygen-awesome.css doxygen Doxyfile - -# build examples begin -EXAMPLES=$(wildcard ./examples/*.c) -EXAMPLE_OBJECTS=$(EXAMPLES:.c=.o) -EXAMPLE_TARGETS=$(EXAMPLES:.c=) -.PHONY: examples -examples: $(EXAMPLE_TARGETS) - -$(EXAMPLE_TARGETS): % : %.o - $(CC) $(CCFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) - -%.o: %.c - $(CC) $(CCFLAGS) -c $< -o $@ -# build examples end - -.PHONY: clean -clean: - cargo clean - rm -rf $(EXAMPLE_OBJECTS) - rm -rf $(EXAMPLE_TARGETS) - rm -rf $(OBJ_DIR) - rm -rf $(DOC_DIR) - diff --git a/bindings/c/examples/basic.c b/bindings/c/examples/basic.c index af55faeea0df..199403ca53b0 100644 --- a/bindings/c/examples/basic.c +++ b/bindings/c/examples/basic.c @@ -37,7 +37,7 @@ int main() }; /* Write this into path "/testpath" */ - opendal_error* error = opendal_operator_write(op, "/testpath", data); + opendal_error* error = opendal_operator_write(op, "/testpath", &data); assert(error == NULL); /* We can read it out, make sure the data is the same */ diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp index 65e97f52bf44..16bea5f10e00 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -133,9 +133,3 @@ TEST_F(OpendalBddTest, FeatureTest) error = opendal_operator_delete(this->p, "tmpdir/"); EXPECT_EQ(error, nullptr); } - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/bindings/c/tests/error_msg.cpp b/bindings/c/tests/error_msg.cpp index 516d27865aaf..16bc57aaed96 100644 --- a/bindings/c/tests/error_msg.cpp +++ b/bindings/c/tests/error_msg.cpp @@ -64,9 +64,3 @@ TEST_F(OpendalErrorTest, ErrorReadTest) // free the error opendal_error_free(r.error); } - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp index 3b64fef460b6..533fda7a8135 100644 --- a/bindings/c/tests/list.cpp +++ b/bindings/c/tests/list.cpp @@ -119,9 +119,3 @@ TEST_F(OpendalListTest, ListEmptyDirTest) { } // todo: Try list a directory that does not exist TEST_F(OpendalListTest, ListNotExistDirTest) { } - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/bindings/c/tests/opinfo.cpp b/bindings/c/tests/opinfo.cpp index 9684b0360473..4312a152a842 100644 --- a/bindings/c/tests/opinfo.cpp +++ b/bindings/c/tests/opinfo.cpp @@ -97,9 +97,3 @@ TEST_F(OpendalOperatorInfoTest, InfoTest) free(scheme); free(root); } - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/bindings/zig/build.zig b/bindings/zig/build.zig index fc1623ddcee0..b5d26bfa473c 100644 --- a/bindings/zig/build.zig +++ b/bindings/zig/build.zig @@ -31,13 +31,12 @@ pub fn build(b: *std.Build) void { opendal_module.addIncludePath(b.path("../c/include")); // Creates a step for building the dependent C bindings - const libopendal_c = b.addSystemCommand(&[_][]const u8{ - "make", - "-C", - "../c", - "build", - }); + const libopendal_c_cmake = b.addSystemCommand(&[_][]const u8{ "cmake", "-S", "../c", "-B", "../c/build" }); + const config_libopendal_c = b.step("libopendal_c_cmake", "Generate OpenDAL C binding CMake files"); + config_libopendal_c.dependOn(&libopendal_c_cmake.step); + const libopendal_c = b.addSystemCommand(&[_][]const u8{ "make", "-C", "../c/build" }); const build_libopendal_c = b.step("libopendal_c", "Build OpenDAL C bindings"); + libopendal_c.step.dependOn(config_libopendal_c); build_libopendal_c.dependOn(&libopendal_c.step); // Creates a step for unit testing. This only builds the test executable