Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0d509f7
Integrate VST3 validator directly into pluginval binary
claude Jan 31, 2026
e2c5ad7
Fix VST3 validator build and runtime issues
claude Jan 31, 2026
cb59faa
Merge branch 'develop' into claude/plan-vstvalidator-integration-4wOI7
drowaudio Jan 31, 2026
956dc9f
Add ARC compile flag for macOS module_mac.mm
claude Jan 31, 2026
eb7b3d4
Add platform-specific library linking for VST3 validator
claude Feb 1, 2026
22cfb9e
Add Windows-specific compile definitions for module_win32.cpp
claude Feb 1, 2026
c786777
Add exception handling and better error output to VST3 validator
claude Feb 1, 2026
1218b64
Use COMPILE_FLAGS for Windows VST3 module definitions
claude Feb 1, 2026
92593df
Use wrapper file for Windows VST3 module compilation
claude Feb 1, 2026
214500d
Use target-level compile definitions for Windows VST3 module
claude Feb 1, 2026
2ee3182
Add SMTG_USE_STATIC_CRT and additional include directories
claude Feb 1, 2026
6c1095d
Create OBJECT library for Windows VST3 module compilation
claude Feb 1, 2026
278ea09
Simplify OBJECT library include directories for Windows VST3 module
claude Feb 1, 2026
ab21528
Simplify Windows VST3 module - use direct file inclusion
claude Feb 1, 2026
9795fbd
Fix Windows build: compile module_win32.cpp with C++17
claude Feb 2, 2026
945e977
Bypass JUCE startup for --vst3-validator-mode subprocess
claude Feb 2, 2026
3ebb512
Document CI run logs and setup instructions
drowaudio Feb 2, 2026
2c20362
Fix JUCE app initialization in custom main()
claude Feb 2, 2026
d40a868
Trigger CI re-run
claude Feb 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,44 @@ Follow these guidelines when working on this codebase:

6. **Never speculate about unread code**: Never make claims about code you haven't opened. If the user references a specific file, you MUST read the file before answering. Investigate and read relevant files BEFORE answering questions about the codebase. Give grounded, hallucination-free answers based on actual file contents.

## Getting CI Run Logs

### Configuration

- **Organisation:** `<organisation>`
- **Repository:** `<repo>`

For this project:
- **Organisation:** `Tracktion`
- **Repository:** `pluginval`

### Setup

Install the GitHub CLI:
```bash
brew install gh # macOS
# or
sudo apt install gh # Ubuntu/Debian
```

Authentication is handled via the `GH_TOKEN` environment variable (already configured).

### Workflow

1. **List recent workflow runs:**
```bash
gh run list -R <organisation>/<repo>
```

2. **Find the most recent run for your branch** from the output above.

3. **View failed log details:**
```bash
gh run view -R <organisation>/<repo> <run_id> --log-failed
```

Replace `<run_id>` with the ID from step 2.

## Directory Structure

