cmake_minimum_required(VERSION 3.25) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS include/xtc/xtc_version.h) file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/xtc/xtc_version.h" version_line REGEX "^#define XTC_VERSION_STRING") string(REGEX MATCH "[0-9]+[.][0-9]+[.][0-9]+" XTC_VERSION "${version_line}") project(xtc VERSION ${XTC_VERSION} LANGUAGES C) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(FATAL_ERROR "Use an out-of-source build: cmake -S . -B build") endif() include(GNUInstallDirs) include(CTest) find_package(Threads REQUIRED) find_package(Python3 3.12 REQUIRED COMPONENTS Interpreter) find_package(PkgConfig QUIET) option(XTC_WITH_JACK "Discover the optional JACK backend" ON) option(XTC_WITH_LADSPA "Enable LADSPA when its system header is available" ON) option(XTC_BUILD_GUI "Build the FLTK application when FLTK is available" ON) option(XTC_ENABLE_LIVE_TESTS "Register tests requiring an existing JACK server" OFF) option(XTC_BUILD_JUCE "Build optional externally supplied JUCE examples" OFF) option(XTC_BUILD_STEINBERG "Build optional externally supplied Steinberg examples" OFF) option(XTC_SANITIZE "Enable address and undefined-behavior sanitizers" OFF) option(XTC_REPRODUCIBLE "Remove source/build paths from optimized release artifacts" OFF) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Keep project warnings/assertions local; external SDKs control their own flags. function(xtc_warnings target) target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -UNDEBUG "$<$:-Wconversion;-Wshadow>") endfunction() if(XTC_SANITIZE) add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer) add_link_options(-fsanitize=address,undefined) endif() if(XTC_REPRODUCIBLE) add_compile_options("-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=." "-ffile-prefix-map=${CMAKE_CURRENT_BINARY_DIR}=build" -g0) endif() if(XTC_WITH_JACK AND PkgConfig_FOUND) pkg_check_modules(JACK QUIET IMPORTED_TARGET jack) endif() add_library(xtc-core STATIC src/plugin/module_path.c src/midi/file.c src/midi/output.c src/project/crossfade.c src/control/tempo.c src/project/chain_tools.c src/project/chain_preset.c src/mixer/track_fx.c src/audio/sample.c src/persistence/collect.c src/audio/normalize.c src/project/sections.c src/project/editing.c src/audio/export.c src/plugin/builtin.c src/settings/settings.c src/control/model.c src/audio/waveform.c src/plugin/ladspa.c src/plugin/host.c src/plugin/modern.c src/native/processor.c src/audio/recovery.c src/dsp/ramp.c src/native/native.c src/mixer/model.c src/mixer/render.c src/project/history.c src/midi/model.c src/midi/instrument.c src/audio/wave.c src/audio/arrangement.c src/audio/recorder.c src/core/xtc.c src/project/project.c src/persistence/project_file.c src/graph/graph.c src/dsp/gain.c src/engine/offline_engine.c src/engine/realtime.c src/backend/jack_backend.c ) set_target_properties(xtc-core PROPERTIES OUTPUT_NAME xtc) xtc_warnings(xtc-core) add_library(xtc::core ALIAS xtc-core) target_include_directories(xtc-core PUBLIC "$" "$") target_include_directories(xtc-core SYSTEM PRIVATE third_party/clap/include third_party/vst3) target_link_libraries(xtc-core PUBLIC m ${CMAKE_DL_LIBS} Threads::Threads) if(JACK_FOUND AND XTC_WITH_JACK) target_compile_definitions(xtc-core PRIVATE XTC_HAVE_JACK) target_link_libraries(xtc-core PUBLIC PkgConfig::JACK) endif() include(CheckIncludeFile) check_include_file(ladspa.h XTC_HAVE_LADSPA_HEADER) if(XTC_HAVE_LADSPA_HEADER AND XTC_WITH_LADSPA) set(XTC_LADSPA_AVAILABLE TRUE) endif() if(NOT XTC_WITH_LADSPA) target_compile_definitions(xtc-core PRIVATE XTC_DISABLE_LADSPA) endif() function(xtc_executable name source) add_executable(${name} ${source} ${ARGN}) target_link_libraries(${name} PRIVATE xtc-core) xtc_warnings(${name}) endfunction() xtc_executable(xtc-check src/main.c) xtc_executable(native-gain examples/native_gain.c) xtc_executable(quality-preview-render examples/quality_preview.c) set_target_properties(quality-preview-render PROPERTIES OUTPUT_NAME xtc-quality-preview) xtc_executable(builtin-preview-render examples/builtin_preview.c) set_target_properties(builtin-preview-render PROPERTIES OUTPUT_NAME xtc-builtin-preview) foreach(preview quality builtin) add_custom_target(${preview}-preview COMMAND ${preview}-preview-render "${CMAKE_CURRENT_BINARY_DIR}/${preview}" DEPENDS ${preview}-preview-render VERBATIM) endforeach() include(cmake/Tests.cmake) include(cmake/Gui.cmake) include(cmake/Developer.cmake) include(cmake/Install.cmake) if(XTC_BUILD_STEINBERG) enable_language(CXX) add_subdirectory(examples/steinberg steinberg-sdk) xtc_executable(steinberg-render examples/steinberg/render.c) add_custom_target(steinberg-sdk DEPENDS xtc-sdk-examples validator) if(BUILD_TESTING) xtc_executable(test_steinberg_sdk tests/integration/test_steinberg_sdk.c) set(steinberg_bundle "$>>") xtc_test(steinberg-sdk test_steinberg_sdk "${steinberg_bundle}") add_test(NAME steinberg-validator COMMAND $ "${steinberg_bundle}") set_tests_properties(steinberg-sdk steinberg-validator PROPERTIES LABELS sdk) endif() endif() if(XTC_BUILD_JUCE) if(BUILD_TESTING) find_program(XTC_XVFB_RUN xvfb-run REQUIRED) endif() enable_language(CXX) add_subdirectory(examples/juce juce) add_custom_target(juce-examples DEPENDS xtc-juce-render XtcJuceSynth_VST3 XtcJuceSynth_Standalone) if(BUILD_TESTING) xtc_executable(test_juce_plugin tests/integration/test_juce_plugin.c) add_test(NAME juce-c-host COMMAND ${XTC_XVFB_RUN} -a $ "$>>") add_test(NAME juce-files-editor COMMAND ${XTC_XVFB_RUN} -a ${Python3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tests/integration/test_juce_examples.py" $) set_tests_properties(juce-c-host juce-files-editor PROPERTIES LABELS sdk) endif() endif() if(BUILD_TESTING) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" USES_TERMINAL) get_property(xtc_build_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) foreach(binary IN LISTS xtc_build_targets) get_target_property(kind ${binary} TYPE) if(kind MATCHES "^(EXECUTABLE|MODULE_LIBRARY|STATIC_LIBRARY)$" AND NOT binary STREQUAL capture_screenshots) add_dependencies(check ${binary}) endif() endforeach() if(XTC_BUILD_JUCE) add_dependencies(check xtc-juce-render XtcJuceSynth_VST3 XtcJuceSynth_Standalone) endif() if(XTC_BUILD_STEINBERG) add_dependencies(check xtc-sdk-examples validator) endif() get_property(xtc_tests DIRECTORY PROPERTY TESTS) foreach(name IN LISTS xtc_tests) if(NOT TARGET test-${name} AND NOT name STREQUAL steinberg-sdk) add_custom_target(test-${name} COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R "^${name}$" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" USES_TERMINAL) foreach(binary IN LISTS xtc_build_targets) get_target_property(kind ${binary} TYPE) if(kind MATCHES "^(EXECUTABLE|MODULE_LIBRARY|STATIC_LIBRARY)$" AND NOT binary STREQUAL capture_screenshots) add_dependencies(test-${name} ${binary}) endif() endforeach() endif() endforeach() endif() if(BUILD_TESTING) include(cmake/Variants.cmake) # Historical workflow names retained as CMake target aliases. add_custom_target(test-builtins DEPENDS test-builtin) if(XTC_BUILD_JUCE) add_custom_target(test-juce COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R "juce" DEPENDS xtc-juce-render XtcJuceSynth_VST3 XtcJuceSynth_Standalone test_juce_plugin USES_TERMINAL) endif() if(XTC_BUILD_STEINBERG) add_custom_target(test-steinberg-sdk COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R steinberg DEPENDS xtc-sdk-examples validator test_steinberg_sdk steinberg-render USES_TERMINAL) endif() endif()