diff --git a/CMakeLists.txt b/CMakeLists.txt index ec10d44..290be4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.22) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export compile commands so cppcheck is aware of compiler options and paths # Define the project name. project(basicSetup) @@ -21,4 +22,32 @@ add_subdirectory(api) add_executable(basicSetup main.cpp) # Add libraries -target_link_libraries(basicSetup PUBLIC kernel api) \ No newline at end of file +target_link_libraries(basicSetup PUBLIC kernel api) + +# Set up cppcheck +# Set up cppcheck build directory and cache +set(CPPCHECK_BUILD_DIR "${CMAKE_BINARY_DIR}/cppcheck") +file(MAKE_DIRECTORY "${CPPCHECK_BUILD_DIR}" "${CPPCHECK_BUILD_DIR}/cache") +find_program(CPPCHECK_EXECUTABLE NAMES cppcheck) + +set(CPPCHECK_ARGS + --enable=all + --check-level=exhaustive + --inline-suppr + --max-configs=120 + --std=c++${CMAKE_CXX_STANDARD} # use the standard from cmake + --cppcheck-build-dir="${CPPCHECK_BUILD_DIR}/cache" + --project="${CMAKE_BINARY_DIR}/compile_commands.json" + --suppress=missingIncludeSystem +) + +if(CPPCHECK_EXECUTABLE) + message(STATUS "Found cppcheck: ${CPPCHECK_EXECUTABLE}") + add_custom_target( + cppcheck + COMMAND ${CPPCHECK_EXECUTABLE} ${CPPCHECK_ARGS} + COMMENT "Running cppcheck static analysis" + ) +else() + message(WARNING "cppcheck not found. Static analysis will be skipped.") +endif() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 198baff..44e094b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,7 +7,7 @@ }, "configurePresets": [ { - "name": "windows-default", + "name": "win-msbuild", "displayName": "Windows x64 Debug", "description": "Sets msvc generator, compilers, x64 architecture, build and install directory, debug build type", "generator": "Visual Studio 17 2022", @@ -15,6 +15,18 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } + }, + { + "name": "win-ninja", + "displayName": "Windows x64 Debug Ninja", + "description": "Sets ninja generator, compilers, x64 architecture, build and install directory, debug build type", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl" + } } ] } diff --git a/README.md b/README.md index 5cba17e..3dddc5f 100644 --- a/README.md +++ b/README.md @@ -3,32 +3,62 @@ This is a repository that represents a basic, minimal, c++ project setup using cmake and conda. This currently includes: - Creation of a `kernel` library. - Creation of an `api` library to expose this `kernel` library. -- Creation of a python interface to enable access to the `api` from python. +- Creation of a python interface to enable access to the `api` from python using pybind. +- Creation of a second python interface using boost (rough first pass) +- cppcheck -Currently only building on windows using `msbuild` has been tested. +Currently only building on windows using `msbuild` and `ninja` has been tested. Instructions to this regard are in the following section. ## Pre-setup requirements - Clone this repository - Ensure `conda` (or preferably `mamba`) is installed. -- Ensure `msbuildtools` are available, and on the path. +## Pre-setup msbuild only -## Setup +- Ensure `msbuildtools` are available, and on the path. These are typically in: `/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin` + + +## Setup msbuild 1. From repository root, create conda environment: `conda env create -f environment.yml`. This will create a conda environment called `basic-setup` 2. Activate this conda environment: `conda activate basic-setup`. -3. Run cmake: `cmake --preset windows-default` +3. Run cmake: `cmake --preset win-ninja` -4. From the created `build` folder, run `msbuild`: `msbuild basicSetup.sln` +4. From the created `build` directory, run `msbuild`: `msbuild basicSetup.sln` 5. Add the directory of the created python interface to the python path: `set PYTHONPATH=%PYTHONPATH%;\build\lib\Debug` +## Setup ninja + +1. On windows this setup must be undertaken using `x64 Native Tools Command Prompt for VS 2022`. + +2. From repository root, create conda environment: `conda env create -f environment.yml`. + This will create a conda environment called `basic-setup` + +3. Activate this conda environment: `conda activate basic-setup`. + +4. Run cmake: `cmake --preset win-ninja` + +5. From the created `build` directory, run `ninja` + +6. Add the directory of the created python interface to the python path: `set PYTHONPATH=%PYTHONPATH%;\build\lib` + ## Testing the repo setup -1. To test that the setup has been successfull, from the repository root run: `python -c "from basicSetupApi import helloWorld; helloWorld()"` +1. To test that the setup has been successfull, from the repository root run: `python -c "from basicSetupApi import helloWorld; helloWorld('')"` + +2. Alternatively (for boost api): `python -c "from api_boost import ComplexNumber; (ComplexNumber(2,5) + ComplexNumber(3,4)).print()"` + +## Running cppcheck + +1. As of the latest version of cppcheck on `conda-forge`, `2.15.0`, there is a bug causing cppcheck to look for `cfg` files in the wrong location: + https://github.com/conda-forge/cppcheck-feedstock/issues/19 + To circumvent this, we must copy the `cfg` directory from `/c/Users//AppData/Local/mambaforge/envs/basic-setup/share/Cppcheck/cfg` to a location on the + path that cppcheck searches: `C:/Users//AppData/Local/mambaforge/envs/basic-setup/Library/bin/cfg` +2. From the `build` directory, run `cmake --build . --target cppcheck` diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 9696db4..fd5d14a 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -11,4 +11,14 @@ target_link_libraries(api PRIVATE kernel) # Setup python interface and link API lib pybind11_add_module(basicSetupApi ${CMAKE_CURRENT_SOURCE_DIR}/pythonInterface/apiBindings.cpp) -target_link_libraries(basicSetupApi PRIVATE api) \ No newline at end of file +target_link_libraries(basicSetupApi PRIVATE api) + +### Boost Python API +find_package(Boost REQUIRED COMPONENTS python312) +find_package(Python3 REQUIRED COMPONENTS Interpreter Development) + +# Create boost api library +add_library(api_boost MODULE ${CMAKE_CURRENT_SOURCE_DIR}/pythonInterface/apiBindingsBoost.cpp) +target_include_directories(api_boost PRIVATE ${Boost_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}) +target_link_libraries(api_boost PRIVATE Boost::boost Boost::python312 ${Python3_LIBRARIES}) +set_target_properties(api_boost PROPERTIES PREFIX "" SUFFIX ".pyd") \ No newline at end of file diff --git a/api/inc/basicSetupApi/helloWorld.h b/api/inc/basicSetupApi/helloWorld.h index d89c379..0850562 100644 --- a/api/inc/basicSetupApi/helloWorld.h +++ b/api/inc/basicSetupApi/helloWorld.h @@ -5,7 +5,7 @@ namespace basicSetup::api{ class helloWorld{ public: helloWorld(); - helloWorld(const std::string &name); + explicit helloWorld(const std::string &name); private: void _print(const std::string &name = "") const; diff --git a/api/pythonInterface/apiBindingsBoost.cpp b/api/pythonInterface/apiBindingsBoost.cpp new file mode 100644 index 0000000..1d17c83 --- /dev/null +++ b/api/pythonInterface/apiBindingsBoost.cpp @@ -0,0 +1,38 @@ +#include +#include + +class ComplexNumber { +private: + double m_real; + double m_imaginary; + +public: + ComplexNumber() : m_real(0), m_imaginary(0) {}; + ComplexNumber(double real, double imaginary) : m_real(real), m_imaginary(imaginary) {}; + const double getReal() const { return m_real; } + const double getImaginary() const { return m_imaginary; } + + ComplexNumber ComplexNumber::operator+(const ComplexNumber& rhs) { + std::cout << "operator+ executed" << std::endl; + return ComplexNumber(m_real + rhs.getReal(), m_imaginary + rhs.getImaginary()); + } + ComplexNumber ComplexNumber::operator-(const ComplexNumber& rhs) { + std::cout << "operator- executed" << std::endl; + return ComplexNumber(m_real - rhs.getReal(), m_imaginary - rhs.getImaginary()); + } + void print() const { std::cout << m_real << " + " << m_imaginary << "i"; } +}; + +int add(int a, int b) { + return a + b; +} + +BOOST_PYTHON_MODULE(api_boost) { + using namespace boost::python; + def("add", add); + + class_("ComplexNumber", init()) + .def(self - self) + .def(self + self) + .def("print", &ComplexNumber::print); +} diff --git a/environment.yml b/environment.yml index 500b254..efcc622 100644 --- a/environment.yml +++ b/environment.yml @@ -1,6 +1,10 @@ name: basic-setup dependencies: - - python + - python==3.12 - pybind11 - - cmake \ No newline at end of file + - cmake + - libboost-devel + - libboost-python-devel + - cppcheck==2.15.0 + - ninja diff --git a/kernel/inc/basicSetupKernel/printer.h b/kernel/inc/basicSetupKernel/printer.h index e16fc3b..45aa03f 100644 --- a/kernel/inc/basicSetupKernel/printer.h +++ b/kernel/inc/basicSetupKernel/printer.h @@ -5,7 +5,7 @@ namespace basicSetup::kernel{ class printer{ public: - printer(const std::string &str); + explicit printer(const std::string &str); private: void _print(const std::string &str) const;