```
Expand All @@ -49,6 +87,9 @@ pluginval/
│ ├── PluginvalLookAndFeel.h # Custom UI styling
│ ├── StrictnessInfoPopup.h # Strictness level info UI
│ ├── binarydata/ # Binary resources (icons)
│ ├── vst3validator/ # Embedded VST3 validator integration
│ │ ├── VST3ValidatorRunner.h
│ │ └── VST3ValidatorRunner.cpp
│ └── tests/ # Individual test implementations
│ ├── BasicTests.cpp # Core plugin tests (info, state, audio)
│ ├── BusTests.cpp # Audio bus configuration tests
Expand Down Expand Up @@ -101,6 +142,7 @@ cmake --build Builds/Debug --config Debug
| Option | Description | Default |
|--------|-------------|---------|
| `PLUGINVAL_FETCH_JUCE` | Fetch JUCE with pluginval | ON |
| `PLUGINVAL_VST3_VALIDATOR` | Build with embedded VST3 validator | ON |
| `WITH_ADDRESS_SANITIZER` | Enable AddressSanitizer | OFF |
| `WITH_THREAD_SANITIZER` | Enable ThreadSanitizer | OFF |
| `VST2_SDK_DIR` | Path to VST2 SDK (env var) | - |
Expand Down Expand Up @@ -177,6 +219,27 @@ struct Requirements {
| `LocaleTest.cpp` | Locale handling verification |
| `ExtremeTests.cpp` | Edge cases, stress tests |

### VST3 Validator Integration

The VST3 validator (Steinberg's vstvalidator) is embedded directly into pluginval when built with `PLUGINVAL_VST3_VALIDATOR=ON` (the default). This eliminates the need to provide an external validator binary path.

**Architecture:**
1. The VST3 SDK is fetched via CPM during CMake configure
2. `VST3ValidatorRunner` (`Source/vst3validator/`) wraps the SDK's validation functionality
3. When the `VST3validator` test runs, it spawns pluginval with `--vst3-validator-mode`
4. This subprocess runs the embedded validator code in isolation (crash protection)

**Internal CLI mode:**
```bash
# Used internally by the VST3validator test - not for direct use
pluginval --vst3-validator-mode /path/to/plugin.vst3 [-e] [-v]
```

**Disabling embedded validator:**
```bash
cmake -B Builds -DPLUGINAL_VST3_VALIDATOR=OFF .
```

## Adding New Tests

1. Create a subclass of `PluginTest`:
Expand Down Expand Up @@ -312,6 +375,7 @@ add_pluginval_tests(MyPluginTarget
- **JUCE** (v8.0.x) - Audio application framework (git submodule)
- **magic_enum** (v0.9.7) - Enum reflection (fetched via CPM)
- **rtcheck** (optional, macOS) - Real-time safety checking (fetched via CPM)
- **VST3 SDK** (v3.7.x) - Steinberg VST3 SDK for embedded validator (fetched via CPM, optional)

### System
- macOS: CoreAudio, AudioUnit frameworks
Expand Down
94 changes: 94 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ if(PLUGINVAL_ENABLE_RTCHECK)
CPMAddPackage("gh:Tracktion/rtcheck#main")
endif()

# VST3 Validator integration - embeds Steinberg's vstvalidator directly into pluginval
option(PLUGINVAL_VST3_VALIDATOR "Build with embedded VST3 validator" ON)

if(PLUGINVAL_VST3_VALIDATOR)
# Use static CRT on Windows to match pluginval's settings
set(SMTG_USE_STATIC_CRT ON CACHE BOOL "" FORCE)

CPMAddPackage(
NAME vst3sdk
GITHUB_REPOSITORY steinbergmedia/vst3sdk
GIT_TAG v3.7.14_build_55
OPTIONS
"SMTG_ENABLE_VST3_PLUGIN_EXAMPLES OFF"
"SMTG_ENABLE_VST3_HOSTING_EXAMPLES OFF"
"SMTG_ENABLE_VSTGUI_SUPPORT OFF"
"SMTG_ADD_VSTGUI OFF"
"SMTG_RUN_VST_VALIDATOR OFF"
"SMTG_CREATE_BUNDLE_FOR_WINDOWS OFF"
)
endif()

option(PLUGINVAL_FETCH_JUCE "Fetch JUCE along with pluginval" ON)

Expand Down Expand Up @@ -103,6 +123,49 @@ set(SourceFiles
target_sources(pluginval PRIVATE ${SourceFiles})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/Source PREFIX Source FILES ${SourceFiles})

# Add VST3 validator sources if enabled
if(PLUGINVAL_VST3_VALIDATOR)
set(VST3ValidatorFiles
Source/vst3validator/VST3ValidatorRunner.h
Source/vst3validator/VST3ValidatorRunner.cpp
)

# Add platform-specific module loading sources from the SDK
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND VST3ValidatorFiles
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting/module_linux.cpp)
elseif(APPLE)
set(VST3_MODULE_MAC_FILE ${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting/module_mac.mm)
list(APPEND VST3ValidatorFiles ${VST3_MODULE_MAC_FILE})
# module_mac.mm requires ARC (Automatic Reference Counting)
set_source_files_properties(${VST3_MODULE_MAC_FILE} PROPERTIES
COMPILE_FLAGS "-fobjc-arc")
elseif(WIN32)
set(VST3_MODULE_WIN32_FILE ${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting/module_win32.cpp)
list(APPEND VST3ValidatorFiles ${VST3_MODULE_WIN32_FILE})
# module_win32.cpp requires C++17 (not C++20) because generic_u8string() returns
# std::u8string in C++20 but std::string in C++17, and the SDK code expects std::string.
# Also set UNICODE definitions only for this file to avoid affecting JUCE's LV2/lilv code.
set_source_files_properties(${VST3_MODULE_WIN32_FILE} PROPERTIES
COMPILE_FLAGS "/std:c++17"
COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN;_UNICODE;UNICODE")
endif()

target_sources(pluginval PRIVATE ${VST3ValidatorFiles})
source_group("Source/vst3validator" FILES ${VST3ValidatorFiles})

target_include_directories(pluginval PRIVATE
${vst3sdk_SOURCE_DIR}
${vst3sdk_SOURCE_DIR}/pluginterfaces
${vst3sdk_SOURCE_DIR}/base
${vst3sdk_SOURCE_DIR}/public.sdk
${vst3sdk_SOURCE_DIR}/public.sdk/source
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/utility
)
endif()

if (DEFINED ENV{VST2_SDK_DIR})
target_compile_definitions(pluginval PRIVATE
JUCE_PLUGINHOST_VST=1)
Expand All @@ -118,8 +181,19 @@ target_compile_definitions(pluginval PRIVATE
JUCE_MODAL_LOOPS_PERMITTED=1
JUCE_GUI_BASICS_INCLUDE_XHEADERS=1
$<$<BOOL:${PLUGINVAL_ENABLE_RTCHECK}>:PLUGINVAL_ENABLE_RTCHECK=1>
$<$<BOOL:${PLUGINVAL_VST3_VALIDATOR}>:PLUGINVAL_VST3_VALIDATOR=1>
VERSION="${CURRENT_VERSION}")

# Windows-specific compile definitions for VST3 SDK compatibility
# Note: _UNICODE and UNICODE are NOT set here - they're set only for module_win32.cpp
# to avoid breaking JUCE's LV2/lilv code which uses ANSI Windows APIs
if(WIN32 AND PLUGINVAL_VST3_VALIDATOR)
target_compile_definitions(pluginval PRIVATE
NOMINMAX
WIN32_LEAN_AND_MEAN
_CRT_SECURE_NO_WARNINGS)
endif()

if(MSVC AND NOT CMAKE_MSVC_RUNTIME_LIBRARY)
# Default to statically linking the runtime libraries
set_property(TARGET pluginval PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
Expand All @@ -137,6 +211,26 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-static-libstdc++)
endif()

if (PLUGINVAL_VST3_VALIDATOR)
target_link_libraries(pluginval PRIVATE
sdk_hosting
sdk)

# Windows needs additional libraries for the module loading code
if(WIN32)
target_link_libraries(pluginval PRIVATE
Ole32
Shell32)
endif()

# macOS needs Cocoa framework for module loading
if(APPLE)
find_library(COCOA_FRAMEWORK Cocoa)
target_link_libraries(pluginval PRIVATE
${COCOA_FRAMEWORK})
endif()
endif()

if (PLUGINVAL_ENABLE_RTCHECK)
target_link_libraries(pluginval PRIVATE
rtcheck)
Expand Down
53 changes: 44 additions & 9 deletions Source/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include "CrashHandler.h"
#include "PluginTests.h"

#if PLUGINVAL_VST3_VALIDATOR
#include "vst3validator/VST3ValidatorRunner.h"
#endif

#if JUCE_MAC
#include <signal.h>
#include <sys/types.h>
Expand Down Expand Up @@ -286,7 +290,6 @@ static Option possibleOptions[] =
{ "--randomise", false },
{ "--sample-rates", true },
{ "--block-sizes", true },
{ "--vst3validator", true },
{ "--rtcheck", false },
};

Expand Down Expand Up @@ -380,8 +383,6 @@ static juce::String getHelpMessage()
--disabled-tests [pathToFile]
If specified, sets a path to a file that should have the names of disabled
tests on each row.
--vst3validator [pathToValidator]
If specified, this will run the VST3 validator as part of the test process.

--output-dir [pathToDir]
If specified, sets a directory to store the log files. This can be useful
Expand Down Expand Up @@ -501,7 +502,11 @@ static juce::ArgumentList createCommandLineArgs (juce::String commandLine)
const bool hasValidateOrOtherCommand = argList.containsOption ("--validate")
|| argList.containsOption ("--help|-h")
|| argList.containsOption ("--version")
|| argList.containsOption ("--run-tests");
|| argList.containsOption ("--run-tests")
#if PLUGINVAL_VST3_VALIDATOR
|| argList.containsOption ("--vst3-validator-mode")
#endif
;

if (! hasValidateOrOtherCommand)
if (isPluginArgument (argList.arguments.getLast().text))
Expand Down Expand Up @@ -542,6 +547,36 @@ static void performCommandLine (CommandLineValidator& validator, const juce::Arg
printStrictnessHelp (level);
}});

#if PLUGINVAL_VST3_VALIDATOR
cli.addCommand ({ "--vst3-validator-mode",
"--vst3-validator-mode [pathToPlugin] [-e] [-v]",
"Runs the embedded VST3 validator on the plugin (internal use).", juce::String(),
[] (const auto& args)
{
auto pluginPath = getOptionValue (args, "--vst3-validator-mode", "",
"Expected a plugin path for --vst3-validator-mode").toString();

if (pluginPath.isEmpty())
{
std::cerr << "Error: No plugin path specified\n";
juce::JUCEApplication::getInstance()->setApplicationReturnValue (1);
juce::JUCEApplication::getInstance()->quit();
return;
}

vst3validator::Options opts;
opts.pluginPath = pluginPath.toStdString();
opts.extendedMode = args.containsOption ("-e");
opts.verbose = args.containsOption ("-v");

auto result = vst3validator::runValidator (opts);

std::cout << result.output;
juce::JUCEApplication::getInstance()->setApplicationReturnValue (result.exitCode);
juce::JUCEApplication::getInstance()->quit();
}});
#endif

if (const auto retValue = cli.findAndRunCommand (args); retValue != 0)
{
juce::JUCEApplication::getInstance()->setApplicationReturnValue (retValue);
Expand All @@ -566,7 +601,11 @@ bool shouldPerformCommandLine (const juce::String& commandLine)
|| args.containsOption ("--version")
|| args.containsOption ("--validate")
|| args.containsOption ("--run-tests")
|| args.containsOption ("--strictness-help");
|| args.containsOption ("--strictness-help")
#if PLUGINVAL_VST3_VALIDATOR
|| args.containsOption ("--vst3-validator-mode")
#endif
;
}

//==============================================================================
Expand Down Expand Up @@ -595,7 +634,6 @@ std::pair<juce::String, PluginTests::Options> parseCommandLine (const juce::Argu
options.disabledTests = getDisabledTests (args);
options.sampleRates = getSampleRates (args);
options.blockSizes = getBlockSizes (args);
options.vst3Validator = getOptionValue (args, "--vst3validator", "", "Expected a path for the --vst3validator option");
options.realtimeCheck = magic_enum::enum_cast<RealtimeCheck> (getOptionValue (args, "--rtcheck", "", "Expected one of [disabled, enabled, relaxed]").toString().toStdString())
.value_or (RealtimeCheck::disabled);

Expand Down Expand Up @@ -665,9 +703,6 @@ juce::StringArray createCommandLine (juce::String fileOrID, PluginTests::Options
args.addArray ({ "--block-sizes", blockSizes.joinIntoString (",") });
}

if (options.vst3Validator != juce::File())
args.addArray ({ "--vst3validator", options.vst3Validator.getFullPathName().quoted() });

if (auto rtCheckMode = options.realtimeCheck;
rtCheckMode != RealtimeCheck::disabled)
{
Expand Down
Loading
Loading