diff --git a/xo-pyreflect/.github/workflows/main.yml b/xo-pyreflect/.github/workflows/main.yml new file mode 100644 index 00000000..5d9c379b --- /dev/null +++ b/xo-pyreflect/.github/workflows/main.yml @@ -0,0 +1,158 @@ +name: build xo-pyreflect + dependencies + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - name: checkout source + uses: actions/checkout@v3 + + - name: Install catch2 + # install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]] + run: sudo apt-get install -y catch2 + + - name: Install pybind11-dev + run: sudo apt-get install -y pybind11-dev + + # ---------------------------------------------------------------- + + - name: Clone xo-cmake + uses: actions/checkout@v3 + with: + repository: Rconybea/xo-cmake + path: repo/xo-cmake + + - name: Configure xo-cmake + run: cmake -B ${{github.workspace}}/build_xo-cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/xo-cmake + + - name: Build xo-cmake (trivial) + run: cmake --build ${{github.workspace}}/build_xo-cmake --config ${{env.BUILD_TYPE}} + + - name: Install xo-cmake + run: cmake --install ${{github.workspace}}/build_xo-cmake + + # ---------------------------------------------------------------- + + - name: Clone indentlog + uses: actions/checkout@v3 + with: + repository: Rconybea/indentlog + path: repo/indentlog + + - name: Configure indentlog + # configure cmake for indentlog in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + + - name: Build indentlog + run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} + + - name: Install indentlog + # install into ${{github.workspace}}/local + run: cmake --install ${{github.workspace}}/build_indentlog + + # ---------------------------------------------------------------- + + - name: Clone subsys + uses: actions/checkout@v3 + with: + repository: Rconybea/subsys + path: repo/subsys + + - name: Configure subsys + # configure cmake for subsys in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_subsys -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/subsys + + - name: Build subsys + run: cmake --build ${{github.workspace}}/build_subsys --config ${{env.BUILD_TYPE}} + + - name: Install subsys + # install into ${{github.workspace}}/local + run: cmake --install ${{github.workspace}}/build_subsys + + # ---------------------------------------------------------------- + + - name: Clone refcnt + uses: actions/checkout@v3 + with: + repository: Rconybea/refcnt + path: repo/refcnt + + - name: Configure refcnt + # configure cmake for refcnt in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/refcnt + + - name: Build refcnt + run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} + + - name: Install refcnt + # install into ${{github.workspace}}/local + run: cmake --install ${{github.workspace}}/build_refcnt + + # ---------------------------------------------------------------- + + - name: Clone reflect + uses: actions/checkout@v3 + with: + repository: Rconybea/reflect + path: repo/reflect + + - name: Configure reflect + # configure cmake for reflect in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_reflect -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/reflect + + - name: Build reflect + run: cmake --build ${{github.workspace}}/build_reflect --config ${{env.BUILD_TYPE}} + + - name: Install reflect + # install into ${{github.workspace}}/local + run: cmake --install ${{github.workspace}}/build_reflect + + # ---------------------------------------------------------------- + + - name: Clone pyutil + uses: actions/checkout@v3 + with: + repository: Rconybea/xo-pyutil + path: repo/pyutil + + - name: Configure pyutil + # configure cmake for pyutil in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_pyutil -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/pyutil + + - name: Build pyutil + run: cmake --build ${{github.workspace}}/build_pyutil --config ${{env.BUILD_TYPE}} + + - name: Install pyutil + # install into ${{github.workspace}}/local + run: cmake --install ${{github.workspace}}/build_pyutil + + # ---------------------------------------------------------------- + + - name: Configure self (pyreflect) + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build_pyreflect -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build self (pyreflect) + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build_pyreflect --config ${{env.BUILD_TYPE}} + + - name: Test self (pyreflect) + working-directory: ${{github.workspace}}/build_pyreflect + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{env.BUILD_TYPE}} diff --git a/xo-pyreflect/.gitignore b/xo-pyreflect/.gitignore new file mode 100644 index 00000000..2d66a655 --- /dev/null +++ b/xo-pyreflect/.gitignore @@ -0,0 +1,8 @@ +# emacs workspace config +.projectile +# lsp keeps state here +.cache +# typical build directory +.build* +# lsp: symlink to file in build directory (established manually) +compile_commands.json diff --git a/xo-pyreflect/CMakeLists.txt b/xo-pyreflect/CMakeLists.txt new file mode 100644 index 00000000..32311792 --- /dev/null +++ b/xo-pyreflect/CMakeLists.txt @@ -0,0 +1,28 @@ +# xo-pyreflect/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_pyreflect VERSION 0.1) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings (usually temporary) + +set(PROJECT_CXX_FLAGS "") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +add_subdirectory(src/pyreflect) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# provide find_package() support + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# end CMakeLists.txt diff --git a/xo-pyreflect/README.md b/xo-pyreflect/README.md new file mode 100644 index 00000000..078e1a1b --- /dev/null +++ b/xo-pyreflect/README.md @@ -0,0 +1,72 @@ +# python bindings for c++ reflection library (xo-pyreflect) + +## Getting Started + +### build + install dependencies + +- [github/Rconybea/xo-pyutil](https://github.com/Rconybea/xo-pyutil) +- [github/Rconybea/xo-reflect](https://github.com/Rconybea/xo-reflect) + +### build + install +``` +$ cd xo-pyreflect +$ mkdir build +$ cd build +$ INSTALL_PREFIX=/usr/local # or wherever you prefer, e.g. ~/local +$ cmake \ + -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ make +$ make install +``` +(also see .github/workflows/main.yml) + +## Examples + +Assumes `xo-pyreflect` installed to `~/local2/lib` + +``` +PYTHONPATH=~/local2/lib python +>>> import xo_pyreflect +>>> dir(xo_pyreflect) +['SelfTagging', 'TaggedRcptr', 'TypeDescr', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__'] +>>> xo_pyreflect.TypeDescr.print_reflected_types() + +``` +(Not _immediately_ interesting: no reflected types in `pyreflect` itself) + +## Development + +### build for unit test coverage +``` +$ cd xo-pyreflect +$ mkdir build-ccov +$ cd build-ccov +$ cmake \ + -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug .. +``` + +### LSP (language server) support + +LSP looks for compile commands in the root of the source tree; +while `cmake` creates them in the root of its build directory. + +``` +$ cd xo-pyreflect +$ ln -s build/compile_commands.json # supply compile commands to LSP +``` + +### display cmake variables + +- `-L` list variables +- `-A` include 'advanced' variables +- `-H` include help text + +``` +$ cd xo-pyprintjson/build +$ cmake -LAH +``` diff --git a/xo-pyreflect/cmake/xo-bootstrap-macros.cmake b/xo-pyreflect/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..aba31169 --- /dev/null +++ b/xo-pyreflect/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/xo-pyreflect/cmake/xo_pyreflectConfig.cmake.in b/xo-pyreflect/cmake/xo_pyreflectConfig.cmake.in new file mode 100644 index 00000000..9c15f36a --- /dev/null +++ b/xo-pyreflect/cmake/xo_pyreflectConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/xo-pyreflect/include/README.md b/xo-pyreflect/include/README.md new file mode 100644 index 00000000..4454f162 --- /dev/null +++ b/xo-pyreflect/include/README.md @@ -0,0 +1 @@ +placeholder for future pyreflect #include files diff --git a/xo-pyreflect/src/pyreflect/CMakeLists.txt b/xo-pyreflect/src/pyreflect/CMakeLists.txt new file mode 100644 index 00000000..8be1e428 --- /dev/null +++ b/xo-pyreflect/src/pyreflect/CMakeLists.txt @@ -0,0 +1,10 @@ +# xo_pyreflect/src/pyreflect/CMakeLists.txt + +set(SELF_LIB xo_pyreflect) +set(SELF_SRCS pyreflect.cpp) + +# ---------------------------------------------------------------- + +xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) +xo_pybind11_dependency(${SELF_LIB} reflect) +xo_pybind11_dependency(${SELF_LIB} xo_pyutil) diff --git a/xo-pyreflect/src/pyreflect/pyreflect.cpp b/xo-pyreflect/src/pyreflect/pyreflect.cpp new file mode 100644 index 00000000..b2302685 --- /dev/null +++ b/xo-pyreflect/src/pyreflect/pyreflect.cpp @@ -0,0 +1,76 @@ +/* @file pyreflect.cpp */ + +// note: need pyreflect/ here bc pyreflect.hpp is generated, located in build directory +#include "pyreflect.hpp" +#include "xo/reflect/TypeDescr.hpp" +#include "xo/reflect/TaggedRcptr.hpp" +#include "xo/reflect/SelfTagging.hpp" +//#include "time/Time.hpp" +//#include "xo/pyutil/pytime.hpp" +#include "xo/pyutil/pyutil.hpp" +//#include +//#include +//#include +//#include + +namespace xo { + using xo::time::utc_nanos; + using xo::ref::unowned_ptr; + using xo::rp; + namespace py = pybind11; + + namespace reflect { + PYBIND11_MODULE(PYREFLECT_MODULE_NAME(), m) { + + m.doc() = "pybind11 plugin for xo-reflect"; + + py::enum_(m, "Metatype") + .value("invalid", Metatype::mt_invalid) + .value("atomic", Metatype::mt_atomic) + .value("pointer", Metatype::mt_pointer) + .value("vector", Metatype::mt_vector) + .value("struct", Metatype::mt_struct) + .value("function", Metatype::mt_function) + ; + + /* note: possibly move this to pytime/ if/when we provide it */ + //py::class_(m, "utc_nanos"); + + //py::class_(m, "TypeDescr"); + /* TypeDescrBase instances are created automatically at library load time + * by static initializers. The reflection library (xo-reflect) is responsible + * for lifetime of TypeDescrobjects. Under no circumstances should python + * (or pybind11) directly destroy a TypeDescrImpl instance, hence use of + * unowned_ptr here. + */ + py::class_>(m, "TypeDescr") + + .def_static("lookup_by_name", &TypeDescrBase::lookup_by_name) + .def_static("print_reflected_types", + [](){ TypeDescrBase::print_reflected_types(std::cout); }) + + .def_property_readonly("canonical_name", &TypeDescrBase::canonical_name) + .def_property_readonly("short_name", &TypeDescrBase::short_name) + .def_property_readonly("metatype", &TypeDescrBase::metatype) + .def_property_readonly("complete_flag", &TypeDescrBase::complete_flag) + .def("__repr__", &TypeDescrBase::display_string); + + /* note: this means python will use + * std::unique_ptr + * when it encounters a TaggedRcptr instance. + * Maintains refcount at cost of 2nd level of indirection. + */ + py::class_(m, "TaggedRcptr") + .def_property_readonly("td", &TaggedPtr::td) + .def("__repr__", &TaggedRcptr::display_string); + + py::class_>(m, "SelfTagging") + .def("self_tp", &SelfTagging::self_tp); + + } /*pyreflect*/ + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end pyreflect.cpp */ diff --git a/xo-pyreflect/src/pyreflect/pyreflect.hpp.in b/xo-pyreflect/src/pyreflect/pyreflect.hpp.in new file mode 100644 index 00000000..62b64a25 --- /dev/null +++ b/xo-pyreflect/src/pyreflect/pyreflect.hpp.in @@ -0,0 +1,25 @@ +/* @file pyreflect.hpp + * + * automatically generated from src/xo_pyreflect/pyreflect.hpp.in + * see src/xo_pyreflect/CMakeLists.txt + */ + +/* python requires module name = library name + * example: + * PYBIND11_MODULE(PYREFLECT_MODULE_NAME(), m) { ... } + */ +#define PYREFLECT_MODULE_NAME() @SELF_LIB@ + +/* example: + * py::module_::import(PYREFLECT_MODULE_NAME_STR) + */ +#define PYREFLECT_MODULE_NAME_STR "@SELF_LIB@" + +/* example: + * PYREFLECT_IMPORT_MODULE() + * replaces + * py::module_::import("pyreflect") + */ +#define PYREFLECT_IMPORT_MODULE() py::module_::import("@SELF_LIB@") + +/* end pyreflect.hpp */