cmake: bump MIN_WX_VERSION to 3.2.0 and retire legacy wx.cmake - #459
Conversation
Every current-stable Linux distro ships wx 3.2: Ubuntu 24.04 LTS
3.2.4, Debian 13 3.2.8, Fedora 42/43 3.2.8, Arch 3.2.10, openSUSE
Tumbleweed 3.2.8. configure.ac has also enforced wx >= 3.2 for a
while (the AS_IF on "$WX_VERSION_MAJOR""$WX_VERSION_MINOR" -lt 32
next to WX_CONFIG_CHECK), so the 2.8.12 floor in CMakeLists.txt and
in the WX_CONFIG_CHECK line has been nominal. Raising it to 3.2.0
unblocks retiring the custom cmake/wx.cmake wrapper.
* CMakeLists.txt: MIN_WX_VERSION 2.8.12 -> 3.2.0.
* configure.ac: WX_CONFIG_CHECK and the error-message comment bumped
to 3.2.0.
* cmake/wx.cmake: 311-line custom wrapper replaced with an ~90-line
thin shim over stock find_package(wxWidgets). The shim still
exposes wxWidgets::{BASE,CORE,NET,ADV} as INTERFACE IMPORTED
targets so existing target_link_libraries(... wxWidgets::CORE)
call sites across src/ don't change. What's gone:
- the WIN32 AND NOT MINGW MSVC-prebuilt-subfolder detection
(WX_BASE / WX_CORE / WX_ADV / WX_NET user vars);
- the manual wxWidgets_LIBRARIES -l parser that hand-rolled what
stock CMake's FindwxWidgets module already does;
- the wx 3.1.2 ADV-merged-into-CORE detection: since the minimum
is now 3.2.0, ADV is always merged, so 'adv' is not requested
from find_package (but wxWidgets::ADV is still created when
wx_NEED_ADV is set so existing generator expressions in
src/CMakeLists.txt keep resolving);
- the wxUSE_UNICODE CHECK_CXX_SYMBOL_EXISTS probe: wx 3.0+ is
unicode-only, so the probe is always true.
What's kept: the MinGW -DUNICODE / -D_UNICODE propagation
originally added in PR amule-project#457. MSYS2's wx-config points at the
unicode wx build but does not emit those defines, and stock
FindwxWidgets just forwards wx-config's output, so the shim
still has to set them explicitly on MinGW; without this the
MinGW build fails with LoadBitmapA/LPCSTR/LPCTSTR mismatches.
* src/MuleTextCtrl.cpp:100: data.GetTextLength() > 0 becomes
!data.GetText().IsEmpty(). wxTextDataObject::GetTextLength() is
[[deprecated]] in wx 3.3 ("Don't call nor override this
function"); GetText().IsEmpty() has the same semantics and is
supported on every wx version we care about.
* src/utils/fileview/Print.h: drop the now-always-true commented
#if wxCHECK_VERSION(2, 8, 4) dead block.
|
Seems a good starting point. I'm not sure if there's another way of building wx, but afaik wx ships ships a useable config, so why not just using this? That would shrink the file to ~30 lines. And if there's only cmake to get wx in, we maybe should think about retireing autotools at all, so it won't ever get out-of-sync again. |
|
Thanks — on the "~30 lines with wx's config" thought: I looked at it specifically, and unfortunately distro packaging isn't uniform here.
wx ships both an autotools and a CMake build system. The CMake config files ( So A hybrid could work as a follow-up — try On retiring autotools: no strong opinion, but if it's on the cards I can look at it separately — it's a bigger cleanup touching |
I hate it when they do this.
For me it's always on cards, as it's a pita to dig through that stuff. Touching isn't the right word, I would say deleting, but I would first want to hear more oppinions an that. For the debian/ dir, there's a lot of old stuff I didn't maintain for a long time. I need to check if it is useful for cpack, otherwise I would delete it completely. |
|
On
So: delete |
I know, as I was in contact with Sandro when adding this, it was clear that debian won't use it.
That's what I meant with "haven't maintained it a long time". In fact with the switch to qt6/plasma6 plasmamule wasn't useable the old way, that's why I started the cmake-stuff, to get that working again (doesn't work, can be also dropped).
The question wasn't if it used in cpack, as I know that it isn't atm., the question is, when cpack creates .deb can it be useful for that (in the future).
plus plasmamule + maybe some skins that don't work + xas as xchat is also dead + |
Fixes the intermittent startup SIGSEGV tracked in upstream PR amule-project#455. The crash happens on Linux when a startup HTTP download redirects to https://, which wxHTTP cannot speak: wxHTTP::Destroy() schedules the current handler for deferred deletion, the redirect handler throws, and before the deferred delete runs an epoll event fires on the still-registered fd — invoking a method on a socket mid-teardown. Reproducible on stock origin/master (~1-in-27 runs of amuled in a tight startup loop); the crash-hunt.sh script from PR amule-project#455 passes >115 iterations clean on Linux/ARM64 with this rewrite. The fault is inside libwx_baseu_net so it cannot be fixed inside aMule's code paths — the only reliable remedy is to avoid the wxSocket-based HTTP path entirely. The fix is to stop doing HTTP on a worker thread with wxSocket under the hood. CHTTPDownloadThread now: * Inherits wxEvtHandler instead of CMuleThread. No worker thread, no wxSocket, no wxEpollDispatcher on the download path. * Owns a wxWebRequest from wxWebSession::GetDefault(), kicks it off from the constructor on the main thread, binds wxEVT_WEBREQUEST_STATE on the same handler, and lets state transitions drive everything: Active → progress event to the optional dialog, Completed → rename wx's storage temp file into the caller-supplied path, Failed / Cancelled → clean up, Unauthorized → treat as failure. On every terminal state the object posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the existing dispatcher (CamuleApp::OnFinishedHTTPDownload, unchanged) and CallAfter()s its own destruction, so wxWebRequest's internal socket is already torn down by the time the handler dies. * Keeps Create() / Run() / Stop() / OnExit() / Entry() as stubs, so the six call sites (new + Create + Run) compile unchanged. When wx 3.3's wxWebRequestSync becomes a reasonable floor on distro defaults those stubs can be made real and the event-driven plumbing collapsed into a synchronous call on a worker thread with no wxSocket anywhere. Reentrancy: no nested event loops anywhere on the fix path. No ShowModal (the existing progress dialog has always been modeless). No blocking wait on the request. Cancellation via the dialog's Cancel button or the app-wide StopAll() is fire-and-forget — it calls Cancel() on the request, returns immediately, and State_Cancelled arrives later on the main loop. Avoids the class of bug that would come from a "sync wrapper = nested wxEventLoop" trick on wx 3.2. Redirects (including HTTP→HTTPS) are handled by wxWebRequest transparently, which removes the ~110-line recursive GetInputStream redirect handler — the actual seat of the upstream race. Proxy: a static ApplyProxyToDefaultSession() helper maps the existing CProxyData prefs onto wxWebSession::SetProxy, applied from the ctor. wxWebProxy / SetProxy are wx 3.3+ only, so the body is guarded with wxCHECK_VERSION(3,3,0); on wx 3.2 the libcurl backend still honours http_proxy / https_proxy / all_proxy env vars, so env-driven proxy keeps working. The legacy wxHTTP path never honoured aMule's Proxy prefs either (it only called SetProxyMode(bool) with no URL ever registered via wxURL::SetProxy / SetDefaultProxy anywhere in the codebase), so this is not a regression. SOCKS is intentionally skipped even on 3.3+ — wxWebProxy is HTTP-only. A libcurl-direct backend would be needed if SOCKS support matters, out of scope here. macOS App Transport Security opt-out ------------------------------------ wxWebRequest on macOS is backed by NSURLSession, which enforces ATS and blocks plaintext http:// by default. The legacy wxHTTP/wxSocket path bypassed ATS because it used raw BSD sockets; the rewrite inherits NSURLSession's policy instead. Without the opt-out every startup download to an http:// URL fails at the OS level — reproducible on the first server.met update: HTTPDownload.cpp: HTTP download failed for http://upd.emule-security.org/server.met: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection. Inject NSAppTransportSecurity:NSAllowsArbitraryLoads=true via plutil as a POST_BUILD step on the `amule` target in src/CMakeLists.txt. Matches the legacy path's behaviour, not a new policy choice. macOS exit-time crash work-around --------------------------------- wx 3.3.2 has a bug in wxWebSessionURLSession::~wxWebSessionURLSession: it releases the NSURLSession and the delegate separately without first calling -invalidateAndCancel. NSURLSession retains its delegate strongly, so the session's dealloc already drops the delegate ref — wx's subsequent release hits a freed object and the process aborts with "pointer being freed was not allocated". This fires in wx module cleanup / atexit / __cxa_finalize on any Mac build after any HTTP download (version check, server.met, …). By the end of CamuleApp::OnExit we have saved state, joined threads, and flushed logs — nothing aMule-owned remains to clean up. std::_Exit bypasses atexit and static destructors, so the buggy wx dtor never runs and the process terminates cleanly. Linux / Windows continue through wx's normal cleanup; this macOS-only block can be dropped once the upstream wx fix lands in a release we depend on. Dialog / thread lifetime fix ---------------------------- CHTTPDownloadDialog holds a raw pointer (m_owner) to the owning CHTTPDownloadThread. On successful download the ordered flow is: FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app dispatcher, then CallAfter()s `delete this`. Main loop: dialog OnShutdown → Destroy() (queues the dialog in wxPendingDelete), then app dispatcher runs, then CallAfter runs and deletes the thread. Later, idle → DeletePendingObjects runs the dialog dtor — which used to dereference the now-freed m_owner via StopDownload → ~vtable lookup → crash. Null the dialog's m_owner inside OnShutdown, so its dtor (which runs later from wxPendingDelete) no longer touches the already-deleted thread. Equivalent cleanup on the reverse direction (dialog closed before download completes) was already handled via DetachCompanion + Stop in StopDownload. Scope ----- No CMake build-flag changes. wxWebRequest is in wxBase_net since wx 3.2.0 (our floor since PR amule-project#459). * src/HTTPDownload.{h,cpp} — class becomes wxEvtHandler, legacy wxThread-style method stubs with a wx 3.3 / wxWebRequestSync TODO, DetachCompanion() on the owning side + m_owner nulling on the dialog side to make the handshake safe from both directions. * src/amule.cpp — macOS-only std::_Exit at end of OnExit. * src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS opt-out into the CMake-generated Info.plist. * Six call sites (version check, server.met, auto-update, IP filter, nodes.dat, GeoIP) compile unchanged.
Fixes the intermittent startup SIGSEGV tracked in upstream PR amule-project#455. The crash happens on Linux when a startup HTTP download redirects to https://, which wxHTTP cannot speak: wxHTTP::Destroy() schedules the current handler for deferred deletion, the redirect handler throws, and before the deferred delete runs an epoll event fires on the still-registered fd — invoking a method on a socket mid-teardown. Reproducible on stock origin/master (~1-in-27 runs of amuled in a tight startup loop); the crash-hunt.sh script from PR amule-project#455 passes >115 iterations clean on Linux/ARM64 with this rewrite. The fault is inside libwx_baseu_net so it cannot be fixed inside aMule's code paths — the only reliable remedy is to avoid the wxSocket-based HTTP path entirely. The fix is to stop doing HTTP on a worker thread with wxSocket under the hood. CHTTPDownloadThread now: * Inherits wxEvtHandler instead of CMuleThread. No worker thread, no wxSocket, no wxEpollDispatcher on the download path. * Owns a wxWebRequest from wxWebSession::GetDefault(), kicks it off from the constructor on the main thread, binds wxEVT_WEBREQUEST_STATE on the same handler, and lets state transitions drive everything: Active → progress event to the optional dialog, Completed → rename wx's storage temp file into the caller-supplied path, Failed / Cancelled → clean up, Unauthorized → treat as failure. On every terminal state the object posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the existing dispatcher (CamuleApp::OnFinishedHTTPDownload, unchanged) and CallAfter()s its own destruction, so wxWebRequest's internal socket is already torn down by the time the handler dies. * Keeps Create() / Run() / Stop() / OnExit() / Entry() as stubs, so the six call sites (new + Create + Run) compile unchanged. When wx 3.3's wxWebRequestSync becomes a reasonable floor on distro defaults those stubs can be made real and the event-driven plumbing collapsed into a synchronous call on a worker thread with no wxSocket anywhere. Reentrancy: no nested event loops anywhere on the fix path. No ShowModal (the existing progress dialog has always been modeless). No blocking wait on the request. Cancellation via the dialog's Cancel button or the app-wide StopAll() is fire-and-forget — it calls Cancel() on the request, returns immediately, and State_Cancelled arrives later on the main loop. Avoids the class of bug that would come from a "sync wrapper = nested wxEventLoop" trick on wx 3.2. Redirects (including HTTP→HTTPS) are handled by wxWebRequest transparently, which removes the ~110-line recursive GetInputStream redirect handler — the actual seat of the upstream race. Proxy: a static ApplyProxyToDefaultSession() helper maps the existing CProxyData prefs onto wxWebSession::SetProxy, applied from the ctor. wxWebProxy / SetProxy are wx 3.3+ only, so the body is guarded with wxCHECK_VERSION(3,3,0); on wx 3.2 the libcurl backend still honours http_proxy / https_proxy / all_proxy env vars, so env-driven proxy keeps working. The legacy wxHTTP path never honoured aMule's Proxy prefs either (it only called SetProxyMode(bool) with no URL ever registered via wxURL::SetProxy / SetDefaultProxy anywhere in the codebase), so this is not a regression. SOCKS is intentionally skipped even on 3.3+ — wxWebProxy is HTTP-only. A libcurl-direct backend would be needed if SOCKS support matters, out of scope here. macOS App Transport Security opt-out ------------------------------------ wxWebRequest on macOS is backed by NSURLSession, which enforces ATS and blocks plaintext http:// by default. The legacy wxHTTP/wxSocket path bypassed ATS because it used raw BSD sockets; the rewrite inherits NSURLSession's policy instead. Without the opt-out every startup download to an http:// URL fails at the OS level — reproducible on the first server.met update: HTTPDownload.cpp: HTTP download failed for http://upd.emule-security.org/server.met: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection. Inject NSAppTransportSecurity:NSAllowsArbitraryLoads=true via plutil as a POST_BUILD step on the `amule` target in src/CMakeLists.txt. Matches the legacy path's behaviour, not a new policy choice. macOS exit-time crash work-around --------------------------------- wx 3.3.2 has a bug in wxWebSessionURLSession::~wxWebSessionURLSession: it releases the NSURLSession and the delegate separately without first calling -invalidateAndCancel. NSURLSession retains its delegate strongly, so the session's dealloc already drops the delegate ref — wx's subsequent release hits a freed object and the process aborts with "pointer being freed was not allocated". This fires in wx module cleanup / atexit / __cxa_finalize on any Mac build after any HTTP download (version check, server.met, …). By the end of CamuleApp::OnExit we have saved state, joined threads, and flushed logs — nothing aMule-owned remains to clean up. std::_Exit bypasses atexit and static destructors, so the buggy wx dtor never runs and the process terminates cleanly. Linux / Windows continue through wx's normal cleanup; this macOS-only block can be dropped once the upstream wx fix lands in a release we depend on. Dialog / thread lifetime fix ---------------------------- CHTTPDownloadDialog holds a raw pointer (m_owner) to the owning CHTTPDownloadThread. On successful download the ordered flow is: FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app dispatcher, then CallAfter()s `delete this`. Main loop: dialog OnShutdown → Destroy() (queues the dialog in wxPendingDelete), then app dispatcher runs, then CallAfter runs and deletes the thread. Later, idle → DeletePendingObjects runs the dialog dtor — which used to dereference the now-freed m_owner via StopDownload → ~vtable lookup → crash. Null the dialog's m_owner inside OnShutdown, so its dtor (which runs later from wxPendingDelete) no longer touches the already-deleted thread. Equivalent cleanup on the reverse direction (dialog closed before download completes) was already handled via DetachCompanion + Stop in StopDownload. Scope ----- No CMake build-flag changes. wxWebRequest is in wxBase_net since wx 3.2.0 (our floor since PR amule-project#459). * src/HTTPDownload.{h,cpp} — class becomes wxEvtHandler, legacy wxThread-style method stubs with a wx 3.3 / wxWebRequestSync TODO, DetachCompanion() on the owning side + m_owner nulling on the dialog side to make the handshake safe from both directions. * src/amule.cpp — macOS-only std::_Exit at end of OnExit. * src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS opt-out into the CMake-generated Info.plist. * Six call sites (version check, server.met, auto-update, IP filter, nodes.dat, GeoIP) compile unchanged.
Fixes the intermittent startup SIGSEGV tracked in upstream PR amule-project#455. The crash happens on Linux when a startup HTTP download redirects to https://, which wxHTTP cannot speak: wxHTTP::Destroy() schedules the current handler for deferred deletion, the redirect handler throws, and before the deferred delete runs an epoll event fires on the still-registered fd — invoking a method on a socket mid-teardown. Reproducible on stock origin/master (~1-in-27 runs of amuled in a tight startup loop); the crash-hunt.sh script from PR amule-project#455 passes >115 iterations clean on Linux/ARM64 with this rewrite. The fault is inside libwx_baseu_net so it cannot be fixed inside aMule's code paths — the only reliable remedy is to avoid the wxSocket-based HTTP path entirely. The fix is to stop doing HTTP on a worker thread with wxSocket under the hood. CHTTPDownloadThread now: * Inherits wxEvtHandler instead of CMuleThread. No worker thread, no wxSocket, no wxEpollDispatcher on the download path. * Owns a wxWebRequest from wxWebSession::GetDefault(), kicks it off from the constructor on the main thread, binds wxEVT_WEBREQUEST_STATE on the same handler, and lets state transitions drive everything: Active → progress event to the optional dialog, Completed → rename wx's storage temp file into the caller-supplied path, Failed / Cancelled → clean up, Unauthorized → treat as failure. On every terminal state the object posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the existing dispatcher (CamuleApp::OnFinishedHTTPDownload, unchanged) and CallAfter()s its own destruction, so wxWebRequest's internal socket is already torn down by the time the handler dies. * Keeps Create() / Run() / Stop() / OnExit() / Entry() as stubs, so the six call sites (new + Create + Run) compile unchanged. When wx 3.3's wxWebRequestSync becomes a reasonable floor on distro defaults those stubs can be made real and the event-driven plumbing collapsed into a synchronous call on a worker thread with no wxSocket anywhere. Reentrancy: no nested event loops anywhere on the fix path. No ShowModal (the existing progress dialog has always been modeless). No blocking wait on the request. Cancellation via the dialog's Cancel button or the app-wide StopAll() is fire-and-forget — it calls Cancel() on the request, returns immediately, and State_Cancelled arrives later on the main loop. Avoids the class of bug that would come from a "sync wrapper = nested wxEventLoop" trick on wx 3.2. Redirects (including HTTP→HTTPS) are handled by wxWebRequest transparently, which removes the ~110-line recursive GetInputStream redirect handler — the actual seat of the upstream race. Proxy: a static ApplyProxyToDefaultSession() helper maps the existing CProxyData prefs onto wxWebSession::SetProxy, applied from the ctor. wxWebProxy / SetProxy are wx 3.3+ only, so the body is guarded with wxCHECK_VERSION(3,3,0); on wx 3.2 the libcurl backend still honours http_proxy / https_proxy / all_proxy env vars, so env-driven proxy keeps working. The legacy wxHTTP path never honoured aMule's Proxy prefs either (it only called SetProxyMode(bool) with no URL ever registered via wxURL::SetProxy / SetDefaultProxy anywhere in the codebase), so this is not a regression. SOCKS is intentionally skipped even on 3.3+ — wxWebProxy is HTTP-only. A libcurl-direct backend would be needed if SOCKS support matters, out of scope here. macOS App Transport Security opt-out ------------------------------------ wxWebRequest on macOS is backed by NSURLSession, which enforces ATS and blocks plaintext http:// by default. The legacy wxHTTP/wxSocket path bypassed ATS because it used raw BSD sockets; the rewrite inherits NSURLSession's policy instead. Without the opt-out every startup download to an http:// URL fails at the OS level — reproducible on the first server.met update: HTTPDownload.cpp: HTTP download failed for http://upd.emule-security.org/server.met: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection. Inject NSAppTransportSecurity:NSAllowsArbitraryLoads=true via plutil as a POST_BUILD step on the `amule` target in src/CMakeLists.txt. Matches the legacy path's behaviour, not a new policy choice. macOS exit-time crash work-around --------------------------------- wx 3.3.2 has a bug in wxWebSessionURLSession::~wxWebSessionURLSession: it releases the NSURLSession and the delegate separately without first calling -invalidateAndCancel. NSURLSession retains its delegate strongly, so the session's dealloc already drops the delegate ref — wx's subsequent release hits a freed object and the process aborts with "pointer being freed was not allocated". This fires in wx module cleanup / atexit / __cxa_finalize on any Mac build after any HTTP download (version check, server.met, …). By the end of CamuleApp::OnExit we have saved state, joined threads, and flushed logs — nothing aMule-owned remains to clean up. std::_Exit bypasses atexit and static destructors, so the buggy wx dtor never runs and the process terminates cleanly. Linux / Windows continue through wx's normal cleanup; this macOS-only block can be dropped once the upstream wx fix lands in a release we depend on. Dialog / thread lifetime fix ---------------------------- CHTTPDownloadDialog holds a raw pointer (m_owner) to the owning CHTTPDownloadThread. On successful download the ordered flow is: FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app dispatcher, then CallAfter()s `delete this`. Main loop: dialog OnShutdown → Destroy() (queues the dialog in wxPendingDelete), then app dispatcher runs, then CallAfter runs and deletes the thread. Later, idle → DeletePendingObjects runs the dialog dtor — which used to dereference the now-freed m_owner via StopDownload → ~vtable lookup → crash. Null the dialog's m_owner inside OnShutdown, so its dtor (which runs later from wxPendingDelete) no longer touches the already-deleted thread. Equivalent cleanup on the reverse direction (dialog closed before download completes) was already handled via DetachCompanion + Stop in StopDownload. Scope ----- No CMake build-flag changes. wxWebRequest is in wxBase_net since wx 3.2.0 (our floor since PR amule-project#459). * src/HTTPDownload.{h,cpp} — class becomes wxEvtHandler, legacy wxThread-style method stubs with a wx 3.3 / wxWebRequestSync TODO, DetachCompanion() on the owning side + m_owner nulling on the dialog side to make the handshake safe from both directions. * src/amule.cpp — macOS-only std::_Exit at end of OnExit. * src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS opt-out into the CMake-generated Info.plist. * Six call sites (version check, server.met, auto-update, IP filter, nodes.dat, GeoIP) compile unchanged.
Fixes the intermittent startup SIGSEGV tracked in upstream PR amule-project#455. The crash happens on Linux when a startup HTTP download redirects to https://, which wxHTTP cannot speak: wxHTTP::Destroy() schedules the current handler for deferred deletion, the redirect handler throws, and before the deferred delete runs an epoll event fires on the still-registered fd — invoking a method on a socket mid-teardown. Reproducible on stock origin/master (~1-in-27 runs of amuled in a tight startup loop); the crash-hunt.sh script from PR amule-project#455 passes >115 iterations clean on Linux/ARM64 with this rewrite. The fault is inside libwx_baseu_net so it cannot be fixed inside aMule's code paths — the only reliable remedy is to avoid the wxSocket-based HTTP path entirely. The fix is to stop doing HTTP on a worker thread with wxSocket under the hood. CHTTPDownloadThread now: * Inherits wxEvtHandler instead of CMuleThread. No worker thread, no wxSocket, no wxEpollDispatcher on the download path. * Owns a wxWebRequest from wxWebSession::GetDefault(), kicks it off from the constructor on the main thread, binds wxEVT_WEBREQUEST_STATE on the same handler, and lets state transitions drive everything: Active → progress event to the optional dialog, Completed → rename wx's storage temp file into the caller-supplied path, Failed / Cancelled → clean up, Unauthorized → treat as failure. On every terminal state the object posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the existing dispatcher (CamuleApp::OnFinishedHTTPDownload, unchanged) and CallAfter()s its own destruction, so wxWebRequest's internal socket is already torn down by the time the handler dies. * Keeps Create() / Run() / Stop() / OnExit() / Entry() as stubs, so the six call sites (new + Create + Run) compile unchanged. When wx 3.3's wxWebRequestSync becomes a reasonable floor on distro defaults those stubs can be made real and the event-driven plumbing collapsed into a synchronous call on a worker thread with no wxSocket anywhere. Reentrancy: no nested event loops anywhere on the fix path. No ShowModal (the existing progress dialog has always been modeless). No blocking wait on the request. Cancellation via the dialog's Cancel button or the app-wide StopAll() is fire-and-forget — it calls Cancel() on the request, returns immediately, and State_Cancelled arrives later on the main loop. Avoids the class of bug that would come from a "sync wrapper = nested wxEventLoop" trick on wx 3.2. Redirects (including HTTP→HTTPS) are handled by wxWebRequest transparently, which removes the ~110-line recursive GetInputStream redirect handler — the actual seat of the upstream race. Proxy: a static ApplyProxyToDefaultSession() helper maps the existing CProxyData prefs onto wxWebSession::SetProxy, applied from the ctor. wxWebProxy / SetProxy are wx 3.3+ only, so the body is guarded with wxCHECK_VERSION(3,3,0); on wx 3.2 the libcurl backend still honours http_proxy / https_proxy / all_proxy env vars, so env-driven proxy keeps working. The legacy wxHTTP path never honoured aMule's Proxy prefs either (it only called SetProxyMode(bool) with no URL ever registered via wxURL::SetProxy / SetDefaultProxy anywhere in the codebase), so this is not a regression. SOCKS is intentionally skipped even on 3.3+ — wxWebProxy is HTTP-only. A libcurl-direct backend would be needed if SOCKS support matters, out of scope here. macOS App Transport Security opt-out ------------------------------------ wxWebRequest on macOS is backed by NSURLSession, which enforces ATS and blocks plaintext http:// by default. The legacy wxHTTP/wxSocket path bypassed ATS because it used raw BSD sockets; the rewrite inherits NSURLSession's policy instead. Without the opt-out every startup download to an http:// URL fails at the OS level — reproducible on the first server.met update: HTTPDownload.cpp: HTTP download failed for http://upd.emule-security.org/server.met: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection. Inject NSAppTransportSecurity:NSAllowsArbitraryLoads=true via plutil as a POST_BUILD step on the `amule` target in src/CMakeLists.txt. Matches the legacy path's behaviour, not a new policy choice. macOS exit-time crash work-around --------------------------------- wx 3.3.2 has a bug in wxWebSessionURLSession::~wxWebSessionURLSession: it releases the NSURLSession and the delegate separately without first calling -invalidateAndCancel. NSURLSession retains its delegate strongly, so the session's dealloc already drops the delegate ref — wx's subsequent release hits a freed object and the process aborts with "pointer being freed was not allocated". This fires in wx module cleanup / atexit / __cxa_finalize on any Mac build after any HTTP download (version check, server.met, …). By the end of CamuleApp::OnExit we have saved state, joined threads, and flushed logs — nothing aMule-owned remains to clean up. std::_Exit bypasses atexit and static destructors, so the buggy wx dtor never runs and the process terminates cleanly. Linux / Windows continue through wx's normal cleanup; this macOS-only block can be dropped once the upstream wx fix lands in a release we depend on. Windows portable install: CA bundle for libcurl HTTPS ----------------------------------------------------- wxWebRequest on MSYS2 (MINGW64 / CLANGARM64) is backed by libcurl built with OpenSSL. MSYS2 libcurl is compiled with an absolute --with-ca-bundle= path (e.g. /clangarm64/etc/ssl/certs/ca-bundle.crt) that only exists on the developer's machine. On an end-user box the CA bundle isn't there and every HTTPS request (including the HTTP→ HTTPS redirect that hits the default startup URLs) fails with "libcurl error 77: Problem with the SSL CA cert". Two-part fix so portable zips work everywhere: * src/CMakeLists.txt (WIN32 AND MINGW install block): ship the MSYS2 CA bundle alongside the bundled DLLs as bin/ca-bundle.crt. * src/amule.cpp (CamuleApp::OnInit, __WINDOWS__-guarded): if CURL_CA_BUNDLE is not already set in the environment, point it at the bundled file resolved from wxStandardPaths::GetExecutablePath. Users running from a portable directory now get working HTTPS without any shell env setup; users who want to point at their own bundle still can by setting CURL_CA_BUNDLE before launch. Dialog / thread lifetime fix ---------------------------- CHTTPDownloadDialog holds a raw pointer (m_owner) to the owning CHTTPDownloadThread. On successful download the ordered flow is: FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app dispatcher, then CallAfter()s `delete this`. Main loop: dialog OnShutdown → Destroy() (queues the dialog in wxPendingDelete), then app dispatcher runs, then CallAfter runs and deletes the thread. Later, idle → DeletePendingObjects runs the dialog dtor — which used to dereference the now-freed m_owner via StopDownload → ~vtable lookup → crash. Null the dialog's m_owner inside OnShutdown, so its dtor (which runs later from wxPendingDelete) no longer touches the already-deleted thread. Equivalent cleanup on the reverse direction (dialog closed before download completes) was already handled via DetachCompanion + Stop in StopDownload. Progress bar safety (wxGTK) --------------------------- wxWebRequest::GetBytesExpectedToReceive() returns wxInvalidOffset (-1) before Content-Length is known (or indefinitely if the server never sends it). Forwarding -1 into CHTTPDownloadDialog::UpdateGauge ended up calling wxGauge::SetRange(-1), which trips an assertion on the next repaint on wxGTK (./src/gtk/gauge.cpp:90, reproducible on IP filter updates on Ubuntu). Clamp the expected size at the event source, and make UpdateGauge only touch the gauge when the total is known and current <= total, so out-of-range values can never reach the widget. Scope ----- wxWebRequest is in wxBase_net since wx 3.2.0 (our floor since PR amule-project#459), so no new build dependency beyond install-time packaging of an MSYS2 CA bundle on Windows. * src/HTTPDownload.{h,cpp} — class becomes wxEvtHandler, legacy wxThread-style method stubs with a wx 3.3 / wxWebRequestSync TODO, DetachCompanion() on the owning side + m_owner nulling on the dialog side to make the handshake safe from both directions, UpdateGauge clamped. * src/amule.cpp — macOS-only std::_Exit at end of OnExit; Windows-only CURL_CA_BUNDLE fallback to the bundled ca-bundle.crt. * src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS opt-out into the CMake-generated Info.plist; Windows-only install of the MSYS2 CA bundle alongside the bundled DLLs. * Six call sites (version check, server.met, auto-update, IP filter, nodes.dat, GeoIP) compile unchanged.
Fixes the intermittent startup SIGSEGV tracked in upstream PR #455. The crash happens on Linux when a startup HTTP download redirects to https://, which wxHTTP cannot speak: wxHTTP::Destroy() schedules the current handler for deferred deletion, the redirect handler throws, and before the deferred delete runs an epoll event fires on the still-registered fd — invoking a method on a socket mid-teardown. Reproducible on stock origin/master (~1-in-27 runs of amuled in a tight startup loop); the crash-hunt.sh script from PR #455 passes >115 iterations clean on Linux/ARM64 with this rewrite. The fault is inside libwx_baseu_net so it cannot be fixed inside aMule's code paths — the only reliable remedy is to avoid the wxSocket-based HTTP path entirely. The fix is to stop doing HTTP on a worker thread with wxSocket under the hood. CHTTPDownloadThread now: * Inherits wxEvtHandler instead of CMuleThread. No worker thread, no wxSocket, no wxEpollDispatcher on the download path. * Owns a wxWebRequest from wxWebSession::GetDefault(), kicks it off from the constructor on the main thread, binds wxEVT_WEBREQUEST_STATE on the same handler, and lets state transitions drive everything: Active → progress event to the optional dialog, Completed → rename wx's storage temp file into the caller-supplied path, Failed / Cancelled → clean up, Unauthorized → treat as failure. On every terminal state the object posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the existing dispatcher (CamuleApp::OnFinishedHTTPDownload, unchanged) and CallAfter()s its own destruction, so wxWebRequest's internal socket is already torn down by the time the handler dies. * Keeps Create() / Run() / Stop() / OnExit() / Entry() as stubs, so the six call sites (new + Create + Run) compile unchanged. When wx 3.3's wxWebRequestSync becomes a reasonable floor on distro defaults those stubs can be made real and the event-driven plumbing collapsed into a synchronous call on a worker thread with no wxSocket anywhere. Reentrancy: no nested event loops anywhere on the fix path. No ShowModal (the existing progress dialog has always been modeless). No blocking wait on the request. Cancellation via the dialog's Cancel button or the app-wide StopAll() is fire-and-forget — it calls Cancel() on the request, returns immediately, and State_Cancelled arrives later on the main loop. Avoids the class of bug that would come from a "sync wrapper = nested wxEventLoop" trick on wx 3.2. Redirects (including HTTP→HTTPS) are handled by wxWebRequest transparently, which removes the ~110-line recursive GetInputStream redirect handler — the actual seat of the upstream race. Proxy: a static ApplyProxyToDefaultSession() helper maps the existing CProxyData prefs onto wxWebSession::SetProxy, applied from the ctor. wxWebProxy / SetProxy are wx 3.3+ only, so the body is guarded with wxCHECK_VERSION(3,3,0); on wx 3.2 the libcurl backend still honours http_proxy / https_proxy / all_proxy env vars, so env-driven proxy keeps working. The legacy wxHTTP path never honoured aMule's Proxy prefs either (it only called SetProxyMode(bool) with no URL ever registered via wxURL::SetProxy / SetDefaultProxy anywhere in the codebase), so this is not a regression. SOCKS is intentionally skipped even on 3.3+ — wxWebProxy is HTTP-only. A libcurl-direct backend would be needed if SOCKS support matters, out of scope here. macOS App Transport Security opt-out ------------------------------------ wxWebRequest on macOS is backed by NSURLSession, which enforces ATS and blocks plaintext http:// by default. The legacy wxHTTP/wxSocket path bypassed ATS because it used raw BSD sockets; the rewrite inherits NSURLSession's policy instead. Without the opt-out every startup download to an http:// URL fails at the OS level — reproducible on the first server.met update: HTTPDownload.cpp: HTTP download failed for http://upd.emule-security.org/server.met: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection. Inject NSAppTransportSecurity:NSAllowsArbitraryLoads=true via plutil as a POST_BUILD step on the `amule` target in src/CMakeLists.txt. Matches the legacy path's behaviour, not a new policy choice. macOS exit-time crash work-around --------------------------------- wx 3.3.2 has a bug in wxWebSessionURLSession::~wxWebSessionURLSession: it releases the NSURLSession and the delegate separately without first calling -invalidateAndCancel. NSURLSession retains its delegate strongly, so the session's dealloc already drops the delegate ref — wx's subsequent release hits a freed object and the process aborts with "pointer being freed was not allocated". This fires in wx module cleanup / atexit / __cxa_finalize on any Mac build after any HTTP download (version check, server.met, …). By the end of CamuleApp::OnExit we have saved state, joined threads, and flushed logs — nothing aMule-owned remains to clean up. std::_Exit bypasses atexit and static destructors, so the buggy wx dtor never runs and the process terminates cleanly. Linux / Windows continue through wx's normal cleanup; this macOS-only block can be dropped once the upstream wx fix lands in a release we depend on. Windows portable install: CA bundle for libcurl HTTPS ----------------------------------------------------- wxWebRequest on MSYS2 (MINGW64 / CLANGARM64) is backed by libcurl built with OpenSSL. MSYS2 libcurl is compiled with an absolute --with-ca-bundle= path (e.g. /clangarm64/etc/ssl/certs/ca-bundle.crt) that only exists on the developer's machine. On an end-user box the CA bundle isn't there and every HTTPS request (including the HTTP→ HTTPS redirect that hits the default startup URLs) fails with "libcurl error 77: Problem with the SSL CA cert". Two-part fix so portable zips work everywhere: * src/CMakeLists.txt (WIN32 AND MINGW install block): ship the MSYS2 CA bundle alongside the bundled DLLs as bin/ca-bundle.crt. * src/amule.cpp (CamuleApp::OnInit, __WINDOWS__-guarded): if CURL_CA_BUNDLE is not already set in the environment, point it at the bundled file resolved from wxStandardPaths::GetExecutablePath. Users running from a portable directory now get working HTTPS without any shell env setup; users who want to point at their own bundle still can by setting CURL_CA_BUNDLE before launch. Dialog / thread lifetime fix ---------------------------- CHTTPDownloadDialog holds a raw pointer (m_owner) to the owning CHTTPDownloadThread. On successful download the ordered flow is: FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app dispatcher, then CallAfter()s `delete this`. Main loop: dialog OnShutdown → Destroy() (queues the dialog in wxPendingDelete), then app dispatcher runs, then CallAfter runs and deletes the thread. Later, idle → DeletePendingObjects runs the dialog dtor — which used to dereference the now-freed m_owner via StopDownload → ~vtable lookup → crash. Null the dialog's m_owner inside OnShutdown, so its dtor (which runs later from wxPendingDelete) no longer touches the already-deleted thread. Equivalent cleanup on the reverse direction (dialog closed before download completes) was already handled via DetachCompanion + Stop in StopDownload. Progress bar safety (wxGTK) --------------------------- wxWebRequest::GetBytesExpectedToReceive() returns wxInvalidOffset (-1) before Content-Length is known (or indefinitely if the server never sends it). Forwarding -1 into CHTTPDownloadDialog::UpdateGauge ended up calling wxGauge::SetRange(-1), which trips an assertion on the next repaint on wxGTK (./src/gtk/gauge.cpp:90, reproducible on IP filter updates on Ubuntu). Clamp the expected size at the event source, and make UpdateGauge only touch the gauge when the total is known and current <= total, so out-of-range values can never reach the widget. Scope ----- wxWebRequest is in wxBase_net since wx 3.2.0 (our floor since PR #459), so no new build dependency beyond install-time packaging of an MSYS2 CA bundle on Windows. * src/HTTPDownload.{h,cpp} — class becomes wxEvtHandler, legacy wxThread-style method stubs with a wx 3.3 / wxWebRequestSync TODO, DetachCompanion() on the owning side + m_owner nulling on the dialog side to make the handshake safe from both directions, UpdateGauge clamped. * src/amule.cpp — macOS-only std::_Exit at end of OnExit; Windows-only CURL_CA_BUNDLE fallback to the bundled ca-bundle.crt. * src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS opt-out into the CMake-generated Info.plist; Windows-only install of the MSYS2 CA bundle alongside the bundled DLLs. * Six call sites (version check, server.met, auto-update, IP filter, nodes.dat, GeoIP) compile unchanged.
|
This breaks Amule with native GUI (wxCocoa or Carbon) for all macOS version < 10.12 (and breaks it completely for these versions with MacPorts, which does not have wxGTK 3.2 port). Is it really justified? |
|
MacOS 10.12 was released in 2016, we’re talking about nearly 10-year-old software. In my opinion, users on such an outdated OS should be comfortable running an older version of aMule (i.e., 2.3.3). |
Summary
Raises the minimum wxWidgets version from 2.8.12 to 3.2.0 and replaces the 311-line custom
cmake/wx.cmakewrapper with an ~90-line thin shim over stockfind_package(wxWidgets). Also removes one wx-2.x-era reference (wxCHECK_VERSION(2, 8, 4)in a commented-out block) and one wx 3.3 deprecation (wxTextDataObject::GetTextLength()) at the same time.First step of the wx-3.2+ migration plan — unblocks subsequent cleanups (retiring the macOS Carbon API use, modernising boost::asio, refactoring the HTTP path onto
wxWebRequest) which will each ship as their own PR.Why 3.2 is safe as a floor
Every current-stable distribution already ships wxWidgets 3.2:
No mainstream distro ships 3.3 yet (released 2025-06-06, ~10 months ago), so this intentionally does not require 3.3.
On top of that,
configure.achas already been hard-failing for wx < 3.2 since 2024-09 (commitbe22aea36aadded theAS_IF([test "$WX_VERSION_MAJOR""$WX_VERSION_MINOR" -lt 32], AC_MSG_ERROR(...))gate right below the oldWX_CONFIG_CHECK([2.8.12])line). The 2.8.12 floor was already nominal on the autotools side; this PR aligns the CMake path and error messages with that reality.Changes
CMakeLists.txt+configure.acMIN_WX_VERSIONandWX_CONFIG_CHECKboth bumped to3.2.0; the "2.8.12 or above" text in the configure.ac error message updated to match.cmake/wx.cmake— 311 lines → ~90 linesReplaced with a thin shim over stock
find_package(wxWidgets)that still exposeswxWidgets::{BASE,CORE,NET,ADV}asINTERFACE IMPORTEDtargets, so existingtarget_link_libraries(... PRIVATE wxWidgets::CORE)call sites acrosssrc/don't change.Gone:
WIN32 AND NOT MINGWMSVC-prebuilt-subfolder detection (WX_BASE/WX_CORE/WX_ADV/WX_NETuser-supplied variables). This was the layout convention from theplatforms/Windows/MSVC12/READMEera; the MSVC build path has bitrotted since.-l<name>parser that hand-walkedwxWidgets_LIBRARIES,find_library'd each piece, and pruned duplicates. Modern CMake'sFindwxWidgetsalready returns proper lists; the hand-parsing was working around an older version.CHECK_CXX_SYMBOL_EXISTS(wxUSE_UNICODE ...)probe. wx 2.8 shipped both unicode and non-unicode builds; from wx 3.0 onward unicode is mandatory, so the probe is always true.advis not requested fromfind_package(butwxWidgets::ADVis still created whenwx_NEED_ADVis set, so existing generator expressions insrc/CMakeLists.txtkeep resolving).Kept: the MinGW
-DUNICODE/-D_UNICODEpropagation originally added in #457. MSYS2'swx-configpoints at the unicode wx build but doesn't emit those defines, and stockFindwxWidgetsjust forwardswx-config's output, so the shim still has to set them explicitly on MinGW (otherwise<wx/msw/winundef.h>lands in a mixed ANSI/UNICODE state and the build fails withLoadBitmapA/LPCSTR/LPCTSTRtype mismatches).src/MuleTextCtrl.cpp:100wxTextDataObject::GetTextLength()is[[deprecated]]in wx 3.3 with the message "Don't call nor override this function".GetText().IsEmpty()has the same semantics and is supported on every wx version we accept.src/utils/fileview/Print.hDropped the commented-out
#if wxCHECK_VERSION(2, 8, 4)block — always-true with the new floor, and the wrapping#if/#else/#endifwere already commented out anyway so it was just stale noise.