From b6ba093000be2e8b0f12b6a2f75e4b1b8da3dfdb Mon Sep 17 00:00:00 2001 From: hanez Date: Fri, 6 Nov 2026 19:05:14 +0100 Subject: [PATCH] Migrated from Make to CMake. --- .gitignore | 2 + AGENT.md | 2 +- CMakeLists.txt | 200 +++++++++ CMakePresets.json | 22 + Makefile | 689 ++---------------------------- README.md | 62 +-- cmake/Developer.cmake | 57 +++ cmake/Gui.cmake | 87 ++++ cmake/Install.cmake | 46 ++ cmake/InstallDriver.cmake.in | 41 ++ cmake/Tests.cmake | 144 +++++++ cmake/Variants.cmake | 29 ++ docs/AUDIO_FILES.md | 11 +- docs/AUDIO_QUALITY.md | 14 +- docs/BUILD.md | 439 +++++++------------ docs/BUILTIN_DEVICES.md | 8 +- docs/CONTROL.md | 2 +- docs/EDITING.md | 2 +- docs/GUI.md | 6 +- docs/IDEA.md | 10 +- docs/JUCE.md | 10 +- docs/LICENSES.md | 6 +- docs/LINUX.md | 38 +- docs/MAINTENANCE.md | 10 +- docs/MIDI.md | 10 +- docs/MODERN_EDITING.md | 4 +- docs/NATIVE_API.md | 6 +- docs/PERFORMANCE.md | 28 +- docs/PLUGIN_HOSTING.md | 8 +- docs/PROJECT_FORMAT.md | 8 +- docs/RECOVERY.md | 6 +- docs/RELEASING.md | 76 ++-- docs/RELIABILITY.md | 16 +- docs/ROADMAP.md | 75 ++++ docs/ROUTING.md | 10 +- docs/STEINBERG.md | 6 + docs/TRACK_INSERTS.md | 2 +- docs/USER_MANUAL.md | 17 +- docs/WORKFLOW.md | 4 +- docs/WORKSPACE.md | 2 +- examples/README.md | 4 +- examples/juce/CMakeLists.txt | 43 +- examples/juce/README.md | 29 +- examples/steinberg/CMakeLists.txt | 1 + examples/steinberg/README.md | 18 +- screenshots/README.md | 21 +- tests/integration/README.md | 60 +-- tests/integration/test_install.py | 52 ++- tests/integration/test_release.py | 17 +- third_party/README.md | 21 + tools/analyze.py | 2 +- tools/package.py | 21 +- 52 files changed, 1285 insertions(+), 1219 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 cmake/Developer.cmake create mode 100644 cmake/Gui.cmake create mode 100644 cmake/Install.cmake create mode 100644 cmake/InstallDriver.cmake.in create mode 100644 cmake/Tests.cmake create mode 100644 cmake/Variants.cmake diff --git a/.gitignore b/.gitignore index 06c326f..fecdb38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # Build products /build/ +/cmake-build-*/ +/CMakeUserPresets.json /*.o *.a *.so diff --git a/AGENT.md b/AGENT.md index fe79ce5..97a802d 100644 --- a/AGENT.md +++ b/AGENT.md @@ -28,7 +28,7 @@ This file is the working contract for AI-assisted development in this repository ## First task -Run `make clean all test`. Inspect compiler and `pkg-config` availability. Do not install packages or alter system configuration without permission. Report current state and propose the next smallest step. +Run `cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug`, `cmake --build build --parallel`, and `ctest --test-dir build --output-on-failure`. Inspect compiler and `pkg-config` availability. Do not install packages or alter system configuration without permission. Report current state and propose the next smallest step. ## Roadmap diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5e9be23 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,200 @@ +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() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..70806eb --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,22 @@ +{ + "version": 6, + "cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 }, + "configurePresets": [ + { "name": "debug", "binaryDir": "${sourceDir}/build/debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, + { "name": "release", "binaryDir": "${sourceDir}/build/release", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "XTC_REPRODUCIBLE": "ON" } }, + { "name": "minimal", "binaryDir": "${sourceDir}/build/minimal", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "XTC_BUILD_GUI": "OFF", "XTC_WITH_JACK": "OFF", "XTC_WITH_LADSPA": "OFF" } }, + { "name": "sanitize", "binaryDir": "${sourceDir}/build/sanitize", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_C_COMPILER": "clang", "XTC_BUILD_GUI": "OFF", "XTC_SANITIZE": "ON" } } + ], + "buildPresets": [ + { "name": "debug", "configurePreset": "debug" }, + { "name": "release", "configurePreset": "release" }, + { "name": "minimal", "configurePreset": "minimal" }, + { "name": "sanitize", "configurePreset": "sanitize" } + ], + "testPresets": [ + { "name": "debug", "configurePreset": "debug", "output": { "outputOnFailure": true } }, + { "name": "release", "configurePreset": "release", "output": { "outputOnFailure": true } }, + { "name": "minimal", "configurePreset": "minimal", "output": { "outputOnFailure": true } }, + { "name": "sanitize", "configurePreset": "sanitize", "output": { "outputOnFailure": true } } + ] +} diff --git a/Makefile b/Makefile index 64c55c4..dd3f36b 100644 --- a/Makefile +++ b/Makefile @@ -1,667 +1,46 @@ +# Convenience shortcuts only; CMakeLists.txt owns the build. .DEFAULT_GOAL := all +# CMake handles parallel compilation; serialize separate wrapper goals. +.NOTPARALLEL: -CC ?= cc -CXX ?= c++ -PKG_CONFIG ?= pkg-config -FLTK_CONFIG ?= fltk-config +CMAKE ?= cmake +CTEST ?= ctest BUILD_DIR ?= build -PREFIX ?= /usr/local -exec_prefix ?= $(PREFIX) -bindir ?= $(exec_prefix)/bin -libdir ?= $(exec_prefix)/lib -includedir ?= $(PREFIX)/include -datarootdir ?= $(PREFIX)/share -datadir ?= $(datarootdir) -docdir ?= $(datarootdir)/doc/xtc -licensedir ?= $(datarootdir)/licenses/xtc -examplesdir ?= $(datadir)/xtc/examples -desktopdir ?= $(datadir)/applications -pkgconfigdir ?= $(libdir)/pkgconfig -INSTALL_LAYOUT = --prefix "$(PREFIX)" --destdir "$(DESTDIR)" --bindir "$(bindir)" --libdir "$(libdir)" --includedir "$(includedir)" --docdir "$(docdir)" --licensedir "$(licensedir)" --examplesdir "$(examplesdir)" --desktopdir "$(desktopdir)" --pkgconfigdir "$(pkgconfigdir)" -PYTHON ?= python3 -VERSION := $(shell sed -n 's/^\#define XTC_VERSION_STRING "\([^"]*\)"/\1/p' include/xtc/xtc_version.h) +BUILD_TYPE ?= Debug +JOBS ?= 4 +CMAKE_ARGS ?= +CTEST_ARGS ?= -CFLAGS ?= -O2 -g -CFLAGS += -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude -CXXFLAGS ?= -O2 -g -CXXFLAGS += -std=c++17 -Wall -Wextra -Wpedantic -Iinclude -LDFLAGS ?= +.PHONY: all build configure test check gui run clean help +all: build -CORE_SOURCES := 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 -JACK_AVAILABLE := $(shell $(PKG_CONFIG) --exists jack 2>/dev/null && echo yes) -ifeq ($(JACK_AVAILABLE),yes) -JACK_CFLAGS := $(shell $(PKG_CONFIG) --cflags jack) -JACK_LIBS := $(shell $(PKG_CONFIG) --libs jack) -BACKEND_TEST := $(BUILD_DIR)/test_backend -$(BUILD_DIR)/tests/unit/test_backend.o: CPPFLAGS += $(JACK_CFLAGS) -$(BUILD_DIR)/src/core/xtc.o: CPPFLAGS += -DXTC_HAVE_JACK -$(BUILD_DIR)/src/backend/jack_backend.o: CPPFLAGS += -DXTC_HAVE_JACK $(JACK_CFLAGS) -endif -CORE_OBJECTS := $(CORE_SOURCES:%.c=$(BUILD_DIR)/%.o) -TEST_OBJECT := $(BUILD_DIR)/tests/unit/test_core.o -ifeq ($(shell $(PKG_CONFIG) --exists fltk 2>/dev/null && echo yes),yes) -FLTK_CXXFLAGS := $(shell $(PKG_CONFIG) --cflags fltk) -FLTK_LIBS := $(shell $(PKG_CONFIG) --libs fltk) -GUI_AVAILABLE := yes -else ifeq ($(shell $(FLTK_CONFIG) --version >/dev/null 2>&1 && echo yes),yes) -FLTK_CXXFLAGS := $(shell $(FLTK_CONFIG) --cxxflags) -FLTK_LIBS := $(shell $(FLTK_CONFIG) --ldflags) -GUI_AVAILABLE := yes -endif +configure: + $(CMAKE) -S . -B "$(BUILD_DIR)" -DCMAKE_BUILD_TYPE="$(BUILD_TYPE)" $(CMAKE_ARGS) -.PHONY: all clean test test-jack-midi test-jack-audio test-jack test-gui test-gui-jack debug gui install -all: $(BUILD_DIR)/libxtc.a $(BUILD_DIR)/xtc-check +build: configure + $(CMAKE) --build "$(BUILD_DIR)" --config "$(BUILD_TYPE)" --parallel "$(JOBS)" -$(BUILD_DIR)/xtc-check: $(BUILD_DIR)/src/main.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl +test: build + $(CTEST) --test-dir "$(BUILD_DIR)" --build-config "$(BUILD_TYPE)" --output-on-failure $(CTEST_ARGS) -$(BUILD_DIR)/libxtc.a: $(CORE_OBJECTS) - @mkdir -p $(@D) - $(AR) rcsD $@ $^ +check: test -$(BUILD_DIR)/%.o: %.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@ +gui: configure + $(CMAKE) --build "$(BUILD_DIR)" --config "$(BUILD_TYPE)" --target xtc --parallel "$(JOBS)" -$(BUILD_DIR)/%.o: %.cpp - @mkdir -p $(@D) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(FLTK_CXXFLAGS) -MMD -MP -c $< -o $@ - -$(TEST_OBJECT): tests/unit/test_core.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@ - -$(BUILD_DIR)/test_core: $(TEST_OBJECT) $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $(TEST_OBJECT) $(BUILD_DIR)/libxtc.a -lm -ldl - -$(BUILD_DIR)/test_offline: $(BUILD_DIR)/tests/unit/test_offline.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_realtime: $(BUILD_DIR)/tests/unit/test_realtime.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -pthread - -$(BUILD_DIR)/test_backend: $(BUILD_DIR)/tests/unit/test_backend.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_graph_edits: $(BUILD_DIR)/tests/unit/test_graph_edits.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_project: $(BUILD_DIR)/tests/unit/test_project.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_audio: $(BUILD_DIR)/tests/unit/test_audio.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -pthread - -$(BUILD_DIR)/test_midi: $(BUILD_DIR)/tests/unit/test_midi.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_mixer: $(BUILD_DIR)/tests/unit/test_mixer.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_native: $(BUILD_DIR)/tests/unit/test_native.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/native-gain: $(BUILD_DIR)/examples/native_gain.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -test: test-modern-editing $(BUILD_DIR)/test_native $(BUILD_DIR)/native-gain $(BUILD_DIR)/test_mixer $(BUILD_DIR)/test_midi $(BUILD_DIR)/test_audio $(BUILD_DIR)/test_project $(BUILD_DIR)/test_graph_edits $(BACKEND_TEST) $(BUILD_DIR)/test_realtime $(BUILD_DIR)/test_core $(BUILD_DIR)/test_offline $(BUILD_DIR)/xtc-check - $(if $(BACKEND_TEST),./$(BACKEND_TEST),@echo "JACK fault tests skipped: JACK development files unavailable") - ./$(BUILD_DIR)/test_native - ./$(BUILD_DIR)/native-gain - ./$(BUILD_DIR)/test_mixer - ./$(BUILD_DIR)/test_midi - ./$(BUILD_DIR)/test_audio - ./$(BUILD_DIR)/test_project - ./$(BUILD_DIR)/test_graph_edits - ./$(BUILD_DIR)/test_realtime - ./$(BUILD_DIR)/test_core - ./$(BUILD_DIR)/test_offline - ./$(BUILD_DIR)/xtc-check - -debug: - $(MAKE) BUILD_DIR="$(BUILD_DIR)/debug" CFLAGS="$(CFLAGS) -O0 -g3" CXXFLAGS="$(CXXFLAGS) -O0 -g3" all test - -ifeq ($(JACK_AVAILABLE),yes) -$(BUILD_DIR)/tests/integration/test_jack_midi.o: CPPFLAGS += $(JACK_CFLAGS) -$(BUILD_DIR)/test_jack_midi: $(BUILD_DIR)/tests/integration/test_jack_midi.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ $(JACK_LIBS) -lm -ldl - -test-jack-midi: $(BUILD_DIR)/test_jack_midi - ./$(BUILD_DIR)/test_jack_midi - -$(BUILD_DIR)/tests/integration/test_jack_audio.o: CPPFLAGS += $(JACK_CFLAGS) -$(BUILD_DIR)/test_jack_audio: $(BUILD_DIR)/tests/integration/test_jack_audio.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ $(JACK_LIBS) -lm -ldl - -test-jack-audio: $(BUILD_DIR)/test_jack_audio - ./$(BUILD_DIR)/test_jack_audio - -$(BUILD_DIR)/tests/integration/test_jack.o: CPPFLAGS += $(JACK_CFLAGS) -$(BUILD_DIR)/test_jack: $(BUILD_DIR)/tests/integration/test_jack.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ $(JACK_LIBS) -lm -ldl - -test-jack: $(BUILD_DIR)/test_jack - ./$(BUILD_DIR)/test_jack -else -test-jack-midi test-jack-audio test-jack: - @echo "JACK development files are required for the live integration test." - @exit 1 -endif - -ifeq ($(GUI_AVAILABLE),yes) -gui: $(BUILD_DIR)/xtc - -$(BUILD_DIR)/test_gui: $(BUILD_DIR)/tests/integration/test_gui.o $(BUILD_DIR)/src/gui/main_window.o $(BUILD_DIR)/src/gui/graph_widget.o $(BUILD_DIR)/src/gui/sequencer_widget.o $(BUILD_DIR)/src/gui/piano_roll.o $(BUILD_DIR)/src/gui/mixer_widget.o $(BUILD_DIR)/src/gui/plugin_dialog.o $(BUILD_DIR)/src/gui/sample_editor.o $(BUILD_DIR)/libxtc.a - $(CXX) $(LDFLAGS) -o $@ $^ $(FLTK_LIBS) -lm -ldl -Wl,--wrap=xtc_history_reserve - -test-gui: $(BUILD_DIR)/test_gui - xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui - -ifeq ($(JACK_AVAILABLE),yes) -$(BUILD_DIR)/test_gui_jack: $(BUILD_DIR)/tests/integration/test_gui_jack.o $(BUILD_DIR)/src/gui/main_window.o $(BUILD_DIR)/src/gui/graph_widget.o $(BUILD_DIR)/src/gui/sequencer_widget.o $(BUILD_DIR)/src/gui/piano_roll.o $(BUILD_DIR)/src/gui/mixer_widget.o $(BUILD_DIR)/src/gui/plugin_dialog.o $(BUILD_DIR)/src/gui/sample_editor.o $(BUILD_DIR)/libxtc.a - $(CXX) $(LDFLAGS) -o $@ $^ $(FLTK_LIBS) $(JACK_LIBS) -lm -ldl - -test-gui-jack: $(BUILD_DIR)/test_gui_jack - xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui_jack -else -test-gui-jack: - @echo "JACK development files are required for the live GUI test." - @exit 1 -endif - -$(BUILD_DIR)/xtc: $(BUILD_DIR)/src/gui/main.o $(BUILD_DIR)/src/gui/main_window.o $(BUILD_DIR)/src/gui/graph_widget.o $(BUILD_DIR)/src/gui/sequencer_widget.o $(BUILD_DIR)/src/gui/piano_roll.o $(BUILD_DIR)/src/gui/mixer_widget.o $(BUILD_DIR)/src/gui/plugin_dialog.o $(BUILD_DIR)/src/gui/sample_editor.o $(BUILD_DIR)/libxtc.a - $(CXX) $(LDFLAGS) -o $@ $^ $(FLTK_LIBS) $(JACK_LIBS) -lm -ldl -else -test-gui-jack test-gui: - @echo "FLTK development files are required for GUI tests." - @exit 1 - -gui: - @echo "FLTK was not found by pkg-config or fltk-config. GUI requires FLTK development files." - @exit 1 -endif - -# install retains the core-only behavior; install-gui adds the application. -.PHONY: install uninstall install-gui dist release test-install test-release test-performance -$(BUILD_DIR)/xtc.pc: packaging/xtc.pc.in tools/pkgconfig.py tools/dist.py include/xtc/xtc_version.h FORCE - $(PYTHON) tools/pkgconfig.py --prefix "$(PREFIX)" --exec-prefix "$(exec_prefix)" --libdir "$(libdir)" --includedir "$(includedir)" --jack "$(if $(JACK_AVAILABLE),jack)" --output "$@" -.PHONY: FORCE -FORCE: - -install: all $(BUILD_DIR)/xtc.pc - $(PYTHON) tools/install.py $(INSTALL_LAYOUT) --build-dir "$(BUILD_DIR)" - -install-gui: install gui - $(PYTHON) tools/install.py $(INSTALL_LAYOUT) --build-dir "$(BUILD_DIR)" --gui - -uninstall: - $(PYTHON) tools/uninstall.py $(INSTALL_LAYOUT) - -dist: - $(PYTHON) tools/dist.py --output "$(BUILD_DIR)/xtc-$(VERSION).tar.gz" - -release: - $(MAKE) BUILD_DIR="$(BUILD_DIR)/release" CFLAGS="-O2 -g0 -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude -ffile-prefix-map=$(CURDIR)=." CXXFLAGS="-O2 -g0 -std=c++17 -Wall -Wextra -Wpedantic -Iinclude -ffile-prefix-map=$(CURDIR)=." FLTK_CXXFLAGS="$(filter -I% -D% -pthread,$(FLTK_CXXFLAGS))" all test $(if $(GUI_AVAILABLE),gui) - -test-install: all $(if $(GUI_AVAILABLE),gui) - $(PYTHON) tests/integration/test_install.py --build-dir "$(abspath $(BUILD_DIR))" $(if $(GUI_AVAILABLE),--gui) - -test-release: dist - $(PYTHON) tests/integration/test_release.py "$(BUILD_DIR)/xtc-$(VERSION).tar.gz" $(if $(GUI_AVAILABLE),--gui) +run: gui + "$(abspath $(BUILD_DIR))/xtc" +# Keep the CMake cache; never recursively delete a caller-supplied path. clean: - rm -rf "$(BUILD_DIR)" - --include $(CORE_OBJECTS:.o=.d) $(TEST_OBJECT:.o=.d) $(BUILD_DIR)/src/main.d $(BUILD_DIR)/tests/unit/test_offline.d $(BUILD_DIR)/tests/unit/test_realtime.d $(BUILD_DIR)/tests/unit/test_backend.d $(BUILD_DIR)/tests/unit/test_graph_edits.d \ - $(BUILD_DIR)/src/gui/main.d $(BUILD_DIR)/src/gui/main_window.d $(BUILD_DIR)/src/gui/graph_widget.d $(BUILD_DIR)/src/gui/sequencer_widget.d $(BUILD_DIR)/src/gui/piano_roll.d $(BUILD_DIR)/tests/integration/test_jack.d $(BUILD_DIR)/tests/integration/test_gui.d $(BUILD_DIR)/tests/integration/test_gui_jack.d - --include $(BUILD_DIR)/tests/unit/test_project.d - --include $(BUILD_DIR)/tests/unit/test_audio.d - --include $(BUILD_DIR)/tests/integration/test_jack_audio.d - --include $(BUILD_DIR)/tests/unit/test_midi.d - --include $(BUILD_DIR)/tests/integration/test_jack_midi.d - --include $(BUILD_DIR)/src/gui/mixer_widget.d - --include $(BUILD_DIR)/tests/unit/test_mixer.d - --include $(BUILD_DIR)/tests/unit/test_native.d $(BUILD_DIR)/examples/native_gain.d - -$(BUILD_DIR)/test_performance: $(BUILD_DIR)/tests/integration/test_performance.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/test_recovery: $(BUILD_DIR)/tests/integration/test_recovery.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -.PHONY: test-recovery -test-performance: $(BUILD_DIR)/test_performance - ./$(BUILD_DIR)/test_performance - -test-recovery: $(BUILD_DIR)/test_recovery - ./$(BUILD_DIR)/test_recovery - --include $(BUILD_DIR)/tests/integration/test_performance.d $(BUILD_DIR)/tests/integration/test_recovery.d - -# Linux/GNU-compatible linker test; no instrumentation in production objects. -$(BUILD_DIR)/test_reliability: $(BUILD_DIR)/tests/integration/test_reliability.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -Wl,--wrap=malloc,--wrap=calloc,--wrap=realloc,--wrap=free,--wrap=fopen,--wrap=fread,--wrap=fwrite,--wrap=read,--wrap=write,--wrap=pthread_mutex_lock,--wrap=pthread_cond_wait -pthread - -.PHONY: test-reliability test-sanitize analyze -test-reliability: test $(BUILD_DIR)/test_reliability - ./$(BUILD_DIR)/test_reliability - -test-sanitize: - ASAN_OPTIONS=detect_leaks=1:halt_on_error=1 UBSAN_OPTIONS=halt_on_error=1 $(MAKE) BUILD_DIR="$(BUILD_DIR)/sanitize" CC=clang CFLAGS="-O1 -g -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude -fsanitize=address,undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address,undefined" test-reliability test-recovery test-scale test-soak - -analyze: - $(PYTHON) tools/analyze.py $(CORE_SOURCES) - --include $(BUILD_DIR)/tests/integration/test_reliability.d - -.PHONY: test-workflow -ifeq ($(GUI_AVAILABLE)$(JACK_AVAILABLE),yesyes) -$(BUILD_DIR)/test_workflow: $(BUILD_DIR)/tests/integration/test_workflow.o $(BUILD_DIR)/src/gui/main_window.o $(BUILD_DIR)/src/gui/graph_widget.o $(BUILD_DIR)/src/gui/sequencer_widget.o $(BUILD_DIR)/src/gui/piano_roll.o $(BUILD_DIR)/src/gui/mixer_widget.o $(BUILD_DIR)/src/gui/plugin_dialog.o $(BUILD_DIR)/src/gui/sample_editor.o $(BUILD_DIR)/libxtc.a - $(CXX) $(LDFLAGS) -o $@ $^ $(FLTK_LIBS) $(JACK_LIBS) -lm -ldl - -test-workflow: $(BUILD_DIR)/test_workflow - xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_workflow -else -test-workflow: - @echo "FLTK and JACK development files are required for the workflow test." - @exit 1 -endif - --include $(BUILD_DIR)/tests/integration/test_workflow.d - -$(BUILD_DIR)/test_quality: $(BUILD_DIR)/tests/unit/test_quality.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-quality -test-quality: $(BUILD_DIR)/test_quality - ./$(BUILD_DIR)/test_quality -test: test-quality --include $(BUILD_DIR)/tests/unit/test_quality.d - -$(BUILD_DIR)/quality-preview: $(BUILD_DIR)/examples/quality_preview.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: quality-preview -quality-preview: $(BUILD_DIR)/quality-preview - ./$(BUILD_DIR)/quality-preview "$(abspath $(BUILD_DIR))/quality" --include $(BUILD_DIR)/examples/quality_preview.d - -$(BUILD_DIR)/test_recording: $(BUILD_DIR)/tests/integration/test_recording.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-recording -test-recording: $(BUILD_DIR)/test_recording - ./$(BUILD_DIR)/test_recording --include $(BUILD_DIR)/tests/integration/test_recording.d - -$(BUILD_DIR)/test_processor: $(BUILD_DIR)/tests/unit/test_processor.o $(BUILD_DIR)/examples/processors.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-processor -test-processor: $(BUILD_DIR)/test_processor - ./$(BUILD_DIR)/test_processor -test: test-processor --include $(BUILD_DIR)/tests/unit/test_processor.d $(BUILD_DIR)/examples/processors.d - -$(BUILD_DIR)/plugin-fixture.so: tests/plugins/fixture.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/test_plugins: $(BUILD_DIR)/tests/integration/test_plugins.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -Wl,--wrap=malloc,--wrap=calloc,--wrap=realloc,--wrap=free,--wrap=dlopen,--wrap=dlclose -.PHONY: test-plugins test-plugins-real -test-plugins: $(BUILD_DIR)/test_plugins $(BUILD_DIR)/plugin-fixture.so - ./$(BUILD_DIR)/test_plugins "$(abspath $(BUILD_DIR))/plugin-fixture.so" fixture -test-plugins-real: $(BUILD_DIR)/test_plugins - ./$(BUILD_DIR)/test_plugins /usr/lib/ladspa/amp.so - ./$(BUILD_DIR)/test_plugins /usr/lib/ladspa/inv_1429.so - ./$(BUILD_DIR)/test_plugins /usr/lib/ladspa/sine.so 3 --include $(BUILD_DIR)/tests/integration/test_plugins.d - -.PHONY: test-gui-plugins -test-gui-plugins: $(BUILD_DIR)/test_gui $(BUILD_DIR)/plugin-fixture.so - XTC_TEST_PLUGIN="$(abspath $(BUILD_DIR))/plugin-fixture.so" xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui - -$(BUILD_DIR)/test_control: $(BUILD_DIR)/tests/unit/test_control.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-control -test-control: $(BUILD_DIR)/test_control - ./$(BUILD_DIR)/test_control -test: test-control --include $(BUILD_DIR)/tests/unit/test_control.d - -$(BUILD_DIR)/test_editing: $(BUILD_DIR)/tests/unit/test_editing.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-editing -test-editing: $(BUILD_DIR)/test_editing - ./$(BUILD_DIR)/test_editing -test: test-editing --include $(BUILD_DIR)/tests/unit/test_editing.d - -$(BUILD_DIR)/test_portability: $(BUILD_DIR)/tests/integration/test_portability.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-portability -test-portability: $(BUILD_DIR)/test_portability - ./$(BUILD_DIR)/test_portability -test: test-portability --include $(BUILD_DIR)/tests/integration/test_portability.d - -$(BUILD_DIR)/benchmark_graph: $(BUILD_DIR)/tests/integration/benchmark_graph.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: benchmark -benchmark: $(BUILD_DIR)/benchmark_graph - $(PYTHON) tools/benchmark.py --binary "$(BUILD_DIR)/benchmark_graph" --output "$(BUILD_DIR)/performance.json" --compiler "$(CC)" --flags "$(CFLAGS)" --blocks "$(BENCHMARK_BLOCKS)" --include $(BUILD_DIR)/tests/integration/benchmark_graph.d - -$(BUILD_DIR)/test_scale: $(BUILD_DIR)/tests/integration/test_scale.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-scale test-stress -test-scale: $(BUILD_DIR)/test_scale - ./$(BUILD_DIR)/test_scale -test-stress: $(BUILD_DIR)/test_realtime $(BUILD_DIR)/test_reliability $(BUILD_DIR)/test_scale $(if $(GUI_AVAILABLE),$(BUILD_DIR)/test_gui) - $(PYTHON) tools/stress.py --build-dir "$(BUILD_DIR)" --rounds "$(STRESS_ROUNDS)" $(if $(GUI_AVAILABLE),--gui) --include $(BUILD_DIR)/tests/integration/test_scale.d - -BENCHMARK_BLOCKS ?= 300 -BENCHMARK_BASELINE ?= docs/performance-baseline.json -BENCHMARK_MAX_REGRESSION ?= 1.5 -.PHONY: benchmark-compare -benchmark-compare: $(BUILD_DIR)/benchmark_graph - $(PYTHON) tools/benchmark.py --binary "$(BUILD_DIR)/benchmark_graph" --output "$(BUILD_DIR)/performance.json" --compiler "$(CC)" --flags "$(CFLAGS)" --blocks "$(BENCHMARK_BLOCKS)" --baseline "$(BENCHMARK_BASELINE)" --max-regression "$(BENCHMARK_MAX_REGRESSION)" - -PACKAGE_ARCH := $(shell uname -m) -PACKAGE_FILE := $(BUILD_DIR)/xtc-$(VERSION)-linux-$(PACKAGE_ARCH).tar.gz -.PHONY: package test-package -package: release - $(PYTHON) tools/package.py --build-dir "$(BUILD_DIR)/release" --output "$(PACKAGE_FILE)" $(if $(GUI_AVAILABLE),--gui) -test-package: package - $(PYTHON) tests/integration/test_package.py "$(PACKAGE_FILE)" --build-dir "$(BUILD_DIR)/release" - -$(BUILD_DIR)/test_composition: $(BUILD_DIR)/tests/unit/test_composition.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-composition -test-composition: $(BUILD_DIR)/test_composition - ./$(BUILD_DIR)/test_composition -test: test-composition --include $(BUILD_DIR)/tests/unit/test_composition.d - -$(BUILD_DIR)/test_audio_editing: $(BUILD_DIR)/tests/unit/test_audio_editing.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-audio-editing -test-audio-editing: $(BUILD_DIR)/test_audio_editing - ./$(BUILD_DIR)/test_audio_editing -test: test-audio-editing --include $(BUILD_DIR)/tests/unit/test_audio_editing.d - -$(BUILD_DIR)/test_routing: $(BUILD_DIR)/tests/unit/test_routing.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-routing -test-routing: $(BUILD_DIR)/test_routing - ./$(BUILD_DIR)/test_routing -test: test-routing --include $(BUILD_DIR)/tests/unit/test_routing.d - -$(BUILD_DIR)/test_buses: $(BUILD_DIR)/tests/unit/test_buses.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-buses -test-buses: $(BUILD_DIR)/test_buses - ./$(BUILD_DIR)/test_buses -test: test-buses --include $(BUILD_DIR)/tests/unit/test_buses.d - --include $(BUILD_DIR)/src/gui/plugin_dialog.d - -$(BUILD_DIR)/test_parameters: $(BUILD_DIR)/tests/unit/test_parameters.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-parameters -test-parameters: $(BUILD_DIR)/test_parameters - ./$(BUILD_DIR)/test_parameters -test: test-parameters --include $(BUILD_DIR)/tests/unit/test_parameters.d - -$(BUILD_DIR)/test_project_diagnostics: $(BUILD_DIR)/tests/unit/test_project_diagnostics.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -Wl,--wrap=fflush,--wrap=fclose,--wrap=fsync,--wrap=rename -.PHONY: test-project-diagnostics -test-project-diagnostics: $(BUILD_DIR)/test_project_diagnostics - ./$(BUILD_DIR)/test_project_diagnostics -test: test-project-diagnostics --include $(BUILD_DIR)/tests/unit/test_project_diagnostics.d - -.PHONY: test-project-inspection -test-project-inspection: $(BUILD_DIR)/xtc-check - $(PYTHON) tests/integration/test_project_inspection.py $(BUILD_DIR)/xtc-check -test: test-project-inspection - -$(BUILD_DIR)/test_soak: $(BUILD_DIR)/tests/integration/test_soak.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-soak benchmark-project benchmark-project-compare check-development -test-soak: $(BUILD_DIR)/test_soak - ./$(BUILD_DIR)/test_soak -PROJECT_BENCHMARK_BASELINE ?= docs/project-performance-baseline.json -STRESS_ROUNDS ?= 3 -test-stress: $(BUILD_DIR)/test_portability $(BUILD_DIR)/test_project_diagnostics $(BUILD_DIR)/test_soak $(BACKEND_TEST) -benchmark-project: $(BUILD_DIR)/test_performance - $(PYTHON) tools/benchmark.py --workload project --binary "$(BUILD_DIR)/test_performance" --output "$(BUILD_DIR)/project-performance.json" --compiler "$(CC)" --flags "$(CFLAGS)" --blocks "$(BENCHMARK_BLOCKS)" -benchmark-project-compare: $(BUILD_DIR)/test_performance - $(PYTHON) tools/benchmark.py --workload project --binary "$(BUILD_DIR)/test_performance" --output "$(BUILD_DIR)/project-performance.json" --compiler "$(CC)" --flags "$(CFLAGS)" --blocks "$(BENCHMARK_BLOCKS)" --baseline "$(PROJECT_BENCHMARK_BASELINE)" --max-regression "$(BENCHMARK_MAX_REGRESSION)" -check-development: all test-reliability test-scale test-soak analyze test-sanitize --include $(BUILD_DIR)/tests/integration/test_soak.d -.PHONY: test-benchmark-tools -test-benchmark-tools: - $(PYTHON) tests/tools/test_benchmark.py -check-development: test-benchmark-tools - -.PHONY: test-stress-plugins -test-stress-plugins: $(BUILD_DIR)/test_realtime $(BUILD_DIR)/test_reliability $(BUILD_DIR)/test_scale $(BUILD_DIR)/test_portability $(BUILD_DIR)/test_project_diagnostics $(BUILD_DIR)/test_soak $(BACKEND_TEST) $(BUILD_DIR)/test_plugins $(BUILD_DIR)/plugin-fixture.so - $(PYTHON) tools/stress.py --build-dir "$(BUILD_DIR)" --rounds "$(STRESS_ROUNDS)" --plugin "$(BUILD_DIR)/plugin-fixture.so" - -.PHONY: test-install-layout -test-install-layout: all $(if $(GUI_AVAILABLE),gui) - $(PYTHON) tests/integration/test_install.py --build-dir "$(abspath $(BUILD_DIR))" --custom-layout $(if $(GUI_AVAILABLE),--gui) - -$(BUILD_DIR)/src/plugin/modern.o: override CPPFLAGS += -isystem third_party/clap/include -isystem third_party/vst3 - -$(BUILD_DIR)/clap-fixture.clap: tests/plugins/clap_fixture.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -isystem third_party/clap/include $(LDFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/vst2-fixture.so: tests/plugins/vst2_fixture.c src/plugin/vst2_abi.h - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/vst3-fixture.so: tests/plugins/vst3_fixture.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -isystem third_party/vst3 $(LDFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/test_modern_plugins: $(BUILD_DIR)/tests/integration/test_modern_plugins.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -Wl,--wrap=malloc,--wrap=calloc,--wrap=realloc,--wrap=free,--wrap=dlopen,--wrap=dlclose -.PHONY: test-modern-plugins -test-modern-plugins: $(BUILD_DIR)/test_modern_plugins $(BUILD_DIR)/clap-fixture.clap $(BUILD_DIR)/vst2-fixture.so $(BUILD_DIR)/vst3-fixture.so - ./$(BUILD_DIR)/test_modern_plugins "$(abspath $(BUILD_DIR))/clap-fixture.clap" "$(abspath $(BUILD_DIR))/vst2-fixture.so" "$(abspath $(BUILD_DIR))/vst3-fixture.so" -test: test-modern-plugins --include $(BUILD_DIR)/tests/integration/test_modern_plugins.d - -.PHONY: test-gui-modern-plugins -ifeq ($(GUI_AVAILABLE),yes) -test-gui-modern-plugins: $(BUILD_DIR)/test_gui $(BUILD_DIR)/clap-fixture.clap $(BUILD_DIR)/vst2-fixture.so $(BUILD_DIR)/vst3-fixture.so - XTC_TEST_CLAP="$(abspath $(BUILD_DIR))/clap-fixture.clap" XTC_TEST_VST2="$(abspath $(BUILD_DIR))/vst2-fixture.so" XTC_TEST_VST3="$(abspath $(BUILD_DIR))/vst3-fixture.so" xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui -else -test-gui-modern-plugins: - @echo "FLTK development files are required for modern plugin GUI tests." - @exit 1 -endif - -$(BUILD_DIR)/clap-synth-fixture.clap: tests/plugins/clap_synth_fixture.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -isystem third_party/clap/include $(LDFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/vst2-synth-fixture.so: tests/plugins/vst2_synth_fixture.c src/plugin/vst2_abi.h - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/vst3-synth-fixture.so: tests/plugins/vst3_synth_fixture.c - @mkdir -p $(@D) - $(CC) $(CPPFLAGS) $(CFLAGS) -isystem third_party/vst3 $(LDFLAGS) -fPIC -shared -o $@ $< -$(BUILD_DIR)/test_instruments: $(BUILD_DIR)/tests/integration/test_instruments.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -Wl,--wrap=malloc,--wrap=calloc,--wrap=realloc,--wrap=free,--wrap=dlopen,--wrap=dlclose -.PHONY: test-instruments -test-instruments: $(BUILD_DIR)/test_instruments $(BUILD_DIR)/clap-synth-fixture.clap $(BUILD_DIR)/vst2-synth-fixture.so $(BUILD_DIR)/vst3-synth-fixture.so - ./$(BUILD_DIR)/test_instruments "$(abspath $(BUILD_DIR))/clap-synth-fixture.clap" "$(abspath $(BUILD_DIR))/vst2-synth-fixture.so" "$(abspath $(BUILD_DIR))/vst3-synth-fixture.so" -test: test-instruments --include $(BUILD_DIR)/tests/integration/test_instruments.d - -.PHONY: test-gui-instruments -test-gui-instruments: $(BUILD_DIR)/test_gui $(BUILD_DIR)/clap-synth-fixture.clap $(BUILD_DIR)/vst2-synth-fixture.so $(BUILD_DIR)/vst3-synth-fixture.so - XTC_TEST_SYNTH_CLAP="$(abspath $(BUILD_DIR))/clap-synth-fixture.clap" XTC_TEST_SYNTH_VST2="$(abspath $(BUILD_DIR))/vst2-synth-fixture.so" XTC_TEST_SYNTH_VST3="$(abspath $(BUILD_DIR))/vst3-synth-fixture.so" xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui - -$(BUILD_DIR)/test_settings: $(BUILD_DIR)/tests/unit/test_settings.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-settings -test-settings: $(BUILD_DIR)/test_settings - ./$(BUILD_DIR)/test_settings -test: test-settings - --include $(BUILD_DIR)/tests/unit/test_settings.d - -$(BUILD_DIR)/test_builtin: tests/unit/test_builtin.c $(BUILD_DIR)/libxtc.a - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(BUILD_DIR)/libxtc.a -lm -ldl -.PHONY: test-builtins -test: test-builtins -test-builtins: $(BUILD_DIR)/test_builtin - ./$(BUILD_DIR)/test_builtin - -$(BUILD_DIR)/builtin-preview: $(BUILD_DIR)/examples/builtin_preview.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: builtin-preview -builtin-preview: $(BUILD_DIR)/builtin-preview - ./$(BUILD_DIR)/builtin-preview "$(abspath $(BUILD_DIR))/builtin" --include $(BUILD_DIR)/examples/builtin_preview.d - -$(BUILD_DIR)/test_modern_editing: tests/unit/test_modern_editing.c $(BUILD_DIR)/libxtc.a - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(BUILD_DIR)/libxtc.a -lm -ldl - -.PHONY: test-modern-editing -test-modern-editing: $(BUILD_DIR)/test_modern_editing - ./$(BUILD_DIR)/test_modern_editing - -.PHONY: test-gui-modern-editing -test-gui-modern-editing: $(BUILD_DIR)/test_gui - XTC_TEST_MODERN_EDITING_ONLY=1 xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui - -$(BUILD_DIR)/test_next_daw: tests/unit/test_next_daw.c $(BUILD_DIR)/libxtc.a - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(BUILD_DIR)/libxtc.a -lm -ldl -.PHONY: test-next-daw test-gui-next-daw -test-next-daw: $(BUILD_DIR)/test_next_daw - ./$(BUILD_DIR)/test_next_daw -test: test-next-daw -test-gui-next-daw: $(BUILD_DIR)/test_gui - XTC_TEST_MODERN_EDITING_ONLY=1 XTC_TEST_NEXT_DAW_ONLY=1 xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui - -# Optional documentation capture; requires Xvfb, ImageMagick import and xdotool. -$(BUILD_DIR)/capture_screenshots: $(BUILD_DIR)/tools/capture_screenshots.o $(BUILD_DIR)/src/gui/main_window.o $(BUILD_DIR)/src/gui/graph_widget.o $(BUILD_DIR)/src/gui/sequencer_widget.o $(BUILD_DIR)/src/gui/piano_roll.o $(BUILD_DIR)/src/gui/mixer_widget.o $(BUILD_DIR)/src/gui/plugin_dialog.o $(BUILD_DIR)/src/gui/sample_editor.o $(BUILD_DIR)/libxtc.a - $(CXX) $(LDFLAGS) -o $@ $^ $(FLTK_LIBS) $(JACK_LIBS) -lm -ldl - -.PHONY: screenshots -screenshots: $(BUILD_DIR)/capture_screenshots $(BUILD_DIR)/plugin-fixture.so $(BUILD_DIR)/clap-synth-fixture.clap $(BUILD_DIR)/vst2-synth-fixture.so $(BUILD_DIR)/vst3-synth-fixture.so - xvfb-run -a -s "-screen 0 1440x960x24" ./$(BUILD_DIR)/capture_screenshots "$(CURDIR)/screenshots" - --include $(BUILD_DIR)/tools/capture_screenshots.d - -$(BUILD_DIR)/test_sample: $(BUILD_DIR)/tests/unit/test_sample.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-sample test-gui-sample -test-sample: $(BUILD_DIR)/test_sample - ./$(BUILD_DIR)/test_sample -test: test-sample -test-gui-sample: $(BUILD_DIR)/test_gui - XTC_TEST_SAMPLE_EDITOR_ONLY=1 xvfb-run -a -s "-screen 0 1280x900x24" ./$(BUILD_DIR)/test_gui --include $(BUILD_DIR)/src/gui/sample_editor.d $(BUILD_DIR)/tests/unit/test_sample.d - -$(BUILD_DIR)/test_track_fx: $(BUILD_DIR)/tests/unit/test_track_fx.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-track-fx -test-track-fx: $(BUILD_DIR)/test_track_fx - ./$(BUILD_DIR)/test_track_fx -test: test-track-fx --include $(BUILD_DIR)/tests/unit/test_track_fx.d - -.PHONY: test-gui-track-fx -test-gui-track-fx: $(BUILD_DIR)/test_gui $(BUILD_DIR)/plugin-fixture.so $(BUILD_DIR)/clap-fixture.clap $(BUILD_DIR)/vst2-fixture.so $(BUILD_DIR)/vst3-fixture.so - XTC_TEST_TRACK_FX_ONLY=1 XTC_TEST_PLUGIN="$(abspath $(BUILD_DIR))/plugin-fixture.so" XTC_TEST_CLAP="$(abspath $(BUILD_DIR))/clap-fixture.clap" XTC_TEST_VST2="$(abspath $(BUILD_DIR))/vst2-fixture.so" XTC_TEST_VST3="$(abspath $(BUILD_DIR))/vst3-fixture.so" xvfb-run -a -s "-screen 0 1280x900x24" ./$(BUILD_DIR)/test_gui - -$(BUILD_DIR)/test_creative: $(BUILD_DIR)/tests/unit/test_creative.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-creative test-gui-creative -test-creative: $(BUILD_DIR)/test_creative - ./$(BUILD_DIR)/test_creative -test: test-creative -test-gui-creative: $(BUILD_DIR)/test_gui - XTC_TEST_CREATIVE_ONLY=1 xvfb-run -a -s "-screen 0 1280x900x24" ./$(BUILD_DIR)/test_gui --include $(BUILD_DIR)/tests/unit/test_creative.d - -.PHONY: test-gui-midi-workflows -test-gui-midi-workflows: $(BUILD_DIR)/test_gui - XTC_TEST_MIDI_WORKFLOWS_ONLY=1 xvfb-run -a -s "-screen 0 1440x960x24" ./$(BUILD_DIR)/test_gui - -$(BUILD_DIR)/test_midi_file: $(BUILD_DIR)/tests/unit/test_midi_file.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl -.PHONY: test-midi-file -test-midi-file: $(BUILD_DIR)/test_midi_file - ./$(BUILD_DIR)/test_midi_file -test: test-midi-file --include $(BUILD_DIR)/tests/unit/test_midi_file.d - -.PHONY: test-configuration-startup -test-configuration-startup: gui - xvfb-run -a -s "-screen 0 1440x960x24" $(PYTHON) tests/integration/test_configuration_startup.py ./$(BUILD_DIR)/xtc - -.PHONY: test-gui-midi-labels -test-gui-midi-labels: $(BUILD_DIR)/test_gui - XTC_TEST_MIDI_LABELS_ONLY=1 xvfb-run -a -s "-screen 0 1440x960x24" ./$(BUILD_DIR)/test_gui - -# Optional external Steinberg SDK qualification. Normal builds never fetch SDKs. -VST3_SDK_ROOT ?= -STEINBERG_BUILD_DIR ?= $(BUILD_DIR)/steinberg-sdk -.PHONY: steinberg-sdk test-steinberg-sdk test-plugin-module-path -steinberg-sdk: - @test -n "$(VST3_SDK_ROOT)" || { echo "Set VST3_SDK_ROOT to an official recursive SDK checkout."; exit 1; } - cmake -S examples/steinberg -B "$(STEINBERG_BUILD_DIR)" -DXTC_VST3_SDK_ROOT="$(abspath $(VST3_SDK_ROOT))" -DCMAKE_BUILD_TYPE=Release - cmake --build "$(STEINBERG_BUILD_DIR)" --target xtc-sdk-examples validator --parallel 4 - -$(BUILD_DIR)/test_plugin_module_path: $(BUILD_DIR)/tests/unit/test_plugin_module_path.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -test-plugin-module-path: $(BUILD_DIR)/test_plugin_module_path - ./$(BUILD_DIR)/test_plugin_module_path - -test: test-plugin-module-path - -$(BUILD_DIR)/test_steinberg_sdk: $(BUILD_DIR)/tests/integration/test_steinberg_sdk.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -$(BUILD_DIR)/steinberg-render: $(BUILD_DIR)/examples/steinberg/render.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -# Linux bundle convention; native Windows SDK examples use CMake directly. -test-steinberg-sdk: steinberg-sdk $(BUILD_DIR)/test_steinberg_sdk $(BUILD_DIR)/steinberg-render - "$(STEINBERG_BUILD_DIR)/bin/Release/validator" "$(abspath $(STEINBERG_BUILD_DIR))/VST3/Release/xtc-sdk-examples.vst3" - ./$(BUILD_DIR)/test_steinberg_sdk "$(abspath $(STEINBERG_BUILD_DIR))/VST3/Release/xtc-sdk-examples.vst3" - --include $(BUILD_DIR)/tests/unit/test_plugin_module_path.d $(BUILD_DIR)/tests/integration/test_steinberg_sdk.d $(BUILD_DIR)/examples/steinberg/render.d - -# Optional JUCE bridge/examples; externally supplied SDK, no downloads or installs. -JUCE_ROOT ?= -JUCE_BUILD_DIR ?= $(BUILD_DIR)/juce -.PHONY: juce-examples test-juce -juce-examples: - @test -n "$(JUCE_ROOT)" || { echo "Set JUCE_ROOT to an external JUCE 9 checkout (AGPLv3/commercial)."; exit 1; } - cmake -S examples/juce -B "$(JUCE_BUILD_DIR)" -DXTC_JUCE_ROOT="$(abspath $(JUCE_ROOT))" -DCMAKE_BUILD_TYPE=Release - cmake --build "$(JUCE_BUILD_DIR)" --target xtc-juce-render XtcJuceSynth_VST3 XtcJuceSynth_Standalone --parallel 3 - -$(BUILD_DIR)/test_juce_plugin: $(BUILD_DIR)/tests/integration/test_juce_plugin.o $(BUILD_DIR)/libxtc.a - $(CC) $(LDFLAGS) -o $@ $^ -lm -ldl - -test-juce: juce-examples $(BUILD_DIR)/test_juce_plugin - xvfb-run -a $(PYTHON) tests/integration/test_juce_examples.py "$(JUCE_BUILD_DIR)/xtc-juce-render_artefacts/Release/xtc-juce-render" - xvfb-run -a ctest --test-dir "$(JUCE_BUILD_DIR)" --output-on-failure - xvfb-run -a ./$(BUILD_DIR)/test_juce_plugin "$(abspath $(JUCE_BUILD_DIR))/XtcJuceSynth_artefacts/Release/VST3/xtc JUCE Synth.vst3" - --include $(BUILD_DIR)/tests/integration/test_juce_plugin.d + @if [ -f "$(BUILD_DIR)/CMakeCache.txt" ]; then \ + $(CMAKE) --build "$(BUILD_DIR)" --config "$(BUILD_TYPE)" --target clean; \ + fi + +help: + @echo 'make [build] Configure and compile (default)' + @echo 'make configure Configure only' + @echo 'make test Build, then run CTest (check is an alias)' + @echo 'make gui/run Build the FLTK app / build and launch it' + @echo 'make clean Clean products, retaining the CMake cache' + @echo 'Overrides: BUILD_DIR=build BUILD_TYPE=Debug JOBS=4 CMAKE_ARGS=... CTEST_ARGS=...' diff --git a/README.md b/README.md index 2670d75..c3378f3 100644 --- a/README.md +++ b/README.md @@ -40,23 +40,37 @@ hosting supports the compatible mono processors documented in ## Build -Requirements: a C compiler, a C++ compiler, GNU Make, and `pkg-config`. FLTK is optional for the core and tests. +Requirements: CMake 3.25+, a C99 compiler, Python 3.12+ and a build backend +such as Ninja or Unix Makefiles. The optional GUI needs C++17 and FLTK; JACK and +LADSPA development files enable their optional integrations. ```sh -make -make test +cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug +cmake --build build --parallel +ctest --test-dir build --output-on-failure ``` -`make` also builds `build/xtc-check`, a headless core startup/shutdown -check. Use `make debug` for a separate debug build and test run. See -[docs/BUILD.md](docs/BUILD.md) for the inspected toolchain and baseline limits. - -Build the optional FLTK GUI if FLTK is installed: +For quick development, the optional Make wrapper provides the same workflow: ```sh -make gui +make # configure and build +make test # build and run CTest +make gui # build just the FLTK application +make run # build and launch it +make clean # clean products; keep the CMake cache ``` +Override `BUILD_DIR`, `BUILD_TYPE` or `JOBS` as needed, for example +`make BUILD_DIR=build/release BUILD_TYPE=Release JOBS=8`. Pass additional options +with `CMAKE_ARGS` and `CTEST_ARGS`. CMake remains the authoritative build system. + +This builds `libxtc.a`, the headless `xtc-check`, tests, examples and the FLTK +application when available. Live JACK tests are opt-in. Use `-DXTC_BUILD_GUI=OFF` +for the C-only build, or the `debug`, `release`, `minimal` and `sanitize` presets +for separate configurations. See [docs/BUILD.md](docs/BUILD.md) for options, +focused tests, installation and IDE setup. [Phase 108](docs/ROADMAP.md#phase-108--complete-cmake-build-migration) records +the CMake migration. + Run the GUI: ```sh @@ -93,24 +107,24 @@ active graph intact; queued changes are labeled until the engine adopts them. See [GUI documentation](docs/GUI.md). For Linux installation, routing and CLI options see [LINUX.md](docs/LINUX.md); for native effects/instruments see [NATIVE_API.md](docs/NATIVE_API.md). -`make release`, `make dist`, `make test-install` and `make test-release` provide -release preparation; see [RELEASING.md](docs/RELEASING.md) for exact guarantees. -With JACK development files and an existing JACK-compatible server, run the -isolated software-loopback test with `make test-jack` and the record/reopen/play -acceptance test with `make test-jack-audio` and MIDI acceptance with -`make test-jack-midi`. It connects only test -clients. See [engine documentation](docs/AUDIO_ENGINE.md) for configuration, -routing, ownership and recovery. +The release preset and CMake’s `dist`, `test-install`, `test-release` and +`test-package` targets provide release preparation; see +[RELEASING.md](docs/RELEASING.md) for exact guarantees. +With JACK development files and an existing JACK-compatible server, configure +with `-DXTC_ENABLE_LIVE_TESTS=ON`, build, and run +`ctest --test-dir build --output-on-failure -L live`. These software-loopback +checks connect only test clients. See [engine documentation](docs/AUDIO_ENGINE.md) +for configuration, routing, ownership and recovery. Clean generated files: ```sh -make clean +cmake --build build --target clean ``` ## Suggested first agent task -Read `AGENT.md` and `docs/ARCHITECTURE.md`. Inspect the existing implementation, run `make test`, and report gaps before adding the next feature. Do not jump directly to plugin hosting or a full sequencer. +Read `AGENT.md` and `docs/ARCHITECTURE.md`. Inspect the existing implementation, run `cmake --build build --target check`, and report gaps before adding the next feature. Do not jump directly to plugin hosting or a full sequencer. ## License @@ -128,19 +142,19 @@ See [RECOVERY.md](docs/RECOVERY.md), [NATIVE_API.md](docs/NATIVE_API.md) and For everyday use see [USER_MANUAL.md](docs/USER_MANUAL.md), [KNOWN_LIMITATIONS.md](docs/KNOWN_LIMITATIONS.md) and [MAINTENANCE.md](docs/MAINTENANCE.md). `xtc-check --diagnose` reports build features; -`make test-package` checks a local system-linked Linux binary archive. +`cmake --build build --target test-package` checks a local system-linked Linux binary archive. Effect automation/controllers, editing commands, v7 project-folder media paths, performance baselines and release/installation checks are also supported. Non-destructive audio splitting, clip gain/fades and cached timeline waveforms are supported, with undo and v8 persistence (reading v1–v7). See [AUDIO_FILES.md](docs/AUDIO_FILES.md#non-destructive-clip-editing). -`make test-audio-editing` checks source preservation and exact boundary output. +`cmake --build build --target test-audio-editing` checks source preservation and exact boundary output. Time stretching and independent pitch shifting remain deferred. Signal-flow graph layout, routing diagnostics, direction/role indicators and atomic unity-gain insertion into a wire are supported. See -[ROUTING.md](docs/ROUTING.md); `make test-routing` verifies the C routing contract. +[ROUTING.md](docs/ROUTING.md); `cmake --build build --target test-routing` verifies the C routing contract. One-level mix buses, routing, coherent track/bus peaks and master clipping indicators are supported, persisted in v9 (reading v1–v8). See [MIXER.md](docs/MIXER.md). @@ -198,12 +212,12 @@ for file support, routing and persistence details. [Steinberg SDK integration (Phase 106)](docs/STEINBERG.md) adds conventional Linux VST3 bundle loading, transport context, official-SDK gain/synth examples and -validator checks. Run `make VST3_SDK_ROOT=/path/to/vst3sdk test-steinberg-sdk` +validator checks. Run `cmake -S . -B build -DXTC_VST3_SDK_ROOT=/path/to/vst3sdk -DXTC_BUILD_STEINBERG=ON && cmake --build build --target test-steinberg-sdk` for optional SDK qualification. A Windows ASIO device diagnostic is provided; a native ASIO playback/capture backend remains planned. [JUCE integration (Phase 107)](docs/JUCE.md) provides an optional VST3/standalone Soft Keys instrument with a native editor, DSP effects, presets, MIDI-to-WAV rendering and VST3 hosting examples. Build and qualify it with -`make JUCE_ROOT=/path/to/JUCE test-juce`; review JUCE’s AGPLv3/commercial licence +`cmake -S . -B build -DXTC_JUCE_ROOT=/path/to/JUCE -DXTC_BUILD_JUCE=ON && cmake --build build --target test-juce`; review JUCE’s AGPLv3/commercial licence before distributing linked binaries. See [the examples](examples/juce/README.md). diff --git a/cmake/Developer.cmake b/cmake/Developer.cmake new file mode 100644 index 0000000..18949c8 --- /dev/null +++ b/cmake/Developer.cmake @@ -0,0 +1,57 @@ +get_target_property(xtc_sources xtc-core SOURCES) +add_custom_target(analyze COMMAND ${Python3_EXECUTABLE} tools/analyze.py ${xtc_sources} + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) +if(BUILD_TESTING) + xtc_executable(benchmark_graph tests/integration/benchmark_graph.c) + set(XTC_BENCHMARK_BLOCKS 300 CACHE STRING "Benchmark iterations") + set(XTC_BENCHMARK_MAX_REGRESSION 1.5 CACHE STRING "Benchmark regression limit") + set(XTC_STRESS_ROUNDS 3 CACHE STRING "Stress iterations") + set(XTC_BENCHMARK_BASELINE "${PROJECT_SOURCE_DIR}/docs/performance-baseline.json" CACHE FILEPATH "Graph benchmark baseline") + set(XTC_PROJECT_BENCHMARK_BASELINE "${PROJECT_SOURCE_DIR}/docs/project-performance-baseline.json" CACHE FILEPATH "Project benchmark baseline") + foreach(workload graph project) + if(workload STREQUAL graph) + set(binary benchmark_graph) + set(stem benchmark) + set(baseline "${XTC_BENCHMARK_BASELINE}") + set(report performance.json) + else() + set(binary test_performance) + set(stem benchmark-project) + set(baseline "${XTC_PROJECT_BENCHMARK_BASELINE}") + set(report project-performance.json) + endif() + foreach(compare "" "-compare") + set(extra) + if(compare) + set(extra --baseline "${baseline}" --max-regression ${XTC_BENCHMARK_MAX_REGRESSION}) + endif() + add_custom_target(${stem}${compare} + COMMAND ${Python3_EXECUTABLE} tools/benchmark.py --workload ${workload} + --binary $ --output "${CMAKE_CURRENT_BINARY_DIR}/${report}" + --compiler "${CMAKE_C_COMPILER}" --flags "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELEASE}" + --blocks ${XTC_BENCHMARK_BLOCKS} ${extra} + DEPENDS ${binary} WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) + endforeach() + endforeach() + set(stress_deps test_realtime test_reliability test_scale test_portability test_project_diagnostics test_soak) + if(TARGET test_backend) + list(APPEND stress_deps test_backend) + endif() + set(gui_arg) + if(TARGET test_gui) + set(gui_arg --gui) + list(APPEND stress_deps test_gui) + endif() + add_custom_target(test-stress COMMAND ${Python3_EXECUTABLE} tools/stress.py + --build-dir "${CMAKE_CURRENT_BINARY_DIR}" --rounds ${XTC_STRESS_ROUNDS} ${gui_arg} + DEPENDS ${stress_deps} WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) + if(XTC_LADSPA_AVAILABLE) + add_custom_target(test-stress-plugins COMMAND ${Python3_EXECUTABLE} tools/stress.py + --build-dir "${CMAKE_CURRENT_BINARY_DIR}" --rounds ${XTC_STRESS_ROUNDS} --plugin $ + DEPENDS ${stress_deps} test_plugins plugin-fixture WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) + endif() + add_custom_target(test-plugins-real + COMMAND test_plugins /usr/lib/ladspa/amp.so + COMMAND test_plugins /usr/lib/ladspa/inv_1429.so + COMMAND test_plugins /usr/lib/ladspa/sine.so 3 DEPENDS test_plugins VERBATIM) +endif() diff --git a/cmake/Gui.cmake b/cmake/Gui.cmake new file mode 100644 index 0000000..c7b45df --- /dev/null +++ b/cmake/Gui.cmake @@ -0,0 +1,87 @@ +if(NOT XTC_BUILD_GUI) + return() +endif() +if(PkgConfig_FOUND) + pkg_check_modules(FLTK QUIET IMPORTED_TARGET fltk) +endif() +if(FLTK_FOUND) + set(xtc_fltk PkgConfig::FLTK) +else() + find_program(XTC_FLTK_CONFIG fltk-config) + if(NOT XTC_FLTK_CONFIG) + message(STATUS "FLTK unavailable: building core and offline tests only") + return() + endif() + execute_process(COMMAND ${XTC_FLTK_CONFIG} --cxxflags OUTPUT_VARIABLE fltk_flags OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) + execute_process(COMMAND ${XTC_FLTK_CONFIG} --ldflags OUTPUT_VARIABLE fltk_libs OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) + separate_arguments(fltk_flags UNIX_COMMAND "${fltk_flags}") + separate_arguments(fltk_libs UNIX_COMMAND "${fltk_libs}") + # Distribution flags must not override this project's debug/release settings. + list(FILTER fltk_flags INCLUDE REGEX "^(-I|-D|-pthread)") + add_library(xtc-fltk INTERFACE) + target_compile_options(xtc-fltk INTERFACE ${fltk_flags}) + target_link_libraries(xtc-fltk INTERFACE ${fltk_libs}) + set(xtc_fltk xtc-fltk) +endif() +enable_language(CXX) +set(XTC_GUI_AVAILABLE TRUE) +add_library(xtc-gui STATIC src/gui/main_window.cpp src/gui/graph_widget.cpp + src/gui/sequencer_widget.cpp src/gui/piano_roll.cpp src/gui/mixer_widget.cpp + src/gui/plugin_dialog.cpp src/gui/sample_editor.cpp) +xtc_warnings(xtc-gui) +target_link_libraries(xtc-gui PUBLIC xtc-core ${xtc_fltk}) +add_executable(xtc src/gui/main.cpp) +xtc_warnings(xtc) +target_link_libraries(xtc PRIVATE xtc-gui) +add_custom_target(gui DEPENDS xtc) +find_program(XTC_XVFB_RUN xvfb-run) +add_executable(capture_screenshots EXCLUDE_FROM_ALL tools/capture_screenshots.cpp) +xtc_warnings(capture_screenshots) +target_link_libraries(capture_screenshots PRIVATE xtc-gui) +if(XTC_XVFB_RUN AND BUILD_TESTING) + if(XTC_LADSPA_AVAILABLE) + add_custom_target(screenshots COMMAND ${XTC_XVFB_RUN} -a -s "-screen 0 1440x960x24" + $ "${PROJECT_SOURCE_DIR}/screenshots" + DEPENDS capture_screenshots plugin-fixture clap-synth-fixture vst2-synth-fixture vst3-synth-fixture + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) + endif() + add_executable(test_gui tests/integration/test_gui.cpp) + xtc_warnings(test_gui) + target_link_libraries(test_gui PRIVATE xtc-gui) + xtc_wrap(test_gui xtc_history_reserve) + function(xtc_gui_test name) + add_test(NAME ${name} COMMAND ${XTC_XVFB_RUN} -a -s "-screen 0 1440x960x24" $) + set_tests_properties(${name} PROPERTIES WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" LABELS gui ENVIRONMENT "${ARGN}") + endfunction() + xtc_gui_test(gui) + if(XTC_LADSPA_AVAILABLE) + xtc_gui_test(gui-plugins "XTC_TEST_PLUGIN=$") + endif() + xtc_gui_test(gui-modern-plugins "XTC_TEST_CLAP=$" "XTC_TEST_VST2=$" "XTC_TEST_VST3=$") + xtc_gui_test(gui-instruments "XTC_TEST_SYNTH_CLAP=$" "XTC_TEST_SYNTH_VST2=$" "XTC_TEST_SYNTH_VST3=$") + foreach(pair "modern-editing|MODERN_EDITING" "next-daw|NEXT_DAW" "sample|SAMPLE_EDITOR" "creative|CREATIVE" "midi-workflows|MIDI_WORKFLOWS" "midi-labels|MIDI_LABELS") + string(REPLACE "|" ";" pair "${pair}") + list(GET pair 0 name) + list(GET pair 1 env) + xtc_gui_test(gui-${name} "XTC_TEST_${env}_ONLY=1") + endforeach() + set_property(TEST gui-next-daw APPEND PROPERTY ENVIRONMENT XTC_TEST_MODERN_EDITING_ONLY=1) + if(XTC_LADSPA_AVAILABLE) + xtc_gui_test(gui-track-fx "XTC_TEST_TRACK_FX_ONLY=1" "XTC_TEST_PLUGIN=$" "XTC_TEST_CLAP=$" "XTC_TEST_VST2=$" "XTC_TEST_VST3=$") + endif() + add_test(NAME configuration-startup COMMAND ${XTC_XVFB_RUN} -a -s "-screen 0 1440x960x24" ${Python3_EXECUTABLE} + "${PROJECT_SOURCE_DIR}/tests/integration/test_configuration_startup.py" $) + set_tests_properties(configuration-startup PROPERTIES LABELS gui) + if(JACK_FOUND AND XTC_WITH_JACK) + foreach(kind gui_jack workflow) + add_executable(test_${kind} tests/integration/test_${kind}.cpp) + xtc_warnings(test_${kind}) + target_link_libraries(test_${kind} PRIVATE xtc-gui) + if(XTC_ENABLE_LIVE_TESTS) + string(REPLACE "_" "-" name "${kind}") + add_test(NAME ${name} COMMAND ${XTC_XVFB_RUN} -a $) + set_tests_properties(${name} PROPERTIES WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" LABELS live RUN_SERIAL TRUE) + endif() + endforeach() + endif() +endif() diff --git a/cmake/Install.cmake b/cmake/Install.cmake new file mode 100644 index 0000000..f206289 --- /dev/null +++ b/cmake/Install.cmake @@ -0,0 +1,46 @@ +# Retain the audited owned-file inventory and symlink-safe staging implementation. +set(XTC_INSTALL_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE PATH "Directory containing built install inputs") +option(XTC_INSTALL_GUI "Include the GUI in installation" ${XTC_GUI_AVAILABLE}) +set(XTC_INSTALL_LICENSEDIR "${CMAKE_INSTALL_DATAROOTDIR}/licenses/xtc" CACHE STRING "License install directory") +set(XTC_INSTALL_EXAMPLESDIR "${CMAKE_INSTALL_DATAROOTDIR}/xtc/examples" CACHE STRING "Example install directory") +set(XTC_INSTALL_DESKTOPDIR "${CMAKE_INSTALL_DATAROOTDIR}/applications" CACHE STRING "Desktop entry directory") +set(XTC_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE STRING "pkg-config directory") +configure_file(cmake/InstallDriver.cmake.in xtc-install.cmake @ONLY) +install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/xtc-install.cmake") +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -DXTC_UNINSTALL=ON -P "${CMAKE_CURRENT_BINARY_DIR}/xtc-install.cmake" VERBATIM) +add_custom_target(dist COMMAND ${Python3_EXECUTABLE} tools/dist.py --output "${CMAKE_CURRENT_BINARY_DIR}/xtc-${PROJECT_VERSION}.tar.gz" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) +set(gui_arg) +if(XTC_GUI_AVAILABLE) + set(gui_arg --gui) +endif() +if(BUILD_TESTING) + foreach(layout "" "-layout") + set(extra) + if(layout) + set(extra --custom-layout) + endif() + add_custom_target(test-install${layout} COMMAND ${Python3_EXECUTABLE} tests/integration/test_install.py + --build-dir "${CMAKE_CURRENT_BINARY_DIR}" ${gui_arg} ${extra} + DEPENDS xtc-core xtc-check + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) + if(XTC_GUI_AVAILABLE) + add_dependencies(test-install${layout} xtc) + endif() + endforeach() + add_custom_target(test-release COMMAND ${Python3_EXECUTABLE} tests/integration/test_release.py + "${CMAKE_CURRENT_BINARY_DIR}/xtc-${PROJECT_VERSION}.tar.gz" --generator "${CMAKE_GENERATOR}" ${gui_arg} + DEPENDS dist WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) +endif() +set(package_file "${CMAKE_CURRENT_BINARY_DIR}/xtc-${PROJECT_VERSION}-linux-${CMAKE_SYSTEM_PROCESSOR}.tar.gz") +add_custom_target(package-binary COMMAND ${Python3_EXECUTABLE} tools/package.py + --build-dir "${CMAKE_CURRENT_BINARY_DIR}" --output "${package_file}" ${gui_arg} + DEPENDS xtc-core xtc-check WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) +if(XTC_GUI_AVAILABLE) + add_dependencies(package-binary xtc) +endif() +add_custom_target(test-package COMMAND ${Python3_EXECUTABLE} tests/integration/test_package.py + "${package_file}" --build-dir "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS package-binary WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" VERBATIM) + +add_custom_target(package DEPENDS package-binary) diff --git a/cmake/InstallDriver.cmake.in b/cmake/InstallDriver.cmake.in new file mode 100644 index 0000000..0d98e1c --- /dev/null +++ b/cmake/InstallDriver.cmake.in @@ -0,0 +1,41 @@ +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@") +endif() +set(layout --prefix "${CMAKE_INSTALL_PREFIX}" --destdir "$ENV{DESTDIR}") +set(keys bindir libdir includedir docdir licensedir examplesdir desktopdir pkgconfigdir) +set(dirs "@CMAKE_INSTALL_BINDIR@" "@CMAKE_INSTALL_LIBDIR@" "@CMAKE_INSTALL_INCLUDEDIR@" "@CMAKE_INSTALL_DOCDIR@" + "@XTC_INSTALL_LICENSEDIR@" "@XTC_INSTALL_EXAMPLESDIR@" "@XTC_INSTALL_DESKTOPDIR@" "@XTC_INSTALL_PKGCONFIGDIR@") +foreach(index RANGE 0 7) + list(GET keys ${index} key) + list(GET dirs ${index} dir) + if(NOT IS_ABSOLUTE "${dir}") + set(dir "${CMAKE_INSTALL_PREFIX}/${dir}") + endif() + list(APPEND layout --${key} "${dir}") +endforeach() +if(XTC_UNINSTALL) + set(operation uninstall) + set(extra) +else() + set(operation install) + set(extra --build-dir "@XTC_INSTALL_BUILD_DIR@") + if("@XTC_INSTALL_GUI@") + list(APPEND extra --gui) + endif() + set(jack) + if("@JACK_FOUND@" AND "@XTC_WITH_JACK@") + set(jack jack) + endif() + list(GET dirs 1 libdir) + list(GET dirs 2 includedir) + foreach(key libdir includedir) + if(NOT IS_ABSOLUTE "${${key}}") + set(${key} "${CMAKE_INSTALL_PREFIX}/${${key}}") + endif() + endforeach() + execute_process(COMMAND "@Python3_EXECUTABLE@" tools/pkgconfig.py --prefix "${CMAKE_INSTALL_PREFIX}" + --libdir "${libdir}" --includedir "${includedir}" --jack "${jack}" --output "@XTC_INSTALL_BUILD_DIR@/xtc.pc" + WORKING_DIRECTORY "@PROJECT_SOURCE_DIR@" COMMAND_ERROR_IS_FATAL ANY) +endif() +execute_process(COMMAND "@Python3_EXECUTABLE@" "tools/${operation}.py" ${layout} ${extra} + WORKING_DIRECTORY "@PROJECT_SOURCE_DIR@" COMMAND_ERROR_IS_FATAL ANY) diff --git a/cmake/Tests.cmake b/cmake/Tests.cmake new file mode 100644 index 0000000..18f926a --- /dev/null +++ b/cmake/Tests.cmake @@ -0,0 +1,144 @@ +# Every test runs from the source root: fixtures/project paths are source-relative. +function(xtc_test name binary) + add_test(NAME ${name} COMMAND ${binary} ${ARGN}) + set_tests_properties(${name} PROPERTIES WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}") +endfunction() +function(xtc_wrap target) + foreach(symbol IN LISTS ARGN) + target_link_options(${target} PRIVATE "LINKER:--wrap=${symbol}") + endforeach() +endfunction() +if(NOT BUILD_TESTING) + return() +endif() +xtc_executable(test_offline tests/unit/test_offline.c) +xtc_test(offline test_offline) +xtc_executable(test_realtime tests/unit/test_realtime.c) +xtc_test(realtime test_realtime) +xtc_executable(test_graph_edits tests/unit/test_graph_edits.c) +xtc_test(graph-edits test_graph_edits) +xtc_executable(test_project tests/unit/test_project.c) +xtc_test(project test_project) +xtc_executable(test_audio tests/unit/test_audio.c) +xtc_test(audio test_audio) +xtc_executable(test_midi tests/unit/test_midi.c) +xtc_test(midi test_midi) +xtc_executable(test_mixer tests/unit/test_mixer.c) +xtc_test(mixer test_mixer) +xtc_executable(test_native tests/unit/test_native.c) +xtc_test(native test_native) +xtc_executable(test_performance tests/integration/test_performance.c) +xtc_test(performance test_performance) +xtc_executable(test_recovery tests/integration/test_recovery.c) +xtc_test(recovery test_recovery) +xtc_executable(test_reliability tests/integration/test_reliability.c) +xtc_wrap(test_reliability malloc calloc realloc free fopen fread fwrite read write pthread_mutex_lock pthread_cond_wait) +xtc_test(reliability test_reliability) +xtc_executable(test_quality tests/unit/test_quality.c) +xtc_test(quality test_quality) +xtc_executable(test_recording tests/integration/test_recording.c) +xtc_test(recording test_recording) +xtc_executable(test_processor tests/unit/test_processor.c examples/processors.c) +xtc_test(processor test_processor) +xtc_executable(test_plugins tests/integration/test_plugins.c) +xtc_wrap(test_plugins malloc calloc realloc free dlopen dlclose) +xtc_executable(test_control tests/unit/test_control.c) +xtc_test(control test_control) +xtc_executable(test_editing tests/unit/test_editing.c) +xtc_test(editing test_editing) +xtc_executable(test_portability tests/integration/test_portability.c) +xtc_test(portability test_portability) +xtc_executable(test_scale tests/integration/test_scale.c) +xtc_test(scale test_scale) +xtc_executable(test_composition tests/unit/test_composition.c) +xtc_test(composition test_composition) +xtc_executable(test_audio_editing tests/unit/test_audio_editing.c) +xtc_test(audio-editing test_audio_editing) +xtc_executable(test_routing tests/unit/test_routing.c) +xtc_test(routing test_routing) +xtc_executable(test_buses tests/unit/test_buses.c) +xtc_test(buses test_buses) +xtc_executable(test_parameters tests/unit/test_parameters.c) +xtc_test(parameters test_parameters) +xtc_executable(test_project_diagnostics tests/unit/test_project_diagnostics.c) +xtc_wrap(test_project_diagnostics fflush fclose fsync rename) +xtc_test(project-diagnostics test_project_diagnostics) +xtc_executable(test_soak tests/integration/test_soak.c) +xtc_test(soak test_soak) +xtc_executable(test_modern_plugins tests/integration/test_modern_plugins.c) +xtc_wrap(test_modern_plugins malloc calloc realloc free dlopen dlclose) +xtc_executable(test_instruments tests/integration/test_instruments.c) +xtc_wrap(test_instruments malloc calloc realloc free dlopen dlclose) +xtc_executable(test_settings tests/unit/test_settings.c) +xtc_test(settings test_settings) +xtc_executable(test_builtin tests/unit/test_builtin.c) +xtc_test(builtin test_builtin) +xtc_executable(test_modern_editing tests/unit/test_modern_editing.c) +xtc_test(modern-editing test_modern_editing) +xtc_executable(test_next_daw tests/unit/test_next_daw.c) +xtc_test(next-daw test_next_daw) +xtc_executable(test_sample tests/unit/test_sample.c) +xtc_test(sample test_sample) +xtc_executable(test_track_fx tests/unit/test_track_fx.c) +xtc_test(track-fx test_track_fx) +xtc_executable(test_creative tests/unit/test_creative.c) +xtc_test(creative test_creative) +xtc_executable(test_midi_file tests/unit/test_midi_file.c) +xtc_test(midi-file test_midi_file) +xtc_executable(test_plugin_module_path tests/unit/test_plugin_module_path.c) +xtc_test(plugin-module-path test_plugin_module_path) +xtc_executable(test_core tests/unit/test_core.c) +xtc_test(core test_core) +if(XTC_LADSPA_AVAILABLE) +add_library(plugin-fixture MODULE tests/plugins/fixture.c) +xtc_warnings(plugin-fixture) +set_target_properties(plugin-fixture PROPERTIES PREFIX "" SUFFIX ".so") +target_include_directories(plugin-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +endif() +add_library(clap-fixture MODULE tests/plugins/clap_fixture.c) +xtc_warnings(clap-fixture) +set_target_properties(clap-fixture PROPERTIES PREFIX "" SUFFIX ".clap") +target_include_directories(clap-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +add_library(vst2-fixture MODULE tests/plugins/vst2_fixture.c) +xtc_warnings(vst2-fixture) +set_target_properties(vst2-fixture PROPERTIES PREFIX "" SUFFIX ".so") +target_include_directories(vst2-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +add_library(vst3-fixture MODULE tests/plugins/vst3_fixture.c) +xtc_warnings(vst3-fixture) +set_target_properties(vst3-fixture PROPERTIES PREFIX "" SUFFIX ".so") +target_include_directories(vst3-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +add_library(clap-synth-fixture MODULE tests/plugins/clap_synth_fixture.c) +xtc_warnings(clap-synth-fixture) +set_target_properties(clap-synth-fixture PROPERTIES PREFIX "" SUFFIX ".clap") +target_include_directories(clap-synth-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +add_library(vst2-synth-fixture MODULE tests/plugins/vst2_synth_fixture.c) +xtc_warnings(vst2-synth-fixture) +set_target_properties(vst2-synth-fixture PROPERTIES PREFIX "" SUFFIX ".so") +target_include_directories(vst2-synth-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +add_library(vst3-synth-fixture MODULE tests/plugins/vst3_synth_fixture.c) +xtc_warnings(vst3-synth-fixture) +set_target_properties(vst3-synth-fixture PROPERTIES PREFIX "" SUFFIX ".so") +target_include_directories(vst3-synth-fixture SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/third_party/clap/include" "${PROJECT_SOURCE_DIR}/third_party/vst3") +if(XTC_LADSPA_AVAILABLE) +xtc_test(plugins test_plugins $ fixture) +endif() +xtc_test(modern-plugins test_modern_plugins $ $ $) +xtc_test(instruments test_instruments $ $ $) +xtc_test(startup xtc-check) +xtc_test(native-example native-gain) +add_test(NAME project-inspection COMMAND ${Python3_EXECUTABLE} "${PROJECT_SOURCE_DIR}/tests/integration/test_project_inspection.py" $) +set_tests_properties(project-inspection PROPERTIES WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}") +add_test(NAME benchmark-tools COMMAND ${Python3_EXECUTABLE} "${PROJECT_SOURCE_DIR}/tests/tools/test_benchmark.py") +set_tests_properties(benchmark-tools PROPERTIES WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}") +if(JACK_FOUND AND XTC_WITH_JACK) + xtc_executable(test_backend tests/unit/test_backend.c) + xtc_test(backend test_backend) + foreach(kind jack jack_audio jack_midi) + xtc_executable(test_${kind} tests/integration/test_${kind}.c) + if(XTC_ENABLE_LIVE_TESTS) + string(REPLACE "_" "-" name "${kind}") + xtc_test(${name} test_${kind}) + set_tests_properties(${name} PROPERTIES LABELS live RUN_SERIAL TRUE) + endif() + endforeach() +endif() diff --git a/cmake/Variants.cmake b/cmake/Variants.cmake new file mode 100644 index 0000000..db69ea7 --- /dev/null +++ b/cmake/Variants.cmake @@ -0,0 +1,29 @@ +# Each compiler/flag variant has its own cache and products. +foreach(variant debug release sanitize) + set(extra) + if(CMAKE_CXX_COMPILER) + list(APPEND extra "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") + endif() + if(variant STREQUAL debug) + set(kind Debug) + else() + set(kind Release) + list(APPEND extra -DXTC_REPRODUCIBLE=ON) + endif() + if(variant STREQUAL sanitize) + set(kind Debug) + list(APPEND extra -DXTC_SANITIZE=ON -DXTC_BUILD_GUI=OFF -DCMAKE_C_COMPILER=clang) + set(target test-sanitize) + else() + set(target ${variant}) + endif() + add_custom_target(${target} + COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -S "${PROJECT_SOURCE_DIR}" -B "${CMAKE_CURRENT_BINARY_DIR}/${variant}" + -DCMAKE_BUILD_TYPE=${kind} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DXTC_WITH_JACK=${XTC_WITH_JACK} -DXTC_WITH_LADSPA=${XTC_WITH_LADSPA} + -DXTC_BUILD_GUI=${XTC_BUILD_GUI} ${extra} + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/${variant}" --parallel 3 + COMMAND ${CMAKE_CTEST_COMMAND} --test-dir "${CMAKE_CURRENT_BINARY_DIR}/${variant}" --output-on-failure + USES_TERMINAL VERBATIM) +endforeach() +add_custom_target(check-development DEPENDS check analyze test-sanitize) diff --git a/docs/AUDIO_FILES.md b/docs/AUDIO_FILES.md index 1fb181f..e365550 100644 --- a/docs/AUDIO_FILES.md +++ b/docs/AUDIO_FILES.md @@ -109,12 +109,9 @@ implemented. ## Verification -make test includes deterministic WAV/graph samples, resampling/overlap/trim, +CTest includes deterministic WAV/graph samples, resampling/overlap/trim, malformed inputs, missing/relinked assets, SPSC concurrency/overflow/cleanup, -and record -> save/reopen -> exact playback through the backend callback. make -test-gui covers the workflow and rollback with a fake backend. make -test-gui-jack records/reopens a silent take using real JACK callbacks. make -test-jack-audio routes only two software clients: known source -> recorded WAV +and record -> save/reopen -> exact playback through the backend callback. The CTest gui test covers the workflow and rollback with a fake backend. The CTest gui-jack test records/reopens a silent take using real JACK callbacks. The CTest jack-audio test routes only two software clients: known source -> recorded WAV -> saved/reopened project -> graph playback -> capture assertion. Physical input/output, device latency, slow-disk endurance and server loss during a real recording remain unverified. @@ -139,7 +136,7 @@ never modified. A trailing partial frame is ignored. This handles xtc's canonical 44-byte mono float32 header only, not arbitrary damaged WAVs. Recovery is explicit and control-thread only; no orphan scan runs during playback. -`make test-recording` simulates 30 minutes at 8 kHz with repeated ring wrapping +`cmake --build build --target test-recording` simulates 30 minutes at 8 kHz with repeated ring wrapping and draining, destination races/symlinks, a child-local disk-size limit, prefix recovery and incomplete/missing asset reopening. `test-recovery` retains process kill coverage; `test_audio` covers saturation and concurrent drain. This is @@ -224,7 +221,7 @@ claimed. Source files should remain unchanged during a session; reopen after external replacement. Cache allocation failure omits previews without changing music metadata. -`make test-audio-editing` verifies exact split/gain/fade playback, odd blocks, +`cmake --build build --target test-audio-editing` verifies exact split/gain/fade playback, odd blocks, seeks, shortened media tails, one-frame files, source/project limits, capacity rollback, duplication/fade inheritance, persistence/invalid metadata, undo, peak bins after source removal and byte-identical source preservation. GUI tests diff --git a/docs/AUDIO_QUALITY.md b/docs/AUDIO_QUALITY.md index 15cf344..ba33f3d 100644 --- a/docs/AUDIO_QUALITY.md +++ b/docs/AUDIO_QUALITY.md @@ -150,13 +150,13 @@ maps and physical recording-offset calibration remain unsupported. ## Verification and Listening ``` -make all test gui test-reliability test-workflow test-gui test-gui-jack -make test-jack test-jack-audio test-jack-midi test-performance -make test-sanitize analyze -make quality-preview +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui test-reliability test-workflow test-gui test-gui-jack +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-jack test-jack-audio test-jack-midi test-performance +cmake --build build --target test-sanitize analyze +cmake --build build --target quality-preview ``` -`test_quality` is part of `make test`. It covers a sample-rate/tempo matrix up +`test_quality` is part of `cmake --build build --target check`. It covers a sample-rate/tempo matrix up to 768 kHz and the maximum project tick, conversion errors, old/new tempo snapshots for both audio and MIDI, exact offset/on/off order, sample boundary behavior, raw versus faded endpoints, media ends shorter than rounded clip ends, @@ -174,8 +174,8 @@ the existing fontconfig-only suppression; core leak checks are unsuppressed. Physical-device listening and human listening acceptance are **not yet verified**. -`make quality-preview` writes ignored build artifacts `quality-raw.wav` and -`quality-smooth.wav` in BUILD_DIR using `examples/quality_preview.c`. Compare at +`cmake --build build --target quality-preview` writes ignored build artifacts `quality-raw.wav` and +`quality-smooth.wav` in the selected binary directory using `examples/quality_preview.c`. Compare at moderate playback level: first listen to the three-note phrase endings, then the constant clip at two seconds, then a mid-period tone stop near three seconds. Check that fades reduce edge clicks, do not add noise and do not unexpectedly diff --git a/docs/BUILD.md b/docs/BUILD.md index 6b73302..2a26a1d 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -1,325 +1,188 @@ -# Build Baseline +# Building xtc with CMake -The existing layout is retained: public C headers in `include/xtc`, C -implementation modules under `src`, optional C++ GUI under `src/gui`, and tests -under `tests`. `src/main.c` is a minimal headless startup/shutdown check; it -does not initialize audio devices or open a GUI. +The root `CMakeLists.txt` is the authoritative build. C99 engine/model/DSP and +public C APIs remain in `include/xtc` and `src`; C++17 is used for the optional +FLTK interface and SDK examples. The small root Makefile only forwards common +commands to CMake/CTest; it contains no source lists or compilation rules. +Direct CMake builds need no Make wrapper. CMake does not download SDKs or +install system dependencies. ---- +## Configure, build and test -## Commands +Requirements: CMake 3.25+, a C99 compiler, Python 3.12+ for build/test/release +tools, a POSIX toolchain with threads, math and dynamic loading, and a CMake +backend such as Ninja or Unix Makefiles. C++17 is required when GUI/SDK targets +are enabled. `pkg-config` discovers optional JACK; FLTK discovery tries +`pkg-config` then `fltk-config`. LADSPA uses its installed development header. +Missing optional development files do not prevent the core from building. + +From the source root: ```sh -make clean all test +cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug +cmake --build build --parallel +ctest --test-dir build --output-on-failure ./build/xtc-check -make debug -make gui +./build/xtc # when FLTK was found ``` -`all` builds the static C library and startup check. `test` runs the existing -core/offline tests, concurrent plan-handoff stress, JACK fault injection (when -JACK headers are available), and the startup check. `debug` builds and tests -separately in `build/debug`. `gui` builds the FLTK shell with backend controls -and status. The shell starts with a silent graph and never connects physical -ports. Generated files remain under `build` and header changes trigger rebuilds. -Compiler, flags, `BUILD_DIR`, `PKG_CONFIG`, `FLTK_CONFIG`, and installation -`PREFIX` can be overridden through Make variables. C uses C99; GUI uses C++17. +All binaries and the static `libxtc.a` are in the chosen binary directory. +The default build includes offline test binaries and plugin fixtures. CTest +runs offline, fault-injection, plugin, recovery, capacity/endurance, and available +GUI tests; live JACK tests require explicit opt-in. GUI tests use Xvfb and +startup checks also require xdotool. No physical ports are connected by tests. +CTest runs source-relative fixtures with an explicit working directory. +Assertions stay enabled in optimized test builds. ---- +Use a new build directory when changing compiler, generator or instrumentation. +For Ninja, add `-G Ninja` to the configure command. CMake also writes +`compile_commands.json` for editors and analysis tools. In CLion, open the root +project and select a CMake profile/preset; do not import a Makefile project. -## Inspected Linux Toolchain (2026-10-26) +Optional presets separate configurations: -| Component | Available version | Role | +```sh +cmake --preset debug +cmake --build --preset debug --parallel +ctest --preset debug +``` + +`release`, `minimal` and `sanitize` presets follow the same three commands. +Release enables optimized builds and source/binary prefix maps. Minimal disables +FLTK, JACK and LADSPA. Sanitize selects Clang ASan/UBSan for offline C tests; +it does not enable GUI or physical-device tests. Existing GUI sanitizer caveats +and fontconfig leak suppressions are described in +[the integration notes](../tests/integration/README.md). + +## Quick Make shortcuts + +With GNU Make installed, `make` (or `make build`) configures and builds; +`make test`/`make check` also runs CTest. `make gui` builds the FLTK app, +`make run` builds and launches it, and `make clean` cleans products while keeping +the cache. `make configure` only configures, and `make help` lists shortcuts. + +Defaults are `BUILD_DIR=build`, `BUILD_TYPE=Debug` and `JOBS=4`. For example: + +```sh +make BUILD_DIR=build/release BUILD_TYPE=Release JOBS=8 +make BUILD_DIR=build/minimal CMAKE_ARGS='-G Ninja -DXTC_BUILD_GUI=OFF -DXTC_WITH_JACK=OFF' +make test CTEST_ARGS='-R ^core$' +``` + +Use a fresh binary directory when changing generator/compiler. Extra configure +flags go in `CMAKE_ARGS`; extra CTest flags go in `CTEST_ARGS`. These shortcuts +use the same CMake targets and cache as the direct commands above. Keep source +lists, dependencies, SDK and packaging rules in CMake. + +## Configuration + +| Cache option | Default | Meaning | | --- | --- | --- | -| GCC / G++ | 16.2.1 | C core and optional C++ GUI | -| GNU Make | 4.4.1 | Build and test driver | -| pkg-config | 3.0.10 | Development package discovery | -| FLTK | 1.4.5 | Optional GUI; discovered through `fltk-config` | -| JACK | 3.1609.0 (pkg-config) | Optional JACK backend (PipeWire compatibility library) | -| ALSA | 1.2.16.1 (pkg-config) | Future MIDI candidate; not linked yet | -| Clang | 23.1.3 | Alternative compiler / diagnostics | +| `BUILD_TESTING` | `ON` | Build/register the test suite | +| `XTC_BUILD_GUI` | `ON` | Discover FLTK and build the application when available | +| `XTC_WITH_JACK` | `ON` | Discover/build the JACK/PipeWire backend | +| `XTC_WITH_LADSPA` | `ON` | Enable LADSPA when the system header exists | +| `XTC_ENABLE_LIVE_TESTS` | `OFF` | Register software JACK loopback and GUI/JACK workflows | +| `XTC_SANITIZE` | `OFF` | Address/undefined sanitizers, frame pointers | +| `XTC_REPRODUCIBLE` | `OFF` | Remove source/binary directory paths and debug information | +| `XTC_BUILD_JUCE` | `OFF` | Build externally supplied JUCE integration | +| `XTC_BUILD_STEINBERG` | `OFF` | Build externally supplied official VST3 SDK examples | -Valgrind is also available. The core tests use a small in-tree C test harness -and require no external test framework. FLTK has no `fltk.pc` on this host; the -Makefile tries pkg-config first, then `fltk-config`. +Use `-DNAME=VALUE` during configuration, not Make variables. Standard CMake +settings include `CMAKE_C_COMPILER`, `CMAKE_CXX_COMPILER`, `CMAKE_BUILD_TYPE`, +`CMAKE_C_FLAGS`, `CMAKE_CXX_FLAGS` and `CMAKE_EXE_LINKER_FLAGS`. CMake propagates +includes, definitions and link dependencies through targets. Core users can link +`xtc::core` inside this build; installed consumers use `pkg-config xtc`. ---- - -## Dependencies and Licensing - -The offline core needs the C runtime and math library. Optional JACK support is -selected with pkg-config; missing JACK does not prevent core/tests from -building. Realtime publication uses GCC/Clang lock-free atomic builtins in C99. -Tests also use POSIX threads. Backend consumers must link JACK explicitly; -backend objects are pulled from the static library only when used. The -repository is MIT licensed. The optional GUI uses maintained FLTK 1.4 (LGPL with -FLTK's static-linking exception); its platform link flags come from the -installed package. Distribution still requires review of all linked dependencies -and notices. The installed PipeWire JACK library is MIT; traditional JACK client -libraries use LGPL licensing. See decision 0004 for the dependency choice and -caveats. ALSA remains a future MIDI candidate. No packages or system settings -are changed by these build commands. - ---- - -## Backend Verification +A fully offline minimal configuration: ```sh -make all test gui debug -make test-jack -# Extended live run after building the integration binary: -./build/test_jack 30 -# Verify optional build without JACK metadata, in a separate directory: -make BUILD_DIR=build/nojack PKG_CONFIG=false all test -# Address/undefined sanitizers (non-realtime tests only): -make BUILD_DIR=build/sanitize CC=clang CFLAGS='-O1 -g -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude -fsanitize=address,undefined -fno-omit-frame-pointer' LDFLAGS='-fsanitize=address,undefined' all test -# Thread sanitizer for publication/reclamation: -make BUILD_DIR=build/tsan CC=cc CFLAGS='-O1 -g -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude -fsanitize=thread' LDFLAGS='-fsanitize=thread' build/tsan/test_realtime -./build/tsan/test_realtime +cmake -S . -B build/minimal -G Ninja -DCMAKE_BUILD_TYPE=Release \ + -DXTC_BUILD_GUI=OFF -DXTC_WITH_JACK=OFF -DXTC_WITH_LADSPA=OFF +cmake --build build/minimal --parallel +ctest --test-dir build/minimal --output-on-failure ``` -Normal, sanitizer and no-JACK tests pass. ThreadSanitizer reports no race in -2,000 concurrent submissions. The 30-second live loopback test passed at 48 kHz -with zero xruns and ten restart cycles; see tests/integration/README.md. Live -server termination/format changes were not performed on the shared server; -fault-injection tests cover those notification paths. No packages installed or -system/device configuration changed. Sanitizers are not enabled in the live -callback qualification run. +This path needs no C++ compiler, FLTK, JACK or LADSPA development files. CLAP and +VST3 C headers stay pinned in `third_party`; VST2 uses original declarations. +No external plugin or SDK is included in normal packages. See [LICENSES.md](LICENSES.md). ---- +## Focused tests and tools -## GUI Shell Verification +Build before invoking CTest, or use `check`, which builds the test dependencies: ```sh -make clean all test gui debug test-gui test-gui-jack +cmake --build build --target check --parallel +ctest --test-dir build --output-on-failure -R '^(core|realtime|modern-plugins)$' +ctest --test-dir build --output-on-failure -L gui +cmake --build build --target test-reliability test-recording test-scale test-soak +cmake --build build --target analyze +cmake --build build --target quality-preview builtin-preview ``` -`test-gui` needs Xvfb and `xvfb-run`, uses a fake C backend and requires no JACK -server. `test-gui-jack` needs a running server; it creates no physical routes. -Both passed, including repeated active-client close and reconnect. The GUI also -builds without JACK metadata and reports unsupported backend support at startup. -A live keyboard/visual smoke run passed under Xvfb. Clang ASan/UBSan GUI tests -passed with a suppression limited to the installed fontconfig library's -exit-time font allocations; see tests/integration/README.md for the distinction -and command. No compiler warnings remain with GCC or Clang. Core/backend tests -pass with unsuppressed ASan/UBSan. No packages or system configuration changed. +Each registered root test also has a `test-NAME` build target (for example +`test-core`, `test-gui`, `test-track-fx`). `test-builtins` aliases `test-builtin`. +`gui` builds the optional main application. Preview targets write WAVs below the +binary directory without playback. Screenshot commands are in +[the gallery](../screenshots/README.md). These commands assume configuration +has already completed; feature-specific docs use the same convention. ---- - -## Graph Editor Verification +To qualify live software routing against an **existing** JACK-compatible server: ```sh -make clean all test gui debug test-gui test-gui-jack -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON +cmake --build build --parallel +ctest --test-dir build --output-on-failure -L live +./build/test_jack 30 # optional longer software loopback run ``` -The normal suite adds test_graph_edits for stable IDs, dense snapshots, -transactional rejection/deletion/disconnect and known rendered samples before -and after edits. Core/backend/realtime tests pass under unsuppressed Clang -ASan/UBSan; ThreadSanitizer passes the concurrent handoff test with revision-to- -gain checks. GUI ASan/UBSan tests pass with the existing fontconfig-only leak -suppression. Live GUI tests build a connected graph, adopt accepted edits during -playback, reject cycles, reconnect and close active clients. Mouse/visual smoke -checks cover node creation, wiring, cycle rejection, dragging and deletion. No -physical routes, new dependencies or system changes are introduced. +Live tests are serialized. They neither start/reconfigure the server nor +establish physical-device acceptance. See [AUDIO_ENGINE.md](AUDIO_ENGINE.md). ---- +Developer targets include `benchmark`, `benchmark-compare`, `benchmark-project`, +`benchmark-project-compare`, `test-stress`, `test-stress-plugins`, +`test-plugins-real` and `check-development`. The latter combines the normal +suite, Clang static analysis and a separate sanitizer build. `debug`, `release` +and `test-sanitize` convenience targets configure/build/test child directories; +prefer presets when selecting the directory explicitly. Benchmark/stress cache +variables are `XTC_BENCHMARK_BLOCKS`, `XTC_BENCHMARK_MAX_REGRESSION`, +`XTC_BENCHMARK_BASELINE`, `XTC_PROJECT_BENCHMARK_BASELINE` and `XTC_STRESS_ROUNDS`. +Run benchmarks without concurrent testing/instrumentation for meaningful timing. -## Project Verification +For ThreadSanitizer, use a separate configuration with +`-DCMAKE_C_FLAGS='-g -O1 -fsanitize=thread'` and +`-DCMAKE_EXE_LINKER_FLAGS=-fsanitize=thread`, build `test_realtime`/`test_audio`, +and run those binaries. Tool/runtime support depends on the host. + +## SDKs, installation and release + +See [JUCE.md](JUCE.md) and [STEINBERG.md](STEINBERG.md) for optional SDK +configuration, licenses and qualification limits. Both now build within the root +CMake project; the JUCE bridge links the same PIC C core target. ```sh -make clean all test gui debug test-gui test-gui-jack -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui +cmake -S . -B build/release -DCMAKE_BUILD_TYPE=Release -DXTC_REPRODUCIBLE=ON \ + -DCMAKE_INSTALL_PREFIX=/usr +cmake --build build/release --parallel +ctest --test-dir build/release --output-on-failure +DESTDIR="$PWD/build/stage" cmake --install build/release --prefix /usr +DESTDIR="$PWD/build/stage" cmake --build build/release --target uninstall ``` -The suite adds test_project: track/clip limits, stable IDs, trims, integer -transport conversion, exact file roundtrip with graph rendering, exhaustive -fixture truncations, byte mutations, failed saves and temporary cleanup. GUI -tests cover region gestures, zoom, save/load and backend rollback. Live JACK -tests verify playhead advance, pause and resume without creating routes. The -updated core and GUI tests pass Clang ASan/UBSan (the GUI uses the existing -fontconfig-only LSan suppression). An Xvfb/xdotool visual check created a track -and region, moved it one bar and zoomed the canvas. No dependencies were added. +For uninstall, configure with `-DCMAKE_INSTALL_PREFIX=/usr` first when that is +the desired permanent prefix, or invoke the generated `xtc-install.cmake` with +`-DCMAKE_INSTALL_PREFIX=/usr -DXTC_UNINSTALL=ON -P` explicitly. The `--prefix` +install override does not persist in CMake's cache. Prefer setting the prefix at +configure time for a matching install/uninstall pair. Standard `DESTDIR` staging +never enters installed metadata. The audited Python installer retains exact +owned-file inventory, atomic file replacement and symlink protection. -Audio assets and recording are supported. Silent arrangement regions, fixed -tempo/4/4, no undo, no persisted GUI layout, and unverified physical-device -playback/endurance remain explicit limits. +`test-install` and `test-install-layout` stage into temporary directories and +compile installed C/C++ consumers. `dist` exports indexed files; `test-release` +rebuilds two exports; `package-binary` (also aliased as `package`) and `test-package` stage and verify a local +Linux archive. See [RELEASING.md](RELEASING.md) for layout/manifest details. ---- - -## Audio Verification - -```sh -make all test gui debug test-gui test-gui-jack test-jack-audio -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui -make BUILD_DIR=build/tsan CC=cc CFLAGS='-O1 -g -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude -fsanitize=thread' LDFLAGS='-fsanitize=thread' build/tsan/test_audio build/tsan/test_realtime -./build/tsan/test_audio -./build/tsan/test_realtime -``` - -No new dependency is required for WAV import, recording or playback. New audio -sources remain C99; only GUI controls use C++/FLTK. test_audio covers decoder -samples, truncations/nonfinite/unsupported input, resampling, trim/overlap, -missing assets and concurrent recording. test_backend records known callback -input, saves/reopens and checks exact graph output. Project tests cover v1 -migration and 1024 maximum-length asset paths in a valid v2 file. - -The live test-jack-audio target connects only its software clients and checks -record -> WAV -> saved/reopened project -> graph playback. GUI/JACK tests also -record, finalize, save and reopen a take without hardware routing. Clang -ASan/UBSan passes the core and GUI tests (GUI retains the existing -fontconfig-only LSan suppression). ThreadSanitizer passes the concurrent -recorder and existing realtime handoff tests. Hardware capture/playback, disk -endurance and real server loss during recording remain unverified. See -AUDIO_FILES.md. - ---- - -## MIDI Verification - -```sh -make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui -``` - -`test_midi` adds exact sample scheduling, trim/seek/panic, block independence, -voice capacities, stable notes and v3 malformed-file/roundtrip/v2 migration -coverage. Backend fault injection checks event offsets, dropped/malformed input, -disconnection, xruns, missing output buffers and stop/restart cleanup. GUI tests -draw/move/resize notes, save/reopen and render them. The live MIDI target -connects only software clients and checks on/off, panic, disconnect, and -saved-note playback/seek. It needs an existing JACK-compatible server. No -hardware routes or system changes are made. MIDI recording/SMF and physical -devices are deferred. - -A fresh `build/verify` build passed all targets above with no compiler warnings. -The no-JACK core/GUI suite and Clang ASan/UBSan core/GUI suite also passed; GUI -leak checking uses only the existing fontconfig suppression documented in the -integration README. Physical MIDI and audio devices remain unverified. - ---- - -## Mixer Verification - -```sh -make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui -``` - -`test_mixer` covers stereo gain/pan, mute/solo, sample automation, block -independence, WAV and graph routing, v4 persistence failures and bounded -history. GUI tests add automation mouse edits, mixer keys, history -rollback/branching and save/reopen. The existing live MIDI test now uses two -software capture ports and checks hard-right saved-note playback and output -meter snapshots; mono audio record/play acceptance remains covered. A native -Xvfb mouse smoke check confirms sliders and automation drawing. See MIXER.md for -capacities and deferred features. - -The fresh verification build, no-JACK core/GUI suite and Clang ASan/UBSan -core/GUI suite passed without compiler warnings. GUI leak checking retains only -the existing fontconfig suppression. ThreadSanitizer also passed the recorder -concurrency and realtime publication tests. Physical devices and long-running -stereo performance remain unverified. - ---- - -## Release Preparation - -```sh -make all test gui debug test-gui test-gui-jack test-jack test-jack-audio test-jack-midi -make test-install test-recovery test-performance -make test-release -``` - -Core test now includes native ABI/lifecycle/retirement and the native-gain -example. Optional release tests use Python 3.12+ standard library, Git for -indexed source exports, GNU/LLVM deterministic ar and temporary directories. No -live prefix is installed. test-install checks core/GUI files, a prefix -containing spaces, headless CLI, pkg-config C/C++ consumers, exact/idempotent -uninstall and preservation of unrelated files. test-release compares two clean -exported optimized builds and repacked archives byte-for-byte. test-recovery -kills private child writers and forces a child-local write limit, checking -complete finalized destinations and explicit orphan temporaries. Performance -timings are reported, not used as a universal realtime guarantee. See -RELEASING.md, RECOVERY.md and PERFORMANCE.md. - -Verification passed a fresh warning-enabled core/debug/GUI build, all software -JACK targets, staged install/uninstall, recovery and performance tests. No-JACK -core/GUI/install checks and Clang ASan/UBSan core/GUI checks also passed (the -GUI retains only its existing fontconfig leak suppression). TSan recorder and -realtime-handoff checks passed. Two independent optimized source exports and -their repacked source archives were byte-identical on this toolchain. - ---- - -## Reliability Audit - -Run `make test-reliability` for the core suites, allocation-failure sweep, -callback allocation/free guard and native reclamation checks. Run -`make test-sanitize` for Clang ASan/UBSan/leak checks and `make analyze` for -Clang static analysis of all core sources. These need installed development -tools; the allocation guard uses a GNU-compatible linker. See -[RELIABILITY.md](RELIABILITY.md) for ownership, audit findings, full -verification commands and remaining limits. - ---- - -## Workflow Acceptance - -`make test-workflow` requires FLTK/JACK development files, Xvfb and an existing -JACK-compatible server. It does not start/configure the server or connect -physical devices. See [WORKFLOW.md](WORKFLOW.md) for the full GUI composition -acceptance sequence and transport semantics. - ---- - -## Audio Quality - -`make test-quality` runs exact timing, source-boundary, gain-ramp, event-order -and numeric/layout tests (also included in `make test`). `make quality-preview` -builds the C example and writes raw/smoothed listening WAVs into BUILD_DIR -without playing them or configuring devices. See -[AUDIO_QUALITY.md](AUDIO_QUALITY.md). - -Parameter-processor and plugin checks: -`make test-processor test-plugins test-gui-plugins`. The latter two need the -system LADSPA development header; real-plugin acceptance is separate -(`make test-plugins-real`, paths listed in PLUGIN_HOSTING.md). With no -`ladspa.h`, the ordinary core/GUI still builds and plugin operations return -UNSUPPORTED; existing plugin metadata can still be loaded/saved. To exercise -that configuration explicitly, use a fresh BUILD_DIR with -`CPPFLAGS=-DXTC_DISABLE_LADSPA JACK_AVAILABLE=`. No dependency is downloaded. -Link static consumers with `-lm -ldl` plus the backend dependencies when used. -See LICENSES.md for header/plugin licensing and decision 0009 for rationale. -Recording stress/recovery is available with `make test-recording`. - -Developer qualification: `make check-development` runs the core build, -regressions, scale/virtual endurance, benchmark-gate checks, Clang analysis and -ASan/UBSan. Run `make benchmark benchmark-project` separately without contention -or sanitizers for timing reports. Optional `make test-stress-plugins` requires -installed LADSPA headers; `make test-stress` supports minimal builds too. - ---- - -## Installation Layouts - -`make test-install test-install-layout` verifies default and custom layouts in -throwaway staging directories, C/C++ installed consumers, examples and exact -removal. Override `PREFIX`, `exec_prefix`, `bindir`, `libdir`, `includedir`, -`datarootdir`, `datadir`, `docdir`, `pkgconfigdir`, `licensedir`, `examplesdir` -or `desktopdir` as needed. `DESTDIR` adds staging separately; pass the same -layout to uninstall. See RELEASING.md for commands and RELEASE_NOTES.md for -upgrades. - -Modern plugin adapters build in C99 using the pinned header-only CLAP and VST3 C -APIs under third_party; there is no network build or SDK runtime. VST2 -compatibility uses original ABI declarations. `make test-modern-plugins` runs -original shared-library fixtures without a server; -`make test-gui-modern-plugins` adds GUI/browser/undo/reopen tests under Xvfb. -The fixtures also run in `make test` and therefore in sanitizer/release builds. -Notices are installed with LICENSE. - -The GUI uses a header-only FLTK theme and existing drawing primitives; no new -GUI dependency or generated asset is required. `make test-gui-instruments` also -exercises sidebar track creation, rollback, undo/reopen and minimum/large -layouts. See WORKSPACE.md for the default size and manual visual verification. +Clean products with `cmake --build build --target clean`; to discard the cache, +remove only the selected build directory. Never configure in the source tree. +Historical validation entries in ROADMAP describe the build used at that time; +Phase 108 records the CMake migration and its verified results. diff --git a/docs/BUILTIN_DEVICES.md b/docs/BUILTIN_DEVICES.md index 160fd9b..9379930 100644 --- a/docs/BUILTIN_DEVICES.md +++ b/docs/BUILTIN_DEVICES.md @@ -38,7 +38,7 @@ fixed presets. Readers still accept formats 1–12. Older xtc builds reject form allocation, file access, library loading, locking or GUI calls. Validation: -`make BUILD_DIR=build/builtins all gui test test-gui-instruments test-gui-modern-plugins test-gui-plugins analyze` +`cmake --build build/builtins --parallel && cmake --build build/builtins --target gui check test-gui-instruments test-gui-modern-plugins test-gui-plugins analyze` (join onto one line). `test-builtins` checks impulse tails, dry mix, feedback bounds, block partition consistency, note release/panic and instrument/effect project round trips. @@ -47,7 +47,7 @@ project round trips. ## Acceptance Checks -`make BUILD_DIR=build/builtin-acceptance all gui test test-reliability test-gui builtin-preview` +`cmake --build build/builtin-acceptance --parallel && cmake --build build/builtin-acceptance --target gui check test-reliability test-gui builtin-preview` checks all four device identities and GUI paths without external plugin binaries. The reliability runner fails each internal preparation allocation in turn and forbids allocation/free, direct file I/O and blocking locks during @@ -57,7 +57,7 @@ cover voice exhaustion, sustain, repeated notes, partitioning and transport reset/seek/panic. The existing portability suite verifies older project migrations. -For repeatable manual listening, run `make builtin-preview` and audition +For repeatable manual listening, run `cmake --build build --target builtin-preview` and audition `build/builtin-{0,1,2,3}.wav` at a conservative level: 0 is Soft Keys, 1 Sub Bass, 2 Delay and 3 Reverb. Each six-second file includes held tones or two impulses followed by silence for tails. Check onset/release, unwanted @@ -164,7 +164,7 @@ Tests compare whole/split blocks, tempo-synced impulse placement, left-to-right energy, bounded extreme feedback/diffusion and graph/direct stereo output (including the engine's subnormal-to-zero policy). GUI tests save/reopen both identities; callback guards and ASan/UBSan cover processing. -`make builtin-preview` also writes files 5/6 for repeatable listening; human +`cmake --build build --target builtin-preview` also writes files 5/6 for repeatable listening; human listening remains pending. --- diff --git a/docs/CONTROL.md b/docs/CONTROL.md index 2fd7780..d93cdad 100644 --- a/docs/CONTROL.md +++ b/docs/CONTROL.md @@ -46,7 +46,7 @@ and the project tempo. Repeated render/save/reopen, odd block partition, exact endpoints, seek/plan replacement, sustain/retrigger and controller ramp tests: ```sh -make test test-control test-plugins test-gui-plugins test-plugins-real +cmake --build build --target check test-control test-plugins test-gui-plugins test-plugins-real ``` Physical controller devices and listening remain unqualified; no tempo map, diff --git a/docs/EDITING.md b/docs/EDITING.md index 27a9465..ddbd85b 100644 --- a/docs/EDITING.md +++ b/docs/EDITING.md @@ -106,7 +106,7 @@ edits do not request extra approval. No operation here deletes source media. ## Evidence and Manual Procedure -`make all test gui test-gui-plugins test-reliability analyze test-workflow test-gui-jack` +`cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui test-gui-plugins test-reliability analyze test-workflow test-gui-jack` covers core commands, grouped gestures/batches, backend rollback, selection retention, take undo/redo and real software JACK state restoration. The GUI fixture injects history-reservation failure before graph/map/import publication diff --git a/docs/GUI.md b/docs/GUI.md index 8a94750..9081eb0 100644 --- a/docs/GUI.md +++ b/docs/GUI.md @@ -61,10 +61,10 @@ process exit. Normal close/reconnect paths permit retry and release ownership only after successful close. The entry point catches C++ exceptions before process exit. Backend operations return C error codes throughout. -Tests: `make test-gui` runs real widgets/callbacks/timers against a fake C +Tests: `cmake --build build --target test-gui` runs real widgets/callbacks/timers against a fake C backend under Xvfb, including control errors, startup without JACK, partially initialized handles, server/format notifications, view switching, retrying -close, and timer removal. `make test-gui-jack` additionally requires a running +close, and timer removal. `cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-gui-jack` additionally requires a running JACK-compatible server, tests repeated start/stop, reconnect and active-client close, and creates no routes. Physical playback is outside this shell. See BUILD.md and the integration test README for sanitizer limitations and keyboard @@ -214,7 +214,7 @@ finalizing a take (and is refused during recording). Home stops and returns to zero. Failed candidate installation now refreshes the actual backend state even when the previous document is retained. -`make test-workflow` uses a live JACK-compatible backend and Xvfb, with exact +`cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-workflow` uses a live JACK-compatible backend and Xvfb, with exact stereo render comparisons across destruction/recreation of the GUI and live playhead/meter/transport checks. `test-gui` covers stop/seek failure and recording guards. No project files need manual editing for this workflow. diff --git a/docs/IDEA.md b/docs/IDEA.md index adb66ef..9f6aca2 100644 --- a/docs/IDEA.md +++ b/docs/IDEA.md @@ -172,7 +172,7 @@ Adapt this to an existing repository instead of replacing it blindly. xtc/ ├── README.md ├── LICENSE -├── Makefile +├── CMakeLists.txt ├── docs/ │ ├── ARCHITECTURE.md │ ├── AUDIO_ENGINE.md @@ -624,8 +624,8 @@ make an edit partially visible if it fails validation. ### Build system -Use a straightforward Makefile unless the existing repository has a sound reason -to use another build system. +Use the root CMake project and CTest. Keep SDK integrations optional and +all generated output in separate build directories. The build must: @@ -736,7 +736,7 @@ feature is exciting. ### Repository and design baseline - Inspect the repository and available toolchain. -- Create or update architecture documentation and the Makefile. +- Create or update architecture documentation and the CMake project. - Verify C compiler, C++ compiler, FLTK, audio development headers, and test tools. - Record dependency and licensing decisions. @@ -851,7 +851,7 @@ Start with the **repository and design baseline** only. development packages, `pkg-config`, and test tools. 3. Do not install packages or modify system configuration without permission. 4. Propose the smallest initial source tree compatible with this brief. -5. Add or update the Makefile and documentation only as needed. +5. Add or update the CMake project and documentation only as needed. 6. Create a minimal program that builds and exits cleanly; do not yet implement a full audio engine or GUI. 7. Build it and run its tests. diff --git a/docs/JUCE.md b/docs/JUCE.md index ca1dc55..9e8b76a 100644 --- a/docs/JUCE.md +++ b/docs/JUCE.md @@ -17,8 +17,9 @@ normal builds or automatically installed. The integration uses: Synthesis is the real xtc **Soft Keys** implementation, called through `xtc_plugin_*`. C++ remains confined to the optional integration. The SDK build -uses a separate PIC build of the C archive with JACK/FLTK disabled; the installed -application and existing build directories are not modified. +links the root CMake PIC C archive through target dependencies; standalone SDK +configuration adds the same CMake core with JACK/FLTK disabled. SDK targets do +not install plugins or modify other binary directories. ## Screenshots @@ -43,10 +44,11 @@ before distributing those binaries. No SDK source or JUCE-linked binary is added to the normal xtc install/package targets. JUCE requires C++17 and its Linux development dependencies, including ALSA, -Fontconfig/FreeType and X11 libraries. CMake >= 3.25 and GNU Make build these +Fontconfig/FreeType and X11 libraries. CMake >= 3.25 and a backend such as Ninja build these examples. Xvfb runs automated checks without a desktop. The SDK's `juceaide` tool is compiled at configure time. Builds are substantially heavier than the -C-only core. Browser/network features are disabled; normal builds never fetch +C-only core. JUCE’s bundled codec C sources use GNU C11; the xtc core stays +strict C99. Browser/network features are disabled; normal builds never fetch SDKs or install system packages. Only the inspected SDK revision has been qualified; use the documented revision for reproducibility. diff --git a/docs/LICENSES.md b/docs/LICENSES.md index 2107b62..a683894 100644 --- a/docs/LICENSES.md +++ b/docs/LICENSES.md @@ -26,9 +26,11 @@ redistributing dependencies. The default install uses system shared libraries. | HarfBuzz, Wayland, libdecor, xkbcommon | MIT-family licenses; check each shipped package. | | X11 libraries | MIT/X11-family notices. | | D-Bus | AFL-2.1 OR GPL-2.0-or-later. | -| Python 3 | PSF-2.0; optional build/test/package scripts use only the standard library. Not an application runtime dependency. | +| Python 3 | PSF-2.0; build/test/package scripts use only the standard library. Not an application runtime dependency. | -GNU Make, GCC/Clang, binutils, pkg-config and optional Xvfb/xdotool/sanitizers +CMake (BSD-3-Clause), a backend such as Ninja (Apache-2.0) or GNU Make +(GPL-3.0-or-later), GCC/Clang, binutils, pkg-config and optional +Xvfb/xdotool/sanitizers are build/test tools, not shipped dependencies. New Python tooling needs Python 3.12+ for safe tar extraction in release tests; this host has Python 3.14.8. No packages were installed and no application dependency was added. diff --git a/docs/LINUX.md b/docs/LINUX.md index 2c01416..b14b7f9 100644 --- a/docs/LINUX.md +++ b/docs/LINUX.md @@ -1,15 +1,15 @@ # Linux Setup and Installation -Use a C99 compiler, C++17 compiler for the GUI, GNU Make, binutils and +Use a C99 compiler, C++17 compiler for the GUI, CMake 3.25+, a CMake backend, binutils and pkg-config. The core needs only the C/math runtime. FLTK 1.4 development files enable the GUI; JACK-compatible development files enable the optional audio/MIDI -backend. Packaging/install tests additionally use Python 3.12+ (standard library -only). No command starts a server, installs dependencies or changes system +backend. Build/test/install tooling uses Python 3.12+ (standard library only). No command starts a server, installs dependencies or changes system settings. ```sh -make all test -make gui +cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug +cmake --build build --parallel +ctest --test-dir build --output-on-failure ./build/xtc ``` @@ -31,17 +31,20 @@ files. ## Staged Installation -Core-only installation retains the original `make install` behavior and now adds -xtc-check, pkg-config metadata, license and documentation. install-gui adds the -GUI executable and desktop launcher. Defaults to PREFIX=/usr/local: +CMake installation includes the core, xtc-check, pkg-config metadata, licenses +and documentation. `XTC_INSTALL_GUI` defaults on when FLTK was found, adding the +application and desktop launcher. Use `-DXTC_INSTALL_GUI=OFF` for core-only +installation. The prefix defaults to `/usr/local`: ```sh -make install-gui DESTDIR="$PWD/build/stage" PREFIX=/usr -make uninstall DESTDIR="$PWD/build/stage" PREFIX=/usr +cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr +cmake --build build --parallel +DESTDIR="$PWD/build/stage" cmake --install build +DESTDIR="$PWD/build/stage" cmake --build build --target uninstall ``` Review staged contents before using a package manager or choosing a writable -live prefix. For a user installation, use an absolute PREFIX such as +live prefix. For a user installation, use an absolute `CMAKE_INSTALL_PREFIX` such as `$HOME/.local` and ensure its bin is in PATH. Prefixes containing spaces are tested; shell metacharacters/newlines, quotes, dollar signs and backslashes are not supported. No root installation is performed by tests. Uninstall from the @@ -59,15 +62,16 @@ C/C++ consumers use `pkg-config --cflags --libs --static xtc` plus the chosen compiler; expose PREFIX/lib/pkgconfig through PKG_CONFIG_PATH when needed. Static backend consumers need JACK if that optional backend was built; the pc file records its private dependency. Public headers contain no JACK/FLTK types. -To build without optional libraries, set PKG_CONFIG=false FLTK_CONFIG=false. +To build without optional libraries, configure with `-DXTC_BUILD_GUI=OFF +-DXTC_WITH_JACK=OFF -DXTC_WITH_LADSPA=OFF`. Python is not needed to run the installed executables or library. --- ## Binary/Development Archive -`make package` creates a system-linked Linux archive plus SHA-256 sidecar below -BUILD_DIR; `make test-package` verifies its contents. It includes usr/bin, +`cmake --build build --target package-binary` creates a system-linked Linux archive plus SHA-256 sidecar below +the selected binary directory; `cmake --build build --target test-package` verifies its contents. It includes usr/bin, headers/static library/pkg-config metadata, desktop file (when FLTK is present), manuals, baseline and MIT license. No shared library, third-party plugin, media, service or dependency installer is bundled. `BINARY_MANIFEST.json` lists file @@ -80,8 +84,8 @@ extract the archive with two leading path components stripped (`xtc-VERSION-linux- ARCH/usr`), placing bin/include/lib/share directly under that prefix. Put its bin on PATH and lib/pkgconfig on PKG_CONFIG_PATH. The archive's pkg-config prefix is relative to its installed location; normal -`make install` retains explicit PREFIX. Use the matching source's uninstall with -that PREFIX, or review the manifest before removing its files. Unknown/unrelated +`cmake --build build --target install` retains its configured prefix. Use the matching source's uninstall with +that prefix, or review the manifest before removing its files. Unknown/unrelated files are retained by source uninstall. This local tar package is not a pacman/RPM/deb registration or public distribution. @@ -90,5 +94,5 @@ without opening a server or executing a plugin. `xtc --diagnose` works headlessly as well. Missing runtime GUI libraries are loader errors: install the distribution's compatible dependencies or rebuild the core-only configuration. Build without optional libraries using a fresh directory, -`PKG_CONFIG=false FLTK_CONFIG=false CPPFLAGS=-DXTC_DISABLE_LADSPA`. No system +`cmake -S . -B build/minimal -DXTC_BUILD_GUI=OFF -DXTC_WITH_JACK=OFF -DXTC_WITH_LADSPA=OFF`. No system setting or package is changed by tests. diff --git a/docs/MAINTENANCE.md b/docs/MAINTENANCE.md index f15caff..79faf43 100644 --- a/docs/MAINTENANCE.md +++ b/docs/MAINTENANCE.md @@ -2,7 +2,7 @@ ## Supported Development Target -Initial target: x86_64 Artix/Arch-family Linux with glibc, GNU Make, GCC/Clang, +Initial target: x86_64 Artix/Arch-family Linux with glibc, CMake 3.25+ and a backend such as Ninja, GCC/Clang, FLTK 1.4 and a JACK-compatible client/server. The inspected host uses GCC 16.2.1, FLTK 1.4.5 and the dependencies in LICENSES.md. Core-only/no-JACK/no-LADSPA builds are checked too. Build on the destination @@ -19,16 +19,16 @@ only. No dependency is fetched automatically. bump, Git tag, public upload or external release is performed by local targets. 2. Run warning-enabled - `make all test gui test-plugins test-plugins-real test-gui-plugins test-workflow test-gui-jack test-jack-midi test-recording test-recovery test-reliability test-scale analyze` + `cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui test-plugins test-plugins-real test-gui-plugins test-workflow test-gui-jack test-jack-midi test-recording test-recovery test-reliability test-scale analyze` where dependencies exist. -3. Use a separate BUILD_DIR for ASan/UBSan/leak runs and another for optional- +3. Use a separate CMake binary directory for ASan/UBSan/leak runs and another for optional- dependency builds. GUI leak checks use only the documented fontconfig suppression; core tests use none. Run publication/recording race tests as described in RELIABILITY.md. Sanitizers are not latency qualification. -4. Run `make benchmark test-performance test-stress`; compare on matching +4. Run `cmake --build build --target benchmark test-performance test-stress`; compare on matching host/compiler/flags against a reviewed baseline. Investigate regressions; do not weaken thresholds or replace evidence without explanation. -5. Run `make test-install test-release test-package`. Inspect archive manifests, +5. Run `cmake --build build --target test-install test-release test-package`. Inspect archive manifests, hashes, dynamic dependency names and licenses. Source exports rebuild twice with identical artifacts on this host. Repeat binary packaging compares bytes. diff --git a/docs/MIDI.md b/docs/MIDI.md index 101e101..caa92a7 100644 --- a/docs/MIDI.md +++ b/docs/MIDI.md @@ -74,11 +74,11 @@ zoom yet. Note edits participate in project snapshot undo/redo. ## Verification -`make test` checks exact onset/off samples, block partition independence, trim, +`cmake --build build --target check` checks exact onset/off samples, block partition independence, trim, seek, panic, channel handling, voice/event limits, persistence migration and -malformed files, plus callback fault recovery. `make test-gui` draws, moves and +malformed files, plus callback fault recovery. `cmake --build build --target test-gui` draws, moves and resizes notes with FLTK events, then saves/reopens and renders them. -`make test-jack-midi` uses only software MIDI/audio ports to verify live on/off, +`cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-jack-midi` uses only software MIDI/audio ports to verify live on/off, panic, disconnection and saved/reopened scheduled playback. JACK port discovery may lag activation under PipeWire; the test waits before connecting. Physical MIDI devices, bridge/device loss behind an unchanged port, latency and @@ -138,10 +138,10 @@ on/off/control messages execute in caller/JACK order, with no implicit priority reordering. The renderer rejects malformed/unsorted/out-of-range events before touching output or runtime; raw velocity-zero note-ons decode to note-off. -`make test-composition` covers filters, property preservation, triplet rounding, +`cmake --build build --target test-composition` covers filters, property preservation, triplet rounding, repeats/fresh IDs, rejected batch rollback, capacity, undo snapshots, equal-offset live order, adjacent/overlapping notes, exact block partitions and -seek chase. `make test-gui` covers mouse edits and the new commands with +seek chase. `cmake --build build --target test-gui` covers mouse edits and the new commands with undo/redo and engine reinstallation. Listening and physical MIDI-device validation remain pending. diff --git a/docs/MODERN_EDITING.md b/docs/MODERN_EDITING.md index c25e754..858d200 100644 --- a/docs/MODERN_EDITING.md +++ b/docs/MODERN_EDITING.md @@ -124,8 +124,8 @@ The code is built with warnings enabled and tested. Reproduce the core, transaction and allocation-failure checks with: ```sh -make BUILD_DIR=build/roadmap-audit all gui test-reliability -make BUILD_DIR=build/roadmap-audit test-gui-modern-editing +cmake --build build/roadmap-audit --parallel && cmake --build build/roadmap-audit --target gui test-reliability +cmake --build build/roadmap-audit --target test-gui-modern-editing ``` The full GUI, instrument/effect plugin fixtures, software JACK lifecycle, Clang diff --git a/docs/NATIVE_API.md b/docs/NATIVE_API.md index f9249f5..6a25c0d 100644 --- a/docs/NATIVE_API.md +++ b/docs/NATIVE_API.md @@ -1,7 +1,7 @@ # Native C Processor API, Version 1 `xtc_native.h` defines a small compile-time extension boundary for trusted C99 -sources, effects and instruments. Build `make build/native-gain` and run it for +sources, effects and instruments. Build `cmake --build build --target native-gain` and run it for a complete graph-effect example in `examples/native_gain.c`. This is a source API for this release, not a promise of binary compatibility across builds/platforms. There is no module scanner, dynamic loader, SDK dependency or @@ -90,7 +90,7 @@ policy must precede serialized native extensions or external plugin hosting. Tests cover ABI/size/field rejection, descriptor copying, aliased buffers, prepare/process failures, stereo compile cleanup, retirement/destruction outside processing, event offsets, seek note reset, source ports, instrument-buffer -graph routing and unsupported-save preservation. Run `make test`. +graph routing and unsupported-save preservation. Run `cmake --build build --target check`. --- @@ -99,7 +99,7 @@ graph routing and unsupported-save preservation. Run `make test`. `xtc_processor.h` is the public C99 parameter-only extension interface (ABI 2). It complements the ABI 1 graph adapter without changing its layout or behavior. `examples/processors.c` implements a mono gain effect and a monophonic MIDI sine -instrument using only public headers; `make test-processor` builds and exercises +instrument using only public headers; `cmake --build build --target test-processor` builds and exercises both. The instrument has immediate note-off and no voice stealing/release tail. Descriptors have an exact ABI version and structure size, a persistent diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md index 973a5bc..7f343a9 100644 --- a/docs/PERFORMANCE.md +++ b/docs/PERFORMANCE.md @@ -41,7 +41,7 @@ termination remain unqualified. ## Matrix, Capacity and Contention Checks -`make benchmark` records 24 warmed graph cases: 2/16/64/256 nodes, mono/stereo, +`cmake --build build --target benchmark` records 24 warmed graph cases: 2/16/64/256 nodes, mono/stereo, 44.1k/31, 48k/128 and 96k/1024. Each reports control-thread compile time separately from 300 process calls, mean/p99/max, block deadline, mean load, and paired graph buffer bytes. Timing, sorting, allocation and logging happen @@ -51,10 +51,10 @@ CPU/compiler/flags/kernel and all measurements. Copy a new run only after reviewing changes and conditions. ```sh -make benchmark test-performance test-scale test-stress -make benchmark-compare BENCHMARK_MAX_REGRESSION=1.5 +cmake --build build --target benchmark test-performance test-scale test-stress +cmake -S . -B build -DXTC_BENCHMARK_MAX_REGRESSION=1.5 && cmake --build build --target benchmark-compare # Select a baseline from this same host/compiler/flags: -make benchmark-compare BENCHMARK_BASELINE=/path/to/performance.json +cmake -S . -B build -DXTC_BENCHMARK_BASELINE=/path/to/performance.json && cmake --build build --target benchmark-compare ``` Comparison rejects mismatched CPU/architecture/compiler/flags or missing cases. @@ -64,7 +64,7 @@ Repeat surprising results under comparable load; do not silently replace the baseline to make a regression disappear. Scheduling, clocks, thermal state and shared-machine contention still affect results. No real-time scheduling/pinning or external profiler is required. CPU-pressure runs are correctness checks, not -comparable timing baselines. Tests/benchmark outputs live below BUILD_DIR. +comparable timing baselines. Tests/benchmark outputs live below the selected binary directory. Initial matrix (Threadripper 1920X, GCC 16.2.1, -O2, 300 blocks): 256-node stereo 48k/128 mean 353.214 us, p99 396.495 us, load .132; 96k/1024 mean @@ -118,13 +118,13 @@ graph matrix separately scales nodes and chain connections from 2 to 256. These synthetic cases do not qualify every possible dense graph or plugin chain. ```sh -make benchmark benchmark-project -make benchmark-compare -make benchmark-project-compare +cmake --build build --target benchmark benchmark-project +cmake --build build --target benchmark-compare +cmake --build build --target benchmark-project-compare # Baselines must match the current CPU/compiler/flags and workload. -make benchmark-project-compare PROJECT_BENCHMARK_BASELINE=/path/to/report.json +cmake -S . -B build -DXTC_PROJECT_BENCHMARK_BASELINE=/path/to/report.json && cmake --build build --target benchmark-project-compare ./build/test_performance 10000 64 128 -make test-benchmark-tools +cmake --build build --target test-benchmark-tools ``` Both matrices report wall-clock mean/p99/max processing duration and the count @@ -154,10 +154,10 @@ on machine timing. Do not replace a failing baseline without investigating. ## Endurance, Faults and Callback Audit ```sh -make test-soak test-stress test-stress-plugins -make test-stress STRESS_ROUNDS=10 +cmake --build build --target test-soak test-stress test-stress-plugins +cmake -S . -B build -DXTC_STRESS_ROUNDS=10 && cmake --build build --target test-stress ./build/test_soak 10000000 -make check-development +cmake --build build --target check-development ``` `test-soak` renders exactly 1,000,000 alternating 31/128/1,024-frame stereo @@ -210,7 +210,7 @@ introduced without a separate correctness/performance justification. Reproduce in an isolated build (GNU gprof must already be installed): ```sh -make BUILD_DIR=build/profile CFLAGS='-O2 -g -pg -std=c99 -Iinclude' LDFLAGS=-pg build/profile/test_performance +cmake -S . -B build/profile -DCMAKE_C_FLAGS='-O2 -g -pg -std=c99 -Iinclude' -DCMAKE_EXE_LINKER_FLAGS=-pg && cmake --build build/profile --target test_performance GMON_OUT_PREFIX="$PWD/build/profile/gmon" ./build/profile/test_performance 10000 64 128 gprof build/profile/test_performance build/profile/gmon.* > build/profile/profile.txt ``` diff --git a/docs/PLUGIN_HOSTING.md b/docs/PLUGIN_HOSTING.md index 0fad541..a826020 100644 --- a/docs/PLUGIN_HOSTING.md +++ b/docs/PLUGIN_HOSTING.md @@ -92,8 +92,8 @@ binary/version identities. ## Repeatable Acceptance ```sh -make all test gui test-plugins test-gui-plugins analyze -make test-plugins-real +cmake --build build --parallel && cmake --build build --target check gui test-plugins test-gui-plugins analyze +cmake --build build --target test-plugins-real ``` The first plugin target builds an original MIT shared-library fixture with gain, @@ -269,8 +269,8 @@ inside the callback. CLAP process errors and VST3 failure results silence a block. VST2 has no process error return. ```sh -make all test gui test-modern-plugins test-gui-modern-plugins -make test-sanitize analyze +cmake --build build --parallel && cmake --build build --target check gui test-modern-plugins test-gui-modern-plugins +cmake --build build --target test-sanitize analyze ``` Original CLAP/VST2/VST3 shared-library fixtures exercise exact in-place and diff --git a/docs/PROJECT_FORMAT.md b/docs/PROJECT_FORMAT.md index 0554137..040e427 100644 --- a/docs/PROJECT_FORMAT.md +++ b/docs/PROJECT_FORMAT.md @@ -230,7 +230,7 @@ project Save intentionally replaces the selected document. In-memory models and the existing destination are preserved on load failure. No automatic unknown-field stripping or media deletion occurs. -`make test-portability test test-plugins test-recovery test-recording` covers +`cmake --build build --target test-portability check test-plugins test-recovery test-recording` covers checked-in v1–v10 fixtures (including missing v5/v6 plugins), current exact roundtrips, moved-folder playback from a different cwd, missing media retention, unknown records, every truncation and deterministic byte mutations, malformed @@ -265,7 +265,7 @@ source references; splitting keeps the original ID on the left, gives the right a fresh ID/source offset, retains outer explicit fades and clears inner ones. No waveform cache or source audio is serialized. Checked-in v1–v7 migrations, v8 exact roundtrips, malformed new metadata and byte-mutation tests cover the -format change (`make test test-portability test-audio-editing`). +format change (`cmake --build build --target check test-portability test-audio-editing`). --- @@ -369,13 +369,13 @@ destination. Plugin binaries and state are not bundled. JACK availability, sample rate, block limits and device routing are runtime dependencies; loading metadata does not restore device connections. -`make test` includes v1–v10 migration, malformed/truncated/mutated/oversized +`cmake --build build --target check` includes v1–v10 migration, malformed/truncated/mutated/oversized input, retained-state diagnostics, CLI dependency inspection, and deterministic save faults at flush, file sync, close, rename and directory sync. Pre-rename faults preserve the old bytes and remove the temporary. After rename, a sync failure reports I/O while leaving a complete new document; inspect it before retrying. Fault wrappers are linked into tests only. These tests complement -killed-writer coverage in `make test-recovery`; they do not qualify power-cut +killed-writer coverage in `cmake --build build --target test-recovery`; they do not qualify power-cut behavior. --- diff --git a/docs/RECOVERY.md b/docs/RECOVERY.md index 408ec7f..dfd28df 100644 --- a/docs/RECOVERY.md +++ b/docs/RECOVERY.md @@ -51,7 +51,7 @@ There is no autosave, crash journal, automatic orphan recovery or persisted Undo history. Existing finalized files are protected against the tested process-level failures; unsaved edits/in-flight takes can still be lost. -`make test-recovery` uses temporary directories and child processes only. It +`cmake --build build --target test-recovery` uses temporary directories and child processes only. It forces an EFBIG-style write failure with a child-local file-size limit, kills eight project writers while temporaries are visible, and kills a recorder after draining but before finalization. Complete old/new destinations and explicit @@ -69,7 +69,7 @@ recorder recovery path before destruction, or use the GUI's reported path. float32 WAV from complete on-disk frames, ignoring a trailing partial frame. It never changes the source or overwrites an existing destination. This supersedes the earlier lack of a WAV recovery tool described above. See AUDIO_FILES.md for -format/ownership/size limits and `make test-recording`. +format/ownership/size limits and `cmake --build build --target test-recording`. --- @@ -95,7 +95,7 @@ native plugin code; LADSPA has no runtime error-return channel. ## Save Diagnostics -`make test-project-diagnostics` deterministically injects flush, file-sync, +`cmake --build build --target test-project-diagnostics` deterministically injects flush, file-sync, close, rename and directory-sync errors. Pre-rename failures retain the previous bytes and clean temporary files. Post-rename sync errors leave a complete new file and return failure; there is no rollback after replacement. Load diff --git a/docs/RELEASING.md b/docs/RELEASING.md index e351778..6439bf4 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -3,29 +3,32 @@ Source packaging, staged install/uninstall and repeatable builds are testable. It does not turn the prototype into a production-qualified DAW. The version remains 0.1.0; no tag, external release or binary publication is created by -`make dist` or `make release`. +`cmake --build build --target dist` or `cmake --build build --target release`. --- ## Build and Package ```sh -make release -make test-install -make test-recovery test-performance -make dist -make test-release +cmake --preset release +cmake --build --preset release --parallel +ctest --preset release +cmake --build build/release --target test-install test-install-layout +cmake --build build/release --target dist test-release +cmake --build build/release --target test-package ``` -release builds optimized, warning-enabled C/C++ code, tests and the optional GUI -in BUILD_DIR/release with no debug paths, a source-prefix map, deterministic ar -metadata and FLTK include/define flags rather than distribution-specific LTO -flags. It retains assertions so the test harness actually runs. dist creates -BUILD_DIR/xtc-0.1.0.tar.gz using Python's standard library. It includes only Git -indexed regular files (working-tree contents, including staged new files), plus -a source manifest; stage intended files first and check `git diff --cached`. -Untracked files, ignored build output and .git are excluded. Exported archives -can reproduce themselves without Git using SOURCE_MANIFEST.json. +The release preset builds optimized, warning-enabled C/C++ code, test binaries +and the available GUI in `build/release`, with source/binary prefix maps, +no debug information, deterministic archive metadata and assertions retained. +FLTK discovery takes include/define flags without overriding the project's +optimization settings. `dist` creates `xtc-0.1.0.tar.gz` below the binary +directory using Python's standard library. It includes only Git-indexed regular +files (working-tree contents, including staged new files), plus a source +manifest; stage intended files first and check `git diff --cached`. Untracked +files, ignored build output and .git are excluded. Exported archives reproduce +themselves without Git using SOURCE_MANIFEST.json. The `test-release` tool uses +CMake and CTest in two independent exported trees, without invoking Make. Archive order, permissions, ownership, gzip filename and timestamps are normalized. SOURCE_DATE_EPOCH defaults to 0; set an unsigned 32-bit epoch @@ -34,8 +37,8 @@ from a clean checkout and record its commit/hash externally. Do not treat a dirty-tree archive as an attested release. No artifact is uploaded by these targets. -test-release unpacks the source into two different temporary paths, runs release -in each, compares libxtc.a/xtc-check/optional xtc SHA-256 values, and repacks +test-release unpacks the source into two different temporary paths, configures +a reproducible Release build and runs CTest in each, compares libxtc.a/xtc-check/optional xtc SHA-256 values, and repacks each export to compare archive bytes. This verifies reproducibility for the same host, compiler, flags and dependency versions. It does not promise bit-identical output across distributions, compilers or library updates. @@ -66,11 +69,11 @@ shared-library extension ABI in this release. ## Local Binary Packaging and Installed Workflow -`make test-package` builds the release, stages installation only in a temporary +`cmake --build build/release --target test-package` uses the selected build and stages installation only in a temporary DESTDIR, writes a normalized Linux binary/development archive, checks its SHA-256 sidecar and per-file manifest, relocatable pkg-config paths and headless commands. Same-host repeated packaging is byte-identical; dependency versions -and toolchain still determine binary compatibility. `make test-install` now +and toolchain still determine binary compatibility. `cmake --build build --target test-install` now repeats installation to verify repair/upgrade of owned files while preserving an unrelated sentinel, then compiles both C99 and C++17 installed consumers that create MIDI, save/reopen and compare nonzero exact rendering. Removal remains @@ -88,21 +91,28 @@ pending. ## Release Workflow -Installation now shares one explicit file inventory with uninstallation. -GNU-style `exec_prefix`, `bindir`, `libdir`, `includedir`, `datarootdir`, -`datadir`, `docdir`, `pkgconfigdir` and the xtc-specific `licensedir`, -`examplesdir`, `desktopdir` can be overridden alongside `PREFIX`/`DESTDIR`. -Paths must be absolute POSIX paths; spaces are supported. Do not include DESTDIR -in a directory variable: staging is applied separately and never embedded in -pkg-config metadata. Staging refuses parent symlinks that escape DESTDIR. Use -exactly the same variables for removal. +Installation shares the existing explicit owned-file inventory with removal. +Use CMake's `CMAKE_INSTALL_PREFIX`, `CMAKE_INSTALL_BINDIR`, +`CMAKE_INSTALL_LIBDIR`, `CMAKE_INSTALL_INCLUDEDIR`, `CMAKE_INSTALL_DATAROOTDIR` +and `CMAKE_INSTALL_DOCDIR`. xtc-specific cache entries are +`XTC_INSTALL_LICENSEDIR`, `XTC_INSTALL_EXAMPLESDIR`, `XTC_INSTALL_DESKTOPDIR` +and `XTC_INSTALL_PKGCONFIGDIR`. Relative directories resolve beneath the prefix; +absolute directories are supported. Spaces work; control characters, quotes, +dollar signs, backslashes and `..` are rejected by the audited layout helper. +`DESTDIR` is a separate staging environment variable. The same cache and prefix +must be used for uninstall; a `cmake --install --prefix` override is temporary. +The internal `XTC_INSTALL_BUILD_DIR` setting lets packaging/test drivers stage +already-built inputs using a temporary configuration without modifying the +caller's cache. SDK products are never in this inventory. ```sh -make BUILD_DIR=build/release-check all test gui -make BUILD_DIR=build/release-check test-install test-install-layout -make PREFIX=/usr DESTDIR=/tmp/xtc-stage libdir=/usr/lib64 install-gui -make PREFIX=/usr DESTDIR=/tmp/xtc-stage libdir=/usr/lib64 uninstall -make test-release test-package +cmake -S . -B build/release-check -DCMAKE_BUILD_TYPE=Release \ + -DXTC_REPRODUCIBLE=ON -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib64 +cmake --build build/release-check --parallel +ctest --test-dir build/release-check --output-on-failure +cmake --build build/release-check --target test-install test-install-layout +DESTDIR=/tmp/xtc-stage cmake --install build/release-check +DESTDIR=/tmp/xtc-stage cmake --build build/release-check --target uninstall ``` Core install includes the static library, public headers, `xtc-check`, @@ -114,7 +124,7 @@ metadata; custom live layouts are tested separately. Existing owned file symlinks are replaced, not followed; installation is atomic per file, not a transaction over the entire prefix. -`make dist` now writes a `.sha256` sidecar as well as the normalized source +`cmake --build build --target dist` now writes a `.sha256` sidecar as well as the normalized source archive. Record that hash, the reviewed commit, version and build/dependency conditions with release logs. `test-release` verifies the hash, builds/tests two fresh source exports and compares artifacts and repacks. It validates exported diff --git a/docs/RELIABILITY.md b/docs/RELIABILITY.md index 3bceea6..79ac87d 100644 --- a/docs/RELIABILITY.md +++ b/docs/RELIABILITY.md @@ -94,11 +94,11 @@ formal proof. From the repository root: ```sh -make BUILD_DIR=build/audit all gui test-reliability test-recovery -make BUILD_DIR=build/audit test-sanitize -make analyze -make BUILD_DIR=build/audit test-gui test-gui-jack test-jack test-jack-audio test-jack-midi -make BUILD_DIR=build/nojack PKG_CONFIG=false all test-reliability gui +cmake --build build/audit --parallel && cmake --build build/audit --target gui test-reliability test-recovery +cmake --build build/audit --target test-sanitize +cmake --build build --target analyze +cmake -S . -B build/audit -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build/audit --target test-gui test-gui-jack test-jack test-jack-audio test-jack-midi +cmake -S . -B build/nojack -DXTC_WITH_JACK=OFF && cmake --build build/nojack --parallel && cmake --build build/nojack --target test-reliability gui ``` `test-sanitize` needs installed Clang and runs core, allocation-failure and @@ -141,10 +141,10 @@ stop/close handles faults or missing callbacks. See AUDIO_QUALITY.md. ## Repeatable Development Checks -`make check-development` combines normal tests, capacity/endurance, +`cmake --build build --target check-development` combines normal tests, capacity/endurance, deterministic benchmark-gate tests, static analysis and ASan/UBSan (including -scale and soak). `make test-stress` repeats project/backend/publication faults -under CPU contention; `make test-stress-plugins` adds the original LADSPA +scale and soak). `cmake --build build --target test-stress` repeats project/backend/publication faults +under CPU contention; `cmake --build build --target test-stress-plugins` adds the original LADSPA failure fixture. The reliability callback guard now also rejects common host file I/O and blocking mutex/condition calls. It audits directly linked host code, not arbitrary dynamic-plugin internals. See PERFORMANCE.md for exact diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index db3526d..cccb518 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,5 +1,9 @@ # Roadmap +Build commands in Phases 0–107 are historical qualification records from the +Makefile era. The current supported commands are in [BUILD.md](BUILD.md); +Phase 108 replaces that build with CMake and CTest. + ## Phase 0 — Baseline ### Goal @@ -3500,6 +3504,77 @@ building JUCE's standalone wrapper does not establish hardware acceptance. --- +## Phase 108 — Complete CMake Build Migration + +### Goal + +Replace the hand-written root Makefile with a full CMake project and CTest, +preserving C99/C++17 boundaries, optional dependencies, SDK examples, fixtures, +developer tools and the audited installation/release workflows. This explicitly +requested build migration can proceed alongside remaining hardware/platform +qualification in earlier phases. + +### Tasks and acceptance criteria + +- [x] Define target-based core, CLI, GUI, example and plugin-fixture builds with + warnings, generated dependencies and optional JACK/FLTK/LADSPA discovery. +- [x] Register offline/unit/integration and GUI checks with CTest; keep live + JACK workflows opt-in and retain linker-based fault injection. +- [x] Provide separate debug/release/minimal/sanitizer presets, screenshot, + analysis, benchmark, stress and focused test targets. +- [x] Build optional Steinberg/JUCE SDK examples through root CMake; eliminate + JUCE's recursive Make dependency while retaining standalone SDK entry points. +- [x] Preserve staged install/uninstall, configurable layouts, pkg-config, + license inventory, reproducible source exports and binary-package checks. +- [x] Remove the root Makefile and update current documentation, examples, + third-party build guidance and agent instructions to use CMake/CTest. +- [x] Validate GNU Makefiles and Ninja, a minimal C-only build, GUI checks, + sanitizer tests, SDK interoperability and release/install/package workflows. + +### Implementation status + +Completed on 2026-11-06 with CMake 4.4.5, GCC 16.2.1, Clang 23.1.4 and +both Unix Makefiles and Ninja. The original build-rule Makefile was removed; see +[BUILD.md](BUILD.md), [RELEASING.md](RELEASING.md) and +[third-party guidance](../third_party/README.md). + +Validation: + +- Debug CMake build with JACK/FLTK: all 55 registered offline/GUI tests passed. +- Ninja Release build with GUI/JACK/LADSPA disabled: all 41 tests passed; the + configuration enabled only C, and `BUILD_TESTING=OFF` also built successfully. +- Clang ASan/UBSan: all 42 offline tests passed, including one million virtual + playback blocks. Clang static analysis passed for all C core sources. +- Default/custom staged install and removal, C/C++ installed pkg-config consumers, + symlink protection, prefix spaces and minimal/no-JACK installation passed. + Binary archive contents, hashes, relocation and repeat packaging passed. +- Two independent source-export Release builds (including the GUI and CTest) + produced byte-identical `libxtc.a`, `xtc-check`, `xtc` and repacked archives. + The check used an isolated temporary Git index to include the new CMake files + without staging the user's working index; normal `dist` still exports indexed + files as documented. +- Root Steinberg SDK build: host interoperability and official validator CTests + passed. Root JUCE build: all four C-host, file/editor, processor and JUCE-host + CTests passed. The standalone JUCE entry point also configured and built its + CMake core target successfully. The bridge does not invoke Make. +- CMake screenshot capture produced four actual creative-workflow images in the + ignored binary tree. Preview targets wrote the expected WAV files; graph and + project benchmark drivers ran successfully (timings were not used as a gate + while compilation was running). Three minimal-build CPU stress rounds passed. +- Five opt-in live JACK/workflow tests were registered and inspected; they were + not rerun as hardware acceptance. External SDK sources can emit upstream + warnings; the normal project builds were warning-free. + +A subsequent user-requested convenience Makefile forwards configure/build, +CTest, GUI/run and clean commands to CMake. It has no compilation rules or source +inventory; CMake remains authoritative. See [quick shortcuts](BUILD.md#quick-make-shortcuts). + +Historical validation above remains as recorded. This migration adds no +Windows/macOS, physical-device, native ASIO, or additional plugin-format +qualification. + +--- + ## Recommended Execution Order Reorder the phases when the audit reveals a blocking dependency. Do not mark a diff --git a/docs/ROUTING.md b/docs/ROUTING.md index c4f7071..d0c312d 100644 --- a/docs/ROUTING.md +++ b/docs/ROUTING.md @@ -102,12 +102,12 @@ public C snapshots/commands and never engine internals or callback storage. ## Verification and Limits -`make test-routing` checks branch reachability, external input, levels, capacity -rollback, exact unity-insertion rendering and complete plan handoff. `make test` -also retains concurrent publication/lifecycle tests. `make test-reliability` -retains the callback-allocation guards. `make test-gui-plugins` checks selection +`cmake --build build --target test-routing` checks branch reachability, external input, levels, capacity +rollback, exact unity-insertion rendering and complete plan handoff. `cmake --build build --target check` +also retains concurrent publication/lifecycle tests. `cmake --build build --target test-reliability` +retains the callback-allocation guards. `cmake --build build --target test-gui-plugins` checks selection diagnostics, unique placement/layout, failed backend publication, atomic -insertion undo/redo and keyboard focus. `make test-gui-jack` +insertion undo/redo and keyboard focus. `cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-gui-jack` inserts/disconnects/restores real routes during live software playback and checks engine revision acknowledgements and cycle rejection. diff --git a/docs/STEINBERG.md b/docs/STEINBERG.md index 46c68d3..eac6aee 100644 --- a/docs/STEINBERG.md +++ b/docs/STEINBERG.md @@ -25,6 +25,12 @@ check without adding a C++ runtime or network dependency to libxtc. - The C render example demonstrates the actual xtc plugin facade and WAV writer, including no-clobber output, without requiring JACK or a GUI. +Enable the root CMake integration with `-DXTC_BUILD_STEINBERG=ON` and +`-DXTC_VST3_SDK_ROOT=/path/to/vst3sdk`, build, then run +`ctest --test-dir build --output-on-failure -R steinberg`. Normal builds leave +the SDK option off. The example subdirectory also remains a standalone CMake +entry point for native SDK/ASIO diagnostics. + See [the example instructions](../examples/steinberg/README.md) for SDK versions, build commands, validation and ASIO diagnostics. diff --git a/docs/TRACK_INSERTS.md b/docs/TRACK_INSERTS.md index 20c3d25..3490715 100644 --- a/docs/TRACK_INSERTS.md +++ b/docs/TRACK_INSERTS.md @@ -86,7 +86,7 @@ host ownership and cleanup. Reproduce focused verification with: ```sh -make BUILD_DIR=build/audio-editor test-track-fx test-gui-track-fx +cmake --build build/audio-editor --target test-track-fx test-gui-track-fx XTC_TEST_HOST_VST3=1 ./build/audio-editor/test_track_fx ``` diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index 355ad54..27f76b7 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -9,20 +9,21 @@ implied. ## Build or Install -Follow BUILD.md and LINUX.md. Core: `make all test`; GUI: `make gui`, then +Configure first with `cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug`; see +BUILD.md and LINUX.md. Core: `cmake --build build --parallel && cmake --build build --target check`; GUI: `cmake --build build --target gui`, then `./build/xtc`. Use an existing JACK-compatible server and reconnect with F6. Connect `midi_in` to a controller, `input` to mono capture and `output` / `output_right` to left/right playback using your JACK routing application. No server, physical routing or system configuration is changed automatically. `xtc --version`, `xtc --help` and `xtc --diagnose` need no display/server. -For a source install stage with -`make install-gui DESTDIR="$PWD/build/stage" PREFIX=/usr`, inspect the stage, -and use your normal deployment process. A user prefix is supported (for example -an absolute path to your `.local` folder). `make uninstall` with the same -DESTDIR/PREFIX removes only that version's known files. See LINUX.md for package -extraction, dependencies and removal. Never use a release built for a different -system ABI without rebuilding/testing it. +For a source installation, configure `-DCMAKE_INSTALL_PREFIX=/usr`, build, then +stage with `DESTDIR="$PWD/build/stage" cmake --install build`. Inspect the stage +and use your normal deployment process. A user prefix such as an absolute path +to `.local` is also supported. Use `cmake --build build --target uninstall` +with the same DESTDIR and configured prefix for exact removal. See LINUX.md for +package extraction, dependencies and removal. Rebuild and test for the intended +system ABI before deployment. --- diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index fbadc61..88062be 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -86,7 +86,7 @@ With installed FLTK/JACK development files, Xvfb and an already running JACK-compatible server: ```sh -make all test gui test-gui test-workflow test-gui-jack test-jack-midi +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui test-gui test-workflow test-gui-jack test-jack-midi ``` `test-workflow` drives real GUI commands and timeline gestures under Xvfb. It @@ -148,7 +148,7 @@ A successful recorded take adds a reference as one undoable edit. Undo/removing its region never deletes the WAV; recording failures retain their recovery path. There is no destructive media-delete operation. New/Open reset history after unsaved-change handling. The capacity limit and allocation-error reporting in -MIXER.md still apply. Tests: `make test-editing test-gui-plugins test-workflow`. +MIXER.md still apply. Tests: `cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-editing test-gui-plugins test-workflow`. --- diff --git a/docs/WORKSPACE.md b/docs/WORKSPACE.md index 00e54b4..292db57 100644 --- a/docs/WORKSPACE.md +++ b/docs/WORKSPACE.md @@ -191,7 +191,7 @@ described below. The floating mixer resizes down to 600 × 420 and retains horizontal track scrolling. Verified with -`make BUILD_DIR=build/workspace-detach gui test-gui test-gui-instruments test-gui-jack` +`cmake -S . -B build/workspace-detach -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build/workspace-detach --target gui test-gui test-gui-instruments test-gui-jack` (one line), plus the GUI suite under AddressSanitizer/UBSan. Tests cover repeated reparenting, close-to-dock, resize, shared BPM/loop state, playback shortcuts, failed shutdown and cleanup. diff --git a/examples/README.md b/examples/README.md index 3742cbf..8a6b783 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,11 +1,11 @@ # Examples `native_gain.c` is a complete trusted native C effect compiled into an offline -graph. Build/run with `make build/native-gain && ./build/native-gain`; it needs +graph. Build/run with `cmake --build build --target native-gain && ./build/native-gain`; it needs no GUI/server/plugin SDK. See ../docs/NATIVE_API.md for lifecycle/event contracts. `quality_preview.c` uses public C MIDI, recorder and gain-ramp APIs to generate -matched raw/smoothed listening fixtures. Run `make quality-preview`; generated +matched raw/smoothed listening fixtures. Run `cmake --build build --target quality-preview`; generated WAVs remain in the ignored build directory. See docs/AUDIO_QUALITY.md for the listening sequence and the distinction between automated checks and human listening acceptance. It never starts a backend or plays audio. diff --git a/examples/juce/CMakeLists.txt b/examples/juce/CMakeLists.txt index e842179..6bd1342 100644 --- a/examples/juce/CMakeLists.txt +++ b/examples/juce/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.25) project(xtc_juce_examples VERSION 0.1.0 LANGUAGES C CXX) +include(CTest) set(XTC_JUCE_ROOT "" CACHE PATH "External JUCE source checkout") if(NOT EXISTS "${XTC_JUCE_ROOT}/modules/juce_audio_utils/juce_audio_utils.h") message(FATAL_ERROR "Set XTC_JUCE_ROOT to an external JUCE checkout; normal xtc builds do not download JUCE") @@ -11,21 +12,17 @@ message(STATUS "JUCE is AGPLv3/commercial: review the external SDK LICENSE.md be set(JUCE_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) set(JUCE_BUILD_EXTRAS OFF CACHE BOOL "" FORCE) add_subdirectory("${XTC_JUCE_ROOT}" juce-sdk EXCLUDE_FROM_ALL) -find_program(XTC_MAKE_EXECUTABLE NAMES gmake make REQUIRED) get_filename_component(XTC_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE) -set(XTC_CORE_BUILD "${CMAKE_CURRENT_BINARY_DIR}/c-core") -add_custom_target(xtc-juce-core - COMMAND "${XTC_MAKE_EXECUTABLE}" -C "${XTC_ROOT}" - "BUILD_DIR=${XTC_CORE_BUILD}" "CC=${CMAKE_C_COMPILER}" - "CFLAGS=-O2 -g -fPIC -std=c99 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Iinclude" - PKG_CONFIG=false FLTK_CONFIG=false "${XTC_CORE_BUILD}/libxtc.a" - BYPRODUCTS "${XTC_CORE_BUILD}/libxtc.a" - VERBATIM USES_TERMINAL) -add_library(xtc-juce-c-core STATIC IMPORTED) -set_target_properties(xtc-juce-c-core PROPERTIES - IMPORTED_LOCATION "${XTC_CORE_BUILD}/libxtc.a" - INTERFACE_INCLUDE_DIRECTORIES "${XTC_ROOT}/include") -add_dependencies(xtc-juce-c-core xtc-juce-core) +if(NOT TARGET xtc-core) + set(XTC_BUILD_GUI OFF CACHE BOOL "" FORCE) + set(XTC_WITH_JACK OFF CACHE BOOL "" FORCE) + set(XTC_BUILD_JUCE OFF CACHE BOOL "" FORCE) + set(XTC_BUILD_STEINBERG OFF CACHE BOOL "" FORCE) + set(xtc_saved_testing ${BUILD_TESTING}) + set(BUILD_TESTING OFF) + add_subdirectory("${XTC_ROOT}" c-core EXCLUDE_FROM_ALL) + set(BUILD_TESTING ${xtc_saved_testing}) +endif() juce_add_plugin(XtcJuceSynth COMPANY_NAME xtc @@ -42,12 +39,15 @@ juce_add_plugin(XtcJuceSynth NEEDS_WEB_BROWSER FALSE NEEDS_CURL FALSE) target_sources(XtcJuceSynth PRIVATE Processor.cpp Editor.cpp PluginEntry.cpp) +# JUCE’s bundled FLAC uses POSIX fileno; its C module sources need GNU extensions. +# The xtc-core target remains strict C99. +set_target_properties(XtcJuceSynth PROPERTIES C_STANDARD 11 C_EXTENSIONS ON) target_compile_features(XtcJuceSynth PUBLIC cxx_std_17) target_compile_definitions(XtcJuceSynth PUBLIC JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0 JUCE_PLUGINHOST_VST3=1 ) target_include_directories(XtcJuceSynth PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" "${XTC_ROOT}/include") -target_link_libraries(XtcJuceSynth PRIVATE xtc-juce-c-core juce::juce_audio_utils juce::juce_dsp +target_link_libraries(XtcJuceSynth PRIVATE xtc-core juce::juce_audio_utils juce::juce_dsp PUBLIC juce::juce_recommended_config_flags juce::juce_recommended_warning_flags) add_executable(xtc-juce-render Render.cpp) @@ -57,8 +57,11 @@ target_include_directories(xtc-juce-render PRIVATE "${XTC_JUCE_ROOT}/modules") # Exact float comparisons intentionally check bit-identical C bridge output. set_source_files_properties(Render.cpp PROPERTIES COMPILE_OPTIONS "-Wno-float-equal") target_compile_definitions(xtc-juce-render PRIVATE JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1) -target_link_libraries(xtc-juce-render PRIVATE XtcJuceSynth xtc-juce-c-core) -enable_testing() -add_test(NAME xtc-juce-processor COMMAND xtc-juce-render --self-test) - -add_test(NAME xtc-juce-vst3 COMMAND xtc-juce-render --host-check "$/../..") +target_link_libraries(xtc-juce-render PRIVATE XtcJuceSynth xtc-core) +if(BUILD_TESTING) + find_program(XTC_XVFB_RUN xvfb-run REQUIRED) + add_test(NAME xtc-juce-processor COMMAND ${XTC_XVFB_RUN} -a $ --self-test) + add_test(NAME xtc-juce-vst3 COMMAND ${XTC_XVFB_RUN} -a $ + --host-check "$>>") + set_tests_properties(xtc-juce-processor xtc-juce-vst3 PROPERTIES LABELS sdk) +endif() diff --git a/examples/juce/README.md b/examples/juce/README.md index 9a62391..f7ab022 100644 --- a/examples/juce/README.md +++ b/examples/juce/README.md @@ -10,27 +10,34 @@ No SDK or linked binary is copied into normal xtc packages. ## Build and test (Linux) Supply JUCE explicitly, for example in `/your/path/JUCE`. With CMake >= 3.25, -GNU Make, a C/C++17 toolchain and JUCE's Linux development prerequisites: +Ninja or another CMake backend, a C/C++17 toolchain and JUCE's Linux development +prerequisites, configure from the repository root: ```sh -make JUCE_ROOT=/your/path/JUCE juce-examples -make JUCE_ROOT=/your/path/JUCE test-juce +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + -DXTC_BUILD_JUCE=ON -DXTC_JUCE_ROOT=/your/path/JUCE +cmake --build build --target juce-examples --parallel 3 +cmake --build build --target test-juce ``` The build never downloads JUCE, installs dependencies or copies plugins into -user plugin directories. It builds its own C99 PIC archive with JACK/FLTK disabled -under `build/juce/c-core`. `test-juce` also requires Xvfb and runs the processor -and JUCE-host checks, then loads the built instrument through xtc's C VST3 host. -Normal `make`, `make gui` and `make test` remain JUCE-independent. +user plugin directories. The bridge links the root `xtc-core` PIC C99 target; +there is no recursive Make build or duplicate core archive. `test-juce` requires +Xvfb and runs the processor, JUCE-host, file/editor and xtc C-host checks. +Normal builds leave `XTC_BUILD_JUCE=OFF` and remain JUCE-independent. -Build products: +Build products (Release): - `build/juce/XtcJuceSynth_artefacts/Release/VST3/xtc JUCE Synth.vst3` - `build/juce/XtcJuceSynth_artefacts/Release/Standalone/xtc JUCE Synth` - `build/juce/xtc-juce-render_artefacts/Release/xtc-juce-render` -Set `BUILD_DIR=...` to move the main build root, or `JUCE_BUILD_DIR=...` to select -only the SDK example build directory. +Use CMake's `-B` argument to select another binary directory. Debug builds use +`Debug` in place of `Release`. The examples can also be configured directly with +`cmake -S examples/juce -B build/juce-only -DXTC_JUCE_ROOT=/your/path/JUCE +-DCMAKE_BUILD_TYPE=Release`; that standalone path adds the same root CMake core +with GUI/JACK/tests disabled rather than invoking Make. Its artefacts are directly +under `build/juce-only`, without the additional `juce` directory. ## Play and shape Soft Keys @@ -84,7 +91,7 @@ VST3 host. These operations do not start an audio device. ## Regression checks and an actual editor image ```sh -xvfb-run -a ctest --test-dir build/juce --output-on-failure +ctest --test-dir build --output-on-failure -R juce xvfb-run -a ./build/juce/xtc-juce-render_artefacts/Release/xtc-juce-render --snapshot /absolute/new-editor.png ``` diff --git a/examples/steinberg/CMakeLists.txt b/examples/steinberg/CMakeLists.txt index 3c31f11..6f87f1a 100644 --- a/examples/steinberg/CMakeLists.txt +++ b/examples/steinberg/CMakeLists.txt @@ -16,6 +16,7 @@ if(XTC_VST3_SDK_ROOT) set(SMTG_ENABLE_VST3_HOSTING_EXAMPLES ON CACHE BOOL "" FORCE) add_subdirectory("${XTC_VST3_SDK_ROOT}" sdk) set(public_sdk_SOURCE_DIR "${XTC_VST3_SDK_ROOT}/public.sdk") + set(SMTG_CUSTOM_BINARY_LOCATION "${CMAKE_CURRENT_BINARY_DIR}") smtg_add_vst3plugin(xtc-sdk-examples sdk_plugins.cpp) target_link_libraries(xtc-sdk-examples PRIVATE sdk) target_compile_features(xtc-sdk-examples PRIVATE cxx_std_17) diff --git a/examples/steinberg/README.md b/examples/steinberg/README.md index b10d606..929d26c 100644 --- a/examples/steinberg/README.md +++ b/examples/steinberg/README.md @@ -1,6 +1,6 @@ # Official Steinberg SDK examples -These are optional integration examples. Normal `make` / `make test` uses the +These are optional integration examples. Normal CMake builds and CTest use the existing pinned C ABI headers and never downloads SDKs. Original example source is MIT-licensed like xtc. SDK code is compiled from an external checkout, not vendored or installed by these targets. C++ is confined to the SDK examples; @@ -26,13 +26,17 @@ trademark distribution rights. From the xtc repository root: ```sh -make VST3_SDK_ROOT=/your/path/vst3sdk test-steinberg-sdk +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + -DXTC_BUILD_STEINBERG=ON -DXTC_VST3_SDK_ROOT=/your/path/vst3sdk +cmake --build build --parallel 3 +ctest --test-dir build --output-on-failure -R steinberg ``` This builds the original `xtc-sdk-examples.vst3` bundle using Steinberg's factory, AudioEffect, controller and platform-entry implementations; runs the official -validator; and runs xtc's gain/MIDI interoperability checks. Build output stays -under `build/steinberg-sdk`; automatic plugin-directory links and installation +validator; and runs xtc's gain/MIDI interoperability checks. The plugin bundle stays +under `build/steinberg-sdk`; SDK helper executables use `build/bin`. Automatic +plugin-directory links and installation are disabled. SDK source may emit its own compiler warnings. The bundle contains: @@ -56,9 +60,9 @@ xtc synth -> effect host path: /absolute/path/to/new-sdk-demo.wav ``` -The destination must not exist. This executable is built by `test-steinberg-sdk`. -To use a different xtc build directory, pass `BUILD_DIR=...`; the SDK build then -lives under that directory unless `STEINBERG_BUILD_DIR=...` is explicitly set. +The destination must not exist. This executable is built when the SDK option is enabled. +Use CMake’s `-B` argument to select another binary directory; the SDK build then +lives under its `steinberg-sdk` subdirectory. The main app's existing plugin loader can use the bundle path through its C API; its file chooser may require selecting the inner `.so`. The effect loads as a diff --git a/screenshots/README.md b/screenshots/README.md index 06ca8fc..d7dfc4f 100644 --- a/screenshots/README.md +++ b/screenshots/README.md @@ -56,10 +56,11 @@ The host is stopped and meters are idle. No ASIO interface is shown or implied. See [Steinberg integration](../docs/STEINBERG.md) and [JUCE integration](../docs/JUCE.md). -After building both SDK examples, reproduce only these host captures with: +After configuring the root CMake project and building both SDK examples, +reproduce only these host captures with: ```sh -make build/capture_screenshots +cmake --build build --target capture_screenshots XTC_CAPTURE_STEINBERG_BUNDLE="$PWD/build/steinberg-sdk/VST3/Release/xtc-sdk-examples.vst3" \ XTC_CAPTURE_JUCE_BUNDLE="$PWD/build/juce/XtcJuceSynth_artefacts/Release/VST3/xtc JUCE Synth.vst3" \ xvfb-run -a -s '-screen 0 1440x960x24' \ @@ -91,14 +92,15 @@ mean no automatic connection. Actual device choices depend on host JACK/PipeWire exposure. Configuration paths are disabled in captures to preserve user settings. See [MIDI workflows and limits](../docs/MIDI_WORKFLOWS.md). -Reproduce these five captures with -`XTC_CAPTURE_MIDI_ONLY=1 make BUILD_DIR=build/audio-editor screenshots`. +After configuring `build/audio-editor` with the root CMake project, +reproduce these five captures with +`XTC_CAPTURE_MIDI_ONLY=1 cmake --build build/audio-editor --target screenshots`. The open input dropdown was captured after the device-label lifetime fix. It shows actual host sources, including UMC204HD, USB MIDI, Oxygen Pro 25 and BLE MIDI ports. No route is selected and user preferences are preserved. Reproduce just this image with real MIDI sources exposed by JACK/PipeWire: -`XTC_CAPTURE_MIDI_INPUT_ONLY=1 make BUILD_DIR=build/audio-editor screenshots`. +`XTC_CAPTURE_MIDI_INPUT_ONLY=1 cmake --build build/audio-editor --target screenshots`. The capture fails explicitly if the host exposes no input sources. ## Creative workflows (Phases 90–99) @@ -112,7 +114,7 @@ creative controls; the rest of the gallery also reflects the current UI. - [Strummed chord notes in the piano roll](creative-strum-result.png) Reproduce only these captures with -`XTC_CAPTURE_CREATIVE_ONLY=1 make BUILD_DIR=build/audio-editor screenshots`. +`XTC_CAPTURE_CREATIVE_ONLY=1 cmake --build build/audio-editor --target screenshots`. The rack shows the actual template and idle meter; no live peak readings are fabricated. See [creative workflow limits](../docs/CREATIVE_WORKFLOWS.md). @@ -219,7 +221,8 @@ to return it to the main window. From the repository root, run: ```sh -make BUILD_DIR=build/next-daw screenshots +cmake -S . -B build/next-daw -DCMAKE_BUILD_TYPE=Debug +cmake --build build/next-daw --target screenshots ``` This optional documentation target requires the existing FLTK/JACK build @@ -230,7 +233,7 @@ PNGs. All demonstration edits and collection outputs use an automatically remove temporary directory; no preferences are saved. It neither installs dependencies nor starts/reconfigures the audio server. -Set `XTC_CAPTURE_SAMPLE_ONLY=1` for the capture tool (or the make target) to write +Set `XTC_CAPTURE_SAMPLE_ONLY=1` for the capture tool (or the CMake target) to write only the two sample-editor images while retaining the existing gallery. @@ -241,6 +244,6 @@ XTC_CAPTURE_HOST_VST3=1 xvfb-run -a -s '-screen 0 1440x960x24' \ ./build/audio-editor/capture_screenshots "$PWD/screenshots" ``` -Run `make BUILD_DIR=build/audio-editor screenshots` first to build the tool and +Run `cmake --build build/audio-editor --target screenshots` first to build the tool and refresh the full fixture/demo gallery. All captures use temporary projects and preserve user settings and original media. diff --git a/tests/integration/README.md b/tests/integration/README.md index 1151d86..7d76fcb 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -1,12 +1,20 @@ # Integration tests -`make test` is hardware/server independent: offline render tests, concurrent +Configure the root project first with `cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug`, +then `cmake --build build --parallel`. Run `ctest --test-dir build +--output-on-failure` for registered tests. Live tests require a separate configure +with `-DXTC_ENABLE_LIVE_TESTS=ON` and an existing JACK server; CTest serializes them. +See [BUILD.md](../../docs/BUILD.md) for presets and feature options. Older validation +records below describe tests performed at that time; commands use the CMake +workflow and assume the relevant configuration has already completed. + +`cmake --build build --target check` is hardware/server independent: offline render tests, concurrent plan-replacement stress, and (when JACK headers are present) JACK ABI fault -injection. The fake links against installed declarations without linking -libjack. It does not claim to reproduce JACK scheduling; the separate +injection. The fake exports JACK entry points against the installed declarations; +the CMake core target carries the optional JACK link dependency. It does not claim to reproduce JACK scheduling; the separate threaded stress test exercises publication/reclamation. -`make test-jack` explicitly runs a five-second live JACK loopback test. Requires +`cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-jack` explicitly runs a five-second live JACK loopback test. Requires an existing JACK or PipeWire JACK-compatible server; does not start a server, change its sample rate/block size, or connect physical outputs. Source output is connected only to the test's capture client, then disconnected and removed. @@ -14,7 +22,7 @@ It checks finite/bounded captured samples at two gains, active plan replacement, status and ten restart cycles. For a longer run: ```sh -make build/test_jack +cmake --build build --target test_jack ./build/test_jack 30 ``` @@ -30,9 +38,9 @@ JACK callbacks/results in `tests/unit/test_backend.c`. ## FLTK shell tests ```sh -make gui test-gui +cmake --build build --target gui test-gui # Optional, requires a running JACK-compatible server: -make test-gui-jack +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-gui-jack ``` These targets require FLTK development files, `xvfb-run` and Xvfb. No additional @@ -58,15 +66,15 @@ the third-party font stack is leak-free. Apply only to GUI sanitizer tests: LSAN_OPTIONS=suppressions=tests/integration/lsan.supp xvfb-run -a -s '-screen 0 1280x800x24' ./build/gui-sanitize/test_gui ``` -Build that binary with Clang/Clang++, `-fsanitize=address,undefined` in both -CFLAGS/CXXFLAGS and LDFLAGS, using the baseline warning/include/standard flags -from BUILD.md. Keep FLTK's `-I` and `-D` flags from `fltk-config --cxxflags`, -but exclude the distribution's GCC-specific LTO/object-code flags for Clang. +Build that binary in a separate CMake directory with +`-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DXTC_SANITIZE=ON`. +CMake propagates sanitizer compile/link options and filters FLTK discovery flags +so they do not override the chosen compiler or optimization settings. No suppressions are used for core/backend/realtime sanitizer tests. ## Phase 4 graph editing coverage -The same `make test-gui` target now adds nodes through toolbar callbacks, +The same `cmake --build build --target test-gui` target now adds nodes through toolbar callbacks, connects the exact rendered graph, validates expected oscillator/gain samples in the submitted backend plan, checks cycle/direction/duplicate rejection, checks backend I/O/pending-capacity rollback, deletes wires and incident edges, @@ -74,7 +82,7 @@ and verifies stable IDs, preserved positions, prepared/queued/active feedback and empty-graph silence. The fake backend compiles submitted C snapshots rather than accepting an unrelated GUI model. Move-only actions do not submit plans. -`make test-gui-jack` exercises the same widget against real JACK: it builds a +`cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-gui-jack` exercises the same widget against real JACK: it builds a graph while stopped, starts it, adds/deletes nodes during active processing, waits for revision acknowledgement, and rejects cycles without changing the model. Start/stop/reconnect and callback-safe active close remain covered. No @@ -99,14 +107,14 @@ A sample-status update drives the cursor to an exact tick; a graph edit retains that position. test_gui_jack checks actual playhead advancement and a stable paused cursor across repeated starts/stops. No routes are created. test_project additionally covers serialization, exact graph samples/IDs, -truncation/mutation input, capacity, save failures and cleanup (part of make test). +truncation/mutation input, capacity, save failures and cleanup (part of CTest). The core/backend tests remain unsuppressed under ASan/UBSan; GUI checks retain the narrowly scoped fontconfig-only suppression described above. ## Phase 6 audio acceptance -make test-jack-audio requires a running JACK-compatible server. It creates only +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-jack-audio requires a running JACK-compatible server. It creates only software routes between its probe and xtc backend: known 0.25 source, input capture through the recorder ring, WAV finalization, project save/reopen, then clip source graph playback observed at the probe input. All test ports, @@ -117,14 +125,14 @@ test_gui covers record/Stop, exact take playback, saving/reopening, failed impor missing assets, and blocked edits/file actions/close while recording. The live test_gui_jack records a silent take, finalizes and saves/reopens it, then starts playback before callback-safe close. Decoder and producer/consumer concurrency -checks are part of make test; run test_audio and test_realtime under TSan as in +checks are part of CTest; run test_audio and test_realtime under TSan as in BUILD.md. Physical input/output and latency compensation are not tested. ## Phase 7 verification ```sh -make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui debug test-gui test-gui-jack test-jack-audio test-jack-midi +cmake -S . -B build/nojack -DXTC_WITH_JACK=OFF && cmake --build build/nojack --parallel && cmake --build build/nojack --target check gui test-gui ``` `test_midi` adds exact sample scheduling, trim/seek/panic, block independence, @@ -139,8 +147,8 @@ or system changes are made. MIDI recording/SMF and physical devices are deferred ## Phase 8 verification ```sh -make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi -make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui debug test-gui test-gui-jack test-jack-audio test-jack-midi +cmake -S . -B build/nojack -DXTC_WITH_JACK=OFF && cmake --build build/nojack --parallel && cmake --build build/nojack --target check gui test-gui ``` `test_mixer` covers stereo gain/pan, mute/solo, sample automation, block @@ -160,9 +168,9 @@ long-running stereo performance remain unverified. ## Phase 9 release preparation ```sh -make all test gui debug test-gui test-gui-jack test-jack test-jack-audio test-jack-midi -make test-install test-recovery test-performance -make test-release +cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --parallel && cmake --build build --target check gui debug test-gui test-gui-jack test-jack test-jack-audio test-jack-midi +cmake --build build --target test-install test-recovery test-performance +cmake --build build --target test-release ``` Core test now includes native ABI/lifecycle/retirement and the native-gain example. @@ -185,9 +193,9 @@ and their repacked source archives were byte-identical on this toolchain. ## Phase 10 reliability audit -Run `make test-reliability` for the core suites, allocation-failure sweep, +Run `cmake --build build --target test-reliability` for the core suites, allocation-failure sweep, callback allocation/free guard and native reclamation checks. Run -`make test-sanitize` for Clang ASan/UBSan/leak checks and `make analyze` for +`cmake --build build --target test-sanitize` for Clang ASan/UBSan/leak checks and `cmake --build build --target analyze` for Clang static analysis of all core sources. These need installed development tools; the allocation guard uses a GNU-compatible linker. See [RELIABILITY.md](../../docs/RELIABILITY.md) for ownership, audit findings, @@ -195,7 +203,7 @@ full verification commands and remaining limits. ## Phase 11 composition workflow -`make test-workflow` builds and runs `test_workflow.cpp` under Xvfb with the real +`cmake -S . -B build -DXTC_ENABLE_LIVE_TESTS=ON && cmake --build build --target test-workflow` builds and runs `test_workflow.cpp` under Xvfb with the real JACK-compatible backend. It requires an already running server. It creates and arranges MIDI and imported audio through GUI commands, saves/closes/destroys the window, reopens in another window, compares stereo rendering exactly, verifies diff --git a/tests/integration/test_install.py b/tests/integration/test_install.py index 5c81a1b..261d385 100644 --- a/tests/integration/test_install.py +++ b/tests/integration/test_install.py @@ -27,20 +27,39 @@ def main(): sentinel = root / 'lib/keep-unrelated.txt' sentinel.parent.mkdir(parents=True) sentinel.write_text('keep\n') - make = shlex.split(os.environ.get('MAKE', 'make')) - # MAKEFLAGS jobserver descriptors do not cross Python subprocesses. - env = dict(os.environ, MAKEFLAGS='', MFLAGS='') - variables = [f'BUILD_DIR={args.build_dir}', f'PREFIX={prefix}', f'DESTDIR={stage}'] + # Generate the real CMake install driver against the already-built inputs. + # The temporary cache keeps the caller's build configuration untouched. + installer = stage / 'cmake-driver' + cache = (args.build_dir / 'CMakeCache.txt').read_text() + generator = next(line.partition('=')[2] for line in cache.splitlines() + if line.startswith('CMAKE_GENERATOR:INTERNAL=')) + jack = 'OFF' if 'XTC_WITH_JACK:BOOL=OFF' in cache else 'ON' + env = dict(os.environ) + variables = ['-DBUILD_TESTING=OFF', f'-DXTC_BUILD_GUI={"ON" if args.gui else "OFF"}', + f'-DCMAKE_INSTALL_PREFIX={prefix}', + f'-DXTC_INSTALL_BUILD_DIR={args.build_dir.resolve()}', + f'-DXTC_WITH_JACK={jack}', '-DXTC_INSTALL_GUI=OFF', '-DCMAKE_INSTALL_DOCDIR=share/doc/xtc'] if args.custom_layout: - variables += [f'exec_prefix={prefix}/runtime', f'libdir={prefix}/lib64', - f'includedir={prefix}/headers', f'datarootdir={prefix}/resources', - f'docdir={prefix}/manual', f'pkgconfigdir={prefix}/metadata'] + variables += ['-DCMAKE_INSTALL_BINDIR=runtime/bin', '-DCMAKE_INSTALL_LIBDIR=lib64', + '-DCMAKE_INSTALL_INCLUDEDIR=headers', '-DCMAKE_INSTALL_DATAROOTDIR=resources', + '-DCMAKE_INSTALL_DOCDIR=manual', '-DXTC_INSTALL_PKGCONFIGDIR=metadata'] + + def install_command(operation, options, **kwargs): + config = subprocess.run(['cmake', '-G', generator, '-S', str(source), '-B', str(installer), + *options], env=env, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, text=True) + if config.returncode: + raise RuntimeError(config.stdout) + command = (['cmake', '--build', str(installer), '--target', 'uninstall'] + if operation == 'uninstall' else ['cmake', '--install', str(installer)]) + return subprocess.run(command, env=dict(env, DESTDIR=str(stage)), **kwargs) + binaries = root / ('runtime/bin' if args.custom_layout else 'bin') headers = root / ('headers' if args.custom_layout else 'include') resources = root / ('resources' if args.custom_layout else 'share') docs = root / ('manual' if args.custom_layout else 'share/doc/xtc') metadata = root / ('metadata' if args.custom_layout else 'lib/pkgconfig') - run(make + ['install'] + variables, env=env) + install_command('install', variables, check=True) assert not (binaries / 'xtc').exists() assert (resources / 'licenses/xtc/LICENSE').read_bytes() == (source / 'LICENSE').read_bytes() assert (docs / 'PROJECT_FORMAT.md').is_file() @@ -64,7 +83,7 @@ def main(): (binaries / 'xtc-check').symlink_to(protected) # Upgrade/repair repeats the same owned-file install and keeps unrelated data. (headers / 'xtc/xtc.h').write_text('old installation marker\n') - run(make + ['install'] + variables, env=env) + install_command('install', variables, check=True) assert (headers / 'xtc/xtc.h').read_bytes() == (source / 'include/xtc/xtc.h').read_bytes() assert sentinel.read_text() == 'keep\n' assert protected.read_text() == 'keep unrelated target\n' @@ -79,8 +98,7 @@ def main(): pkg_env = dict(env, PKG_CONFIG_PATH=str(metadata) + ':' + env.get('PKG_CONFIG_PATH', ''), PKG_CONFIG_SYSROOT_DIR=str(stage)) pkg = shlex.split(os.environ.get('PKG_CONFIG', 'pkg-config')) - # A no-JACK test can be invoked with PKG_CONFIG=false; package consumer - # discovery itself still uses the installed pkg-config tool. + # The build may disable JACK; consumer discovery still uses pkg-config. if pkg == ['false']: pkg = ['pkg-config'] flags = shlex.split(subprocess.check_output(pkg + ['--cflags', '--libs', '--static', 'xtc'], env=pkg_env, text=True)) @@ -110,7 +128,7 @@ int main(int argc, char **argv) { run(command + [f'-std={standard}', '-Wall', '-Wextra', '-Werror', str(consumer)] + flags + ['-o', str(output)]) run([str(output), str(stage / ("installed-" + compiler + ".xtc"))]) if args.gui: - run(make + ['install-gui'] + variables, env=env) + install_command('install', variables + ['-DXTC_INSTALL_GUI=ON'], check=True) gui_env = dict(env, DISPLAY='', WAYLAND_DISPLAY='') assert subprocess.check_output([str(binaries / 'xtc'), '--version'], env=gui_env, text=True) == version run([str(binaries / 'xtc'), '--help'], env=gui_env) @@ -120,9 +138,9 @@ int main(int argc, char **argv) { with tempfile.TemporaryDirectory(prefix='xtc-install-outside-') as outside: escape = root / 'escape' escape.symlink_to(outside, target_is_directory=True) - redirected = variables + [f'docdir={prefix}/escape/docs'] + redirected = variables + [f'-DCMAKE_INSTALL_DOCDIR={prefix}/escape/docs'] for operation in ('install', 'uninstall'): - refusal = subprocess.run(make + [operation] + redirected, env=env, + refusal = install_command(operation, redirected, stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert refusal.returncode != 0 and not list(Path(outside).iterdir()) assert (headers / 'xtc/xtc.h').is_file() @@ -131,13 +149,13 @@ int main(int argc, char **argv) { saved_binary = stage / 'saved-check' (binaries / 'xtc-check').rename(saved_binary) (binaries / 'xtc-check').mkdir() - refusal = subprocess.run(make + ['uninstall'] + variables, env=env, + refusal = install_command('uninstall', variables + ['-DCMAKE_INSTALL_DOCDIR=manual' if args.custom_layout else '-DCMAKE_INSTALL_DOCDIR=share/doc/xtc'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert refusal.returncode != 0 and (headers / 'xtc/xtc.h').is_file() (binaries / 'xtc-check').rmdir() saved_binary.rename(binaries / 'xtc-check') - run(make + ['uninstall'] + variables, env=env) - run(make + ['uninstall'] + variables, env=env) + install_command('uninstall', variables, check=True) + install_command('uninstall', variables, check=True) remaining = sorted(p.relative_to(root).as_posix() for p in root.rglob('*') if p.is_file() or p.is_symlink()) assert remaining == ['lib/keep-unrelated.txt'], remaining assert sentinel.read_text() == 'keep\n' diff --git a/tests/integration/test_release.py b/tests/integration/test_release.py index ac5d642..16457f5 100644 --- a/tests/integration/test_release.py +++ b/tests/integration/test_release.py @@ -4,7 +4,6 @@ import argparse import hashlib import os from pathlib import Path -import shlex import subprocess import tarfile import tempfile @@ -18,6 +17,7 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('archive', type=Path) parser.add_argument('--gui', action='store_true') + parser.add_argument('--generator', default=os.environ.get('CMAKE_GENERATOR')) args = parser.parse_args() archive = args.archive.resolve() assert archive.with_name(archive.name + '.sha256').read_text().split()[0] == digest(archive) @@ -33,16 +33,23 @@ def main(): assert all('__pycache__' not in Path(n).parts and '.git' not in Path(n).parts for n in names) for name in ('first', 'different-path'): source.extractall(root / name, filter='data') - env = dict(os.environ, SOURCE_DATE_EPOCH=str(epoch), MAKEFLAGS='', MFLAGS='') + env = dict(os.environ, SOURCE_DATE_EPOCH=str(epoch)) + if args.generator: + env['CMAKE_GENERATOR'] = args.generator builds = [] for name in ('first', 'different-path'): project = root / name / top log = root / (name + '.log') - make = shlex.split(env.get('MAKE', 'make')) with log.open('w') as output: try: - subprocess.run(make + ['BUILD_DIR=build', 'release'], cwd=project, env=env, stdout=output, - stderr=subprocess.STDOUT, check=True) + subprocess.run(['cmake', '-S', '.', '-B', 'build/release', + '-DCMAKE_BUILD_TYPE=Release', '-DXTC_REPRODUCIBLE=ON', + f'-DXTC_BUILD_GUI={"ON" if args.gui else "OFF"}'], + cwd=project, env=env, stdout=output, stderr=subprocess.STDOUT, check=True) + subprocess.run(['cmake', '--build', 'build/release', '--parallel', '3'], + cwd=project, env=env, stdout=output, stderr=subprocess.STDOUT, check=True) + subprocess.run(['ctest', '--test-dir', 'build/release', '--output-on-failure'], + cwd=project, env=env, stdout=output, stderr=subprocess.STDOUT, check=True) except subprocess.CalledProcessError: print(log.read_text()[-12000:]) raise diff --git a/third_party/README.md b/third_party/README.md index 0b4fa90..d89b4ff 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -29,3 +29,24 @@ vendored headers. The inspected JUCE 9.0.4 revision is AGPLv3/commercial; a commercial licence may be required. Original example source does not relicense JUCE or its dependencies. JUCE-linked outputs stay outside normal install and package targets. See [JUCE dependency/license details](../docs/JUCE.md). + +## CMake integration + +The root CMake target `xtc-core` uses the pinned CLAP/VST3 directories as private +system include paths. No download step or SDK runtime is required. Configure +optional full SDK builds explicitly: + +```sh +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + -DXTC_BUILD_STEINBERG=ON -DXTC_VST3_SDK_ROOT=/path/to/vst3sdk \ + -DXTC_BUILD_JUCE=ON -DXTC_JUCE_ROOT=/path/to/JUCE +cmake --build build --parallel 3 +ctest --test-dir build --output-on-failure -R 'steinberg|juce' +``` + +Leave both options off for normal builds. ASIO's native Windows diagnostic +retains its separate CMake configuration described in the Steinberg examples; +the Linux core is not a Windows/ASIO backend. See [BUILD.md](../docs/BUILD.md) +for target options and [RELEASING.md](../docs/RELEASING.md) for staging. The +owned install inventory retains the CLAP and VST3 C-header license notices; +JUCE/official SDK plugin binaries are excluded from normal install/package targets. diff --git a/tools/analyze.py b/tools/analyze.py index 8f8af4c..cc2065b 100644 --- a/tools/analyze.py +++ b/tools/analyze.py @@ -6,7 +6,7 @@ import subprocess import sys if not shutil.which("clang"): - sys.exit("clang is required for make analyze") + sys.exit("clang is required for the CMake analyze target") jack = subprocess.run(["pkg-config", "--cflags", "jack"], capture_output=True, text=True) if shutil.which("pkg-config") else None for source in sys.argv[1:]: flags = [] diff --git a/tools/package.py b/tools/package.py index 3ef3a03..2c46dc6 100644 --- a/tools/package.py +++ b/tools/package.py @@ -9,7 +9,6 @@ import os from pathlib import Path import platform import re -import shlex import subprocess import tarfile import tempfile @@ -29,9 +28,23 @@ def main(): a.output.parent.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory(prefix='xtc-binary-stage-') as temporary: stage = Path(temporary) - env = dict(os.environ, MAKEFLAGS='', MFLAGS='') - subprocess.run(shlex.split(env.get('MAKE', 'make')) + ['install-gui' if a.gui else 'install', - f'BUILD_DIR={a.build_dir.resolve()}', f'DESTDIR={stage}', 'PREFIX=/usr'], env=env, check=True) + env = dict(os.environ) + driver = stage / 'cmake-driver' + cache = (a.build_dir / 'CMakeCache.txt').read_text() + generator = next(line.partition('=')[2] for line in cache.splitlines() + if line.startswith('CMAKE_GENERATOR:INTERNAL=')) + jack = 'OFF' if 'XTC_WITH_JACK:BOOL=OFF' in cache else 'ON' + subprocess.run(['cmake', '-G', generator, '-S', str(Path.cwd()), '-B', str(driver), + '-DBUILD_TESTING=OFF', '-DCMAKE_INSTALL_PREFIX=/usr', '-DCMAKE_INSTALL_LIBDIR=lib', + f'-DXTC_BUILD_GUI={"ON" if a.gui else "OFF"}', + '-DXTC_INSTALL_PKGCONFIGDIR=lib/pkgconfig', + f'-DXTC_INSTALL_BUILD_DIR={a.build_dir.resolve()}', + f'-DXTC_WITH_JACK={jack}', f'-DXTC_INSTALL_GUI={"ON" if a.gui else "OFF"}'], + env=env, check=True) + subprocess.run(['cmake', '--install', str(driver)], env=dict(env, DESTDIR=str(stage)), check=True) + # Only the installed /usr tree belongs to the archive. + import shutil + shutil.rmtree(driver) pc = stage / 'usr/lib/pkgconfig/xtc.pc' pc.write_text(pc.read_text().replace('prefix=/usr\n', 'prefix=${pcfiledir}/../..\n')) entries = []