Skip to content

build: stop enabling Asio separate compilation without an implementation - #928

Open
jcelerier wants to merge 1 commit into
masterfrom
fix/asio-mingw-separate-compilation
Open

build: stop enabling Asio separate compilation without an implementation#928
jcelerier wants to merge 1 commit into
masterfrom
fix/asio-mingw-separate-compilation

Conversation

@jcelerier

Copy link
Copy Markdown
Member

On Windows, a shared build defined BOOST_ASIO_DYN_LINK=1 for every compiler. That macro, like BOOST_ASIO_SEPARATE_COMPILATION, switches off Asio's header-only default, so the separately-compiled part of Asio has to come from somewhere. It only ever did on MSVC: src/ossia/context.cpp includes <boost/asio/impl/src.hpp> under _MSC_VER.

With mingw-w64 the macro was therefore switched on while no translation unit compiled the implementation, and every non-inline Asio entry point went undefined.

The chain, from Asio's own headers

  1. BOOST_ASIO_DYN_LINK defined ⇒ BOOST_ASIO_HEADER_ONLY not defined — detail/config.hpp:38-44, "a DLL/shared library implies separate compilation".
  2. Every header gates its implementation include on that macro, e.g. io_context.hpp:1356-1358:
    #if defined(BOOST_ASIO_HEADER_ONLY) # include <boost/asio/impl/io_context.ipp> #endif. With it undefined the .ipp files are never included anywhere.
  3. detail/impl/winsock_init.ipp:34 defines winsock_init_base::startup — exactly the symbol both linkers report undefined.

Note this is not "declarations became dllimport": that branch is _MSC_VER || __BORLANDC__ || __CODEGEARC__ only (config.hpp:50), so on MinGW BOOST_ASIO_DECL expands to nothing at all.

Boost 1.91 only made it legible — BOOST_ASIO_ENABLE_VERSION_NAMESPACE puts the missing symbols in an inline namespace tagged with the Asio configuration, so the diagnostics name boost::asio::v103801_bdemn:: rather than boost::asio::. The tag matches on both sides of the link: the symbols are missing, not mismatched.

The fix

Move BOOST_ASIO_DYN_LINK into the if(MSVC) branch next to BOOST_ASIO_SEPARATE_COMPILATION, so the macros and the src.hpp include agree. Non-MSVC Windows returns to Asio's header-only default; MSVC is unchanged.

Why it went unnoticed

The block dates from 41bfc692a ("Fix boost.asio separate compilation only working on windows"), the same commit that narrowed the src.hpp include to _MSC_VER. It only shows up in a from-scratch build dir with dynamic plugins: libossia's sources change rarely enough that ninja does not relink the DLL in an incremental tree.

Validation

Ten from-scratch configurations, three OSes. DYN_LINK is grep -c BOOST_ASIO_DYN_LINK build.ninja — the macro as the generator actually emitted it.

# toolchain mode patched DYN_LINK artifact undefined boost::asio result
1 MSYS2 UCRT64 gcc 16.2 dynamic no 1490 none 16467 FAIL
2 MSYS2 UCRT64 gcc 16.2 dynamic yes 0 libossia_x64.dll 0 PASS
3 MSYS2 UCRT64 gcc 16.2 static yes 0 libossia_x64.a 0 PASS
4 MSYS2 CLANG64 clang 22.1 dynamic no 1380 none 20 FAIL
5 MSYS2 CLANG64 clang 22.1 dynamic yes 0 libossia_x64.dll 0 PASS
6 MSYS2 CLANG64 clang 22.1 static yes 0 libossia_x64.a 0 PASS
7 Linux gcc, Qt 6.12 shared yes n/a libossia.so 0 PASS
8 Linux gcc, Qt 6.12 static yes n/a libossia.a 0 PASS
9 macOS 26 arm64 shared yes n/a libossia.dylib 0 PASS
10 macOS 26 arm64 static yes n/a libossia.a 0 PASS

