diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 5ce2d07..12fe458 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -10,20 +10,31 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +env: + PIP_BREAK_SYSTEM_PACKAGES: 1 + # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - build: + unix: + strategy: + matrix: + runs-on: [ubuntu-latest] + build_tool: [bazel, cmake] - runs-on: ubuntu-latest + name: "${{matrix.runs-on}} ${{matrix.build_tool}}" + runs-on: ${{matrix.runs-on}} # Steps represent a sequence of tasks that will be executed as part of the job steps: + - name: Show env + run: env + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - # Runs a single command using the runners shell - - name: Install Bazel + - name: Install bazel + if: matrix.build_tool == 'bazel' # Install Bazel, see https://docs.bazel.build/versions/master/install-ubuntu.html#step-1-install-required-packages run: | sudo apt install curl gnupg @@ -32,11 +43,28 @@ jobs: echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list sudo apt update && sudo apt install bazel -y + - name: Show bazel version + if: matrix.build_tool == 'bazel' + run: bazel --version + + - name: Update cmake + if: matrix.build_tool == 'cmake' + uses: jwlawson/actions-setup-cmake@v1.14 + + - name: Show cmake version + if: matrix.build_tool == 'cmake' + run: cmake --version + + - name: Show platform info + run: python -m platform + - name: Install virtualenv run: | sudo apt-get install python3-pip -y sudo pip3 install virtualenv - # Runs a set of commands using the runners shell - - name: Run tests - run: bash ./scripts/build_and_run_tests.sh + - name: Show nproc + run: echo nproc=$(nproc) + + - name: Build and run tests + run: bash ./scripts/build_and_run_tests_${{matrix.build_tool}}.sh -j $(nproc) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a7b42e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +tmp_build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2866dd8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.11) +project(pybind11_abseil LANGUAGES CXX) + +include(FetchContent) +include(CTest) + +if(NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif(NOT DEFINED CMAKE_CXX_STANDARD) +set(ABSL_PROPAGATE_CXX_STD ON) +set(BUILD_TESTING OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) + +find_package(PythonLibs REQUIRED) + +FetchContent_Declare( + abseil-cpp + URL https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.tar.gz + URL_HASH + SHA256=dcf71b9cba8dc0ca9940c4b316a0c796be8fab42b070bb6b7cab62b48f0e66c4) + +FetchContent_Declare( + pybind11 + URL https://github.com/pybind/pybind11/archive/refs/heads/master.tar.gz) + +FetchContent_MakeAvailable(abseil-cpp pybind11) + +set(TOP_LEVEL_DIR ${CMAKE_CURRENT_LIST_DIR}) +include_directories(${TOP_LEVEL_DIR} ${PYTHON_INCLUDE_DIR} + ${pybind11_INCLUDE_DIRS}) + +add_subdirectory(pybind11_abseil) diff --git a/README.md b/README.md index 230769e..5a7b7d1 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,13 @@ in the .cc file with your bindings: #include "pybind11_abseil/absl_casters.h" ``` + ## Installation +pybind11_abseil can be built with Bazel or CMake. Instructions for both are below. + +### Bazel + You will need to depend on `pybind11`, `pybind11_bazel`(see [doc](https://github.com/pybind/pybind11_bazel#installation), and on `pybind11_abseil`, e.g. @@ -54,6 +59,25 @@ Then, in your BUILD file: load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") ``` +### CMake + +In your project, add a FetchContent for pybind11_abseil. This will also fetch the +appropriate versions of Abseil and pybind11 which your project can use +(eliminating the need for submoduling Abseil or using find_package). + +Add the following to your CMakeLists.txt: +``` +include(FetchContent) + +FetchContent_Declare { + pybind11_abseil + GIT_REPOSITORY https://github.com/pybind/pybind11_abseil.git + GIT_TAG master +} + +FetchContent_MakeAvailable(pybind11 abseil-cpp pybind11_abseil) +``` + ## absl::Duration `absl::Duration` objects are converted to/ from python datetime.timedelta objects. diff --git a/pybind11_abseil/CMakeLists.txt b/pybind11_abseil/CMakeLists.txt new file mode 100644 index 0000000..7b6d85b --- /dev/null +++ b/pybind11_abseil/CMakeLists.txt @@ -0,0 +1,180 @@ +add_subdirectory(cpp_capsule_tools) + +# absl_casters ============================================================ +add_library(absl_casters INTERFACE) +add_library(pybind11_abseil::absl_casters ALIAS absl_casters) + +target_include_directories(absl_casters + INTERFACE $) + +target_link_libraries( + absl_casters + INTERFACE absl::cleanup + absl::btree + absl::flat_hash_map + absl::flat_hash_set + absl::node_hash_map + absl::node_hash_set + absl::strings + absl::time + absl::optional + absl::span) + +# ok_status_singleton_lib ====================================================== + +add_library(ok_status_singleton_lib STATIC ok_status_singleton_lib.cc) +add_library(pybind11_abseil::ok_status_singleton_lib ALIAS + ok_status_singleton_lib) + +target_include_directories(ok_status_singleton_lib + INTERFACE $) + +target_link_libraries(ok_status_singleton_lib PUBLIC absl::status) + +# ok_status_singleton_pyinit_google3 =========================================== + +add_library(ok_status_singleton_pyinit_google3 STATIC + ok_status_singleton_pyinit_google3.cc) + +target_link_libraries(ok_status_singleton_pyinit_google3 + PUBLIC ok_status_singleton_lib) + +# ok_status_singleton ======================================================= + +add_library(ok_status_singleton SHARED ok_status_singleton_py_extension_stub.cc) +add_library(pybind11_abseil::ok_status_singleton ALIAS ok_status_singleton) + +target_include_directories(ok_status_singleton + INTERFACE $) + +set_target_properties(ok_status_singleton PROPERTIES PREFIX "") + +target_link_libraries(ok_status_singleton + PUBLIC ok_status_singleton_pyinit_google3) + +# no_throw_status ============================================================== + +add_library(no_throw_status INTERFACE) +add_library(pybind11_abseil::no_throw_status ALIAS no_throw_status) + +target_include_directories(no_throw_status + INTERFACE $) + +# status_not_ok_exception ====================================================== + +add_library(status_not_ok_exception INTERFACE) +add_library(pybind11_abseil::status_not_ok_exception ALIAS + status_not_ok_exception) + +target_include_directories(status_not_ok_exception + INTERFACE $) + +target_link_libraries(status_not_ok_exception INTERFACE absl::status) + +# check_status_module_imported ================================================= + +add_library(check_status_module_imported INTERFACE) + +target_link_libraries(check_status_module_imported INTERFACE absl::status) + +# status_caster ================================================================ + +add_library(status_caster INTERFACE) +add_library(pybind11_abseil::status_caster ALIAS status_caster) + +target_include_directories(status_caster + INTERFACE $) + +target_link_libraries( + status_caster + INTERFACE check_status_module_imported + no_throw_status + ok_status_singleton_lib + status_not_ok_exception + raw_ptr_from_capsule + absl::status + absl::statusor) + +# statusor_caster ============================================================== + +add_library(statusor_caster INTERFACE) +add_library(pybind11_abseil::statusor_caster ALIAS statusor_caster) + +target_include_directories(statusor_caster + INTERFACE $) + +target_link_libraries( + statusor_caster INTERFACE check_status_module_imported no_throw_status + status_caster absl::status absl::statusor) + +# init_from_tag ================================================================ + +add_library(init_from_tag INTERFACE) +add_library(pybind11_abseil::init_from_tag ALIAS init_from_tag) + +target_include_directories(init_from_tag + INTERFACE $) + +# utils_pybind11_absl ========================================================== + +add_library(utils_pybind11_absl STATIC utils_pybind11_absl.cc) + +target_link_libraries(utils_pybind11_absl PUBLIC absl::strings) + +# register_status_bindings ===================================================== + +add_library(register_status_bindings STATIC register_status_bindings.cc) + +target_link_libraries( + register_status_bindings + PUBLIC absl_casters + init_from_tag + no_throw_status + ok_status_singleton_lib + status_caster + status_not_ok_exception + utils_pybind11_absl + raw_ptr_from_capsule + absl::status + absl::statusor + absl::strings) + +# status_pyinit_google3 ======================================================== + +add_library(status_pyinit_google3 STATIC status_pyinit_google3.cc) + +target_link_libraries(status_pyinit_google3 PUBLIC register_status_bindings) + +# status ==================================================================== + +add_library(status SHARED status_py_extension_stub.cc) +add_library(pybind11_abseil::status ALIAS status) + +target_include_directories(status INTERFACE $) + +set_target_properties(status PROPERTIES PREFIX "") + +target_link_libraries(status PUBLIC status_pyinit_google3 absl::status) + +# import_status_module ========================================================= + +add_library(import_status_module STATIC import_status_module.cc) +add_library(pybind11_abseil::import_status_module ALIAS import_status_module) + +target_include_directories(import_status_module + INTERFACE $) + +target_link_libraries(import_status_module PUBLIC status) + +# status_casters =============================================================== + +add_library(status_casters INTERFACE) +add_library(pybind11_abseil::status_casters ALIAS status_casters) + +target_include_directories(status_casters + INTERFACE $) + +target_link_libraries(status_casters INTERFACE import_status_module + status_caster statusor_caster) + +add_subdirectory(tests) diff --git a/pybind11_abseil/cpp_capsule_tools/CMakeLists.txt b/pybind11_abseil/cpp_capsule_tools/CMakeLists.txt new file mode 100644 index 0000000..6ad81b9 --- /dev/null +++ b/pybind11_abseil/cpp_capsule_tools/CMakeLists.txt @@ -0,0 +1,48 @@ +# void_ptr_from_capsule ======================================================== + +add_library(void_ptr_from_capsule STATIC void_ptr_from_capsule.cc) +add_library(pybind11_abseil::cpp_capsule_tools::void_ptr_from_capsule ALIAS + void_ptr_from_capsule) + +target_include_directories(void_ptr_from_capsule + INTERFACE $) + +target_link_libraries(void_ptr_from_capsule PUBLIC absl::status absl::statusor + absl::strings) + +# raw_ptr_from_capsule ========================================================= + +add_library(raw_ptr_from_capsule INTERFACE) +add_library(pybind11_abseil::cpp_capsule_tools::raw_ptr_from_capsule ALIAS + raw_ptr_from_capsule) + +target_include_directories(raw_ptr_from_capsule + INTERFACE $) + +target_link_libraries( + raw_ptr_from_capsule INTERFACE void_ptr_from_capsule absl::statusor + # python_headers does not need to be linked +) + +# make_shared_ptr_capsule ====================================================== + +add_library(make_shared_ptr_capsule INTERFACE) +add_library(pybind11_abseil::cpp_capsule_tools::make_shared_ptr_capsule ALIAS + make_shared_ptr_capsule) + +target_include_directories(make_shared_ptr_capsule + INTERFACE $) + +# shared_ptr_from_capsule ====================================================== + +add_library(shared_ptr_from_capsule INTERFACE) +add_library(pybind11_abseil::cpp_capsule_tools::shared_ptr_from_capsule ALIAS + shared_ptr_from_capsule) + +target_include_directories(shared_ptr_from_capsule + INTERFACE $) + +target_link_libraries( + shared_ptr_from_capsule INTERFACE void_ptr_from_capsule absl::statusor + # python_headers does not need to be linked +) diff --git a/pybind11_abseil/tests/CMakeLists.txt b/pybind11_abseil/tests/CMakeLists.txt new file mode 100644 index 0000000..9ac8091 --- /dev/null +++ b/pybind11_abseil/tests/CMakeLists.txt @@ -0,0 +1,89 @@ +# cpp_capsule_tools_testing ==================================================== + +pybind11_add_module(cpp_capsule_tools_testing SHARED + cpp_capsule_tools_testing.cc) + +target_link_libraries( + cpp_capsule_tools_testing PUBLIC make_shared_ptr_capsule raw_ptr_from_capsule + shared_ptr_from_capsule absl::statusor) +# cpp_capsule_tools_testing_test =============================================== + +add_test( + NAME cpp_capsule_tools_testing_test + COMMAND + ${CMAKE_COMMAND} -E env PYTHONPATH=$PYTHONPATH:${CMAKE_BINARY_DIR} + ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/cpp_capsule_tools_testing_test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# absl_example ================================================================= + +pybind11_add_module(absl_example SHARED absl_example.cc) + +target_link_libraries( + absl_example + PRIVATE absl_casters + absl::btree + absl::flat_hash_set + absl::strings + absl::time + absl::optional + absl::span) + +# absl_test ==================================================================== + +add_test( + NAME absl_test + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=$PYTHONPATH:${CMAKE_BINARY_DIR} + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/absl_test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# ok_status_singleton_test ===================================================== + +add_test( + NAME ok_status_singleton_test + COMMAND + ${CMAKE_COMMAND} -E env PYTHONPATH=$PYTHONPATH:${CMAKE_BINARY_DIR} + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/ok_status_singleton_test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# missing_import =============================================================== + +pybind11_add_module(missing_import SHARED missing_import.cc) + +target_compile_options(missing_import PUBLIC -UNDEBUG) + +target_link_libraries(missing_import PRIVATE status_casters absl::status + absl::statusor) + +# missing_import_test ========================================================== + +add_test( + NAME missing_import_test + COMMAND + ${CMAKE_COMMAND} -E env PYTHONPATH=$PYTHONPATH:${CMAKE_BINARY_DIR} + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/missing_import_test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# status_test ================================================================== + +add_test( + NAME status_test + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=$PYTHONPATH:${CMAKE_BINARY_DIR} + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/status_test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# status_example =============================================================== + +pybind11_add_module(status_example SHARED status_example.cc) + +target_link_libraries(status_example PRIVATE status_casters absl::status + absl::statusor) +# status_example_test ========================================================== + +add_test( + NAME status_example_test + COMMAND + ${CMAKE_COMMAND} -E env PYTHONPATH=$PYTHONPATH:${CMAKE_BINARY_DIR} + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/status_example_test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pybind11_abseil/tests/ok_status_singleton_test.py b/pybind11_abseil/tests/ok_status_singleton_test.py index a0fe0d0..2c71b56 100644 --- a/pybind11_abseil/tests/ok_status_singleton_test.py +++ b/pybind11_abseil/tests/ok_status_singleton_test.py @@ -3,6 +3,8 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. +import sys + from absl.testing import absltest from pybind11_abseil import ok_status_singleton @@ -12,7 +14,11 @@ class OkStatusSingletonTest(absltest.TestCase): def test_singleton(self): cap = ok_status_singleton.OkStatusSingleton() - self.assertStartsWith(repr(cap), '