From fa009a414001a774e682724ddc263de8911bfd3c Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Thu, 14 Nov 2024 15:23:12 +0000 Subject: [PATCH 01/10] #20 Test on ubuntu noble --- .github/workflows/test-cells-conda.yml | 1 + .github/workflows/test-cells-ubuntu.yml | 59 +++++++++++++++++-------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-cells-conda.yml b/.github/workflows/test-cells-conda.yml index 5dfb488..d26d758 100644 --- a/.github/workflows/test-cells-conda.yml +++ b/.github/workflows/test-cells-conda.yml @@ -40,6 +40,7 @@ jobs: miniforge-version: latest python-version: ${{ matrix.python-version }} channels: conda-forge + conda-remove-defaults: "true" - name: Install cppwg run: | diff --git a/.github/workflows/test-cells-ubuntu.yml b/.github/workflows/test-cells-ubuntu.yml index 0e8545a..15f713f 100644 --- a/.github/workflows/test-cells-ubuntu.yml +++ b/.github/workflows/test-cells-ubuntu.yml @@ -6,13 +6,22 @@ on: branches: - "**" -concurrency: - group: test-cells-ubuntu-${{ github.ref }} - cancel-in-progress: true - jobs: test-cells-ubuntu: - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: ["ubuntu-22.04", "ubuntu-24.04"] + + concurrency: + group: test-cells-ubuntu-${{ github.ref }}-${{ matrix.os }} + cancel-in-progress: true + + defaults: + run: + shell: bash -el {0} # -l needed to activate pipx steps: - name: Checkout @@ -21,25 +30,39 @@ jobs: - name: Install system dependencies run: | sudo apt-get update + + for vtk_ver in $(seq 7 9); do + available=1 + dpkg -s "libvtk${vtk_ver}-dev" || available=0 + if [ "${available}" -eq 1 ]; then + break + fi + done + echo "VTK version: ${vtk_ver}" + sudo apt-get install \ cmake \ git \ libboost-all-dev \ - libpetsc-real3.15 \ - libpetsc-real3.15-dbg \ - libpetsc-real3.15-dev \ - libvtk9-dev \ + libpetsc-real-dev \ + libvtk${vtk_ver}-dev \ mpi-default-bin \ - mpi-default-dev \ + mpi-default-dev + + petsc_ver=$(dpkg-query -W -f='${Version}' libpetsc-real-dev | grep -oE '^[0-9]+\.[0-9]+') + echo "PETSc version: ${petsc_ver}" + + sudo apt-get install \ + pipx \ python3-mpi4py \ - python3-petsc4py-real3.15 \ - python3-vtk9 \ - vtk9 + python3-petsc4py-real${petsc_ver} \ + python3-pip \ + python3-vtk${vtk_ver} + + pipx ensurepath - name: Install cppwg - run: | - python -m pip install --upgrade pip - python -m pip install . + run: pipx install . - name: Configure run: | @@ -61,9 +84,9 @@ jobs: working-directory: examples/cells - name: Build - run: python -m pip install -v . + run: PIP_BREAK_SYSTEM_PACKAGES=1 python3 -m pip install --user -v . working-directory: examples/cells - name: Test - run: python -m unittest discover tests + run: python3 -m unittest discover tests working-directory: examples/cells From 219e3713c1d4782631212dee967805b64072a179 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Thu, 14 Nov 2024 16:09:28 +0000 Subject: [PATCH 02/10] #20 Find cppwg in pipx --- .github/workflows/test-cells-conda.yml | 4 ++-- examples/cells/CMakeLists.txt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-cells-conda.yml b/.github/workflows/test-cells-conda.yml index d26d758..306cd04 100644 --- a/.github/workflows/test-cells-conda.yml +++ b/.github/workflows/test-cells-conda.yml @@ -39,8 +39,8 @@ jobs: use-mamba: true miniforge-version: latest python-version: ${{ matrix.python-version }} - channels: conda-forge - conda-remove-defaults: "true" + channels: conda-forge,defaults + conda-remove-defaults: "false" - name: Install cppwg run: | diff --git a/examples/cells/CMakeLists.txt b/examples/cells/CMakeLists.txt index e67d6d3..c10d482 100644 --- a/examples/cells/CMakeLists.txt +++ b/examples/cells/CMakeLists.txt @@ -101,8 +101,9 @@ if(SKBUILD) endif() # Target for re-generating wrappers +find_program(CPPWG cppwg) add_custom_target(pycells_wrappers - COMMAND ${Python3_EXECUTABLE} -m cppwg ${CMAKE_SOURCE_DIR}/src/cpp + COMMAND ${CPPWG} ${CMAKE_SOURCE_DIR}/src/cpp -w ${CMAKE_SOURCE_DIR}/dynamic/wrappers -p ${CMAKE_SOURCE_DIR}/dynamic/config.yaml -i "$,;>" From e58c5f9cbd66e475c7133a8495e6ddfe9cde866b Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Thu, 14 Nov 2024 17:16:27 +0000 Subject: [PATCH 03/10] #20 Fix cppwg pipx install --- .github/workflows/test-cells-ubuntu.yml | 2 +- examples/cells/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cells-ubuntu.yml b/.github/workflows/test-cells-ubuntu.yml index 15f713f..5e29257 100644 --- a/.github/workflows/test-cells-ubuntu.yml +++ b/.github/workflows/test-cells-ubuntu.yml @@ -62,7 +62,7 @@ jobs: pipx ensurepath - name: Install cppwg - run: pipx install . + run: pipx install . --include-deps - name: Configure run: | diff --git a/examples/cells/CMakeLists.txt b/examples/cells/CMakeLists.txt index c10d482..acec276 100644 --- a/examples/cells/CMakeLists.txt +++ b/examples/cells/CMakeLists.txt @@ -101,7 +101,7 @@ if(SKBUILD) endif() # Target for re-generating wrappers -find_program(CPPWG cppwg) +find_program(CPPWG cppwg REQUIRED) add_custom_target(pycells_wrappers COMMAND ${CPPWG} ${CMAKE_SOURCE_DIR}/src/cpp -w ${CMAKE_SOURCE_DIR}/dynamic/wrappers From f325144ddc3677f4f2f8899dfeea1b275f4e7556 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 15 Nov 2024 14:11:06 +0000 Subject: [PATCH 04/10] #20 Check ubuntu dependency versions --- .github/workflows/test-cells-ubuntu.yml | 36 ++++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-cells-ubuntu.yml b/.github/workflows/test-cells-ubuntu.yml index 5e29257..b1360c9 100644 --- a/.github/workflows/test-cells-ubuntu.yml +++ b/.github/workflows/test-cells-ubuntu.yml @@ -47,22 +47,33 @@ jobs: libpetsc-real-dev \ libvtk${vtk_ver}-dev \ mpi-default-bin \ - mpi-default-dev - - petsc_ver=$(dpkg-query -W -f='${Version}' libpetsc-real-dev | grep -oE '^[0-9]+\.[0-9]+') - echo "PETSc version: ${petsc_ver}" - - sudo apt-get install \ + mpi-default-dev \ pipx \ python3-mpi4py \ - python3-petsc4py-real${petsc_ver} \ + python3-petsc4py-real \ python3-pip \ python3-vtk${vtk_ver} pipx ensurepath + # Check installed package versions + dpkg-query -W \ + cmake \ + g++ \ + git \ + libboost-all-dev \ + libpetsc-real-dev \ + libvtk${vtk_ver}-dev \ + mpi-default-bin \ + mpi-default-dev \ + pipx \ + python3-mpi4py \ + python3-petsc4py-real \ + python3-pip \ + python3-vtk${vtk_ver} + - name: Install cppwg - run: pipx install . --include-deps + run: pipx install . --include-deps - name: Configure run: | @@ -84,9 +95,14 @@ jobs: working-directory: examples/cells - name: Build - run: PIP_BREAK_SYSTEM_PACKAGES=1 python3 -m pip install --user -v . + run: | + python3 -m venv --system-site-packages .venv + . .venv/bin/activate + python3 -m pip install -v . working-directory: examples/cells - name: Test - run: python3 -m unittest discover tests + run: | + . .venv/bin/activate + python3 -m unittest discover tests working-directory: examples/cells From 2db10e469bd89ad8d8e14d4edb68ee28a1e90fd6 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Thu, 21 Nov 2024 22:21:08 +0000 Subject: [PATCH 05/10] #20 Make castxml and clang system dependencies --- .github/workflows/test-cells-conda.yml | 5 +++-- .github/workflows/test-cells-ubuntu.yml | 7 +++++-- .github/workflows/test-shapes-pip.yml | 2 +- README.md | 6 ++++++ examples/cells/CMakeLists.txt | 25 ++++++++++++++----------- setup.cfg | 1 - 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test-cells-conda.yml b/.github/workflows/test-cells-conda.yml index 306cd04..23e490a 100644 --- a/.github/workflows/test-cells-conda.yml +++ b/.github/workflows/test-cells-conda.yml @@ -30,7 +30,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install cmake git + sudo apt-get install castxml clang cmake git - name: Setup Miniconda Python ${{ matrix.python-version }} uses: conda-incubator/setup-miniconda@v3 @@ -81,7 +81,8 @@ jobs: run: mamba install boa conda-build conda-verify - name: Build - run: conda mambabuild recipe -m variants/python${{ matrix.python-version }}.yaml + run: | + conda mambabuild recipe -m variants/python${{ matrix.python-version }}.yaml working-directory: examples/cells/conda - name: Install diff --git a/.github/workflows/test-cells-ubuntu.yml b/.github/workflows/test-cells-ubuntu.yml index b1360c9..715b687 100644 --- a/.github/workflows/test-cells-ubuntu.yml +++ b/.github/workflows/test-cells-ubuntu.yml @@ -41,6 +41,8 @@ jobs: echo "VTK version: ${vtk_ver}" sudo apt-get install \ + castxml \ + clang \ cmake \ git \ libboost-all-dev \ @@ -58,8 +60,9 @@ jobs: # Check installed package versions dpkg-query -W \ + castxml \ + clang \ cmake \ - g++ \ git \ libboost-all-dev \ libpetsc-real-dev \ @@ -73,7 +76,7 @@ jobs: python3-vtk${vtk_ver} - name: Install cppwg - run: pipx install . --include-deps + run: pipx install . - name: Configure run: | diff --git a/.github/workflows/test-shapes-pip.yml b/.github/workflows/test-shapes-pip.yml index bea7477..a7d01b8 100644 --- a/.github/workflows/test-shapes-pip.yml +++ b/.github/workflows/test-shapes-pip.yml @@ -26,7 +26,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install cmake git + sudo apt-get install castxml clang cmake git - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v5 diff --git a/README.md b/README.md index 697dfd6..6927ced 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,12 @@ Automatically generate pybind11 Python wrapper code for C++ projects. ## Installation +Install CastXML (required) and Clang (recommended). On Ubuntu, this would be: + +```bash +sudo apt-get install castxml clang +``` + Clone the repository and install cppwg: ```bash diff --git a/examples/cells/CMakeLists.txt b/examples/cells/CMakeLists.txt index acec276..433499b 100644 --- a/examples/cells/CMakeLists.txt +++ b/examples/cells/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16...3.22) +cmake_minimum_required(VERSION 3.16...3.28) project(pycells LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 17) @@ -53,25 +53,27 @@ file(GLOB_RECURSE MAIN_SOURCES ${CMAKE_SOURCE_DIR}/src/cpp/*.hpp ) add_library(cells SHARED ${MAIN_SOURCES}) -target_link_libraries(cells PUBLIC - ${Boost_LIBRARIES} - ${VTK_LIBRARIES} - ${PETSC_LIBRARIES} - ${MPI_C_LIBRARIES} - ${MPI_CXX_LIBRARIES} -) target_include_directories(cells PUBLIC ${CMAKE_SOURCE_DIR}/src/cpp ${CMAKE_SOURCE_DIR}/src/cpp/cell ${CMAKE_SOURCE_DIR}/src/cpp/mesh ${CMAKE_SOURCE_DIR}/src/cpp/utils ${CMAKE_SOURCE_DIR}/src/cpp/visualization +) +target_include_directories(cells SYSTEM PUBLIC ${Boost_INCLUDE_DIR} ${VTK_INCLUDE_DIRS} ${PETSC_INCLUDES} ${MPI_C_INCLUDE_PATH} ${MPI_CXX_INCLUDE_PATH} ) +target_link_libraries(cells PUBLIC + ${Boost_LIBRARIES} + ${VTK_LIBRARIES} + ${PETSC_LIBRARIES} + ${MPI_C_LIBRARIES} + ${MPI_CXX_LIBRARIES} +) # Set up the pycells module with pybind11 file(GLOB WRAPPER_SOURCES @@ -79,14 +81,14 @@ file(GLOB WRAPPER_SOURCES ${CMAKE_SOURCE_DIR}/dynamic/wrappers/all/*.hpp ) pybind11_add_module(_pycells_all MODULE ${WRAPPER_SOURCES}) -target_link_libraries(_pycells_all PRIVATE cells) target_include_directories(_pycells_all PRIVATE ${CMAKE_SOURCE_DIR}/dynamic/wrappers/all ${CMAKE_SOURCE_DIR}/dynamic/typecasters/ ${CMAKE_SOURCE_DIR}/dynamic/typecasters/thirdparty/dolfinx ${CMAKE_SOURCE_DIR}/dynamic/typecasters/thirdparty/smtk - ${PETSC4PY_INCLUDES} ) +target_include_directories(_pycells_all SYSTEM PRIVATE ${PETSC4PY_INCLUDES}) +target_link_libraries(_pycells_all PRIVATE cells) # Add install targets for scikit-build if(SKBUILD) @@ -106,7 +108,8 @@ add_custom_target(pycells_wrappers COMMAND ${CPPWG} ${CMAKE_SOURCE_DIR}/src/cpp -w ${CMAKE_SOURCE_DIR}/dynamic/wrappers -p ${CMAKE_SOURCE_DIR}/dynamic/config.yaml - -i "$,;>" + -i "$,;>" + "$,;>" -l ${CMAKE_BINARY_DIR}/cppwg.log --std c++17 COMMAND_EXPAND_LISTS diff --git a/setup.cfg b/setup.cfg index 2022a7a..04f5f7a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,6 @@ python_requires = >=3.8 install_requires = pyyaml>=6.0 pygccxml>=2.2 - castxml>=0.4 [options.entry_points] console_scripts = From 309835f4451d9615c47964ca032c7506ae361ff3 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 22 Nov 2024 10:55:19 +0000 Subject: [PATCH 06/10] #20 Add castxml_compiler option --- .flake8 | 1 + README.md | 27 ++++++++++--------- cppwg/__main__.py | 8 ++++++ cppwg/generators.py | 16 ++++++++++- cppwg/info/class_info.py | 2 -- cppwg/info/cpp_entity_info.py | 1 - cppwg/info/free_function_info.py | 1 - cppwg/info/method_info.py | 1 - cppwg/info/variable_info.py | 1 - cppwg/parsers/source_parser.py | 12 +++++---- cppwg/writers/base_writer.py | 1 - cppwg/writers/class_writer.py | 4 +-- cppwg/writers/constructor_writer.py | 1 - cppwg/writers/free_function_writer.py | 1 - cppwg/writers/header_collection_writer.py | 1 - cppwg/writers/method_writer.py | 1 - examples/cells/src/py/pycells/_syntax.py | 1 - examples/cells/tests/test_cells.py | 1 - examples/shapes/src/py/pyshapes/_syntax.py | 1 - examples/shapes/src/py/tests/test_classes.py | 3 --- .../shapes/src/py/tests/test_functions.py | 1 - 21 files changed, 46 insertions(+), 40 deletions(-) diff --git a/.flake8 b/.flake8 index b3432bc..5d3b091 100644 --- a/.flake8 +++ b/.flake8 @@ -5,6 +5,7 @@ exclude = __pycache__, .git, .github, + .venv, build, cppwg/templates, doc, diff --git a/README.md b/README.md index 6927ced..3b48845 100644 --- a/README.md +++ b/README.md @@ -25,30 +25,30 @@ pip install . ## Usage ``` -usage: cppwg [-h] [-w WRAPPER_ROOT] [-p PACKAGE_INFO] [-c CASTXML_BINARY] - [--std STD] [-i [INCLUDES ...]] [-q] [-l [LOGFILE]] [-v] - SOURCE_ROOT +usage: cppwg [-h] [-w WRAPPER_ROOT] [-p PACKAGE_INFO] [-c CASTXML_BINARY] + [-m CASTXML_COMPILER] [--std STD] [-i [INCLUDES ...]] [-q] + [-l [LOGFILE]] [-v] SOURCE_ROOT Generate Python Wrappers for C++ code positional arguments: - SOURCE_ROOT Path to the root directory of the input C++ source - code. + SOURCE_ROOT Path to the root directory of the input C++ source code. options: -h, --help show this help message and exit - -w WRAPPER_ROOT, --wrapper_root WRAPPER_ROOT - Path to the output directory for the Pybind11 wrapper - code. - -p PACKAGE_INFO, --package_info PACKAGE_INFO + -w, --wrapper_root WRAPPER_ROOT + Path to the output directory for the Pybind11 wrapper code. + -p, --package_info PACKAGE_INFO Path to the package info file. - -c CASTXML_BINARY, --castxml_binary CASTXML_BINARY + -c, --castxml_binary CASTXML_BINARY Path to the castxml executable. + -m, --castxml_compiler CASTXML_COMPILER + Path to a compiler to be used by castxml. --std STD C++ standard e.g. c++17. - -i [INCLUDES ...], --includes [INCLUDES ...] + -i, --includes [INCLUDES ...] List of paths to include directories. -q, --quiet Disable informational messages. - -l [LOGFILE], --logfile [LOGFILE] + -l, --logfile [LOGFILE] Output log messages to a file. -v, --version Print cppwg version. ``` @@ -97,7 +97,8 @@ cd examples/shapes cppwg src/cpp \ --wrapper_root wrapper \ --package_info wrapper/package_info.yaml \ - --includes src/cpp/geometry src/cpp/math_funcs src/cpp/mesh src/cpp/primitives + --includes src/cpp/geometry src/cpp/math_funcs src/cpp/primitives \ + --std c++17 ``` For the `Rectangle` class, this creates two files in diff --git a/cppwg/__main__.py b/cppwg/__main__.py index 0d6e209..11fb62e 100644 --- a/cppwg/__main__.py +++ b/cppwg/__main__.py @@ -45,6 +45,13 @@ def parse_args() -> argparse.Namespace: help="Path to the castxml executable.", ) + parser.add_argument( + "-m", + "--castxml_compiler", + type=str, + help="Path to a compiler to be used by castxml.", + ) + # Note: we're passing in std directly because syntax like # --castxml_cflags "-std=c++17" isn't supported by argparse because of # the initial "-" in the argument. See https://bugs.python.org/issue9334 @@ -112,6 +119,7 @@ def generate(args: argparse.Namespace) -> None: package_info_path=args.package_info, castxml_binary=args.castxml_binary, castxml_cflags=castxml_cflags, + castxml_compiler=args.castxml_compiler, ) generator.generate() diff --git a/cppwg/generators.py b/cppwg/generators.py index c6aef3d..5b9b23d 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -3,6 +3,7 @@ import logging import os import re +import shutil import subprocess import uuid from pathlib import Path @@ -40,6 +41,8 @@ class CppWrapperGenerator: The path to the castxml binary castxml_cflags : str Optional cflags to be passed to castxml e.g. "-std=c++17" + castxml_compiler : str + Optional compiler path to be passed to CastXML package_info_path : str The path to the package info yaml config file; defaults to "package_info.yaml" source_ns : pygccxml.declarations.namespace_t @@ -56,6 +59,7 @@ def __init__( castxml_binary: Optional[str] = None, package_info_path: Optional[str] = None, castxml_cflags: Optional[str] = None, + castxml_compiler: Optional[str] = None, ): logger = logging.getLogger() @@ -100,6 +104,16 @@ def __init__( if castxml_cflags: self.castxml_cflags = f"{self.castxml_cflags} {castxml_cflags}" + # Try to set castxml compiler + if castxml_compiler: + self.castxml_compiler = castxml_compiler + else: + compiler_path = shutil.which("clang++") + if compiler_path: + self.castxml_compiler = compiler_path + else: + self.castxml_compiler = None + # Sanitize source_root self.source_root: str = os.path.abspath(source_root) if not os.path.isdir(self.source_root): @@ -195,7 +209,6 @@ def log_unknown_classes(self) -> None: # Check for uninstantiated class templates not parsed by pygccxml for hpp_file_path in self.package_info.source_hpp_files: - class_list = utils.find_classes_in_source_file(hpp_file_path) for _, class_name, _ in class_list: @@ -216,6 +229,7 @@ def parse_headers(self) -> None: self.castxml_binary, self.source_includes, self.castxml_cflags, + self.castxml_compiler, ) self.source_ns = source_parser.parse() diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index ff1cf8e..2542c3b 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -27,7 +27,6 @@ class CppClassInfo(CppEntityInfo): """ def __init__(self, name: str, class_config: Optional[Dict[str, Any]] = None): - super().__init__(name, class_config) self.base_decls: List["declaration_t"] = [] # noqa: F821 @@ -298,7 +297,6 @@ class instantiation. For example, class "Foo" with template arguments template_string = "" for idx, arg in enumerate(template_arg_list): - # Do standard name replacements arg_str = str(arg) for name, replacement in self.name_replacements.items(): diff --git a/cppwg/info/cpp_entity_info.py b/cppwg/info/cpp_entity_info.py index bbc60f0..2602659 100644 --- a/cppwg/info/cpp_entity_info.py +++ b/cppwg/info/cpp_entity_info.py @@ -32,7 +32,6 @@ class CppEntityInfo(BaseInfo): """ def __init__(self, name: str, entity_config: Optional[Dict[str, Any]] = None): - super().__init__(name, entity_config) self.name_override: str = "" diff --git a/cppwg/info/free_function_info.py b/cppwg/info/free_function_info.py index 3c927a9..b89cb41 100644 --- a/cppwg/info/free_function_info.py +++ b/cppwg/info/free_function_info.py @@ -11,7 +11,6 @@ class CppFreeFunctionInfo(CppEntityInfo): def __init__( self, name: str, free_function_config: Optional[Dict[str, Any]] = None ): - super().__init__(name, free_function_config) def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 diff --git a/cppwg/info/method_info.py b/cppwg/info/method_info.py index d004903..0e010b3 100644 --- a/cppwg/info/method_info.py +++ b/cppwg/info/method_info.py @@ -16,7 +16,6 @@ class CppMethodInfo(CppEntityInfo): """ def __init__(self, name: str, _) -> None: - super().__init__(name) self.class_info: Optional["CppClassInfo"] = None # noqa: F821 diff --git a/cppwg/info/variable_info.py b/cppwg/info/variable_info.py index 8e58f93..c8c0dde 100644 --- a/cppwg/info/variable_info.py +++ b/cppwg/info/variable_info.py @@ -9,5 +9,4 @@ class CppVariableInfo(CppEntityInfo): """An information structure for individual variables to be wrapped.""" def __init__(self, name: str, variable_config: Optional[Dict[str, Any]] = None): - super().__init__(name, variable_config) diff --git a/cppwg/parsers/source_parser.py b/cppwg/parsers/source_parser.py index 211b797..785fb0b 100644 --- a/cppwg/parsers/source_parser.py +++ b/cppwg/parsers/source_parser.py @@ -30,14 +30,12 @@ class CppSourceParser: ---------- castxml_cflags : str Optional cflags to be passed to CastXML e.g. "-std=c++17" + castxml_compiler : str + Optional compiler path to be passed to CastXML castxml_binary : str The path to the CastXML binary - global_ns : namespace_t - The namespace containing all parsed C++ declarations source_includes : List[str] The list of source include paths - source_ns : namespace_t - The namespace containing C++ declarations from the source tree source_root : str The root directory of the source code wrapper_header_collection : str @@ -51,12 +49,14 @@ def __init__( castxml_binary: str, source_includes: List[str], castxml_cflags: str = "", + castxml_compiler: str = None, ): self.source_root: str = source_root self.wrapper_header_collection: str = wrapper_header_collection self.castxml_binary: str = castxml_binary self.source_includes: List[str] = source_includes self.castxml_cflags: str = castxml_cflags + self.castxml_compiler: str = castxml_compiler def parse(self) -> namespace_t: """ @@ -74,8 +74,10 @@ def parse(self) -> namespace_t: xml_generator_path=self.castxml_binary, xml_generator="castxml", cflags=self.castxml_cflags, + compiler_path=self.castxml_compiler, include_paths=self.source_includes, ) + logger.info(f"Using compiler: {xml_generator_config.compiler_path}") # Parse all the C++ source code to extract declarations logger.info("Parsing source code for declarations.") @@ -85,7 +87,7 @@ def parse(self) -> namespace_t: compilation_mode=parser.COMPILATION_MODE.ALL_AT_ONCE, ) - # Get access to the global namespace + # Get access to the global namespace containing all parsed C++ declarations global_ns: namespace_t = declarations.get_global_namespace(decls) # Filter declarations for which files exist diff --git a/cppwg/writers/base_writer.py b/cppwg/writers/base_writer.py index ca2f3cd..77c5536 100644 --- a/cppwg/writers/base_writer.py +++ b/cppwg/writers/base_writer.py @@ -17,7 +17,6 @@ class CppBaseWrapperWriter: """ def __init__(self, wrapper_templates: Dict[str, str]) -> None: - self.wrapper_templates = wrapper_templates self.tidy_replacements = OrderedDict( [ diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 9efd8cf..c35afdf 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -292,9 +292,7 @@ def write(self, work_dir: str) -> None: continue # Find and define virtual function "trampoline" overrides - methods_needing_override: List["member_function_t"] = ( # noqa: F821 - self.add_virtual_overrides(idx) - ) + methods_needing_override = self.add_virtual_overrides(idx) # Add the virtual "trampoline" overrides from "Foo_Overrides" to # the "Foo" wrapper class definition if needed diff --git a/cppwg/writers/constructor_writer.py b/cppwg/writers/constructor_writer.py index 02073ba..e643550 100644 --- a/cppwg/writers/constructor_writer.py +++ b/cppwg/writers/constructor_writer.py @@ -39,7 +39,6 @@ def __init__( ctor_decl: "constructor_t", # noqa: F821 wrapper_templates: Dict[str, str], ) -> None: - super().__init__(wrapper_templates) self.class_info: "CppClassInfo" = class_info # noqa: F821 diff --git a/cppwg/writers/free_function_writer.py b/cppwg/writers/free_function_writer.py index de02532..4b931cc 100644 --- a/cppwg/writers/free_function_writer.py +++ b/cppwg/writers/free_function_writer.py @@ -21,7 +21,6 @@ class CppFreeFunctionWrapperWriter(CppBaseWrapperWriter): """ def __init__(self, free_function_info, wrapper_templates) -> None: - super().__init__(wrapper_templates) self.free_function_info: CppFreeFunctionInfo = free_function_info diff --git a/cppwg/writers/header_collection_writer.py b/cppwg/writers/header_collection_writer.py index 838e0cd..61bb278 100644 --- a/cppwg/writers/header_collection_writer.py +++ b/cppwg/writers/header_collection_writer.py @@ -39,7 +39,6 @@ def __init__( wrapper_root: str, hpp_collection_file: str, ): - self.package_info: PackageInfo = package_info self.wrapper_root: str = wrapper_root self.hpp_collection_file: str = hpp_collection_file diff --git a/cppwg/writers/method_writer.py b/cppwg/writers/method_writer.py index 5cb7f4b..e55298c 100644 --- a/cppwg/writers/method_writer.py +++ b/cppwg/writers/method_writer.py @@ -39,7 +39,6 @@ def __init__( method_decl: "member_function_t", # noqa: F821 wrapper_templates: Dict[str, str], ) -> None: - super().__init__(wrapper_templates) self.class_info: "CppClassInfo" = class_info # noqa: F821 diff --git a/examples/cells/src/py/pycells/_syntax.py b/examples/cells/src/py/pycells/_syntax.py index a6733ea..c37f900 100644 --- a/examples/cells/src/py/pycells/_syntax.py +++ b/examples/cells/src/py/pycells/_syntax.py @@ -5,7 +5,6 @@ class TemplateClassDict: - def __init__(self, template_dict): self._dict = {} for arg_tuple, cls in template_dict.items(): diff --git a/examples/cells/tests/test_cells.py b/examples/cells/tests/test_cells.py index 6b4390d..1e2e727 100644 --- a/examples/cells/tests/test_cells.py +++ b/examples/cells/tests/test_cells.py @@ -7,7 +7,6 @@ class TestCells(unittest.TestCase): - def testVtkCaster(self): scene = Scene[2]() renderer = scene.GetRenderer() diff --git a/examples/shapes/src/py/pyshapes/_syntax.py b/examples/shapes/src/py/pyshapes/_syntax.py index 2afd7e6..786444a 100644 --- a/examples/shapes/src/py/pyshapes/_syntax.py +++ b/examples/shapes/src/py/pyshapes/_syntax.py @@ -3,7 +3,6 @@ class TemplateClassDict: - def __init__(self, template_dict): self._dict = {} for arg_tuple, cls in template_dict.items(): diff --git a/examples/shapes/src/py/tests/test_classes.py b/examples/shapes/src/py/tests/test_classes.py index d723b25..b45b870 100644 --- a/examples/shapes/src/py/tests/test_classes.py +++ b/examples/shapes/src/py/tests/test_classes.py @@ -5,9 +5,7 @@ class TestClasses(unittest.TestCase): - def testGeometry(self): - p0 = pyshapes.geometry.Point_2() self.assertTrue(p0.GetLocation() == [0.0, 0.0]) @@ -36,7 +34,6 @@ def testGeometry(self): self.assertTrue(len(cuboid.rGetVertices()) == 8) def testSyntax(self): - self.assertEqual(pyshapes.geometry.Point[2], pyshapes.geometry.Point_2) point = pyshapes.geometry.Point[3](0.0, 1.0, 2.0) diff --git a/examples/shapes/src/py/tests/test_functions.py b/examples/shapes/src/py/tests/test_functions.py index 62a546b..47db051 100644 --- a/examples/shapes/src/py/tests/test_functions.py +++ b/examples/shapes/src/py/tests/test_functions.py @@ -4,7 +4,6 @@ class TestFunctions(unittest.TestCase): - def testAdd(self): a = 4 b = 5 From 812b61d4ad056f98d3f88cf459fe24a2abbf46ac Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 22 Nov 2024 11:54:45 +0000 Subject: [PATCH 07/10] #20 Standardize base class names in wrappers --- cppwg/writers/class_writer.py | 14 +++++++------- cppwg/writers/module_writer.py | 16 +++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index c35afdf..5810459 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -27,8 +27,8 @@ class CppClassWrapperWriter(CppBaseWrapperWriter): The class information wrapper_templates : Dict[str, str] String templates with placeholders for generating wrapper code - module_class_decls : List[pygccxml.declarations.class_t] - A list of decls for all classes in the module + module_classes : Dict[pygccxml.declarations.class_t, str] + A dictionary of decls and names for all classes in the module has_shared_ptr : bool Whether the class uses shared pointers hpp_string : str @@ -41,19 +41,19 @@ def __init__( self, class_info: "CppClassInfo", # noqa: F821 wrapper_templates: Dict[str, str], - module_class_decls: List["class_t"], # noqa: F821 + module_classes: Dict["class_t", str], # noqa: F821 ) -> None: logger = logging.getLogger() super().__init__(wrapper_templates) - self.class_info: "CppClassInfo" = class_info # noqa: F821 + self.class_info = class_info if len(self.class_info.cpp_names) != len(self.class_info.py_names): logger.error("C++ and Python class name lists should be the same length") raise AssertionError() - self.module_class_decls: List["class_t"] = module_class_decls # noqa: F821 + self.module_classes = module_classes self.has_shared_ptr: bool = True @@ -318,8 +318,8 @@ def write(self, work_dir: str) -> None: continue # Check if the base class is also wrapped in the module - if base.related_class in self.module_class_decls: - bases += f", {base.related_class.name}" + if base.related_class in self.module_classes: + bases += f", {self.module_classes[base]}" # Add the class registration class_definition_dict = { diff --git a/cppwg/writers/module_writer.py b/cppwg/writers/module_writer.py index 50fca41..269a9c8 100644 --- a/cppwg/writers/module_writer.py +++ b/cppwg/writers/module_writer.py @@ -2,7 +2,7 @@ import logging import os -from typing import Dict, List +from typing import Dict from cppwg.utils.constants import CPPWG_EXT, CPPWG_HEADER_COLLECTION_FILENAME from cppwg.writers.class_writer import CppClassWrapperWriter @@ -26,8 +26,9 @@ class CppModuleWrapperWriter: String templates with placeholders for generating wrapper code wrapper_root : str The output directory for the generated wrapper code - class_decls : List[pygccxml.declarations.class_t] - A list of declarations of all classes to be wrapped in the module + + classes : Dict[pygccxml.declarations.class_t, str] + A dictionary of decls and names for all classes to be wrapped in the module """ def __init__( @@ -40,16 +41,17 @@ def __init__( self.wrapper_templates: Dict[str, str] = wrapper_templates self.wrapper_root: str = wrapper_root - # For convenience, store a list of declarations of all + # For convenience, store a dictionary of decl->name pairs for all # classes to be wrapped in the module - self.class_decls: List["class_t"] = [] # noqa: F821 + self.classes: Dict["class_t", str] = {} # noqa: F821 for class_info in self.module_info.class_collection: # Skip excluded classes if class_info.excluded: continue - self.class_decls.extend(class_info.decls) + for decl, cpp_name in zip(class_info.decls, class_info.cpp_names): + self.classes[decl] = cpp_name def write_module_wrapper(self) -> None: """ @@ -162,7 +164,7 @@ def write_class_wrappers(self) -> None: class_writer = CppClassWrapperWriter( class_info, self.wrapper_templates, - self.class_decls, + self.classes, ) # Write the class wrappers into /path/to/wrapper_root/modulename/ From 8ba02e8530f8941021497e6f5ad12518e7e7a6a9 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 22 Nov 2024 12:23:09 +0000 Subject: [PATCH 08/10] #20 Update base class names --- cppwg/writers/class_writer.py | 2 +- examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp | 2 +- examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 5810459..e344665 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -319,7 +319,7 @@ def write(self, work_dir: str) -> None: # Check if the base class is also wrapped in the module if base.related_class in self.module_classes: - bases += f", {self.module_classes[base]}" + bases += f", {self.module_classes[base.related_class]}" # Add the class registration class_definition_dict = { diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp index a26f649..21a2ede 100644 --- a/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp @@ -27,7 +27,7 @@ class PottsMesh_2_Overrides : public PottsMesh_2 void register_PottsMesh_2_class(py::module &m) { - py::class_, AbstractMesh<2>>(m, "PottsMesh_2") + py::class_, AbstractMesh<2, 2>>(m, "PottsMesh_2") .def(py::init<>()) .def("Scale", (void(PottsMesh_2::*)(double const)) &PottsMesh_2::Scale, diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp index 9ac2287..408c8bb 100644 --- a/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp @@ -27,7 +27,7 @@ class PottsMesh_3_Overrides : public PottsMesh_3 void register_PottsMesh_3_class(py::module &m) { - py::class_, AbstractMesh<3>>(m, "PottsMesh_3") + py::class_, AbstractMesh<3, 3>>(m, "PottsMesh_3") .def(py::init<>()) .def("Scale", (void(PottsMesh_3::*)(double const)) &PottsMesh_3::Scale, From 5c80b77602bcc8dfdcead4e361def5e52d0eed6a Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 22 Nov 2024 14:16:19 +0000 Subject: [PATCH 09/10] #20 Fix template class names --- cppwg/info/class_info.py | 50 +++++++++++++--------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index 2542c3b..0056307 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -172,41 +172,23 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 if self.excluded: return - for class_cpp_name in self.cpp_names: - class_name = class_cpp_name.replace(" ", "") # e.g. Foo<2,2> - + for class_cpp_name, class_py_name in zip(self.cpp_names, self.py_names): try: - class_decl = source_ns.class_(class_name) - - except declaration_not_found_t as e1: - if "=" not in self.template_signature: - logger.error(f"Could not find declaration for class {class_name}") - raise e1 - - # If class has default args, try to compress the template signature - logger.warning( - f"Could not find declaration for class {class_name}: trying a partial match." - ) - - # Try to find the class without default template args - # e.g. for template class Foo {}; - # Look for Foo<2> instead of Foo<2,2> - pos = 0 - for i, s in enumerate(self.template_signature.split(",")): - if "=" in s: - pos = i - break - - class_name = ",".join(class_name.split(",")[0:pos]) + " >" - - try: - class_decl = source_ns.class_(class_name) - - except declaration_not_found_t as e2: - logger.error(f"Could not find declaration for class {class_name}") - raise e2 - - logger.info(f"Found {class_name}") + cpp_name = class_cpp_name.replace(" ", "") # e.g. Foo<2,2,1> + class_decl = source_ns.class_(cpp_name) + + except declaration_not_found_t: + # Parsed names for templated classes which have default args + # may vary between CastXML versions and compiler versions. + # Try to look up the class name via the typedef e.g. for + # `template class Foo {};` + # the parsed name for Foo<2,2,1> could be Foo<2,2>, or Foo<2> + # but the typedef name will always be Foo_2_2_1 + py_name = class_py_name.replace(" ", "") # e.g. Foo_2_2_1 + typedef_decl = source_ns.typedef(py_name) + class_decl = typedef_decl.decl_type.declaration + + logger.info(f"Found {class_decl.name} for {class_cpp_name}") self.decls.append(class_decl) From f69abd55325f6735e9684876821d0b494e1ad4e6 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 22 Nov 2024 14:26:05 +0000 Subject: [PATCH 10/10] #20 Increment version to 0.3.4 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 04f5f7a..908756c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = cppwg -version = 0.3.3 +version = 0.3.4 author = Chaste Developers author_email = chaste-users@maillist.ox.ac.uk description = An automatic Python wrapper generator for C++ code