Skip to content

Commit 622c1d5

Browse files
committed
VST3: Build validator from sdk and extract to a temp file at run time
1 parent c67cafa commit 622c1d5

File tree

8 files changed

+88
-425
lines changed

8 files changed

+88
-425
lines changed

CLAUDE.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ pluginval/
100100
├── modules/
101101
│ └── juce/ # JUCE framework (git submodule)
102102
├── cmake/
103-
│ └── CPM.cmake # CMake Package Manager
103+
│ ├── CPM.cmake # CMake Package Manager
104+
│ └── GenerateBinaryHeader.cmake # Binary-to-C-header converter
104105
├── tests/
105106
│ ├── AddPluginvalTests.cmake # CMake module for CTest integration
106107
│ ├── test_plugins/ # Test plugin files
@@ -221,23 +222,22 @@ struct Requirements {
221222
222223
### VST3 Validator Integration
223224
224-
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.
225+
The VST3 validator (Steinberg's vstvalidator) is embedded into pluginval when built with `PLUGINVAL_VST3_VALIDATOR=ON` (the default). This provides single-file distribution while keeping vstvalidator completely isolated from pluginval's link dependencies.
225226
226227
**Architecture:**
227228
1. The VST3 SDK is fetched via CPM during CMake configure
228-
2. `VST3ValidatorRunner` (`Source/vst3validator/`) wraps the SDK's validation functionality
229-
3. When the `VST3validator` test runs, it spawns pluginval with `--vst3-validator-mode`
230-
4. This subprocess runs the embedded validator code in isolation (crash protection)
229+
2. The SDK's own `validator` target is built as a separate executable
230+
3. A CMake script (`cmake/GenerateBinaryHeader.cmake`) converts the compiled binary into a C byte array header
231+
4. `VST3ValidatorRunner` (`Source/vst3validator/`) extracts the embedded binary to a temp file on first use
232+
5. When the `VST3validator` test runs, it spawns the extracted validator as a subprocess
231233
232-
**Internal CLI mode:**
233-
```bash
234-
# Used internally by the VST3validator test - not for direct use
235-
pluginval --vst3-validator-mode /path/to/plugin.vst3 [-e] [-v]
236-
```
234+
**Key files:**
235+
- `cmake/GenerateBinaryHeader.cmake` — binary-to-C-header conversion script
236+
- `Source/vst3validator/VST3ValidatorRunner.h/cpp` — extracts embedded binary, returns `juce::File`
237237
238238
**Disabling embedded validator:**
239239
```bash
240-
cmake -B Builds -DPLUGINAL_VST3_VALIDATOR=OFF .
240+
cmake -B Builds -DPLUGINVAL_VST3_VALIDATOR=OFF .
241241
```
242242

243243
## Adding New Tests

CMakeLists.txt

Lines changed: 15 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,6 @@ if(PLUGINVAL_VST3_VALIDATOR)
8787
)
8888
endif()
8989

90-
option(PLUGINVAL_FETCH_JUCE "Fetch JUCE along with pluginval" ON)
91-
92-
if(PLUGINVAL_FETCH_JUCE)
93-
add_subdirectory(modules/juce)
94-
endif()
95-
9690
if (DEFINED ENV{VST2_SDK_DIR})
9791
MESSAGE(STATUS "Building with VST2 SDK: $ENV{VST2_SDK_DIR}")
9892
juce_set_vst2_sdk_path($ENV{VST2_SDK_DIR})
@@ -137,47 +131,14 @@ set(SourceFiles
137131
target_sources(pluginval PRIVATE ${SourceFiles})
138132
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/Source PREFIX Source FILES ${SourceFiles})
139133

140-
# Add VST3 validator sources if enabled
134+
# Add VST3 validator runner sources to pluginval (extracts embedded binary at runtime)
141135
if(PLUGINVAL_VST3_VALIDATOR)
142136
set(VST3ValidatorFiles
143137
Source/vst3validator/VST3ValidatorRunner.h
144138
Source/vst3validator/VST3ValidatorRunner.cpp
145139
)
146-
147-
# Add platform-specific module loading sources from the SDK
148-
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
149-
list(APPEND VST3ValidatorFiles
150-
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting/module_linux.cpp)
151-
elseif(APPLE)
152-
set(VST3_MODULE_MAC_FILE ${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting/module_mac.mm)
153-
list(APPEND VST3ValidatorFiles ${VST3_MODULE_MAC_FILE})
154-
# module_mac.mm requires ARC (Automatic Reference Counting)
155-
set_source_files_properties(${VST3_MODULE_MAC_FILE} PROPERTIES
156-
COMPILE_FLAGS "-fobjc-arc")
157-
elseif(WIN32)
158-
set(VST3_MODULE_WIN32_FILE ${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting/module_win32.cpp)
159-
list(APPEND VST3ValidatorFiles ${VST3_MODULE_WIN32_FILE})
160-
# module_win32.cpp requires C++17 (not C++20) because generic_u8string() returns
161-
# std::u8string in C++20 but std::string in C++17, and the SDK code expects std::string.
162-
# Also set UNICODE definitions only for this file to avoid affecting JUCE's LV2/lilv code.
163-
set_source_files_properties(${VST3_MODULE_WIN32_FILE} PROPERTIES
164-
COMPILE_FLAGS "/std:c++17"
165-
COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN;_UNICODE;UNICODE")
166-
endif()
167-
168140
target_sources(pluginval PRIVATE ${VST3ValidatorFiles})
169141
source_group("Source/vst3validator" FILES ${VST3ValidatorFiles})
170-
171-
target_include_directories(pluginval PRIVATE
172-
${vst3sdk_SOURCE_DIR}
173-
${vst3sdk_SOURCE_DIR}/pluginterfaces
174-
${vst3sdk_SOURCE_DIR}/base
175-
${vst3sdk_SOURCE_DIR}/public.sdk
176-
${vst3sdk_SOURCE_DIR}/public.sdk/source
177-
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst
178-
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/hosting
179-
${vst3sdk_SOURCE_DIR}/public.sdk/source/vst/utility
180-
)
181142
endif()
182143

183144
if (DEFINED ENV{VST2_SDK_DIR})
@@ -198,15 +159,6 @@ target_compile_definitions(pluginval PRIVATE
198159
$<$<BOOL:${PLUGINVAL_VST3_VALIDATOR}>:PLUGINVAL_VST3_VALIDATOR=1>
199160
VERSION="${CURRENT_VERSION}")
200161

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

211163
if(MSVC AND NOT CMAKE_MSVC_RUNTIME_LIBRARY)
212164
# Default to statically linking the runtime libraries
@@ -225,24 +177,20 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
225177
-static-libstdc++)
226178
endif()
227179

228-
if (PLUGINVAL_VST3_VALIDATOR)
229-
target_link_libraries(pluginval PRIVATE
230-
sdk_hosting
231-
sdk)
232-
233-
# Windows needs additional libraries for the module loading code
234-
if(WIN32)
235-
target_link_libraries(pluginval PRIVATE
236-
Ole32
237-
Shell32)
238-
endif()
239-
240-
# macOS needs Cocoa framework for module loading
241-
if(APPLE)
242-
find_library(COCOA_FRAMEWORK Cocoa)
243-
target_link_libraries(pluginval PRIVATE
244-
${COCOA_FRAMEWORK})
245-
endif()
180+
# Embed the SDK's validator binary into pluginval
181+
if(PLUGINVAL_VST3_VALIDATOR)
182+
set(VSTVALIDATOR_HEADER "${CMAKE_BINARY_DIR}/generated/vstvalidator_data.h")
183+
add_custom_command(
184+
OUTPUT "${VSTVALIDATOR_HEADER}"
185+
COMMAND ${CMAKE_COMMAND}
186+
-DINPUT_FILE=$<TARGET_FILE:validator>
187+
-DOUTPUT_FILE=${VSTVALIDATOR_HEADER}
188+
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateBinaryHeader.cmake
189+
DEPENDS validator
190+
COMMENT "Embedding vstvalidator binary into header...")
191+
192+
target_sources(pluginval PRIVATE "${VSTVALIDATOR_HEADER}")
193+
target_include_directories(pluginval PRIVATE "${CMAKE_BINARY_DIR}/generated")
246194
endif()
247195

248196
if (PLUGINVAL_ENABLE_RTCHECK)

Source/CommandLine.cpp

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
#include "CrashHandler.h"
1818
#include "PluginTests.h"
1919

20-
#if PLUGINVAL_VST3_VALIDATOR
21-
#include "vst3validator/VST3ValidatorRunner.h"
22-
#endif
23-
2420
#if JUCE_MAC
2521
#include <signal.h>
2622
#include <sys/types.h>
@@ -506,11 +502,7 @@ static juce::ArgumentList createCommandLineArgs (juce::String commandLine)
506502
const bool hasValidateOrOtherCommand = argList.containsOption ("--validate")
507503
|| argList.containsOption ("--help|-h")
508504
|| argList.containsOption ("--version")
509-
|| argList.containsOption ("--run-tests")
510-
#if PLUGINVAL_VST3_VALIDATOR
511-
|| argList.containsOption ("--vst3-validator-mode")
512-
#endif
513-
;
505+
|| argList.containsOption ("--run-tests");
514506

515507
if (! hasValidateOrOtherCommand)
516508
if (isPluginArgument (argList.arguments.getLast().text))
@@ -551,36 +543,6 @@ static void performCommandLine (CommandLineValidator& validator, const juce::Arg
551543
printStrictnessHelp (level);
552544
}});
553545

554-
#if PLUGINVAL_VST3_VALIDATOR
555-
cli.addCommand ({ "--vst3-validator-mode",
556-
"--vst3-validator-mode [pathToPlugin] [-e] [-v]",
557-
"Runs the embedded VST3 validator on the plugin (internal use).", juce::String(),
558-
[] (const auto& args)
559-
{
560-
auto pluginPath = getOptionValue (args, "--vst3-validator-mode", "",
561-
"Expected a plugin path for --vst3-validator-mode").toString();
562-
563-
if (pluginPath.isEmpty())
564-
{
565-
std::cerr << "Error: No plugin path specified\n";
566-
juce::JUCEApplication::getInstance()->setApplicationReturnValue (1);
567-
juce::JUCEApplication::getInstance()->quit();
568-
return;
569-
}
570-
571-
vst3validator::Options opts;
572-
opts.pluginPath = pluginPath.toStdString();
573-
opts.extendedMode = args.containsOption ("-e");
574-
opts.verbose = args.containsOption ("-v");
575-
576-
auto result = vst3validator::runValidator (opts);
577-
578-
std::cout << result.output;
579-
juce::JUCEApplication::getInstance()->setApplicationReturnValue (result.exitCode);
580-
juce::JUCEApplication::getInstance()->quit();
581-
}});
582-
#endif
583-
584546
if (const auto retValue = cli.findAndRunCommand (args); retValue != 0)
585547
{
586548
juce::JUCEApplication::getInstance()->setApplicationReturnValue (retValue);
@@ -605,11 +567,7 @@ bool shouldPerformCommandLine (const juce::String& commandLine)
605567
|| args.containsOption ("--version")
606568
|| args.containsOption ("--validate")
607569
|| args.containsOption ("--run-tests")
608-
|| args.containsOption ("--strictness-help")
609-
#if PLUGINVAL_VST3_VALIDATOR
610-
|| args.containsOption ("--vst3-validator-mode")
611-
#endif
612-
;
570+
|| args.containsOption ("--strictness-help");
613571
}
614572

615573
//==============================================================================

Source/Main.cpp

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818
#include "CommandLine.h"
1919
#include "PluginvalLookAndFeel.h"
2020

21-
#if PLUGINVAL_VST3_VALIDATOR
22-
#include "vst3validator/VST3ValidatorRunner.h"
23-
#include <cstring>
24-
#include <iostream>
25-
#endif
2621

2722
//==============================================================================
2823
class PluginValidatorApplication : public juce::JUCEApplication,
@@ -180,61 +175,7 @@ class PluginValidatorApplication : public juce::JUCEApplication,
180175
};
181176

182177
//==============================================================================
183-
#if PLUGINVAL_VST3_VALIDATOR
184-
// Forward declaration for JUCE application factory
185-
juce::JUCEApplicationBase* juce_CreateApplication();
186-
187-
// Custom main() to intercept --vst3-validator-mode before JUCE starts.
188-
// This avoids the "Periodic events are already being generated" crash on macOS
189-
// that occurs when JUCE's event loop conflicts with the validator subprocess.
190-
int main (int argc, char* argv[])
191-
{
192-
// Check for --vst3-validator-mode before starting JUCE
193-
for (int i = 1; i < argc; ++i)
194-
{
195-
if (std::strcmp (argv[i], "--vst3-validator-mode") == 0)
196-
{
197-
// Parse arguments for validator mode
198-
vst3validator::Options opts;
199-
200-
// The plugin path should be the next argument
201-
if (i + 1 < argc && argv[i + 1][0] != '-')
202-
opts.pluginPath = argv[i + 1];
203-
204-
// Check for optional flags
205-
for (int j = 1; j < argc; ++j)
206-
{
207-
if (std::strcmp (argv[j], "-e") == 0)
208-
opts.extendedMode = true;
209-
else if (std::strcmp (argv[j], "-v") == 0)
210-
opts.verbose = true;
211-
}
212-
213-
if (opts.pluginPath.empty())
214-
{
215-
std::cerr << "Error: No plugin path specified for --vst3-validator-mode\n";
216-
return 1;
217-
}
218-
219-
// Run the validator directly without JUCE
220-
auto result = vst3validator::runValidator (opts);
221-
std::cout << result.output;
222-
return result.exitCode;
223-
}
224-
}
225-
226-
// Normal JUCE application startup - must set createInstance before calling main()
227-
juce::JUCEApplicationBase::createInstance = &juce_CreateApplication;
228-
return juce::JUCEApplicationBase::main (argc, const_cast<const char**> (argv));
229-
}
230-
231-
// Provide the JUCE application class (required by JUCE's application framework)
232-
juce::JUCEApplicationBase* juce_CreateApplication() { return new PluginValidatorApplication(); }
233-
234-
#else
235-
// Standard JUCE application macro when VST3 validator is disabled
236178
START_JUCE_APPLICATION (PluginValidatorApplication)
237-
#endif
238179

