diff --git a/.github/workflows/cicd-mingw64.yml b/.github/workflows/cicd-mingw64.yml deleted file mode 100644 index ac0f4b75..00000000 --- a/.github/workflows/cicd-mingw64.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: CI/CD for mingw64 debian package -on: - push: - branches: - - master - pull_request: - branches: - - master -jobs: - build: - name: Build debian package - timeout-minutes: 10 - runs-on: ubuntu-latest - container: debian:buster - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Install dependencies - run: | - apt-get update - apt-get upgrade -y - apt-get install -y curl cmake gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 - update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix - update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix - curl -1sLf 'https://dl.cloudsmith.io/public/ultimaker/cura-public/cfg/setup/bash.deb.sh' | bash - apt-get install -y cura-libprotobuf-mingw-w64-dev - - name: Build debian package - run: | - docker/build-deb-mingw64.sh - - name: Upload build directory - uses: actions/upload-artifact@v1 - with: - name: build - path: build/ - publish: - name: Publish debian package - if: github.event_name != 'pull_request' - needs: build - timeout-minutes: 5 - runs-on: ubuntu-latest - container: python:3.10-slim-buster - env: - CLOUDSMITH_API_KEY: "${{ secrets.CLOUDSMITH_API_KEY }}" - CLOUDSMITH_DEB_REPO: "${{ secrets.CLOUDSMITH_DEB_REPO }}" - steps: - - name: Check repository - uses: actions/checkout@v2 - - name: Prepare cloudsmith-cli - run: | - pip3 install --upgrade cloudsmith-cli - - name: Download artifacts - uses: actions/download-artifact@v1 - with: - name: build - - name: Publish debian package - run: | - docker/publish-debs.sh diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml deleted file mode 100644 index 3a0b57d7..00000000 --- a/.github/workflows/cicd.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: CI/CD -on: [push, pull_request] -jobs: - build: - name: Build and test - runs-on: ubuntu-latest - container: ultimaker/cura-build-environment - steps: - - name: Checkout master - uses: actions/checkout@v1.2.0 - - name: Build and test - run: docker/build.sh diff --git a/.gitignore b/.gitignore index e2bde249..8f2cac30 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,4 @@ nbproject/* # Generated headers /src/ArcusExport.h +/cmake-build-* diff --git a/CMakeLists.txt b/CMakeLists.txt index a699a722..3eb15437 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,54 +1,13 @@ project(arcus) -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.20) +include(cmake/StandardProjectSettings.cmake) -include(GNUInstallDirs) include(CMakePackageConfigHelpers) include(GenerateExportHeader) -option(BUILD_PYTHON "Build " ON) -option(BUILD_EXAMPLES "Build the example programs" ON) -option(BUILD_STATIC "Build as a static library" OFF) +option(BUILD_PYTHON "Build Python bindings for this library" ON) -if(WIN32) - option(MSVC_STATIC_RUNTIME "Link the MSVC runtime statically" OFF) -endif() - -# We want to have access to protobuf_generate_cpp and other FindProtobuf features. -# However, if ProtobufConfig is used instead, there is a CMake option that controls -# this, which defaults to OFF. We need to force this option to ON instead. -set(protobuf_MODULE_COMPATIBLE ON CACHE INTERNAL "" FORCE) -find_package(Protobuf 3.0.0 REQUIRED) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) #Required if a patch to libArcus needs to be made via templates. - -if(BUILD_PYTHON) - list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) - - if(NOT DEFINED Python_VERSION) - set(Python_VERSION - 3.10 - CACHE STRING "Python Version" FORCE) - message(STATUS "Setting Python version to ${Python_VERSION}. Set Python_VERSION if you want to compile against an other version.") - endif() - if(APPLE) - set(Python_FIND_FRAMEWORK NEVER) - endif() - find_package(Python ${Python_VERSION} EXACT REQUIRED COMPONENTS Interpreter Development) - message(STATUS "Linking and building ${project_name} against Python ${Python_VERSION}") - - find_package(SIP REQUIRED) - if(NOT DEFINED LIB_SUFFIX) - set(LIB_SUFFIX "") - endif() - - include_directories(python/ src/ ${SIP_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) -endif() - -set(CMAKE_CXX_STANDARD 17) - -if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif() +find_package(Protobuf 3.17.1 REQUIRED) set(arcus_SRCS src/Socket.cpp @@ -67,58 +26,40 @@ set(arcus_HDRS ${CMAKE_CURRENT_BINARY_DIR}/src/ArcusExport.h ) -set(ARCUS_VERSION 1.1.0) -set(ARCUS_SOVERSION 3) - -OPTION(SET_RPATH ON) +set(ARCUS_VERSION 5.0.0) +set(ARCUS_SOVERSION 5) -if(SET_RPATH) - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") -endif() - -if(BUILD_STATIC) - add_library(Arcus STATIC ${arcus_SRCS}) - if(NOT WIN32 OR CMAKE_COMPILER_IS_GNUCXX) - target_link_libraries(Arcus PRIVATE pthread) - set_target_properties(Arcus PROPERTIES COMPILE_FLAGS -fPIC) - endif() -else() +if(BUILD_SHARED_LIBS) add_library(Arcus SHARED ${arcus_SRCS}) +else() + add_library(Arcus STATIC ${arcus_SRCS}) endif() - -if(MSVC_STATIC_RUNTIME) - foreach(flag_var - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") - endif(${flag_var} MATCHES "/MD") - endforeach(flag_var) -endif() - -if(BUILD_PYTHON) - set(SIP_EXTRA_FILES_DEPEND python/SocketListener.sip python/Types.sip python/PythonMessage.sip python/Error.sip) - set(SIP_EXTRA_SOURCE_FILES python/PythonMessage.cpp) - set(SIP_EXTRA_OPTIONS -g -n PyQt5.sip) # -g means always release the GIL before calling C++ methods. -n PyQt5.sip is required to not get the PyCapsule error - add_sip_python_module(Arcus python/Socket.sip Arcus) -endif() +set_project_standards(Arcus) +use_threads(Arcus) +set_rpath(TARGETS + Arcus + PATHS + "$<$:usr/bin>" + "$<$:usr/bin/lib>" + "$<$:../lib>" + RELATIVE) target_include_directories(Arcus PUBLIC $ $ ${PROTOBUF_INCLUDE_DIR} ) -target_link_libraries(Arcus PUBLIC ${PROTOBUF_LIBRARIES}) +target_link_libraries(Arcus PUBLIC protobuf::libprotobuf) if(WIN32) - add_definitions(-D_WIN32_WINNT=0x0600) # Declare we require Vista or higher, this allows us to use IPv6 functions. + target_compile_definitions(Arcus PRIVATE -D_WIN32_WINNT=0x0600) + # Declare we require Vista or higher, this allows us to use IPv6 functions. + target_link_libraries(Arcus PUBLIC Ws2_32) endif() -if(${CMAKE_BUILD_TYPE}) - if(${CMAKE_BUILD_TYPE} STREQUAL "Debug" OR ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") - add_definitions(-DARCUS_DEBUG) - endif() +if(${CMAKE_BUILD_TYPE} STREQUAL "Debug" OR ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + target_compile_definitions(Arcus PRIVATE -DARCUS_DEBUG) endif() set_target_properties(Arcus PROPERTIES @@ -138,10 +79,6 @@ generate_export_header(Arcus # The compiler won't find the generated header otherwise. include_directories(${CMAKE_BINARY_DIR}/src) -if(BUILD_EXAMPLES) - add_subdirectory(examples) -endif() - install(TARGETS Arcus EXPORT Arcus-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} @@ -163,4 +100,57 @@ install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Arcus ) -include(CPackConfig.cmake) +# Create the Python bindings +if(BUILD_PYTHON) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + + if(NOT DEFINED Python_VERSION) + set(Python_VERSION + 3.10 + CACHE STRING "Python Version" FORCE) + message(STATUS "Setting Python version to ${Python_VERSION}. Set Python_VERSION if you want to compile against an other version.") + endif() + if(APPLE) + set(Python_FIND_FRAMEWORK NEVER) + endif() + find_package(cpython ${Python_VERSION} QUIET COMPONENTS Interpreter Development) + if(NOT TARGET cpython::cpython) + find_package(Python ${Python_VERSION} EXACT REQUIRED COMPONENTS Interpreter Development) + else() + add_library(Python::Python ALIAS cpython::python) + set(Python_SITEARCH "${CMAKE_INSTALL_PREFIX}/lib/python${Python_VERSION}/site-packages") + set(Python_EXECUTABLE ${cpython_PACKAGE_FOLDER_RELEASE}/bin/python3) + set(ENV{PYTHONPATH} ${Python_SITEARCH}) + endif() + message(STATUS "Linking and building ${project_name} against Python ${Python_VERSION}") + + find_package(SIP REQUIRED 6.5.0) + + add_library(pyArcus INTERFACE ${CMAKE_SOURCE_DIR}/python/PythonMessage.cpp) + set_project_standards(pyArcus) + set_rpath(TARGETS + pyArcus + PATHS + "$<$:usr/bin>" + "$<$:usr/bin/lib>" + "$<$:../lib>" + "$<$:../Resources/lib/>" + "../../" + RELATIVE) + use_threads(pyArcus) + + target_include_directories(pyArcus + INTERFACE + $ + $ + ) + + target_link_libraries(pyArcus INTERFACE Arcus protobuf::libprotobuf Python::Python) + + add_sip_module(pyArcus) + if (DEFINED Python_SITELIB_LOCAL) + install_sip_module(pyArcus ${Python_SITELIB_LOCAL}) + else() + install_sip_module(pyArcus) + endif () +endif() \ No newline at end of file diff --git a/CPackConfig.cmake b/CPackConfig.cmake deleted file mode 100644 index bd1680e6..00000000 --- a/CPackConfig.cmake +++ /dev/null @@ -1,24 +0,0 @@ -set(CPACK_PACKAGE_VENDOR "Ultimaker") -set(CPACK_PACKAGE_CONTACT "Ruben Dulek ") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "libArcus Communication library") -if(NOT DEFINED CPACK_PACKAGE_VERSION) - set(CPACK_PACKAGE_VERSION "15.05.91") -endif() -set(CPACK_GENERATOR "DEB") -if(NOT DEFINED CPACK_DEBIAN_PACKAGE_ARCHITECTURE) - execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE) -endif() -set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") - -set(DEB_DEPENDS - "python3 (>= 3.4.0)" - "libgcc1 (>= 4.9.0)" - "libstdc++6 (>= 4.9.0)" - "libc6 (>= 2.19)" - "zlib1g (>= 1.2.0)" - "protobuf (>= 3.0.0)" -) -string(REPLACE ";" ", " DEB_DEPENDS "${DEB_DEPENDS}") -set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_DEPENDS}) - -include(CPack) diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index f0afbafa..00000000 --- a/Jenkinsfile +++ /dev/null @@ -1,45 +0,0 @@ -parallel_nodes(["linux && cura", "windows && cura"]) { - stage('Prepare') { - step([$class: 'WsCleanup']) - - checkout scm - } - - catchError { - dir('build') { - stage('Build') { - def branch = env.BRANCH_NAME - if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) { - branch = "master" - } - - def extra_cmake_options = "" - if(!isUnix()) - { - // Disable building of Python bindings on Windows for now, - // since it requires MSVC to build properly. - extra_cmake_options = "-DBUILD_PYTHON=OFF" - - // Building example requires dynamic linking, while with MingGW - // on Windows in the build environment doesn't have dynamic protobuf - // library, so we can't do dynamic linking. - extra_cmake_options = "${extra_cmake_options} -DBUILD_EXAMPLES=OFF" - extra_cmake_options = "${extra_cmake_options} -DBUILD_STATIC=ON" - - // The CI is using MinGW as the CMake target, but the MinGW protobuf - // libraries are installed in "lib-mingw" instead of the default "lib" - // directory. We need to specify to use "lib-mingw" specifically so - // the linking process can use the correct library. - extra_cmake_options = "${extra_cmake_options} -DPROTOBUF_LIBRARY=\"${env.CURA_ENVIRONMENT_PATH}/${branch}/inst/lib-mingw/libprotobuf.a\"" - } - - cmake '..', "-DCMAKE_PREFIX_PATH=\"${env.CURA_ENVIRONMENT_PATH}/${branch}\" -DCMAKE_BUILD_TYPE=Release ${extra_cmake_options}" - make('') - } - } - } - - stage('Finalize') { - notify_build_result(env.CURA_EMAIL_RECIPIENTS, '#cura-dev', ['master']) - } -} diff --git a/cmake/CMakeBuilder.py b/cmake/CMakeBuilder.py new file mode 100644 index 00000000..f7aa40fb --- /dev/null +++ b/cmake/CMakeBuilder.py @@ -0,0 +1,13 @@ +from sipbuild import SetuptoolsBuilder + + +class CMakeBuilder(SetuptoolsBuilder): + def __init__(self, project, **kwargs): + print("Using the CMake builder") + super().__init__(project, **kwargs) + + def build(self): + """ Only Generate the source files """ + print("Generating the source files") + self._generate_bindings() + self._generate_scripts() \ No newline at end of file diff --git a/cmake/FindSIP.cmake b/cmake/FindSIP.cmake index e15e2b4e..828e82d4 100644 --- a/cmake/FindSIP.cmake +++ b/cmake/FindSIP.cmake @@ -8,85 +8,58 @@ # # This file defines the following variables: # -# SIP_VERSION - SIP version. +# SIP_VERSION - The version of SIP found expressed as a 6 digit hex number +# suitable for comparison as a string. # -# SIP_EXECUTABLE - Path to the SIP executable. +# SIP_VERSION_STR - The version of SIP found as a human readable string. # -# SIP_INCLUDE_DIRS - The SIP include directories. +# SIP_BINARY_PATH - Path and filename of the SIP command line executable. # +# SIP_INCLUDE_DIR - Directory holding the SIP C++ header file. +# +# SIP_DEFAULT_SIP_DIR - Default directory where .sip files should be installed +# into. # Copyright (c) 2007, Simon Edwards # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. -if(APPLE) - # Workaround for broken FindPythonLibs. It will always find Python 2.7 libs on OSX - set(CMAKE_FIND_FRAMEWORK LAST) -endif() - -# FIXME: Use FindPython3 to find Python, new in CMake 3.12. -# However currently on our CI server it finds the wrong Python version and then doesn't find the headers. -find_package(PythonInterp 3.5 REQUIRED) -find_package(PythonLibs 3.5 REQUIRED) - -# Define variables that are available in FindPython3, so there's no need to branch off in the later part. -set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) -set(Python3_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) -set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -set(Python3_VERSION_MINOR "${PYTHON_VERSION_MINOR}") - -execute_process( - COMMAND ${Python3_EXECUTABLE} -c - "import distutils.sysconfig; print(distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=False))" - RESULT_VARIABLE _process_status - OUTPUT_VARIABLE _process_output - OUTPUT_STRIP_TRAILING_WHITESPACE -) -if(${_process_status} EQUAL 0) - string(STRIP ${_process_output} Python3_SITELIB) -else() - message(FATAL_ERROR "Failed to get Python3_SITELIB. Error: ${_process_output}") -endif() - -execute_process( - COMMAND ${Python3_EXECUTABLE} -c - "import distutils.sysconfig; print(distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False))" - RESULT_VARIABLE _process_status - OUTPUT_VARIABLE _process_output - OUTPUT_STRIP_TRAILING_WHITESPACE -) -if(${_process_status} EQUAL 0) - string(STRIP ${_process_output} Python3_SITEARCH) -else() - message(FATAL_ERROR "Failed to get Python3_SITEARCH. Error: ${_process_output}") -endif() - -get_filename_component(_python_binary_path ${Python3_EXECUTABLE} DIRECTORY) -find_program(SIP_EXECUTABLE sip - HINTS ${CMAKE_PREFIX_PATH}/bin ${CMAKE_INSTALL_PATH}/bin ${_python_binary_path} ${Python3_SITELIB}/PyQt5 -) -find_path(SIP_INCLUDE_DIRS sip.h - HINTS ${CMAKE_PREFIX_PATH}/include ${CMAKE_INSTALL_PATH}/include ${Python3_INCLUDE_DIRS} ${Python3_SITELIB}/PyQt5 -) +IF(SIP_VERSION OR SIP_BUILD_EXECUTABLE) + # Already in cache, be silent + SET(SIP_FOUND TRUE) +ELSE() -execute_process( - COMMAND ${Python3_EXECUTABLE} -c "import sip; print(sip.SIP_VERSION_STR)" - RESULT_VARIABLE _process_status - OUTPUT_VARIABLE _process_output - OUTPUT_STRIP_TRAILING_WHITESPACE -) + FIND_FILE(_find_sip_py FindSIP.py PATHS ${CMAKE_MODULE_PATH} NO_CMAKE_FIND_ROOT_PATH) -if(${_process_status} EQUAL 0) - string(STRIP ${_process_output} SIP_VERSION) -endif() + EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) + IF(sip_config) + STRING(REGEX REPLACE "^sip_version:([^\n]+).*$" "\\1" SIP_VERSION ${sip_config}) + STRING(REGEX REPLACE ".*\nsip_version_num:([^\n]+).*$" "\\1" SIP_VERSION_NUM ${sip_config}) + STRING(REGEX REPLACE ".*\nsip_version_str:([^\n]+).*$" "\\1" SIP_VERSION_STR ${sip_config}) + STRING(REGEX REPLACE ".*\ndefault_sip_dir:([^\n]+).*$" "\\1" SIP_DEFAULT_SIP_DIR ${sip_config}) + IF(${SIP_VERSION_STR} VERSION_LESS 5) + STRING(REGEX REPLACE ".*\nsip_bin:([^\n]+).*$" "\\1" SIP_BINARY_PATH ${sip_config}) + STRING(REGEX REPLACE ".*\nsip_inc_dir:([^\n]+).*$" "\\1" SIP_INCLUDE_DIR ${sip_config}) + STRING(REGEX REPLACE ".*\nsip_module_dir:([^\n]+).*$" "\\1" SIP_MODULE_DIR ${sip_config}) + ELSE(${SIP_VERSION_STR} VERSION_LESS 5) + FIND_PROGRAM(SIP_BUILD_EXECUTABLE sip-build) + ENDIF(${SIP_VERSION_STR} VERSION_LESS 5) + SET(SIP_FOUND TRUE) + ENDIF(sip_config) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SIP REQUIRED_VARS SIP_EXECUTABLE SIP_INCLUDE_DIRS VERSION_VAR SIP_VERSION) + IF(SIP_FOUND) + IF(NOT SIP_FIND_QUIETLY) + MESSAGE(STATUS "Found SIP version: ${SIP_VERSION_STR}") + ENDIF(NOT SIP_FIND_QUIETLY) + ELSE(SIP_FOUND) + IF(SIP_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find SIP") + ENDIF(SIP_FIND_REQUIRED) + ENDIF(SIP_FOUND) -if(SIP_FOUND) - include(${CMAKE_CURRENT_LIST_DIR}/SIPMacros.cmake) -endif() +ENDIF() -mark_as_advanced(SIP_EXECUTABLE SIP_INCLUDE_DIRS SIP_VERSION) +include(${CMAKE_SOURCE_DIR}/cmake/SIPMacros.cmake) +ADD_DEFINITIONS(-DSIP_VERSION=0x${SIP_VERSION}) diff --git a/cmake/FindSIP.py b/cmake/FindSIP.py new file mode 100644 index 00000000..b7bfc9a7 --- /dev/null +++ b/cmake/FindSIP.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2007, Simon Edwards +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the Simon Edwards nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY Simon Edwards ''AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL Simon Edwards BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# FindSIP.py +# Copyright (c) 2007, Simon Edwards +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +try: + import sipbuild + + print("sip_version:%06.0x" % sipbuild.version.SIP_VERSION) + print("sip_version_num:%d" % sipbuild.version.SIP_VERSION) + print("sip_version_str:%s" % sipbuild.version.SIP_VERSION_STR) + + from distutils.sysconfig import get_python_lib + python_modules_dir = get_python_lib(plat_specific=1) + print("default_sip_dir:%s" % python_modules_dir) +except ImportError: # Code for SIP v4 + import sipconfig + + sipcfg = sipconfig.Configuration() + print("sip_version:%06.0x" % sipcfg.sip_version) + print("sip_version_num:%d" % sipcfg.sip_version) + print("sip_version_str:%s" % sipcfg.sip_version_str) + print("sip_bin:%s" % sipcfg.sip_bin) + print("default_sip_dir:%s" % sipcfg.default_sip_dir) + print("sip_inc_dir:%s" % sipcfg.sip_inc_dir) + # SIP 4.19.10+ has new sipcfg.sip_module_dir + if hasattr(sipcfg, "sip_module_dir"): + print("sip_module_dir:%s" % sipcfg.sip_module_dir) + else: + print("sip_module_dir:%s" % sipcfg.sip_mod_dir) diff --git a/cmake/SIPMacros.cmake b/cmake/SIPMacros.cmake index 50553e0f..51645245 100644 --- a/cmake/SIPMacros.cmake +++ b/cmake/SIPMacros.cmake @@ -1,128 +1,117 @@ # Macros for SIP # ~~~~~~~~~~~~~~ -# Copyright (c) 2007, Simon Edwards -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# -# SIP website: http://www.riverbankcomputing.co.uk/sip/index.php -# -# This file defines the following macros: -# -# ADD_SIP_PYTHON_MODULE (MODULE_NAME MODULE_SIP [library1, libaray2, ...]) -# Specifies a SIP file to be built into a Python module and installed. -# MODULE_NAME is the name of Python module including any path name. (e.g. -# os.sys, Foo.bar etc). MODULE_SIP the path and filename of the .sip file -# to process and compile. libraryN are libraries that the Python module, -# which is typically a shared library, should be linked to. The built -# module will also be install into Python's site-packages directory. -# -# The behaviour of the ADD_SIP_PYTHON_MODULE macro can be controlled by a -# number of variables: -# -# SIP_INCLUDE_DIRS - List of directories which SIP will scan through when looking -# for included .sip files. (Corresponds to the -I option for SIP.) -# -# SIP_TAGS - List of tags to define when running SIP. (Corresponds to the -t -# option for SIP.) -# -# SIP_CONCAT_PARTS - An integer which defines the number of parts the C++ code -# of each module should be split into. Defaults to 8. (Corresponds to the -# -j option for SIP.) -# -# SIP_DISABLE_FEATURES - List of feature names which should be disabled -# running SIP. (Corresponds to the -x option for SIP.) -# -# SIP_EXTRA_OPTIONS - Extra command line options which should be passed on to -# SIP. -SET(SIP_INCLUDE_DIRS) -SET(SIP_TAGS) -SET(SIP_CONCAT_PARTS 8) -SET(SIP_DISABLE_FEATURES) -SET(SIP_EXTRA_OPTIONS) - -MACRO(ADD_SIP_PYTHON_MODULE MODULE_NAME MODULE_SIP) - - SET(EXTRA_LINK_LIBRARIES ${ARGN}) - - STRING(REPLACE "." "/" _x ${MODULE_NAME}) - GET_FILENAME_COMPONENT(_parent_module_path ${_x} PATH) - GET_FILENAME_COMPONENT(_child_module_name ${_x} NAME) - - GET_FILENAME_COMPONENT(_module_path ${MODULE_SIP} PATH) - GET_FILENAME_COMPONENT(_abs_module_sip ${MODULE_SIP} ABSOLUTE) - - # We give this target a long logical target name. - # (This is to avoid having the library name clash with any already - # install library names. If that happens then cmake dependency - # tracking get confused.) - STRING(REPLACE "." "_" _logical_name ${MODULE_NAME}) - SET(_logical_name "python_module_${_logical_name}") - - FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_module_path}) # Output goes in this dir. - - SET(_sip_includes) - FOREACH (_inc ${SIP_INCLUDES}) - GET_FILENAME_COMPONENT(_abs_inc ${_inc} ABSOLUTE) - LIST(APPEND _sip_includes -I ${_abs_inc}) - ENDFOREACH (_inc ) - - SET(_sip_tags) - FOREACH (_tag ${SIP_TAGS}) - LIST(APPEND _sip_tags -t ${_tag}) - ENDFOREACH (_tag) - - SET(_sip_x) - FOREACH (_x ${SIP_DISABLE_FEATURES}) - LIST(APPEND _sip_x -x ${_x}) - ENDFOREACH (_x ${SIP_DISABLE_FEATURES}) - - SET(_message "-DMESSAGE=Generating CPP code for module ${MODULE_NAME}") - SET(_sip_output_files) - FOREACH(CONCAT_NUM RANGE 0 ${SIP_CONCAT_PARTS} ) - IF( ${CONCAT_NUM} LESS ${SIP_CONCAT_PARTS} ) - SET(_sip_output_files ${_sip_output_files} ${CMAKE_CURRENT_BINARY_DIR}/${_module_path}/sip${_child_module_name}part${CONCAT_NUM}.cpp ) - ENDIF( ${CONCAT_NUM} LESS ${SIP_CONCAT_PARTS} ) - ENDFOREACH(CONCAT_NUM RANGE 0 ${SIP_CONCAT_PARTS} ) - - # Suppress warnings - IF(PEDANTIC) - IF(MSVC) - # 4996 deprecation warnings (bindings re-export deprecated methods) - # 4701 potentially uninitialized variable used (sip generated code) - # 4702 unreachable code (sip generated code) - ADD_DEFINITIONS( /wd4996 /wd4701 /wd4702 ) - ELSE(MSVC) - # disable all warnings - ADD_DEFINITIONS( -w ) - ENDIF(MSVC) - ENDIF(PEDANTIC) - - ADD_CUSTOM_COMMAND( - OUTPUT ${_sip_output_files} - COMMAND ${CMAKE_COMMAND} -E echo ${message} - COMMAND ${CMAKE_COMMAND} -E touch ${_sip_output_files} - COMMAND ${SIP_EXECUTABLE} ${_sip_tags} ${_sip_x} ${SIP_EXTRA_OPTIONS} -j ${SIP_CONCAT_PARTS} -c ${CMAKE_CURRENT_BINARY_DIR}/${_module_path} ${_sip_includes} ${_abs_module_sip} - DEPENDS ${_abs_module_sip} ${SIP_EXTRA_FILES_DEPEND} +set(SIP_ARGS --pep484-pyi --no-protected-is-public) + +function(add_sip_module MODULE_TARGET) + if(NOT SIP_BUILD_EXECUTABLE) + set(SIP_BUILD_EXECUTABLE ${CMAKE_PREFIX_PATH}/Scripts/sip-build) + endif() + + message(STATUS "SIP: Generating pyproject.toml") + configure_file(${CMAKE_SOURCE_DIR}/pyproject.toml.in ${CMAKE_CURRENT_BINARY_DIR}/pyproject.toml) + configure_file(${CMAKE_SOURCE_DIR}/cmake/CMakeBuilder.py ${CMAKE_CURRENT_BINARY_DIR}/CMakeBuilder.py) + if(WIN32) + set(ext .pyd) + set(env_path_sep ";") + else() + set(ext .so) + set(env_path_sep ":") + endif() + + message(STATUS "SIP: Generating source files") + execute_process( + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${PYTHONPATH}${env_path_sep}$ENV{PYTHONPATH}${env_path_sep}${CMAKE_CURRENT_BINARY_DIR}" ${SIP_BUILD_EXECUTABLE} ${SIP_ARGS} + COMMAND_ECHO STDOUT + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ + ) + # This will generate the source-files during the configuration step in CMake. Needed to obtain the sources + + # Touch the generated files (8 in total) to make them dirty and force them to rebuild + message(STATUS "SIP: Touching the source files") + set(_sip_output_files) + list(LENGTH SIP_FILES _no_outputfiles) + foreach(_concat_file_nr RANGE 0 ${_no_outputfiles}) + if(${_concat_file_nr} LESS 8) + list(APPEND _sip_output_files "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}/${MODULE_TARGET}/sip${MODULE_TARGET}part${_concat_file_nr}.cpp") + endif() + endforeach() + + # Find the generated source files + message(STATUS "SIP: Collecting the generated source files") + file(GLOB sip_c "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}/${MODULE_TARGET}/*.c") + file(GLOB sip_cpp "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}/${MODULE_TARGET}/*.cpp") + file(GLOB sip_hdr "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}/${MODULE_TARGET}/*.h") + + # Add the user specified source files + message(STATUS "SIP: Collecting the user specified source files") + get_target_property(usr_src ${MODULE_TARGET} SOURCES) + + # create the target library and link all the files (generated and user specified + message(STATUS "SIP: Linking the interface target against the shared library") + set(sip_sources "${sip_c}" "${sip_cpp}" "${usr_src}") + + if (BUILD_SHARED_LIBS) + add_library("sip_${MODULE_TARGET}" SHARED ${sip_sources}) + else() + add_library("sip_${MODULE_TARGET}" STATIC ${sip_sources}) + endif() + + # Make sure that the library name of the target is the same as the MODULE_TARGET with the appropriate extension + target_link_libraries("sip_${MODULE_TARGET}" PRIVATE "${MODULE_TARGET}") + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES PREFIX "") + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES SUFFIX ${ext}) + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES OUTPUT_NAME "${MODULE_TARGET}") + + # Make sure all rpaths are set from the INTERFACE target + get_target_property(_SKIP_BUILD_RPATH ${MODULE_TARGET} SKIP_BUILD_RPATH) + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES SKIP_BUILD_RPATH "${_SKIP_BUILD_RPATH}") + get_target_property(_BUILD_WITH_INSTALL_RPATH ${MODULE_TARGET} BUILD_WITH_INSTALL_RPATH) + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES BUILD_WITH_INSTALL_RPATH "${_BUILD_WITH_INSTALL_RPATH}") + get_target_property(_INSTALL_RPATH_USE_LINK_PATH ${MODULE_TARGET} INSTALL_RPATH_USE_LINK_PATH) + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES INSTALL_RPATH_USE_LINK_PATH "${_INSTALL_RPATH_USE_LINK_PATH}") + get_target_property(_MACOSX_RPATH ${MODULE_TARGET} MACOSX_RPATH) + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES MACOSX_RPATH "${_MACOSX_RPATH}") + get_target_property(_INSTALL_RPATH ${MODULE_TARGET} INSTALL_RPATH) + set_target_properties("sip_${MODULE_TARGET}" PROPERTIES INSTALL_RPATH "${_INSTALL_RPATH}") + + # Add the custom command to (re-)generate the files and mark them as dirty. This allows the user to actually work + # on the sip definition files without having to reconfigure the complete project. + if (NOT DEFINED PYTHONPATH) + set(PYTHONPATH "") + endif () + add_custom_command( + TARGET "sip_${MODULE_TARGET}" + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${PYTHONPATH}${env_path_sep}$ENV{PYTHONPATH}${env_path_sep}${CMAKE_CURRENT_BINARY_DIR}" ${SIP_BUILD_EXECUTABLE} ${SIP_ARGS} + COMMAND ${CMAKE_COMMAND} -E touch ${_sip_output_files} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ + MAIN_DEPENDENCY ${MODULE_SIP} + DEPENDS ${sip_sources} + VERBATIM ) - ADD_LIBRARY(${_logical_name} MODULE ${_sip_output_files} ${SIP_EXTRA_SOURCE_FILES}) - IF (NOT APPLE) - IF ("${Python3_VERSION_MINOR}" GREATER 7) - MESSAGE(STATUS "Python > 3.7 - not linking to libpython") - ELSE () - TARGET_LINK_LIBRARIES(${_logical_name} ${Python3_LIBRARIES}) - ENDIF () - ENDIF (NOT APPLE) - TARGET_LINK_LIBRARIES(${_logical_name} ${EXTRA_LINK_LIBRARIES}) - IF (APPLE) - SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") - ENDIF (APPLE) - SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES PREFIX "" OUTPUT_NAME ${_child_module_name}) - - IF (WIN32) - SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES SUFFIX ".pyd" IMPORT_PREFIX "_") - ENDIF (WIN32) - - INSTALL(TARGETS ${_logical_name} DESTINATION "${Python3_SITEARCH}/${_parent_module_path}") -ENDMACRO(ADD_SIP_PYTHON_MODULE) + set_target_properties("sip_${MODULE_TARGET}" + PROPERTIES + RESOURCE "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}/${MODULE_TARGET}/${MODULE_TARGET}.pyi") +endfunction() + +function(install_sip_module MODULE_TARGET) + if(DEFINED ARGV1) + set(_install_path ${ARGV1}) + else() + if(DEFINED Python_SITEARCH) + set(_install_path ${Python_SITEARCH}) + elseif(DEFINED Python_SITELIB) + set(_install_path ${Python_SITELIB}) + else() + message(FATAL_ERROR "SIP: Specify the site-packages location") + endif() + endif() + message(STATUS "SIP: Installing Python module and PEP 484 file in ${_install_path}") + install(TARGETS "sip_${MODULE_TARGET}" + ARCHIVE DESTINATION ${_install_path} + LIBRARY DESTINATION ${_install_path} + RUNTIME DESTINATION ${_install_path} + RESOURCE DESTINATION ${_install_path} + ) +endfunction() \ No newline at end of file diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake new file mode 100644 index 00000000..37ff00e5 --- /dev/null +++ b/cmake/StandardProjectSettings.cmake @@ -0,0 +1,360 @@ +include(GNUInstallDirs) # Standard install dirs + + +if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) + message(STATUS "Setting BUILD_SHARED_LIBS to ${BUILD_SHARED_LIBS}") +endif() + +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Release' as none was specified.") + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui, ccmake + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" + "Release" + "MinSizeRel" + "RelWithDebInfo") +endif() + +# Generate compile_commands.json to make it easier to work with clang based tools +message(STATUS "Generating compile commands to ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" ON) +if(ENABLE_IPO) + include(CheckIPOSupported) + check_ipo_supported( + RESULT + result + OUTPUT + output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(SEND_ERROR "IPO is not supported: ${output}") + endif() +endif() + +if (NOT MSVC) + # Compile with the -fPIC options if supported + if(DEFINED POSITION_INDEPENDENT_CODE) # Use the user/Conan set value + message(STATUS "Using POSITION_INDEPENDENT_CODE: ${POSITION_INDEPENDENT_CODE}") + else() + set(POSITION_INDEPENDENT_CODE ON) # Defaults to on + message(STATUS "Setting POSITION_INDEPENDENT_CODE: ${POSITION_INDEPENDENT_CODE}") + endif() +else() + # Set Visual Studio flags MD/MDd or MT/MTd + if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) + if(BUILD_STATIC OR NOT BUILD_SHARED_LIBS) + message(STATUS "Setting MT/MTd flags") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + else() + message(STATUS "Setting MD/MDd flags") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + endif() + endif() +endif() + +# Use C++17 Standard +message(STATUS "Setting C++17 support with extensions off and standard required") +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Set common project options for the target +function(set_project_standards project_name) + get_target_property(type ${project_name} TYPE) + if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + option(ENABLE_BUILD_WITH_TIME_TRACE "Enable -ftime-trace to generate time tracing .json files on clang" OFF) + if(ENABLE_BUILD_WITH_TIME_TRACE) + message(STATUS "Enabling time tracing for ${project_name}") + if (${type} STREQUAL "INTERFACE_LIBRARY") + add_compile_definitions(${project_name} INTERFACE -ftime-trace) + else() + add_compile_definitions(${project_name} PRIVATE -ftime-trace) + endif() + endif() + if (APPLE) + message(STATUS "Compiling ${project_name} against libc++") + if (${type} STREQUAL "INTERFACE_LIBRARY") + target_compile_options(${project_name} INTERFACE "-stdlib=libc++") + else() + target_compile_options(${project_name} PRIVATE "-stdlib=libc++") + endif() + endif() + endif() +endfunction() + +function(set_rpath) + # Sets the RPATHS for targets (Linux and Windows, these can either be absolute paths or relative to the executable + # Usage: + # set_rpath(TARGETS + # PATHS + # RELATIVE) + # if the RELATIVE option is used the paths will be specified from either $ORIGIN on Linux or @executable_path on Mac + set(options RELATIVE) + set(oneValueArgs ) + set(multiValueArgs TARGETS PATHS) + cmake_parse_arguments(SET_RPATH "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + foreach(_target IN ITEMS ${SET_RPATH_TARGETS}) + message(STATUS "Setting SKIP_BUILD_RPATH for target ${_target} to FALSE") + set_target_properties(${_target} PROPERTIES SKIP_BUILD_RPATH FALSE) + message(STATUS "Setting BUILD_WITH_INSTALL_RPATH for target ${_target} to FALSE") + set_target_properties(${_target} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE) + message(STATUS "Setting INSTALL_RPATH_USE_LINK_PATH for target ${_target} to TRUE") + set_target_properties(${_target} PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) + if(APPLE) + message(STATUS "Setting MACOSX_RPATH for target ${_target}") + set_target_properties(${_target} PROPERTIES MACOSX_RPATH ON) + endif() + if(SET_RPATH_RELATIVE) + list(PREPEND SET_RPATH_PATHS "") + if(APPLE) + set(loader_path "${SET_RPATH_PATHS}") + list(TRANSFORM loader_path PREPEND "@loader_path/") + list(TRANSFORM SET_RPATH_PATHS PREPEND "@executable_path/") + list(APPEND SET_RPATH_PATHS ${loader_path}) + else(LINUX) + list(TRANSFORM SET_RPATH_PATHS PREPEND "\$ORIGIN/") + endif() + endif() + set_target_properties(${_target} PROPERTIES INSTALL_RPATH "${SET_RPATH_PATHS}") + message(STATUS "Setting install RPATH for target ${_target} to ${SET_RPATH_PATHS}") + endforeach() +endfunction() + +# Ultimaker uniform Python linking method +function(use_python project_name) + find_package(Python REQUIRED) + get_target_property(type ${project_name} TYPE) + if(${type} STREQUAL "INTERFACE_LIBRARY") + target_link_libraries(${project_name} INTERFACE Python::Python) + else() + target_link_libraries(${project_name} PRIVATE Python::Python) + endif() + message(STATUS "Linking and building ${project_name} against Python ${Python_VERSION}") +endfunction() + +# Ultimaker uniform Thread linking method +function(use_threads project_name) + message(STATUS "Enabling threading support for ${project_name}") + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads) + get_target_property(type ${project_name} TYPE) + if (${type} STREQUAL "INTERFACE_LIBRARY") + target_link_libraries(${project_name} INTERFACE Threads::Threads) + else() + target_link_libraries(${project_name} PRIVATE Threads::Threads) + endif() +endfunction() + +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md +function(set_project_warnings project_name) + message(STATUS "Setting warnings for ${project_name}") + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + ) + + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + + if(MSVC) + set(PROJECT_WARNINGS ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + endif() + + get_target_property(type ${project_name} TYPE) + if (${type} STREQUAL "INTERFACE_LIBRARY") + target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS}) + else() + target_compile_options(${project_name} PRIVATE ${PROJECT_WARNINGS}) + endif() +endfunction() + +# This function will prevent in-source builds +function(assure_out_of_source_builds) + # make sure the user doesn't play dirty with symlinks + get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) + get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) + + # disallow in-source builds + if("${srcdir}" STREQUAL "${bindir}") + message("######################################################") + message("Warning: in-source builds are disabled") + message("Please create a separate build directory and run cmake from there") + message("######################################################") + message(FATAL_ERROR "Quitting configuration") + endif() +endfunction() + +option(ALLOW_IN_SOURCE_BUILD "Allow building in your source folder. Strongly discouraged" OFF) +if(NOT ALLOW_IN_SOURCE_BUILD) + assure_out_of_source_builds() +endif() + +function(enable_sanitizers project_name) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) + + if(ENABLE_COVERAGE) + target_compile_options(${project_name} INTERFACE --coverage -O0 -g) + target_link_libraries(${project_name} INTERFACE --coverage) + endif() + + set(SANITIZERS "") + + option(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE) + if(ENABLE_SANITIZER_ADDRESS) + list(APPEND SANITIZERS "address") + endif() + + option(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" FALSE) + if(ENABLE_SANITIZER_LEAK) + list(APPEND SANITIZERS "leak") + endif() + + option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) + if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) + list(APPEND SANITIZERS "undefined") + endif() + + option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) + if(ENABLE_SANITIZER_THREAD) + if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) + message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "thread") + endif() + endif() + + option(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE) + if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + if("address" IN_LIST SANITIZERS + OR "thread" IN_LIST SANITIZERS + OR "leak" IN_LIST SANITIZERS) + message(WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "memory") + endif() + endif() + + list( + JOIN + SANITIZERS + "," + LIST_OF_SANITIZERS) + + endif() + + if(LIST_OF_SANITIZERS) + if(NOT + "${LIST_OF_SANITIZERS}" + STREQUAL + "") + target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + target_link_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + endif() + endif() + +endfunction() + +option(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) +option(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF) +option(ENABLE_INCLUDE_WHAT_YOU_USE "Enable static analysis with include-what-you-use" OFF) + +if(ENABLE_CPPCHECK) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + message(STATUS "Using cppcheck") + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK} + --suppress=missingInclude + --enable=all + --inline-suppr + --inconclusive + -i + ${CMAKE_SOURCE_DIR}/imgui/lib) + else() + message(WARNING "cppcheck requested but executable not found") + endif() +endif() + +if(ENABLE_CLANG_TIDY) + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + message(STATUS "Using clang-tidy") + set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option) + else() + message(WARNING "clang-tidy requested but executable not found") + endif() +endif() + +if(ENABLE_INCLUDE_WHAT_YOU_USE) + find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) + if(INCLUDE_WHAT_YOU_USE) + message(STATUS "Using include-what-you-use") + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) + else() + message(WARNING "include-what-you-use requested but executable not found") + endif() +endif() \ No newline at end of file diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 00000000..dee88ad2 --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,10 @@ +[requires] +protobuf/3.17.1 +cpython/3.10.0 + +[generators] +virtualrunenv +CMakeToolchain +CMakeDeps + +[options] \ No newline at end of file diff --git a/docker/build-deb-mingw64.sh b/docker/build-deb-mingw64.sh deleted file mode 100755 index b5927bfb..00000000 --- a/docker/build-deb-mingw64.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# -# This scripts uses CMake to build protobuf with static libraries using MinGW -# W64 targeting Windows x64. It also creates a debian package with cpack. The -# contents of the package are installed under "/usr/x86_64-w64-mingw32". -# - -# Include binaries in the cura development environment -CURA_DEV_ENV_ROOT=/opt/cura-dev -export PATH="${CURA_DEV_ENV_ROOT}/bin:${PATH}" - -mkdir build -cd build -cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_SYSTEM_NAME="Windows" \ - -DCMAKE_FIND_ROOT_PATH=/usr/x86_64-w64-mingw32 \ - -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc \ - -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ \ - -DBUILD_PYTHON=OFF \ - -DBUILD_EXAMPLES=OFF \ - -DBUILD_STATIC=ON \ - .. -make -cpack \ - --config ../cmake/cpack_config_deb_mingw64.cmake \ - -D CPACK_INSTALL_CMAKE_PROJECTS="$(pwd);libArcus;ALL;/" diff --git a/docker/build.sh b/docker/build.sh deleted file mode 100755 index 0f28c5b8..00000000 --- a/docker/build.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -e - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -PROJECT_DIR="$( cd "${SCRIPT_DIR}/.." && pwd )" - -# Make sure that environment variables are set properly -export PATH="${CURA_BUILD_ENV_PATH}/bin:${PATH}" -export PKG_CONFIG_PATH="${CURA_BUILD_ENV_PATH}/lib/pkgconfig:${PKG_CONFIG_PATH}" -export LD_LIBRARY_PATH="${CURA_BUILD_ENV_PATH}/lib:${LD_LIBRARY_PATH}" - -cd "${PROJECT_DIR}" - -mkdir build -cd build -cmake \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_PREFIX_PATH="${CURA_BUILD_ENV_PATH}" \ - -DBUILD_TESTS=ON \ - .. -make -ctest --output-on-failure -T Test diff --git a/docker/publish-debs.sh b/docker/publish-debs.sh deleted file mode 100755 index 70e48929..00000000 --- a/docker/publish-debs.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# -# This scripts lists all the .deb files in the "build/" directory and -# publishes them one by one to Cloudsmith.io -# - -for f in build/*.deb -do - echo "Uploading '${f}' to Cloudsmith..." - cloudsmith push deb \ - --api-key "${CLOUDSMITH_API_KEY}" \ - --republish "${CLOUDSMITH_DEB_REPO}" "${f}" -done diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt deleted file mode 100644 index 012d2ebb..00000000 --- a/examples/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ - -include_directories(example ${CMAKE_CURRENT_BINARY_DIR}) - -set(example_SRCS - example.cpp - example_pb2.py -) - -protobuf_generate_cpp(example_PB_SRCS example_PB_HDRS "example.proto") - -add_executable(example ${example_SRCS} ${example_PB_SRCS}) -target_link_libraries(example Arcus) -if(NOT WIN32 OR CMAKE_COMPILER_IS_GNUCXX) - target_link_libraries(example pthread) - set_target_properties(example PROPERTIES COMPILE_FLAGS "-std=c++11") -endif() - -add_custom_command( - OUTPUT example_pb2.py - COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} - ARGS --python_out ${CMAKE_CURRENT_BINARY_DIR} --proto_path=${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/example.proto - COMMENT "Running Python protocol buffer compiler on example.proto" - VERBATIM ) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/example_py.sh ${CMAKE_CURRENT_BINARY_DIR}/example_py.sh) diff --git a/examples/example.cpp b/examples/example.cpp deleted file mode 100644 index f3a7defd..00000000 --- a/examples/example.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include - -#include "../src/Socket.h" -#include "../src/SocketListener.h" -#include "../src/Error.h" - -#include "example.pb.h" - -struct Object -{ -public: - int id; - std::string vertices; - std::string normals; - std::string indices; -}; - -class Listener : public Arcus::SocketListener -{ -public: - void stateChanged(Arcus::SocketState::SocketState new_state) - { - std::cout << "State Changed: " << new_state << std::endl; - } - - void messageReceived() - { - } - - void error(const Arcus::Error& new_error) - { - std::cout << new_error << std::endl; - } -}; - -std::vector objects; - -void handleMessage(Arcus::Socket& socket, Arcus::MessagePtr message); - -int main(int argc, char** argv) -{ - Arcus::Socket socket; - - socket.registerMessageType(&Example::ObjectList::default_instance()); - socket.registerMessageType(&Example::ProgressUpdate::default_instance()); - socket.registerMessageType(&Example::SlicedObjectList::default_instance()); - - socket.addListener(new Listener()); - - std::cout << "Connecting to server\n"; - socket.connect("127.0.0.1", 56789); - - while(socket.getState() != Arcus::SocketState::Connected) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - std::cout << "Connected to server\n"; - - while(true) - { - if(socket.getState() == Arcus::SocketState::Connected) - { - auto message = socket.takeNextMessage(); - if(message) - { - handleMessage(socket, message); - } - std::this_thread::sleep_for(std::chrono::milliseconds(250)); - } - else if(socket.getState() == Arcus::SocketState::Closed || socket.getState() == Arcus::SocketState::Error) - { - break; - } - } - - socket.close(); - return 0; -} - -void handleMessage(Arcus::Socket& socket, Arcus::MessagePtr message) -{ - // (Dynamicly) cast the message to one of our types. If this works (does not return a nullptr), we've found the right type. - auto objectList = dynamic_cast(message.get()); - if(objectList) - { - objects.clear(); - - std::cout << "Received object list containing " << objectList->objects_size() << " objects" << std::endl; - - for(auto objectDesc : objectList->objects()) - { - Object obj; - obj.id = objectDesc.id(); - obj.vertices = objectDesc.vertices(); - obj.normals = objectDesc.normals(); - obj.indices = objectDesc.indices(); - objects.push_back(obj); - } - - auto msg = std::make_shared(); - int progress = 0; - for(auto object : objects) - { - auto slicedObject = msg->add_objects(); - slicedObject->set_id(object.id); - - for(int i = 0; i < 1000; ++i) - { - auto polygon = slicedObject->add_polygons(); - polygon->set_type(i % 2 == 0 ? Example::Polygon_Type_InnerType : Example::Polygon_Type_OuterType); - polygon->set_points(object.vertices); - } - - auto update = std::make_shared(); - update->set_objectid(object.id); - update->set_amount((float(++progress) / float(objects.size())) * 100.f); - socket.sendMessage(update); - } - - std::cout << "Sending SlicedObjectList" << std::endl; - socket.sendMessage(msg); - - return; - } -} diff --git a/examples/example.proto b/examples/example.proto deleted file mode 100644 index 91568aa6..00000000 --- a/examples/example.proto +++ /dev/null @@ -1,40 +0,0 @@ -syntax = "proto3"; - -package Example; - -message ObjectList { - repeated Object objects = 1; -} - -message Object { - int32 id = 1; - bytes vertices = 2; //An array of 3 floats. - bytes normals = 3; //An array of 3 floats. - bytes indices = 4; //An array of ints. -} - -message ProgressUpdate { - int32 objectId = 1; - int32 amount = 2; -} - -message SlicedObjectList { - repeated SlicedObject objects = 1; -} - -message SlicedObject { - int32 id = 1; - - repeated Polygon polygons = 2; -} - -message Polygon { - enum Type - { - InnerType = 0; - OuterType = 1; - } - - Type type = 1; - bytes points = 2; -} diff --git a/examples/example.py b/examples/example.py deleted file mode 100644 index 709dce91..00000000 --- a/examples/example.py +++ /dev/null @@ -1,82 +0,0 @@ -import Arcus - -import time -import os.path - -class Listener(Arcus.SocketListener): - def stateChanged(self, state): - print("Socket state changed:", state) - - def messageReceived(self): - message = self.getSocket().takeNextMessage() - - if message.getTypeName() == "Example.ProgressUpdate": - print("Progress:", message.amount) - - if message.getTypeName() == "Example.SlicedObjectList": - print("Sliced Objects:", message.repeatedMessageCount("objects")) - - for i in range(message.repeatedMessageCount("objects")): - obj = message.getRepeatedMessage("objects", i) - print(" Object ID:", obj.id) - print(" Polygon Count:", obj.repeatedMessageCount("polygons")) - - def error(self, error): - print(error) - -print("Creating socket") - -socket = Arcus.Socket() - -print("Registering message types") - -path = os.path.dirname(os.path.abspath(__file__)).replace("\\", "/") -if not socket.registerAllMessageTypes(path + "/example.proto"): - print("Failed to register messages:", socket.getLastError()) - - -print("Creating listener") - -listener = Listener() -socket.addListener(listener) - -print("Listening for connections on 127.0.0.1:56789") - -socket.listen('127.0.0.1', 56789) - -while(socket.getState() != Arcus.SocketState.Connected): - time.sleep(0.1) - -#time.sleep(5) #Sleep for a bit so the other side can connect - -if(socket.getState() == Arcus.SocketState.Connected): - print("Connection established") -else: - print(socket.getState()) - print("Could not establish a connection:", socket.getLastError()) - exit(1) - -for i in range(10): - msg = socket.createMessage("Example.ObjectList") - - for i in range(100): - obj = msg.addRepeatedMessage("objects") - obj.id = i - obj.vertices = b'abcdefghijklmnopqrstuvwxyz' * 10 - obj.normals = b'abcdefghijklmnopqrstuvwxyz' * 10 - obj.indices = b'abcdefghijklmnopqrstuvwxyz' * 10 - - print("Sending message containing", msg.repeatedMessageCount("objects"), "objects") - socket.sendMessage(msg) - - time.sleep(1) - - if socket.getState() != Arcus.SocketState.Connected: - print("Socket lost connection, aborting!") - break - -time.sleep(5) #Sleep for a bit more so we can receive replies to what we just sent. - -print("Closing connection") - -socket.close() diff --git a/examples/example_py.sh b/examples/example_py.sh deleted file mode 100755 index fb0baa8e..00000000 --- a/examples/example_py.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${CMAKE_SOURCE_DIR}/python python3 ${CMAKE_CURRENT_SOURCE_DIR}/example.py diff --git a/pyproject.toml.in b/pyproject.toml.in new file mode 100644 index 00000000..81a85f33 --- /dev/null +++ b/pyproject.toml.in @@ -0,0 +1,17 @@ +[build-system] +requires = ["sip >=6, <7"] +build-backend = "sipbuild.api" + +[tool.sip.metadata] +name = "pyArcus" + +[tool.sip.project] +builder-factory = "CMakeBuilder" +sip-files-dir = "${CMAKE_CURRENT_SOURCE_DIR}/python/" +sip-include-dirs = ["CMAKE_CURRENT_SOURCE_DIR/python/"] +build-dir = "${CMAKE_CURRENT_BINARY_DIR}/pyArcus/" + +[tool.sip.bindings.pyArcus] +exceptions = true +release-gil = true +concatenate = 8 \ No newline at end of file diff --git a/python/Error.sip b/python/Error.sip index f996704e..56f585b3 100644 --- a/python/Error.sip +++ b/python/Error.sip @@ -16,25 +16,22 @@ * along with this program. If not, see . */ -namespace ErrorCode +enum class ErrorCode { - enum ErrorCode - { - UnknownError, - CreationError, - ConnectFailedError, - BindFailedError, - AcceptFailedError, - SendFailedError, - ReceiveFailedError, - UnknownMessageTypeError, - ParseFailedError, - ConnectionResetError, - MessageRegistrationFailedError, - InvalidStateError, - InvalidMessageError, - Debug, - }; + UnknownError, + CreationError, + ConnectFailedError, + BindFailedError, + AcceptFailedError, + SendFailedError, + ReceiveFailedError, + UnknownMessageTypeError, + ParseFailedError, + ConnectionResetError, + MessageRegistrationFailedError, + InvalidStateError, + InvalidMessageError, + Debug, }; class Error @@ -45,10 +42,10 @@ class Error public: Error(); - Error(ErrorCode::ErrorCode error_code, const std::string& error_message); + Error(ErrorCode error_code, const std::string& error_message); Error(const Error& error); - ErrorCode::ErrorCode getErrorCode() const; + ErrorCode getErrorCode() const; std::string getErrorMessage() const; bool isFatalError() const; bool isValid() const; diff --git a/python/SocketListener.sip b/python/SocketListener.sip index a3f28e09..fcc86ffb 100644 --- a/python/SocketListener.sip +++ b/python/SocketListener.sip @@ -28,7 +28,7 @@ public: Socket* getSocket(); - virtual void stateChanged(SocketState::SocketState newState) = 0 /HoldGIL/; + virtual void stateChanged(SocketState newState) = 0 /HoldGIL/; virtual void messageReceived() = 0 /HoldGIL/; virtual void error(const Error& error) = 0 /HoldGIL/; }; diff --git a/python/Types.sip b/python/Types.sip index ada586ca..5f1deed4 100644 --- a/python/Types.sip +++ b/python/Types.sip @@ -134,17 +134,14 @@ #include "Types.h" %End -namespace SocketState +enum class SocketState { - enum SocketState - { - Initial, - Connecting, - Connected, - Opening, - Listening, - Closing, - Closed, - Error - }; + Initial, + Connecting, + Connected, + Opening, + Listening, + Closing, + Closed, + Error }; diff --git a/python/Socket.sip b/python/pyArcus.sip similarity index 94% rename from python/Socket.sip rename to python/pyArcus.sip index 5603c663..5fa6caa0 100644 --- a/python/Socket.sip +++ b/python/pyArcus.sip @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -%Module(name = Arcus, call_super_init = True) +%Module(name = pyArcus, call_super_init = True) %Include Types.sip %Include SocketListener.sip @@ -37,7 +37,7 @@ public: Socket(); virtual ~Socket(); - SocketState::SocketState getState() const; + SocketState getState() const; Error getLastError() const; void clearError(); diff --git a/src/Error.cpp b/src/Error.cpp index 4acbe183..62bf73a4 100644 --- a/src/Error.cpp +++ b/src/Error.cpp @@ -26,14 +26,14 @@ Arcus::Error::Error() { } -Arcus::Error::Error(ErrorCode::ErrorCode error_code, const std::string& error_message) +Arcus::Error::Error(ErrorCode error_code, const std::string& error_message) : _error_code(ErrorCode::UnknownError), _fatal_error(false), _native_error_code(0) { _error_code = error_code; _error_message = error_message; } -ErrorCode::ErrorCode Arcus::Error::getErrorCode() const +ErrorCode Arcus::Error::getErrorCode() const { return _error_code; } diff --git a/src/Error.h b/src/Error.h index b2bca19c..13b6ac42 100644 --- a/src/Error.h +++ b/src/Error.h @@ -26,28 +26,24 @@ namespace Arcus { /** * Possible error codes. - */ - namespace ErrorCode + */ + enum class ErrorCode { - // Note: Not using enum class due to incompatibility with SIP. - enum ErrorCode - { - UnknownError, ///< An unknown error occurred. - CreationError, ///< Socket creation failed. - ConnectFailedError, ///< Connection failed. - BindFailedError, ///< Bind to IP and port failed. - AcceptFailedError, ///< Accepting an incoming connection failed. - SendFailedError, ///< Sending a message failed. - ReceiveFailedError, ///< Receiving a message failed. - UnknownMessageTypeError, ///< Received a message with an unknown message type. - ParseFailedError, ///< Parsing the received message failed. - ConnectionResetError, ///< The connection was reset by peer. - MessageRegistrationFailedError, ///< Message registration failed. - InvalidStateError, ///< Socket is in an invalid state. - InvalidMessageError, ///< Message being handled is a nullptr or otherwise invalid. - Debug, //Debug messages - }; - } + UnknownError, ///< An unknown error occurred. + CreationError, ///< Socket creation failed. + ConnectFailedError, ///< Connection failed. + BindFailedError, ///< Bind to IP and port failed. + AcceptFailedError, ///< Accepting an incoming connection failed. + SendFailedError, ///< Sending a message failed. + ReceiveFailedError, ///< Receiving a message failed. + UnknownMessageTypeError, ///< Received a message with an unknown message type. + ParseFailedError, ///< Parsing the received message failed. + ConnectionResetError, ///< The connection was reset by peer. + MessageRegistrationFailedError, ///< Message registration failed. + InvalidStateError, ///< Socket is in an invalid state. + InvalidMessageError, ///< Message being handled is a nullptr or otherwise invalid. + Debug, //Debug messages + }; /** * A class representing an error with an error code and an error message. @@ -62,13 +58,13 @@ namespace Arcus /** * Create an error with an error code and error message. */ - Error(ErrorCode::ErrorCode error_code, const std::string& error_message); + Error(ErrorCode error_code, const std::string& error_message); /** * Get the error code of this error. */ - ErrorCode::ErrorCode getErrorCode() const; + ErrorCode getErrorCode() const; /** * Get the error message. */ @@ -99,7 +95,7 @@ namespace Arcus std::string toString() const; private: - ErrorCode::ErrorCode _error_code; + ErrorCode _error_code; std::string _error_message; bool _fatal_error; int _native_error_code; diff --git a/src/Socket.cpp b/src/Socket.cpp index c95146d1..82a6d36e 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -50,7 +50,7 @@ Socket::~Socket() } } -SocketState::SocketState Socket::getState() const +SocketState Socket::getState() const { return d->state; } diff --git a/src/Socket.h b/src/Socket.h index 52e5dcb4..27d33af3 100644 --- a/src/Socket.h +++ b/src/Socket.h @@ -48,7 +48,7 @@ namespace Arcus * * \return The current socket state. */ - virtual SocketState::SocketState getState() const; + virtual SocketState getState() const; /** * Get the last error. diff --git a/src/SocketListener.h b/src/SocketListener.h index ce229d44..448de33f 100644 --- a/src/SocketListener.h +++ b/src/SocketListener.h @@ -57,7 +57,7 @@ namespace Arcus * * \param newState The new state of the socket. */ - virtual void stateChanged(SocketState::SocketState newState) = 0; + virtual void stateChanged(SocketState newState) = 0; /** * Called whenever a new message has been received and * correctly parsed. diff --git a/src/Socket_p.h b/src/Socket_p.h index 9c3c084a..024064ce 100644 --- a/src/Socket_p.h +++ b/src/Socket_p.h @@ -93,11 +93,11 @@ namespace Arcus #ifdef ARCUS_DEBUG void debug(const std::string& message); #endif - void error(ErrorCode::ErrorCode error_code, const std::string& message); - void fatalError(ErrorCode::ErrorCode error_code, const std::string& msg); + void error(ErrorCode error_code, const std::string& message); + void fatalError(ErrorCode error_code, const std::string& msg); - SocketState::SocketState state; - SocketState::SocketState next_state; + SocketState state; + SocketState next_state; bool received_close; @@ -148,7 +148,7 @@ namespace Arcus #endif // Report an error that should not cause the connection to abort. - void Socket::Private::error(ErrorCode::ErrorCode error_code, const std::string& message) + void Socket::Private::error(ErrorCode error_code, const std::string& message) { Error error(error_code, message); error.setNativeErrorCode(platform_socket.getNativeErrorCode()); @@ -162,7 +162,7 @@ namespace Arcus } // Report an error that should cause the socket to go into an error state and abort the connection. - void Socket::Private::fatalError(ErrorCode::ErrorCode error_code, const std::string& message) + void Socket::Private::fatalError(ErrorCode error_code, const std::string& message) { Error error(error_code, message); error.setFatalError(true); diff --git a/src/Types.h b/src/Types.h index ac55c0ef..53e4576b 100644 --- a/src/Types.h +++ b/src/Types.h @@ -40,21 +40,17 @@ namespace Arcus /** * Socket state. */ - namespace SocketState + enum class SocketState { - // Note: Not using enum class due to incompatibility with SIP. - enum SocketState - { - Initial, ///< Created, not running. - Connecting, ///< Connecting to an address and port. - Connected, ///< Connected and capable of sending and receiving messages. - Opening, ///< Opening for incoming connections. - Listening, ///< Listening for incoming connections. - Closing, ///< Closing down. - Closed, ///< Closed, not running. - Error ///< A fatal error happened that blocks the socket from operating. - }; - } + Initial, ///< Created, not running. + Connecting, ///< Connecting to an address and port. + Connected, ///< Connected and capable of sending and receiving messages. + Opening, ///< Opening for incoming connections. + Listening, ///< Listening for incoming connections. + Closing, ///< Closing down. + Closed, ///< Closed, not running. + Error ///< A fatal error happened that blocks the socket from operating. + }; } #endif //ARCUS_TYPES_H