From d048a0ae76dcee0405d3d0c04a80220712614bc1 Mon Sep 17 00:00:00 2001 From: Matt Nish-Lapidus Date: Fri, 20 Jun 2025 15:44:46 -0400 Subject: [PATCH] trying to patch yabridge --- overlays/hardcode-dependencies.patch | 41 ++ overlays/libyabridge-from-nix-profiles.patch | 71 ++++ overlays/meson-p.build | 385 +++++++++++++++++++ overlays/meson.build | 385 +++++++++++++++++++ overlays/wine-6006.patch | 173 +++++++++ overlays/yabridge.nix | 36 ++ 6 files changed, 1091 insertions(+) create mode 100644 overlays/hardcode-dependencies.patch create mode 100644 overlays/libyabridge-from-nix-profiles.patch create mode 100644 overlays/meson-p.build create mode 100644 overlays/meson.build create mode 100644 overlays/wine-6006.patch create mode 100644 overlays/yabridge.nix diff --git a/overlays/hardcode-dependencies.patch b/overlays/hardcode-dependencies.patch new file mode 100644 index 0000000..e5ab002 --- /dev/null +++ b/overlays/hardcode-dependencies.patch @@ -0,0 +1,41 @@ +diff --git a/meson.build b/meson.build +index 9e69128d..8c53ac88 100644 +--- a/meson.build ++++ b/meson.build +@@ -226,7 +226,7 @@ if is_64bit_system + xcb_64bit_dep = dependency('xcb') + endif + if with_32bit_libraries or with_bitbridge +- xcb_32bit_dep = winegcc.find_library('xcb') ++ xcb_32bit_dep = winegcc.find_library('xcb', dirs: ['@libxcb32@/lib']) + endif + + # These are all headers-only libraries, and thus won't require separate 32-bit +diff --git a/src/common/notifications.cpp b/src/common/notifications.cpp +index 654b6c83..78ba2fe7 100644 +--- a/src/common/notifications.cpp ++++ b/src/common/notifications.cpp +@@ -29,8 +29,8 @@ + #include "process.h" + #include "utils.h" + +-constexpr char libdbus_library_name[] = "libdbus-1.so.3"; +-constexpr char libdbus_library_fallback_name[] = "libdbus-1.so"; ++constexpr char libdbus_library_name[] = "@libdbus@/lib/libdbus-1.so.3"; ++constexpr char libdbus_library_fallback_name[] = "@libdbus@/lib/libdbus-1.so"; + + std::atomic libdbus_handle = nullptr; + std::mutex libdbus_mutex; +diff --git a/src/plugin/utils.cpp b/src/plugin/utils.cpp +index 441345c6..f3e51cff 100644 +--- a/src/plugin/utils.cpp ++++ b/src/plugin/utils.cpp +@@ -103,7 +103,7 @@ std::string PluginInfo::wine_version() const { + // The '*.exe' scripts generated by winegcc allow you to override the binary + // used to run Wine, so will will handle this in the same way for our Wine + // version detection. We'll be using `execvpe` +- std::string wine_path = "wine"; ++ std::string wine_path = "@wine@/bin/wine"; + // NOLINTNEXTLINE(concurrency-mt-unsafe) + if (const char* wineloader_path = getenv("WINELOADER"); + wineloader_path && access(wineloader_path, X_OK) == 0) { diff --git a/overlays/libyabridge-from-nix-profiles.patch b/overlays/libyabridge-from-nix-profiles.patch new file mode 100644 index 0000000..bcc55d4 --- /dev/null +++ b/overlays/libyabridge-from-nix-profiles.patch @@ -0,0 +1,71 @@ +diff --git a/src/chainloader/utils.cpp b/src/chainloader/utils.cpp +index fa90b8f7..bd44d0ea 100644 +--- a/src/chainloader/utils.cpp ++++ b/src/chainloader/utils.cpp +@@ -29,8 +29,10 @@ + namespace fs = ghc::filesystem; + + void* find_plugin_library(const std::string& name) { ++ Logger logger = Logger::create_exception_logger(); ++ + // Just using a goto for this would probably be cleaner, but yeah... +- const auto impl = [&name]() -> void* { ++ const auto impl = [&name, &logger]() -> void* { + // If `name` exists right next to the Wine plugin host binary, then + // we'll try loading that. Otherwise we'll fall back to regular + // `dlopen()` for distro packaged versions of yabridge +@@ -52,27 +54,28 @@ void* find_plugin_library(const std::string& name) { + } + } + +- if (void* handle = dlopen(name.c_str(), RTLD_LAZY | RTLD_LOCAL)) { +- return handle; ++ auto nix_profiles = getenv("NIX_PROFILES"); ++ if (!nix_profiles || nix_profiles[0] == '\0') { ++ logger.log(""); ++ logger.log("ERROR: 'NIX_PROFILES' environment variable is not set"); ++ logger.log(""); ++ return nullptr; + } + +- // One last Hail Mary, in case ldconfig was not set up correctly. This +- // might be relevant for some of the `/usr/local/*` locations (although +- // you really, really shouldn't install yabridge there, please, thank +- // you). Yabridgectl searches through these same directories. +- for (const auto& lib_dir : { +- "/usr/lib", +- "/usr/lib/x86_64-linux-gnu", +- "/usr/lib64", +- "/usr/local/lib", +- "/usr/local/lib/x86_64-linux-gnu", +- "/usr/local/lib64", +- }) { +- const fs::path candidate = fs::path(lib_dir) / name; +- if (void* handle = +- dlopen(candidate.c_str(), RTLD_LAZY | RTLD_LOCAL)) { ++ // NIX_PROFILES is iterated in reverse from the most specific (the ++ // user profile) to the least specific (the system profile). ++ const std::string_view nix_profiles_view = nix_profiles; ++ auto segment_end = nix_profiles_view.size(); ++ while (segment_end != std::string::npos) { ++ const auto next_segment_end = nix_profiles_view.rfind(' ', segment_end - 1); ++ const auto segment_begin = next_segment_end + 1; ++ const auto profile = nix_profiles_view.substr(segment_begin, segment_end - segment_begin); ++ const auto candidate = fs::path(profile) / "lib" / name; ++ if (auto handle = dlopen(candidate.c_str(), RTLD_LAZY | RTLD_LOCAL)) { + return handle; + } ++ ++ segment_end = next_segment_end; + } + + return nullptr; +@@ -82,8 +85,6 @@ void* find_plugin_library(const std::string& name) { + if (!handle) { + const fs::path this_plugin_path = get_this_file_location(); + +- Logger logger = Logger::create_exception_logger(); +- + logger.log(""); + logger.log("Could not find '" + name + "'"); + logger.log(""); diff --git a/overlays/meson-p.build b/overlays/meson-p.build new file mode 100644 index 0000000..a6e39ff --- /dev/null +++ b/overlays/meson-p.build @@ -0,0 +1,385 @@ +project( + 'yabridge', + 'cpp', + version : '5.1.1', + meson_version : '>=0.56', + default_options : [ + 'warning_level=3', + 'cpp_std=c++2a', + # Even though Meson will complain that this option does not exist, without + # this Meson will not apply the above option to native targets + 'build.cpp_std=c++2a', + ], +) + +# +# Build options +# + +# In theory yabridge should compile fine on a 32-bit system, but you will always +# need to pass `-Dbitbridge=true`. We just make sure that we won't build +# any 64-bit binaries in that situation. +is_64bit_system = build_machine.cpu_family() not in ['x86', 'arm'] +with_bitbridge = get_option('bitbridge') +with_clap = get_option('clap') +with_winedbg = get_option('winedbg') +with_vst3 = get_option('vst3') + +# +# Compiler flags +# + +# Depending on the `bitbridge` flag we'll enable building secondary 32-bit +# host applications that can act as a bit bridge for using 32-bit Windows +# plugins in 64-bit Linux VST hosts. The plugin will determine which host +# application to use based on the `.dll` file it's trying to load. This setup is +# necessary until Meson provides a way to have multiple cross-builds for a +# single build directory: https://github.com/mesonbuild/meson/issues/5125 + +# These variables are used to generate a `config.h` file. The library names will +# be prefixed with `lib` and suffixed with `.so`, and the host names will be +# suffixed with `.exe`. +clap_plugin_name = 'yabridge-clap' +vst2_plugin_name = 'yabridge-vst2' +vst3_plugin_name = 'yabridge-vst3' +host_name_64bit = 'yabridge-host' +host_name_32bit = 'yabridge-host-32' + +compiler_options = [ + '-fvisibility=hidden', + '-fvisibility-inlines-hidden', + # We use an intrinsic to force flush-to-zero. SSE2 is always enabled in x86_64 + # CPUs, but when we're compiling the 32-bit bitbridge we need to manually add + # this flag. + '-msse2', + # FIXME: Bitsery relies on the definitions from ``, which is no + # longer included transitively with GCC 13. This should be removed once + # bitsery is updated to support GCC 13. + '-include', + 'cstdint', +] + +chainloader_compiler_options = [ + # We use our process library for sending notifications from the chainloaders, + # but we don't need the Asio pipe support there + '-DWITHOUT_ASIO', +] + +# HACK: Some stuff from `windows.h` that we don't need results in conflicting +# definitions, so we'll try to exclude those bits +wine_compiler_options = [ + '-DNOMINMAX', + # Since Wine 5.12 any use of attributes (like visibility specifiers, or + # calling conventions) in templated member or variable types causes a warning + '-Wno-attributes', + '-Wno-ignored-attributes', + # Winsock conflicts with the Posix sockets API. Before Wine 6.8 there was a + # `WINE_NOWINSOCK` that would exclude just `winsock.h` from `windows.h`, but + # they got rid of that so we now need to explicitly define the ifdef guards + '-D__WINE_WINSOCKAPI_STDLIB_H', + '-D_WINSOCKAPI_', + # This is only relevant for Wine 6.2, but commit + # `0c19e2e487d36a89531daf4897c0b6390d82a843`, broke compilation of + # `shobjidl.h` under C++. + # + # https://bugs.winehq.org/show_bug.cgi?id=50670 + '-D__IFileOperation_INTERFACE_DEFINED__', + # This Wine 6.20 commit `dfdf56fbe47f8ff50ebe533e6d73f2de6546f008` added a + # bunch of new SAL includes to `windows.h`, which include things like `__in` + # and `__out`. This breaks libstdc++ compilation since they often use those + # names for function parameters. + # + # https://bugs.winehq.org/show_bug.cgi?id=51919 + '-D__WINE_SAL_H__', +] + +# NOTE: GCC doesn't 8-byte align doubles in structs on x86 for ABI-compatibilty +# reasons, but MSVC++ does. We need to force this same alignment to be +# ABI-compatible with 32-bit binaries created with MSVC++ on Windows. +wine_32bit_compiler_options = wine_compiler_options + ['-m32', '-malign-double'] +wine_64bit_compiler_options = wine_compiler_options + ['-m64'] + +# Enable addition assertions on the STL containers during debug builds. Meson +# has a `cpp_debugstl` option, but it's nicer having this automatically tied to +# debug builds. +if get_option('buildtype') == 'debug' + compiler_options += ['-D_GLIBCXX_DEBUG'] +endif + +if with_bitbridge + compiler_options += '-DWITH_BITBRIDGE' +endif + +if with_clap + compiler_options += '-DWITH_CLAP' +endif + +# This provides an easy way to start the Wine plugin host using winedbg since it +# can be quite a pain to set up +if with_winedbg + compiler_options += '-DWITH_WINEDBG' +endif + +if with_vst3 + compiler_options += '-DWITH_VST3' +endif + +# +# Wine checks +# + +# Meson does not let us set a default cross compiler, which makes sense, but it +# also means that it's easy to forget. This will cause the setup process to +# abort if no cross compiler has been set up. +winelib_check = '''#ifndef __WINE__ +#error 1 +#endif''' +if not meson.get_compiler('cpp').compiles(winelib_check) + error('You need to set up a cross compiler, check the README for compilation instructions.') +endif + +# Wine versions after Wine 5.6 and before 6.0 require a `__cdecl` calling +# convention to be specified on the `main()` functions or else `argc` and `argv` +# will point to the wrong memory. Similarly, with other versions of Wine this +# should _not_ be specified for the same reason. We'll try to figure out the +# current Wine version and add this calling convention based on that. Also, +# printing the configure-time Wine version might be useful in diagnosing build +# issues so we'll do just that. +# +# https://bugs.winehq.org/show_bug.cgi?id=49138 +wine_version = run_command( + 'sh', '-c', '''wine --version | grep --only-matching -E '[0-9]+\.[0-9]+(-?rc[0-9]+)?' | head -n1''', + check : false +) +if wine_version.returncode() == 0 + wine_version = wine_version.stdout() + message('Targetting Wine @0@'.format(wine_version)) + + # Wine versions below 5.7 will segfault in `CoCreateGuid` which gets called + # during static initialization. I'm not exactly sure why this is happening, + # but to prevent this from causing more headaches and confusion in the future + # we should just immediately error out when building yabridge's VST3 support + # with these older Wine versions. + if wine_version.version_compare('<5.7') and with_vst3 + error('Because of a bug in Wine < 5.7\n' + + 'you cannot build yabridge with VST3 support using these older Wine versions.\n' + + 'Use the \'-Dvst3=false\' build option to disable VST3 support.\n\n' + + 'https://github.com/robbert-vdh/yabridge/issues/63#issuecomment-757369645') + endif + # This version of yabridge will not work when built against Wine 7.21, 7.22, + # or 8.0-rc1 because of https://bugs.winehq.org/show_bug.cgi?id=53912. We'll + # outright prevent building yabridge with these versions to avoid broken + # yabridge builds. If anyone's reading this because you ran into the error + # below, either build with Wine 8.0-rc2+, or stick with yabridge 5.0.2 if + # you're stuck with Wine 7.22. + # NOTE: Meson considers 8.0 to be below 8.0rc2, so this third check is also + # needed + if wine_version.version_compare('>=7.21') and \ + wine_version.version_compare('<8.0rc2') and \ + wine_version.version_compare('!=8.0') + error('Building this version of yabridge against Wine ' + wine_version + + 'would result in nonfunctional binaries. Either build yabridge 5.0.2 ' + + 'with Wine 7.22, or switch to Wine 8.0-rc2+. Yabridge built with 8.0-rc2+ ' + + 'will also work with older Wine versions, but yabridge built against older ' + + 'Wine versions will not work with Wine 7.21+.\n\n' + + 'https://bugs.winehq.org/show_bug.cgi?id=53912') + endif + + if wine_version.version_compare('>=5.7') and \ + wine_version.version_compare('<6.0') + message('- Using the cdecl calling convention') + compiler_options += '-DWINE_USE_CDECL' + endif + if wine_version.version_compare('<6.23') and with_winedbg + message('- Using legacy winedbg argument quoting') + compiler_options += '-DWINEDBG_LEGACY_ARGUMENT_QUOTING' + endif +else + warning('Unable to determine the current Wine version') +endif + +# +# Dependencies +# + +include_dir = include_directories('src/include', is_system : true) + +# These dependencies require separate linking flags for the 32-bit and 64-bit +# versions + +# I honestly have no idea what the correct way is to have `dependency()` or +# `compiler.find_dependency()` search for 32-bit versions of libraries when +# cross-compiling. Meson also doesn't seem to respect the default linker +# search path set by the system in `find_library()`. If anyone does know how +# to properly do this, please let me know! +winegcc = meson.get_compiler('cpp', native : false) + +if is_64bit_system + xcb_64bit_dep = dependency('xcb') +endif +if with_bitbridge + xcb_32bit_dep = winegcc.find_library('xcb', dirs: ['@libxcb32@/lib']) +endif + +# These are all headers-only libraries, and thus won't require separate 32-bit +# and 64-bit versions + +asio_dep = dependency('asio', version : '>=1.28.0') + +if meson.version().version_compare('>=0.60') + # Bitsery's CMake build definition is capitalized for some reason + bitsery_dep = dependency('bitsery', 'Bitsery', version : '>=5.2.0') +else + # Mmeson <=0.6.0 didn't support multiple names for a dependency, and since at + # the moment this is only relevant for packing on Arch btw, it's probably + # better to remove this conditional later than it is to bump the minimum Meson + # version now. + bitsery_dep = dependency('bitsery', version : '>=5.2.0') +endif + +# The D-Bus headers are also only accessed through the include path. We don't +# link to libdbus-1 to make soname changes don't completely break yabridge. +dbus_dep = dependency('dbus-1').partial_dependency(compile_args : true, includes : true) +function2_dep = dependency('function2', version : '>=4.0.0') +ghc_filesystem_dep = dependency('ghc_filesystem', modules : 'ghcFilesystem::ghc_filesystem', version : '>=1.5.0') +threads_dep = dependency('threads') +# Tomlplusplus recently added a shraed library version. We don't want to link to +# that. `compile_library` is deprecated but it (incorrectly) defaults to `true` +# so we can't omit it. +tomlplusplus_dep = dependency('tomlplusplus', version : '>=3.4.0', default_options : ['compile_library=false']).partial_dependency(compile_args : true, includes : true) + +dl_dep = declare_dependency(link_args : '-ldl') +rt_dep = declare_dependency(link_args : '-lrt') + +wine_ole32_dep = declare_dependency(link_args : '-lole32') +# The SDK includes a comment pragma that would link to this on MSVC +wine_shell32_dep = declare_dependency(link_args : '-lshell32') +# The built in threads dependency does not know how to handle winegcc +wine_threads_dep = declare_dependency(link_args : '-lpthread') +wine_uuid_dep = declare_dependency(link_args : '-luuid') + +if with_clap + clap_dep = dependency('clap', version : ['>=1.1.7', '<1.2']) +endif + +# We need to build the VST3 SDK dependencies in tree because Meson won't let us +# build both native, 32-bit cross compiled and 64-bit cross compiled +# dependencies from a (CMake) subproject +if with_vst3 + subdir('src/common/vst3') +endif + +# +# Binaries +# +# The application consists of a plugin (`libyabridge-{clap,vst2,vst3}.so`) that calls +# a Winelib application (`yabridge-host{,-32}.exe`) that can host Windows VST2 +# and VST3 plugins. These plugins can in turn be loaded from small stub +# libraries dubbed chainloaders to avoid having to copy large plugin libraries +# around. More information about the way these two components work together can +# be found in `docs/architecture.md`. +# + +# Generate header files for configuration variables such as the current git tag +# and the name of the host binary +subdir('src/common/config') + +# These only contain the definitions for sources and dependencies. It would be +# nice to define the libraries and executables inside of these meson.build +# files, but that will also scatter the build artifacts around in the `build/` +# directory and it's much more convenient having all of the important files +# directory under `build/`. +# https://github.com/mesonbuild/meson/pull/4037 +subdir('src/chainloader') +subdir('src/plugin') +subdir('src/wine-host') + +shared_library( + vst2_plugin_name, + vst2_plugin_sources, + native : true, + include_directories : include_dir, + dependencies : vst2_plugin_deps, + # NOTE: LTO does not support Winelibs, and it seems to break + # `libyabridge-vst2.so` in Bitwig for some reason. It should be left + # turned off for the time being except for on the chainloader + # libraries. + cpp_args : compiler_options, +) +shared_library( + 'yabridge-chainloader-vst2', + vst2_chainloader_sources, + native : true, + dependencies : chainloader_deps, + cpp_args : compiler_options + chainloader_compiler_options, + # LTO is useful here to get rid of unused code + override_options : ['b_lto=true'], +) + +if with_clap + # This is the CLAP equivalent of `libyabridge-vst2.so`. The Wine host + # applications can handle VST2, VST3, and CLAP plugins. + shared_library( + clap_plugin_name, + clap_plugin_sources, + native : true, + include_directories : include_dir, + dependencies : clap_plugin_deps, + cpp_args : compiler_options, + ) + shared_library( + 'yabridge-chainloader-clap', + clap_chainloader_sources, + native : true, + dependencies : clap_chainloader_deps, + cpp_args : compiler_options + chainloader_compiler_options, + # See above + override_options : ['b_lto=true'], + ) +endif + +if with_vst3 + # This is the VST3 equivalent of `libyabridge-vst2.so`. The Wine host + # applications can handle both VST2, VST3 and CLAP plugins. + shared_library( + vst3_plugin_name, + vst3_plugin_sources, + native : true, + include_directories : include_dir, + dependencies : vst3_plugin_deps, + cpp_args : compiler_options, + ) + shared_library( + 'yabridge-chainloader-vst3', + vst3_chainloader_sources, + native : true, + dependencies : chainloader_deps, + cpp_args : compiler_options + chainloader_compiler_options, + # See above + override_options : ['b_lto=true'], + ) +endif + +if is_64bit_system + executable( + host_name_64bit, + host_sources, + native : false, + include_directories : include_dir, + dependencies : host_64bit_deps, + cpp_args : compiler_options + wine_64bit_compiler_options, + link_args : ['-m64'], + ) +endif + +if with_bitbridge + executable( + host_name_32bit, + host_sources, + native : false, + include_directories : include_dir, + dependencies : host_32bit_deps, + cpp_args : compiler_options + wine_32bit_compiler_options, + link_args : ['-m32'], + ) +endif diff --git a/overlays/meson.build b/overlays/meson.build new file mode 100644 index 0000000..12ecb97 --- /dev/null +++ b/overlays/meson.build @@ -0,0 +1,385 @@ +project( + 'yabridge', + 'cpp', + version : '5.1.1', + meson_version : '>=0.56', + default_options : [ + 'warning_level=3', + 'cpp_std=c++2a', + # Even though Meson will complain that this option does not exist, without + # this Meson will not apply the above option to native targets + 'build.cpp_std=c++2a', + ], +) + +# +# Build options +# + +# In theory yabridge should compile fine on a 32-bit system, but you will always +# need to pass `-Dbitbridge=true`. We just make sure that we won't build +# any 64-bit binaries in that situation. +is_64bit_system = build_machine.cpu_family() not in ['x86', 'arm'] +with_bitbridge = get_option('bitbridge') +with_clap = get_option('clap') +with_winedbg = get_option('winedbg') +with_vst3 = get_option('vst3') + +# +# Compiler flags +# + +# Depending on the `bitbridge` flag we'll enable building secondary 32-bit +# host applications that can act as a bit bridge for using 32-bit Windows +# plugins in 64-bit Linux VST hosts. The plugin will determine which host +# application to use based on the `.dll` file it's trying to load. This setup is +# necessary until Meson provides a way to have multiple cross-builds for a +# single build directory: https://github.com/mesonbuild/meson/issues/5125 + +# These variables are used to generate a `config.h` file. The library names will +# be prefixed with `lib` and suffixed with `.so`, and the host names will be +# suffixed with `.exe`. +clap_plugin_name = 'yabridge-clap' +vst2_plugin_name = 'yabridge-vst2' +vst3_plugin_name = 'yabridge-vst3' +host_name_64bit = 'yabridge-host' +host_name_32bit = 'yabridge-host-32' + +compiler_options = [ + '-fvisibility=hidden', + '-fvisibility-inlines-hidden', + # We use an intrinsic to force flush-to-zero. SSE2 is always enabled in x86_64 + # CPUs, but when we're compiling the 32-bit bitbridge we need to manually add + # this flag. + '-msse2', + # FIXME: Bitsery relies on the definitions from ``, which is no + # longer included transitively with GCC 13. This should be removed once + # bitsery is updated to support GCC 13. + '-include', + 'cstdint', +] + +chainloader_compiler_options = [ + # We use our process library for sending notifications from the chainloaders, + # but we don't need the Asio pipe support there + '-DWITHOUT_ASIO', +] + +# HACK: Some stuff from `windows.h` that we don't need results in conflicting +# definitions, so we'll try to exclude those bits +wine_compiler_options = [ + '-DNOMINMAX', + # Since Wine 5.12 any use of attributes (like visibility specifiers, or + # calling conventions) in templated member or variable types causes a warning + '-Wno-attributes', + '-Wno-ignored-attributes', + # Winsock conflicts with the Posix sockets API. Before Wine 6.8 there was a + # `WINE_NOWINSOCK` that would exclude just `winsock.h` from `windows.h`, but + # they got rid of that so we now need to explicitly define the ifdef guards + '-D__WINE_WINSOCKAPI_STDLIB_H', + '-D_WINSOCKAPI_', + # This is only relevant for Wine 6.2, but commit + # `0c19e2e487d36a89531daf4897c0b6390d82a843`, broke compilation of + # `shobjidl.h` under C++. + # + # https://bugs.winehq.org/show_bug.cgi?id=50670 + '-D__IFileOperation_INTERFACE_DEFINED__', + # This Wine 6.20 commit `dfdf56fbe47f8ff50ebe533e6d73f2de6546f008` added a + # bunch of new SAL includes to `windows.h`, which include things like `__in` + # and `__out`. This breaks libstdc++ compilation since they often use those + # names for function parameters. + # + # https://bugs.winehq.org/show_bug.cgi?id=51919 + '-D__WINE_SAL_H__', +] + +# NOTE: GCC doesn't 8-byte align doubles in structs on x86 for ABI-compatibilty +# reasons, but MSVC++ does. We need to force this same alignment to be +# ABI-compatible with 32-bit binaries created with MSVC++ on Windows. +wine_32bit_compiler_options = wine_compiler_options + ['-m32', '-malign-double'] +wine_64bit_compiler_options = wine_compiler_options + ['-m64'] + +# Enable addition assertions on the STL containers during debug builds. Meson +# has a `cpp_debugstl` option, but it's nicer having this automatically tied to +# debug builds. +if get_option('buildtype') == 'debug' + compiler_options += ['-D_GLIBCXX_DEBUG'] +endif + +if with_bitbridge + compiler_options += '-DWITH_BITBRIDGE' +endif + +if with_clap + compiler_options += '-DWITH_CLAP' +endif + +# This provides an easy way to start the Wine plugin host using winedbg since it +# can be quite a pain to set up +if with_winedbg + compiler_options += '-DWITH_WINEDBG' +endif + +if with_vst3 + compiler_options += '-DWITH_VST3' +endif + +# +# Wine checks +# + +# Meson does not let us set a default cross compiler, which makes sense, but it +# also means that it's easy to forget. This will cause the setup process to +# abort if no cross compiler has been set up. +winelib_check = '''#ifndef __WINE__ +#error 1 +#endif''' +if not meson.get_compiler('cpp').compiles(winelib_check) + error('You need to set up a cross compiler, check the README for compilation instructions.') +endif + +# Wine versions after Wine 5.6 and before 6.0 require a `__cdecl` calling +# convention to be specified on the `main()` functions or else `argc` and `argv` +# will point to the wrong memory. Similarly, with other versions of Wine this +# should _not_ be specified for the same reason. We'll try to figure out the +# current Wine version and add this calling convention based on that. Also, +# printing the configure-time Wine version might be useful in diagnosing build +# issues so we'll do just that. +# +# https://bugs.winehq.org/show_bug.cgi?id=49138 +wine_version = run_command( + 'sh', '-c', '''wine --version | grep --only-matching -E '[0-9]+\.[0-9]+(-?rc[0-9]+)?' | head -n1''', + check : false +) +if wine_version.returncode() == 0 + wine_version = wine_version.stdout() + message('Targetting Wine @0@'.format(wine_version)) + + # Wine versions below 5.7 will segfault in `CoCreateGuid` which gets called + # during static initialization. I'm not exactly sure why this is happening, + # but to prevent this from causing more headaches and confusion in the future + # we should just immediately error out when building yabridge's VST3 support + # with these older Wine versions. + if wine_version.version_compare('<5.7') and with_vst3 + error('Because of a bug in Wine < 5.7\n' + + 'you cannot build yabridge with VST3 support using these older Wine versions.\n' + + 'Use the \'-Dvst3=false\' build option to disable VST3 support.\n\n' + + 'https://github.com/robbert-vdh/yabridge/issues/63#issuecomment-757369645') + endif + # This version of yabridge will not work when built against Wine 7.21, 7.22, + # or 8.0-rc1 because of https://bugs.winehq.org/show_bug.cgi?id=53912. We'll + # outright prevent building yabridge with these versions to avoid broken + # yabridge builds. If anyone's reading this because you ran into the error + # below, either build with Wine 8.0-rc2+, or stick with yabridge 5.0.2 if + # you're stuck with Wine 7.22. + # NOTE: Meson considers 8.0 to be below 8.0rc2, so this third check is also + # needed + if wine_version.version_compare('>=7.21') and \ + wine_version.version_compare('<8.0rc2') and \ + wine_version.version_compare('!=8.0') + error('Building this version of yabridge against Wine ' + wine_version + + 'would result in nonfunctional binaries. Either build yabridge 5.0.2 ' + + 'with Wine 7.22, or switch to Wine 8.0-rc2+. Yabridge built with 8.0-rc2+ ' + + 'will also work with older Wine versions, but yabridge built against older ' + + 'Wine versions will not work with Wine 7.21+.\n\n' + + 'https://bugs.winehq.org/show_bug.cgi?id=53912') + endif + + if wine_version.version_compare('>=5.7') and \ + wine_version.version_compare('<6.0') + message('- Using the cdecl calling convention') + compiler_options += '-DWINE_USE_CDECL' + endif + if wine_version.version_compare('<6.23') and with_winedbg + message('- Using legacy winedbg argument quoting') + compiler_options += '-DWINEDBG_LEGACY_ARGUMENT_QUOTING' + endif +else + warning('Unable to determine the current Wine version') +endif + +# +# Dependencies +# + +include_dir = include_directories('src/include', is_system : true) + +# These dependencies require separate linking flags for the 32-bit and 64-bit +# versions + +# I honestly have no idea what the correct way is to have `dependency()` or +# `compiler.find_dependency()` search for 32-bit versions of libraries when +# cross-compiling. Meson also doesn't seem to respect the default linker +# search path set by the system in `find_library()`. If anyone does know how +# to properly do this, please let me know! +winegcc = meson.get_compiler('cpp', native : false) + +if is_64bit_system + xcb_64bit_dep = dependency('xcb') +endif +if with_bitbridge + xcb_32bit_dep = winegcc.find_library('xcb') +endif + +# These are all headers-only libraries, and thus won't require separate 32-bit +# and 64-bit versions + +asio_dep = dependency('asio', version : '>=1.28.0') + +if meson.version().version_compare('>=0.60') + # Bitsery's CMake build definition is capitalized for some reason + bitsery_dep = dependency('bitsery', 'Bitsery', version : '>=5.2.0') +else + # Mmeson <=0.6.0 didn't support multiple names for a dependency, and since at + # the moment this is only relevant for packing on Arch btw, it's probably + # better to remove this conditional later than it is to bump the minimum Meson + # version now. + bitsery_dep = dependency('bitsery', version : '>=5.2.0') +endif + +# The D-Bus headers are also only accessed through the include path. We don't +# link to libdbus-1 to make soname changes don't completely break yabridge. +dbus_dep = dependency('dbus-1').partial_dependency(compile_args : true, includes : true) +function2_dep = dependency('function2', version : '>=4.0.0') +ghc_filesystem_dep = dependency('ghc_filesystem', modules : 'ghcFilesystem::ghc_filesystem', version : '>=1.5.0') +threads_dep = dependency('threads') +# Tomlplusplus recently added a shraed library version. We don't want to link to +# that. `compile_library` is deprecated but it (incorrectly) defaults to `true` +# so we can't omit it. +tomlplusplus_dep = dependency('tomlplusplus', version : '>=3.4.0', default_options : ['compile_library=false']).partial_dependency(compile_args : true, includes : true) + +dl_dep = declare_dependency(link_args : '-ldl') +rt_dep = declare_dependency(link_args : '-lrt') + +wine_ole32_dep = declare_dependency(link_args : '-lole32') +# The SDK includes a comment pragma that would link to this on MSVC +wine_shell32_dep = declare_dependency(link_args : '-lshell32') +# The built in threads dependency does not know how to handle winegcc +wine_threads_dep = declare_dependency(link_args : '-lpthread') +wine_uuid_dep = declare_dependency(link_args : '-luuid') + +if with_clap + clap_dep = dependency('clap', version : ['>=1.1.7', '<1.2']) +endif + +# We need to build the VST3 SDK dependencies in tree because Meson won't let us +# build both native, 32-bit cross compiled and 64-bit cross compiled +# dependencies from a (CMake) subproject +if with_vst3 + subdir('src/common/vst3') +endif + +# +# Binaries +# +# The application consists of a plugin (`libyabridge-{clap,vst2,vst3}.so`) that calls +# a Winelib application (`yabridge-host{,-32}.exe`) that can host Windows VST2 +# and VST3 plugins. These plugins can in turn be loaded from small stub +# libraries dubbed chainloaders to avoid having to copy large plugin libraries +# around. More information about the way these two components work together can +# be found in `docs/architecture.md`. +# + +# Generate header files for configuration variables such as the current git tag +# and the name of the host binary +subdir('src/common/config') + +# These only contain the definitions for sources and dependencies. It would be +# nice to define the libraries and executables inside of these meson.build +# files, but that will also scatter the build artifacts around in the `build/` +# directory and it's much more convenient having all of the important files +# directory under `build/`. +# https://github.com/mesonbuild/meson/pull/4037 +subdir('src/chainloader') +subdir('src/plugin') +subdir('src/wine-host') + +shared_library( + vst2_plugin_name, + vst2_plugin_sources, + native : true, + include_directories : include_dir, + dependencies : vst2_plugin_deps, + # NOTE: LTO does not support Winelibs, and it seems to break + # `libyabridge-vst2.so` in Bitwig for some reason. It should be left + # turned off for the time being except for on the chainloader + # libraries. + cpp_args : compiler_options, +) +shared_library( + 'yabridge-chainloader-vst2', + vst2_chainloader_sources, + native : true, + dependencies : chainloader_deps, + cpp_args : compiler_options + chainloader_compiler_options, + # LTO is useful here to get rid of unused code + override_options : ['b_lto=true'], +) + +if with_clap + # This is the CLAP equivalent of `libyabridge-vst2.so`. The Wine host + # applications can handle VST2, VST3, and CLAP plugins. + shared_library( + clap_plugin_name, + clap_plugin_sources, + native : true, + include_directories : include_dir, + dependencies : clap_plugin_deps, + cpp_args : compiler_options, + ) + shared_library( + 'yabridge-chainloader-clap', + clap_chainloader_sources, + native : true, + dependencies : clap_chainloader_deps, + cpp_args : compiler_options + chainloader_compiler_options, + # See above + override_options : ['b_lto=true'], + ) +endif + +if with_vst3 + # This is the VST3 equivalent of `libyabridge-vst2.so`. The Wine host + # applications can handle both VST2, VST3 and CLAP plugins. + shared_library( + vst3_plugin_name, + vst3_plugin_sources, + native : true, + include_directories : include_dir, + dependencies : vst3_plugin_deps, + cpp_args : compiler_options, + ) + shared_library( + 'yabridge-chainloader-vst3', + vst3_chainloader_sources, + native : true, + dependencies : chainloader_deps, + cpp_args : compiler_options + chainloader_compiler_options, + # See above + override_options : ['b_lto=true'], + ) +endif + +if is_64bit_system + executable( + host_name_64bit, + host_sources, + native : false, + include_directories : include_dir, + dependencies : host_64bit_deps, + cpp_args : compiler_options + wine_64bit_compiler_options, + link_args : ['-m64'], + ) +endif + +if with_bitbridge + executable( + host_name_32bit, + host_sources, + native : false, + include_directories : include_dir, + dependencies : host_32bit_deps, + cpp_args : compiler_options + wine_32bit_compiler_options, + link_args : ['-m32'], + ) +endif diff --git a/overlays/wine-6006.patch b/overlays/wine-6006.patch new file mode 100644 index 0000000..88d20ca --- /dev/null +++ b/overlays/wine-6006.patch @@ -0,0 +1,173 @@ +From 23a6646d697ddfc51e3deab018853a22376dacdf Mon Sep 17 00:00:00 2001 +From: Grazvydas Ignotas +Date: Sun, 7 Jul 2024 00:11:26 +0300 +Subject: [PATCH 1/2] dwmapi: Drop unnecessary static, improve variable names. + +--- + dlls/dwmapi/dwmapi_main.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index adc02552ba7..8d4ccbb1fc4 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -184,9 +184,9 @@ HRESULT WINAPI DwmEnableBlurBehindWindow(HWND hWnd, const DWM_BLURBEHIND *pBlurB + */ + BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) + { +- static int i; ++ static BOOL once; + +- if (!i++) FIXME("stub\n"); ++ if (!once++) FIXME("stub\n"); + + return FALSE; + } +@@ -273,7 +273,8 @@ static int get_display_frequency(void) + HRESULT WINAPI DwmGetCompositionTimingInfo(HWND hwnd, DWM_TIMING_INFO *info) + { + LARGE_INTEGER performance_frequency, qpc; +- static int i, display_frequency; ++ int display_frequency; ++ static BOOL once; + + if (!info) + return E_INVALIDARG; +@@ -281,7 +282,7 @@ HRESULT WINAPI DwmGetCompositionTimingInfo(HWND hwnd, DWM_TIMING_INFO *info) + if (info->cbSize != sizeof(DWM_TIMING_INFO)) + return MILERR_MISMATCHED_SIZE; + +- if(!i++) FIXME("(%p %p)\n", hwnd, info); ++ if (!once++) FIXME("(%p %p)\n", hwnd, info); + + memset(info, 0, info->cbSize); + info->cbSize = sizeof(DWM_TIMING_INFO); +-- +GitLab + + +From 9bd6b35aa8d9b92593db1722ce533e0e4d8985ad Mon Sep 17 00:00:00 2001 +From: Grazvydas Ignotas +Date: Sun, 7 Jul 2024 00:18:40 +0300 +Subject: [PATCH 2/2] dwmapi: Simulate DwmFlush blocking behavior. + +The documentation states it's a blocking call. There are reports of it +being used for WaitForVBlank-like purposes [1]. Without blocking Softube +VST plugin logic breaks and they never update their UIs. + +[1] https://news.ycombinator.com/item?id=34501612 + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56935 +--- + dlls/dwmapi/dwmapi_main.c | 23 +++++++++++++++++++-- + dlls/dwmapi/tests/dwmapi.c | 41 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 62 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 8d4ccbb1fc4..419216534aa 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -32,6 +32,7 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(dwmapi); + ++static int get_display_frequency(void); + + /********************************************************************** + * DwmIsCompositionEnabled (DWMAPI.@) +@@ -88,9 +89,26 @@ HRESULT WINAPI DwmGetColorizationColor(DWORD *colorization, BOOL *opaque_blend) + */ + HRESULT WINAPI DwmFlush(void) + { ++ static volatile LONG last_time; + static BOOL once; ++ DWORD now, interval, last = last_time, target; ++ int freq; + +- if (!once++) FIXME("() stub\n"); ++ if (!once++) FIXME("() semi-stub\n"); ++ ++ // simulate the WaitForVBlank-like blocking behavior ++ freq = get_display_frequency(); ++ interval = 1000 / freq; ++ now = GetTickCount(); ++ if (now - last < interval) ++ target = last + interval; ++ else ++ { ++ // act as if we were called midway between 2 vsyncs ++ target = now + interval / 2; ++ } ++ Sleep(target - now); ++ InterlockedCompareExchange(&last_time, target, last); + + return S_OK; + } +@@ -262,7 +280,8 @@ static int get_display_frequency(void) + } + else + { +- WARN("Failed to query display frequency, returning a fallback value.\n"); ++ static BOOL once; ++ if (!once++) WARN("Failed to query display frequency, returning a fallback value.\n"); + return 60; + } + } +diff --git a/dlls/dwmapi/tests/dwmapi.c b/dlls/dwmapi/tests/dwmapi.c +index a89a1fd705b..20628578f41 100644 +--- a/dlls/dwmapi/tests/dwmapi.c ++++ b/dlls/dwmapi/tests/dwmapi.c +@@ -140,9 +140,50 @@ cleanup: + DestroyWindow(hwnd); + } + ++static void test_DwmFlush(void) ++{ ++ LARGE_INTEGER frequency, ts[2]; ++ int i, result, ms; ++ DEVMODEA mode; ++ BOOL enabled; ++ HRESULT hr; ++ ++ enabled = FALSE; ++ hr = DwmIsCompositionEnabled(&enabled); ++ ok(hr == S_OK, "Got hr %#lx.\n", hr); ++ if (!enabled) ++ { ++ skip("DWM is disabled.\n"); ++ return; ++ } ++ ++ memset(&mode, 0, sizeof(mode)); ++ mode.dmSize = sizeof(mode); ++ result = EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &mode); ++ ok(result, "Failed to get display mode %#lx.\n", GetLastError()); ++ ok(mode.dmDisplayFrequency != 0, "dmDisplayFrequency is 0.\n"); ++ ++ result = QueryPerformanceFrequency(&frequency); ++ ok(result, "Failed to get performance counter frequency.\n"); ++ ++ result = QueryPerformanceCounter(&ts[0]); ++ ok(result, "Failed to read performance counter.\n"); ++ for (i = 0; i < 2; i++) ++ { ++ hr = DwmFlush(); ++ ok(hr == S_OK, "Got hr %#lx.\n", hr); ++ } ++ result = QueryPerformanceCounter(&ts[1]); ++ ok(result, "Failed to read performance counter.\n"); ++ ms = (ts[1].QuadPart - ts[0].QuadPart) * 1000 / frequency.QuadPart; ++ ok(ms >= 1000 / mode.dmDisplayFrequency, ++ "DwmFlush() took %dms with dmDisplayFrequency %ld.\n", ms, mode.dmDisplayFrequency); ++} ++ + START_TEST(dwmapi) + { + test_DwmIsCompositionEnabled(); + test_DwmGetCompositionTimingInfo(); + test_DWMWA_EXTENDED_FRAME_BOUNDS(); ++ test_DwmFlush(); + } +-- +GitLab + diff --git a/overlays/yabridge.nix b/overlays/yabridge.nix new file mode 100644 index 0000000..d257905 --- /dev/null +++ b/overlays/yabridge.nix @@ -0,0 +1,36 @@ + +final: prev: { + yabridge = prev.yabridge.overrideAttrs (old: { + src = prev.fetchFromGitHub { + owner = "robbert-vdh"; + repo = "yabridge"; + rev = "HEAD"; + hash = "sha256-pxQ+B4WF9iWNeLholSSqSMLJ3lz/Ub/PfgkAuacUAqc="; + }; + + patches = [ + # Hard code bitbridge & runtime dependencies + (prev.replaceVars ./hardcode-dependencies.patch { + libdbus = prev.pkgs.dbus.lib; + libxcb32 = prev.pkgsi686Linux.xorg.libxcb; + # inherit prev.wine; + }) + + # Patch the chainloader to search for libyabridge through NIX_PROFILES + ./libyabridge-from-nix-profiles.patch + ]; + + }); + + yabridgectl = prev.yabridge.overrideAttrs (old: { + cargoHash = ""; + }); + + wineWowPackages.stagingFull = prev.pkgs-stable.wineWowPackages.stagingFull.overrideAttrs + (old: { + patches = [ ./wine-6006.patch ]; + waylandSupport = true; + fontconfigSupport = true; + vulkanSupport = true; + }); +}