Both mingw toolchains are affected, not just gcc — rows 1 and 4 are the negative controls. The differing counts are reporting granularity, not severity: GNU ld reports every reference site, ld.lld reports each symbol once.

Being explicit about what these rows prove: only rows 1-2 and 4-5 can distinguish patched from unpatched. The static rows are structurally blind (OSSIA_STATIC is ON, and the if(NOT OSSIA_STATIC) guard suppressed the macro in the original code too), and rows 7-10 are blind by construction — if(WIN32) spans lines 45-118 and both hunks fall inside it, so the patch cannot alter a non-Windows configure. Rows 3 and 6-10 are no-regression checks.

Embedding safety unchanged

OSSIA_MAX_ONLY / OSSIA_PD_ONLY both set(OSSIA_STATIC 1) (cmake/OssiaOptions.cmake:141,163), and the block is guarded if(NOT OSSIA_STATIC), so DYN_LINK never fired for the Max/Pd externals before or after. Measured on the harder artifact anyway — the shared libossia_x64.dll: the PE export directory holds 3092 entries (CLANG64) / 3164 (UCRT64) and zero mangle as _ZN5boost4asio. The 131/135 exports containing the substring asio are all ossia::oscquery_asio::*.

On Windows, a shared build defined BOOST_ASIO_DYN_LINK=1 for every
compiler. That macro, like BOOST_ASIO_SEPARATE_COMPILATION, turns off
Asio's header-only default, so the separately compiled part of Asio has
to come from somewhere. It only ever did on MSVC: src/ossia/context.cpp
includes <boost/asio/impl/src.hpp> under _MSC_VER, and so do score's four
puppet executables.

With mingw-w64 the macro was therefore switched on while no translation
unit compiled the implementation, and every Asio entry point that is not
inline went undefined - 16467 references when linking libossia_x64.dll on
MSYS2 UCRT64. Nothing had caught it because the reference build dirs were
incremental, and libossia's sources do not change often enough for ninja
to relink the DLL.

Boost 1.91 only made it legible: BOOST_ASIO_ENABLE_VERSION_NAMESPACE
places the missing symbols in an inline namespace tagged with the Asio
configuration, so the diagnostics name boost::asio::v103801_bdemn::
rather than boost::asio::. The tag matches on both sides of the link -
the symbols are missing, not mismatched.

Move the definition into the MSVC branch next to
BOOST_ASIO_SEPARATE_COMPILATION, so the macros and the src.hpp include
agree. Non-MSVC Windows goes back to Asio's header-only default; MSVC is
unchanged.
@jcelerier
jcelerier deployed to Apple Certificate August 18, 2026 20:44 — with GitHub Actions Active
@jcelerier
jcelerier temporarily deployed to Apple Certificate August 18, 2026 20:44 — with GitHub Actions Inactive
@jcelerier
jcelerier temporarily deployed to Apple Certificate August 18, 2026 20:44 — with GitHub Actions Inactive
@jcelerier
jcelerier temporarily deployed to Apple Certificate August 18, 2026 20:44 — with GitHub Actions Inactive
@jcelerier
jcelerier temporarily deployed to Apple Certificate August 18, 2026 20:44 — with GitHub Actions Inactive
jcelerier added a commit to ossia/score that referenced this pull request Aug 19, 2026
Windows shared builds could not link libossia at all: BOOST_ASIO_DYN_LINK was
defined for every Windows compiler while only MSVC compiles the Asio
implementation, so every non-inline Asio entry point went undefined. Both mingw
toolchains were affected -- 16467 undefined references under UCRT64 gcc 16.2,
and ld.lld reporting the same symbols under CLANG64.

Pinning the fix at the root of the stack means every commit in it can be built
on Windows, which is what the per-commit verification walk needs. Upstream as
ossia/libossia#928; the pin moves to the merge commit once that lands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant