diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..485de36 --- /dev/null +++ b/.clang-format @@ -0,0 +1,91 @@ +BasedOnStyle: Mozilla + +AccessModifierOffset: '-4' +AlignAfterOpenBracket: BlockIndent +AlignEscapedNewlines: Left +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +# Forbid one line lambdas because clang-format makes a weird split when +# single instructions lambdas are too long. +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Allman +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: AfterComma +BreakStringLiterals: false +ColumnLimit: '110' +ConstructorInitializerIndentWidth: '4' +ContinuationIndentWidth: '4' +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Always +EmptyLineBeforeAccessModifier: Always +ExperimentalAutoDetectBinPacking: true +IncludeBlocks: Regroup +IncludeCategories: +- Regex: <[^.]+> + Priority: 1 +- Regex: + Priority: 3 +- Regex: <.+> + Priority: 2 +- Regex: '"sparrow/.+"' + Priority: 4 +- Regex: '".+"' + Priority: 5 +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: '4' +IndentWrappedFunctionNames: false +InsertBraces: true +InsertTrailingCommas: Wrapped +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +Language: Cpp +MaxEmptyLinesToKeep: '2' +NamespaceIndentation: All +ObjCBlockIndentWidth: '4' +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PackConstructorInitializers: Never +PenaltyBreakAssignment: 100000 +PenaltyBreakBeforeFirstCallParameter: 0 +PenaltyBreakComment: 10 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakTemplateDeclaration: 0 +PenaltyExcessCharacter: 10 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 10 +PointerAlignment: Left +QualifierAlignment: Custom # Experimental +QualifierOrder: [inline, static, constexpr, const, volatile, type] +ReflowComments: true +SeparateDefinitionBlocks: Always +SortIncludes: CaseInsensitive +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: '2' +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++20 +TabWidth: '4' +UseTab: Never \ No newline at end of file diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..1fa6d87 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,18 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,performance-*,portability-*,-modernize-use-trailing-return-type' +WarningsAsErrors: '' +HeaderFileExtensions: + - '' + - h + - hh + - hpp + - hxx +ImplementationFileExtensions: + - c + - cc + - cpp + - cxx +HeaderFilterRegex: '' +FormatStyle: file +SystemHeaders: false +... diff --git a/CMakeLists.txt b/CMakeLists.txt index 52387a8..bda1794 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,22 @@ project(sparrow-ipc CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_SCAN_FOR_MODULES OFF) +include(CMakeDependentOption) set(SPARROW_IPC_COMPILE_DEFINITIONS "" CACHE STRING "List of public compile definitions of the sparrow-ipc target") +# Linter options +# ============= +OPTION(ACTIVATE_LINTER "Create targets to run clang-format" OFF) +MESSAGE(STATUS "πŸ”§ Activate linter: ${ACTIVATE_LINTER}") +cmake_dependent_option(ACTIVATE_LINTER_DURING_COMPILATION "Run linter during the compilation" ON "ACTIVATE_LINTER" OFF) + +if(ACTIVATE_LINTER) + include(clang-format) + include(clang-tidy) +endif() + + # Build options # ============= @@ -108,6 +121,11 @@ else() target_compile_definitions(sparrow-ipc PRIVATE SPARROW_IPC_EXPORTS) endif() +target_compile_options(sparrow-ipc + PRIVATE + ${compile_options} +) + target_include_directories(sparrow-ipc PUBLIC ${SPARROW_IPC_INCLUDE_DIR} PRIVATE ${SPARROW_IPC_SOURCE_DIR} ) target_link_libraries(sparrow-ipc PRIVATE flatbuffers_interface) target_link_libraries(sparrow-ipc PUBLIC flatbuffers::flatbuffers sparrow::sparrow) diff --git a/cmake/clang-format.cmake b/cmake/clang-format.cmake new file mode 100644 index 0000000..483f735 --- /dev/null +++ b/cmake/clang-format.cmake @@ -0,0 +1,84 @@ +set(CLANG-FORMAT_MINIMUM_MAJOR_VERSION 18) + +function(get_clang_format_version clang_format_path) + set(CLANG_FORMAT_VERSION_OUTPUT "") + execute_process( + COMMAND ${clang_format_path} --version + OUTPUT_VARIABLE CLANG_FORMAT_VERSION_OUTPUT + ) + string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CLANG_FORMAT_VERSION_OUTPUT ${CLANG_FORMAT_VERSION_OUTPUT}) + set(CLANG_FORMAT_MAJOR_VERSION ${CMAKE_MATCH_1} PARENT_SCOPE) + set(CLANG_FORMAT_MINOR_VERSION ${CMAKE_MATCH_2} PARENT_SCOPE) + set(CLANG_FORMAT_PATCH_VERSION ${CMAKE_MATCH_3} PARENT_SCOPE) +endfunction() + +function(check_clang-format_version validator_result_var item) + set(${validator_result_var} FALSE PARENT_SCOPE) + get_clang_format_version(${item}) + if (CLANG_FORMAT_MAJOR_VERSION LESS CLANG-FORMAT_MINIMUM_MAJOR_VERSION) + message(DEBUG "clang-format found at ${item} | version: ${CLANG_FORMAT_MAJOR_VERSION}.${CLANG_FORMAT_MINOR_VERSION}.${CLANG_FORMAT_PATCH_VERSION}") + message(DEBUG "but version is lower than ${CLANG-FORMAT_MINIMUM_MAJOR_VERSION}") + set(${validator_result_var} FALSE PARENT_SCOPE) + else() + set(${validator_result_var} TRUE PARENT_SCOPE) + endif() +endfunction() + +function(print_clang_format_install_instructions) + message(STATUS "πŸ› οΈ Please install clang-format to enable code formatting") + message(STATUS "Can be installed via conda-forge: https://prefix.dev/channels/conda-forge/packages/clang-format") + if(UNIX) + if(APPLE) + message(STATUS "🍎 On MacOS, you can install clang-format with:") + message(STATUS "\t> brew install clang-format") + else() + message(STATUS "🐧 On Ubuntu, you can install clang-format with:") + message(STATUS "\t> sudo apt-get install clang-format") + endif() + elseif(WIN32) + message(STATUS "πŸͺŸ On Windows, you can install clang-format with:") + message(STATUS "\t> winget llvm") + endif() +endfunction() + +find_program(CLANG_FORMAT clang-format + VALIDATOR check_clang-format_version) + +if(NOT CLANG_FORMAT) + message(WARNING "❗ clang-format not found") + + print_clang_format_install_instructions() +else() + get_clang_format_version(${CLANG_FORMAT}) + message(STATUS "βœ… clang-format (version: ${CLANG_FORMAT_MAJOR_VERSION}.${CLANG_FORMAT_MINOR_VERSION}.${CLANG_FORMAT_PATCH_VERSION}) found at ${CLANG_FORMAT}") + + # list all files to format + set( + FORMAT_PATTERNS + include/*.hpp + test/*.cpp + test/*.hpp + CACHE STRING + "; separated patterns relative to the project source dir to format" + ) + + set(ALL_FILES_TO_FORMAT "") + foreach(PATTERN ${FORMAT_PATTERNS}) + file(GLOB_RECURSE FILES_TO_FORMAT ${CMAKE_SOURCE_DIR}/${PATTERN}) + list(APPEND ALL_FILES_TO_FORMAT ${FILES_TO_FORMAT}) + endforeach() + + add_custom_target( + clang-format + COMMAND ${CLANG_FORMAT} -i -style=file ${ALL_FILES_TO_FORMAT} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running clang-format on all files" + ) + + add_custom_target( + clang-format_dry_run + COMMAND ${CLANG_FORMAT} --dry-run -style=file ${ALL_FILES_TO_FORMAT} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running dry clang-format on all files" + ) +endif() diff --git a/cmake/clang-tidy.cmake b/cmake/clang-tidy.cmake new file mode 100644 index 0000000..ec276b7 --- /dev/null +++ b/cmake/clang-tidy.cmake @@ -0,0 +1,118 @@ +if(CMAKE_GENERATOR MATCHES "Ninja|Unix Makefiles") + message(STATUS "πŸ”§ CMAKE_EXPORT_COMPILE_COMMANDS will be used to enable clang-tidy") + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +else() + message(WARNING "🚧 CMAKE_EXPORT_COMPILE_COMMANDS can't be used because the CMAKE_GENERATOR is ${CMAKE_GENERATOR}. +You have to use Ninja or Unix Makefiles. +Without CMAKE_EXPORT_COMPILE_COMMANDS, clang-tidy will not work. +CMAKE_EXPORT_COMPILE_COMMANDS is used to generate a JSON file that contains all the compiler commands used to build the project. +This file is used by clang-tidy to know how to compile the project.") +endif() + +set(CLANG-TIDY_MINIMUM_MAJOR_VERSION 18) + +function(get_clang_tidy_version clang_tidy_path) + set(CLANG_TIDY_VERSION_OUTPUT "") + execute_process( + COMMAND ${clang_tidy_path} --version + OUTPUT_VARIABLE CLANG_TIDY_VERSION_OUTPUT + ) + string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CLANG_TIDY_VERSION_OUTPUT ${CLANG_TIDY_VERSION_OUTPUT}) + set(CLANG_TIDY_MAJOR_VERSION ${CMAKE_MATCH_1} PARENT_SCOPE) + set(CLANG_TIDY_MINOR_VERSION ${CMAKE_MATCH_2} PARENT_SCOPE) + set(CLANG_TIDY_PATCH_VERSION ${CMAKE_MATCH_3} PARENT_SCOPE) +endfunction() + +function(check_clang-tidy_version validator_result_var item) + set(${validator_result_var} FALSE PARENT_SCOPE) + get_clang_tidy_version(${item}) + if (CLANG_TIDY_MAJOR_VERSION LESS CLANG-TIDY_MINIMUM_MAJOR_VERSION) + message(DEBUG "clang-tidy (version: ${CLANG_TIDY_MAJOR_VERSION}.${CLANG_TIDY_MINOR_VERSION}.${CLANG_TIDY_PATCH_VERSION}) found at ${item}") + message(DEBUG "but clang-tidy with version >= ${CLANG-TIDY_MINIMUM_MAJOR_VERSION} must be used.") + set(${validator_result_var} FALSE PARENT_SCOPE) + else() + set(${validator_result_var} TRUE PARENT_SCOPE) + endif() +endfunction() + +function(print_clang_tidy_install_instructions) + message(STATUS "πŸ› οΈ Please install clang-tidy to enable code formatting") + if(UNIX) + if(APPLE) + message(STATUS "🍎 On MacOS, you can install clang-tidy with:") + message(STATUS "\t> brew install clang-tidy") + else() + message(STATUS "🐧 On Ubuntu, you can install clang-tidy with:") + message(STATUS "\t> sudo apt-get install clang-tidy") + endif() + elseif(WIN32) + message(STATUS "πŸͺŸ On Windows, you can install clang-tidy with:") + message(STATUS "\t> winget llvm") + endif() +endfunction() + +find_program(CLANG_TIDY clang-tidy + VALIDATOR check_clang-tidy_version) + +if(NOT CLANG_TIDY) + message(WARNING "❗clang-tidy with version >= ${CLANG-TIDY_MINIMUM_MAJOR_VERSION} not found") + + print_clang_tidy_install_instructions() +else() + get_clang_tidy_version(${CLANG_TIDY}) + message(STATUS "βœ… clang-tidy (version: ${CLANG_TIDY_MAJOR_VERSION}.${CLANG_TIDY_MINOR_VERSION}.${CLANG_TIDY_PATCH_VERSION}) found at ${CLANG_TIDY}") + + if(ACTIVATE_LINTER_DURING_COMPILATION) + message(STATUS "πŸ”§ clang-tidy will be activated during compilation") + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY}) + else() + message(STATUS "πŸ”§ clang-tidy will not be activated during compilation") + set(CMAKE_CXX_CLANG_TIDY "") + endif() + + find_package (Python COMPONENTS Interpreter) + if(Python_Interpreter_FOUND) + message(DEBUG "Python found at ${Python_EXECUTABLE}") + get_filename_component(CLANG_TIDY_FOLDER ${CLANG_TIDY} DIRECTORY) + find_file(CLANG_TIDY_PYTHON_SCRIPT run-clang-tidy PATHS ${CLANG_TIDY_FOLDER} NO_DEFAULT_PATH) + if(CLANG_TIDY_PYTHON_SCRIPT) + message(DEBUG "run-clang-tidy.py found at ${CLANG_TIDY_PYTHON_SCRIPT}") + endif() + set(CLANG_TIDY_COMMAND ${Python_EXECUTABLE} ${CLANG_TIDY_PYTHON_SCRIPT}) + else() + set(CLANG_TIDY_COMMAND ${CLANG_TIDY}) + endif() + + set(CLANG_TIDY_COMMON_ARGUMENTS + $<$>:->-use-color + -p ${CMAKE_BINARY_DIR}) + + set( + PATTERNS + include/*.hpp + test/*.cpp + test/*.hpp + CACHE STRING + "; separated patterns relative to the project source dir to analyse" + ) + + set(ALL_FILES_TO_FORMAT "") + foreach(PATTERN ${PATTERNS}) + file(GLOB_RECURSE FILES_TO_ANALYZE ${CMAKE_SOURCE_DIR}/${PATTERN}) + list(APPEND ALL_FILES_TO_ANALYZE ${FILES_TO_ANALYZE}) + endforeach() + + add_custom_target( + clang-tidy + COMMAND ${CLANG_TIDY_COMMAND} $<$>:->-fix ${CLANG_TIDY_COMMON_ARGUMENTS} ${ALL_FILES_TO_ANALYZE} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running clang-tidy on all files" + ) + + add_custom_target( + clang-tidy_dry_run + COMMAND ${CLANG_TIDY_COMMAND} ${CLANG_TIDY_COMMON_ARGUMENTS} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running dry clang-tidy on all files" + ) +endif() diff --git a/cmake/compile_options.cmake b/cmake/compile_options.cmake new file mode 100644 index 0000000..f170cfa --- /dev/null +++ b/cmake/compile_options.cmake @@ -0,0 +1,83 @@ + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(compile_options + /bigobj + /permissive- + /WX # treat warnings as errors + /W4 # Baseline reasonable warnings + /we4242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /we4244 # conversion from 'type1' to 'type_2', possible loss of data + /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4263 # 'function': member function does not override any base class virtual member function + /we4265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not be destructed correctly + /we4287 # '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 + /we4296 # 'operator': expression is always 'boolean_value' + /we4311 # 'variable': pointer truncation from 'type1' to 'type2' + /we4545 # expression before comma evaluates to a function which is missing an argument list + /we4546 # function call before comma missing argument list + /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect + /we4549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /we4555 # expression has no effect; expected expression with side- effect + /we4619 # pragma warning: there is no warning number 'number' + /we4640 # Enable warning on thread un-safe static member initialization + /we4826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /we4905 # wide string literal cast to 'LPSTR' + /we4906 # string literal cast to 'LPWSTR' + /we4928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /we5038 # data member 'member1' will be initialized after data member 'member2' + /Zc:__cplusplus + PARENT_SCOPE) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(compile_options + -Wall # reasonable and standard + -Wcast-align # warn for potential performance problem casts + -Wconversion # warn on type conversions that may lose data + -Wdouble-promotion # warn if float is implicitly promoted to double + -Werror # treat warnings as errors + -Wextra + -Wformat=2 # warn on security issues around functions that format output (i.e., printf) + -Wimplicit-fallthrough # Warns when case statements fall-through. (Included with -Wextra in GCC, not in clang) + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -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 + -Wnull-dereference # warn if a null dereference is detected + -Wold-style-cast # warn for c-style casts + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wsign-conversion # warn on sign conversions + -Wunused # warn on anything being unused + $<$:-Wno-c++98-compat> # do not warn on use of non-C++98 standard + $<$:-Wno-c++98-compat-pedantic> + $<$:-Wno-documentation> + $<$:-Wno-extra-semi-stmt> + $<$:-Wno-c++20-compat> + $<$:-Wno-pre-c++20-compat-pedantic> + $<$:-Wno-reserved-identifier> + $<$:-Wno-undef> + $<$:-Wno-switch-default> + $<$:-Wno-switch-enum> + $<$:-Wno-missing-prototypes> + $<$:-Wno-unused-template> + $<$:-Wno-unsafe-buffer-usage> + $<$:-Wno-documentation-unknown-command> + $<$:-Wno-float-equal> + $<$:-Wno-exit-time-destructors> + $<$:-Wno-global-constructors> + $<$:-Wno-newline-eof> + $<$:-Wno-ctad-maybe-unsupported> + $<$:-Wno-maybe-uninitialized> + $<$:-Wno-array-bounds> + $<$:-Wno-stringop-overread> + $<$:-Wduplicated-branches> # warn if if / else branches have duplicated code + $<$:-Wduplicated-cond> # warn if if / else chain has duplicated conditions + $<$:-Wlogical-op> # warn about logical operations being used where bitwise were probably wanted + $<$:-Wno-subobject-linkage> # suppress warnings about subobject linkage + ) + if (NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + set(compile_options ${compile_options} -ftemplate-backtrace-limit=0 -pedantic) + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.3) + set(compile_options ${compile_optoins} PRIVATE "-Wno-error=shift-negative-value") + endif() +endif()