239180
juce::PropertiesFile& getAppPreferences()
240181
{

Source/tests/BasicTests.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
#include "../TestUtilities.h"
1717
#include "../RTCheck.h"
1818

19+
#if PLUGINVAL_VST3_VALIDATOR
20+
#include "../vst3validator/VST3ValidatorRunner.h"
21+
#endif
22+
1923
#include <future>
2024
#include <thread>
2125
#include <chrono>
@@ -895,19 +899,25 @@ struct VST3validator : public PluginTest
895899
ut.logMessage ("INFO: Skipping vst3 validator (not built with VST3 validator support)");
896900
return;
897901
#else
898-
// Use the current pluginval executable with --vst3-validator-mode
899-
auto pluginvalExe = juce::File::getSpecialLocation (juce::File::currentExecutableFile);
902+
auto tempFile = vst3validator::getValidatorExecutable();
903+
auto vstvalidatorExe = tempFile->getFile();
904+
905+
if (! vstvalidatorExe.existsAsFile())
906+
{
907+
ut.logMessage ("WARNING: Could not extract vstvalidator binary");
908+
return;
909+
}
900910

901911
juce::StringArray cmd;
902-
cmd.add (pluginvalExe.getFullPathName());
903-
cmd.add ("--vst3-validator-mode");
904-
cmd.add (ut.getFileOrID());
912+
cmd.add (vstvalidatorExe.getFullPathName());
905913

906914
if (ut.getOptions().strictnessLevel > 5)
907915
cmd.add ("-e");
908916

909-
if (ut.getOptions().verbose)
910-
cmd.add ("-v");
917+
if (! ut.getOptions().verbose)
918+
cmd.add ("-q");
919+
920+
cmd.add (ut.getFileOrID());
911921

912922
juce::ChildProcess cp;
913923
const auto started = cp.start (cmd);
@@ -967,4 +977,4 @@ struct VST3validator : public PluginTest
967977
}
968978
};
969979

970-
static VST3validator vst3validator;
980+
static VST3validator vst3validatorTest;

0 commit comments

Comments
 (0)