diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000..1bb47e3 --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,27 @@ +# From: https://github.com/cpp-best-practices/cmake_template +additional_commands: + foo: + flags: + - BAR + - BAZ + kwargs: + DEPENDS: '*' + HEADERS: '*' + SOURCES: '*' +bullet_char: '*' +dangle_parens: true +enum_char: . +line_ending: unix +line_width: 120 +max_pargs_hwrap: 3 +separate_ctrl_name_with_space: false +separate_fn_name_with_space: false +tab_size: 2 +autosort: true + +markup: + enable_markup: false + +lint: + disabled_codes: [C0307, W0105, C0111, E1126] + diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 1b7dbb7..3c695a7 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -22,9 +22,9 @@ jobs: - name: List Xcode installations if: matrix.os == 'macos-14' run: sudo ls -1 /Applications | grep "Xcode" - - name: Select Xcode 16.0 + - name: Select Xcode 16.2 if: matrix.os == 'macos-14' - run: sudo xcode-select -s /Applications/Xcode_16.app/Contents/Developer + run: sudo xcode-select -s /Applications/Xcode_16.2.0.app/Contents/Developer - uses: actions/checkout@v4 - name: Cache dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e3d2d5f..739a9b2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,13 @@ repos: - repo: https://github.com/pre-commit/mirrors-clang-format - rev: 'v15.0.6' # Use the sha / tag you want to point at + rev: v20.1.0 hooks: - id: clang-format files: '.(c|h|hpp|cpp)$' +- repo: https://github.com/cheshirekow/cmake-format-precommit + rev: v0.6.13 + hooks: + - id: cmake-format + additional_dependencies: [pyyaml>=5.1] + - id: cmake-lint + additional_dependencies: [pyyaml>=5.1] diff --git a/CMakeLists.txt b/CMakeLists.txt index eb5f121..c3ad0c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,3 @@ - # Mandatory line, sets the minimum version of CMake that should be used with this repository. # I specified 3.22 because I trust it. However, currently I have 3.26 installed on my machine. # To verify your version run @@ -21,31 +20,43 @@ set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs) include(cmake/cpm.cmake) # This commands downloads AND configures JUCE. It sets up some variables, like JUCE_SOURCE_DIR. -CPMAddPackage( - NAME JUCE - GIT_TAG 8.0.2 - VERSION 8.0.2 - GITHUB_REPOSITORY juce-framework/JUCE - SOURCE_DIR ${LIB_DIR}/juce +cpmaddpackage( + NAME + JUCE + GIT_TAG + 8.0.6 + VERSION + 8.0.6 + GITHUB_REPOSITORY + juce-framework/JUCE + SOURCE_DIR + ${LIB_DIR}/juce ) # Adds googletest. -CPMAddPackage( - NAME GOOGLETEST - GITHUB_REPOSITORY google/googletest - GIT_TAG v1.15.2 - VERSION 1.15.2 - SOURCE_DIR ${LIB_DIR}/googletest - OPTIONS - "INSTALL_GTEST OFF" - "gtest_force_shared_crt ON" +cpmaddpackage( + NAME + GOOGLETEST + GITHUB_REPOSITORY + google/googletest + VERSION + 1.16.0 + SOURCE_DIR + ${LIB_DIR}/googletest + OPTIONS + "INSTALL_GTEST OFF" + "gtest_force_shared_crt ON" ) -# This command allows running tests from the "build" folder (the one where CMake generates the project to). -enable_testing() +# Add compiler warning utilities +include(cmake/CompilerWarnings.cmake) +include(cmake/Util.cmake) # Adds all the targets configured in the "plugin" folder. add_subdirectory(plugin) +# This command allows running tests from the "build" folder (the one where CMake generates the project to). +enable_testing() + # Adds all the targets configured in the "test" folder. add_subdirectory(test) diff --git a/CMakePresets.json b/CMakePresets.json index 1fa0994..c5b644c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -17,7 +17,7 @@ }, { "name": "release", - "generator": "Ninja", + "inherits": "default", "binaryDir": "release-build", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" @@ -38,6 +38,18 @@ { "name": "default", "configurePreset": "default" + }, + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "vs", + "configurePreset": "vs" + }, + { + "name": "Xcode", + "configurePreset": "Xcode" } ], "testPresets": [ @@ -64,7 +76,6 @@ "name": "Xcode", "inherits": "default", "configurePreset": "Xcode", - // Use Debug by default "configuration": "Debug" } ] diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 0000000..3955909 --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,106 @@ +# from here: +# +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md + +set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type2', 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 'type2' 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(GCC_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) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + -Wunused-variable + # flags recommended by JUCE + -Wstrict-aliasing + -Wuninitialized + -Wconversion + -Wsign-compare + -Wunreachable-code + -Wno-ignored-qualifiers + -Wswitch-enum + -Wdeprecated + -Wfloat-equal + -Wmissing-field-initializers + -Wzero-as-null-pointer-constant + -Wreorder +) + +set(CLANG_WARNINGS + ${GCC_CLANG_WARNINGS} + # flags recommended by JUCE + -Wshadow-all + -Wshorten-64-to-32 + -Wconditional-uninitialized + -Wconstant-conversion + -Wbool-conversion + -Wextra-semi + -Wnullable-to-nonnull-conversion + -Wshift-sign-overflow + -Wint-conversion + -Wmissing-prototypes + -Winconsistent-missing-destructor-override +) + +set(GCC_WARNINGS + ${GCC_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 +) + +message(TRACE "Warnings are treated as errors") +list(APPEND CLANG_WARNINGS -Werror) +list(APPEND GCC_WARNINGS -Werror) +list(APPEND MSVC_WARNINGS /WX) + +if(MSVC) + set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) +elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) +else() + message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + # TODO support Intel compiler +endif() + +# use the same warning flags for C +set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") diff --git a/cmake/Util.cmake b/cmake/Util.cmake new file mode 100644 index 0000000..7c3f2b8 --- /dev/null +++ b/cmake/Util.cmake @@ -0,0 +1,43 @@ +# Links libraries to the given target and marks their include directories as SYSTEM. +# Source: +# https://stackoverflow.com/questions/52135983/ +# cmake-target-link-libraries-include-as-system-to-suppress-compiler-warnings +function(target_link_libraries_system target) + set(options PRIVATE PUBLIC INTERFACE) + cmake_parse_arguments( + TLLS + "${options}" + "" + "" + ${ARGN} + ) + foreach(op ${options}) + if(TLLS_${op}) + set(scope ${op}) + endif() + endforeach(op) + set(libs ${TLLS_UNPARSED_ARGUMENTS}) + + foreach(lib ${libs}) + get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES) + if(lib_include_dirs) + if(scope) + target_include_directories( + ${target} + SYSTEM + ${scope} + ${lib_include_dirs} + ) + else() + target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs}) + endif() + else() + message("Warning: ${lib} doesn't set INTERFACE_INCLUDE_DIRECTORIES. No include_directories set.") + endif() + if(scope) + target_link_libraries(${target} ${scope} ${lib}) + else() + target_link_libraries(${target} ${lib}) + endif() + endforeach() +endfunction(target_link_libraries_system) diff --git a/cmake/cpm.cmake b/cmake/cpm.cmake index 07c1941..4c8b97d 100644 --- a/cmake/cpm.cmake +++ b/cmake/cpm.cmake @@ -1,4 +1,4 @@ -set(CPM_DOWNLOAD_VERSION 0.40.2) +set(CPM_DOWNLOAD_VERSION 0.40.8) set(CPM_DOWNLOAD_LOCATION "${LIB_DIR}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") @@ -7,8 +7,7 @@ get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) function(download_cpm) message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") - file(DOWNLOAD - https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + file(DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ${CPM_DOWNLOAD_LOCATION} ) endfunction() @@ -25,4 +24,3 @@ else() endif() include(${CPM_DOWNLOAD_LOCATION}) - diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt index 709829b..fff4cfc 100644 --- a/plugin/CMakeLists.txt +++ b/plugin/CMakeLists.txt @@ -8,58 +8,50 @@ project(AudioPlugin VERSION 0.1.0) set(INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/YourPluginName") # Adds a plugin target (that's basically what the Projucer does). -juce_add_plugin(${PROJECT_NAME} - COMPANY_NAME MyCompany # change this - IS_SYNTH FALSE # may change this - NEEDS_MIDI_INPUT FALSE # may change this - NEEDS_MIDI_OUTPUT FALSE # may change this - PLUGIN_MANUFACTURER_CODE MCMP # change this - PLUGIN_CODE EXPL # change this - FORMATS Standalone VST3 # may change this - PRODUCT_NAME "YourPluginName" # change this +juce_add_plugin( + ${PROJECT_NAME} + COMPANY_NAME + MyCompany # change this + IS_SYNTH + FALSE # may change this + NEEDS_MIDI_INPUT + FALSE # may change this + NEEDS_MIDI_OUTPUT + FALSE # may change this + PLUGIN_MANUFACTURER_CODE + MCMP # change this + PLUGIN_CODE + EXPL # change this + FORMATS + Standalone + VST3 # may change this + PRODUCT_NAME + "YourPluginName" # change this ) # Sets the source files of the plugin project. -target_sources(${PROJECT_NAME} - PRIVATE - source/PluginEditor.cpp - source/PluginProcessor.cpp - ${INCLUDE_DIR}/PluginEditor.h - ${INCLUDE_DIR}/PluginProcessor.h -) +set(SOURCE_FILES source/PluginEditor.cpp source/PluginProcessor.cpp) +# Optional; includes header files in the project file tree in Visual Studio +set(HEADER_FILES ${INCLUDE_DIR}/PluginEditor.h ${INCLUDE_DIR}/PluginProcessor.h) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_FILES} ${HEADER_FILES}) # Sets the include directories of the plugin project. -target_include_directories(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include -) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) # Links to all necessary dependencies. The present ones are recommended by JUCE. # If you use one of the additional modules, like the DSP module, you need to specify it here. -target_link_libraries(${PROJECT_NAME} - PRIVATE - juce::juce_audio_utils - PUBLIC - juce::juce_recommended_config_flags - juce::juce_recommended_lto_flags - juce::juce_recommended_warning_flags +target_link_libraries_system(${PROJECT_NAME} PUBLIC juce::juce_audio_utils) +target_link_libraries( + ${PROJECT_NAME} PUBLIC juce::juce_recommended_config_flags juce::juce_recommended_lto_flags + juce::juce_recommended_warning_flags ) # These definitions are recommended by JUCE. -target_compile_definitions(${PROJECT_NAME} - PUBLIC - JUCE_WEB_BROWSER=0 - JUCE_USE_CURL=0 - JUCE_VST3_CAN_REPLACE_VST2=0 -) +target_compile_definitions(${PROJECT_NAME} PUBLIC JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0) -# Enables all warnings and treats warnings as errors. +# Enables strict C++ warnings and treats warnings as errors. # This needs to be set up only for your projects, not 3rd party -if (MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) -else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) -endif() +set_source_files_properties(${SOURCE_FILES} PROPERTIES COMPILE_OPTIONS "${PROJECT_WARNINGS_CXX}") # In Visual Studio this command provides a nice grouping of source files in "filters". source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/..) diff --git a/plugin/source/PluginProcessor.cpp b/plugin/source/PluginProcessor.cpp index 0af79ed..993eeff 100644 --- a/plugin/source/PluginProcessor.cpp +++ b/plugin/source/PluginProcessor.cpp @@ -98,7 +98,7 @@ bool AudioPluginAudioProcessor::isBusesLayoutSupported( layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) return false; - // This checks if the input layout matches the output layout + // This checks if the input layout matches the output layout #if !JucePlugin_IsSynth if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) return false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 11df991..9509623 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,36 +6,32 @@ project(AudioPluginTest) enable_testing() # Creates the test console application. -add_executable(${PROJECT_NAME} - source/AudioProcessorTest.cpp) +set(SOURCE_FILES source/AudioProcessorTest.cpp) +add_executable(${PROJECT_NAME} ${SOURCE_FILES}) -# Sets the necessary include directories: ours, JUCE's, and googletest's. -target_include_directories(${PROJECT_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../plugin/include - ${JUCE_SOURCE_DIR}/modules - ${GOOGLETEST_SOURCE_DIR}/googletest/include) +# Sets the necessary include directories of googletest. +target_include_directories(${PROJECT_NAME} PRIVATE ${GOOGLETEST_SOURCE_DIR}/googletest/include) # Thanks to the fact that we link against the gtest_main library, we don't have to write the main function ourselves. -target_link_libraries(${PROJECT_NAME} - PRIVATE - AudioPlugin - GTest::gtest_main) +target_link_libraries(${PROJECT_NAME} PRIVATE AudioPlugin GTest::gtest_main) -# Enables all warnings and treats warnings as errors. +# Enables strict C++ warnings and treats warnings as errors. # This needs to be set up only for your projects, not 3rd party -if (MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) -else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) -endif() +set_source_files_properties(${SOURCE_FILES} PROPERTIES COMPILE_OPTIONS "${PROJECT_WARNINGS_CXX}") # Adds googletest-specific CMake commands at our disposal. include(GoogleTest) -# Add all tests defined with googletest to the CMake metadata so that these tests are run upon a call to ctest in the test projects' binary directory. -if (CMAKE_GENERATOR STREQUAL Xcode) -# On macOS arm64, all binaries have to be signed before running. In local development, the linker adds an ad-hoc placeholder signature. In Xcode however, the ad-hoc signature is delayed until after the “Run Script” build phase, so the POST_BUILD command added by gtest_discover_tests cannot run. Thus, we need to delay test discovery until run time. -# Source: https://discourse.cmake.org/t/googletest-crash-when-using-cmake-xcode-arm64/5766/8 + +# Add all tests defined with googletest to the CMake metadata +# so that these tests are run upon a call to ctest in the test +# projects' binary directory. +if(CMAKE_GENERATOR STREQUAL Xcode) + # On macOS arm64, all binaries have to be signed before running. + # In local development, the linker adds an ad-hoc placeholder signature. + # In Xcode however, the ad-hoc signature is delayed until after the “Run Script” build phase, + # so the POST_BUILD command added by gtest_discover_tests cannot run. + # Thus, we need to delay test discovery until run time. + # Source: https://discourse.cmake.org/t/googletest-crash-when-using-cmake-xcode-arm64/5766/8 gtest_discover_tests(${PROJECT_NAME} DISCOVERY_MODE PRE_TEST) else() gtest_discover_tests(${PROJECT_NAME})