A guide to set up wxWidgets with MinGW on Windows
14.12.2020 [Software]
While it is actually really easy to build wxWidgets, I still stumbled a few times with getting my toolchain up and running. Hence I decided to write an article about it that will hopefully save you some time.

Getting the files

This was the first step where I made a mistake. The goal I had in mind was to be able to cross-compile on Ubuntu for Windows in addition to compile natively on Windows.
The problem here was that I didn't download the correct MinGW builds for Windows. The MinGW builds that come with Ubuntu use sjlj / seh for exception handling, hence I ran into some issues when I tried to compile / link the stuff up while cross-compiling against the prebuilt libraries I created on Windows.

This guide is using the following toolchain:
  • i686-8.1.0-win32-sjlj-rt_v6-rev0 (for Windows 32)
  • x86_64-8.1.0-win32-seh-rt_v6-rev0 (for Windows 64)
  • wxWidgets-3.1.4
Download locations:

What to build?

My toolchain consists of four builds:
  • Win32/64 Debug: shared libraries
  • Win32/64 Release: static libraries
I also optimize all my builds for size (-Os gcc flag). This gives an executable size of just over 4 MB for the static release builds with a minimal example. In my experience, this can even be faster than -O2 or even -O3 since the smaller size can lead to less cache misses depending on the processor it's run on. With UPX the size of the executable can even be crunched down to 1.5 MB or even lower for easier distribution.

Where to build?

I usually build larger projects on a ramdisk (/dev/shm on Linux or OSFMount On Windows). A size of 1 GB is more than sufficient for building wxWidgets even for the debug builds.
You can obviously build it wherever you want though...

How to build?

  1. Extract the contents of the wxWidgets source somewhere
  2. (Optional) Edit the file makefile.gcc in the folder build\msw and replace the -O2 and -O0 flags for otimization with -Os
  3. Open a command prompt in the build\msw folder (shift-right-click the folder on Windows 7 to show it in the menu) (use cmd, not some other interpreter)
  4. Add your compiler to the PATH:
    set PATH=C:\MinGW\i686-8.1.0-win32-sjlj-rt_v6-rev0\mingw32\bin;%PATH%
  5. Compile wxWidgets (the first line will throw an error, it's needed to work around some parallel compile bug):
    mingw32-make -j 8 SHARED=1 BUILD=debug setup_h
    mingw32-make -j 8 SHARED=1 BUILD=debug
  6. When the build is finished, copy the contents of the folder <wxWidgets-3.1.4>\lib\gcc_lib somewhere (e.g. C:\dev\wxprojects\lib\Win32\Debug\). You also probably want to copy the dlls into the directory where the compiler puts your debug executables (e.g. C:\dev\wxprojects\bin\Win32)

Once you've copied the libraries, delete the source folder and do it again, but this time for the release (don't forget the setup_h step):
mingw32-make -j 8 SHARED=0 RUNTIME_LIBS=static BUILD=release

Copy the resulting libraries (e.g. to C:\dev\wxprojects\lib\Win32\Release\).
I'll leave the Win64 builds to the readers discretion.

Next you'll still need the headers, just copy the "include" folder somewhere (e.g. C:\dev\wxprojects)

After copying the two runtime libs libgcc_s_sjlj-1.dll and libstdc++-6.dll for the debug executables to run to bin\Win32, the Win32 parts of the filesystem tree should look something like this:
C:\dev\wxprojects/
├── bin/
|   ├── Win32/
|   |   ├── libgcc_s_sjlj-1.dll
|   |   ├── libstdc++-6.dll
|   |   ├── simpleWindow.exe
|   |   ├── simpleWindow_Debug.exe
|   |   ├── wxbase314ud_gcc_custom.dll
|   |   ├── wxbase314ud_net_gcc_custom.dll
|   |   ├── wxbase314ud_xml_gcc_custom.dll
|   |   ├── wxmsw314ud_adv_gcc_custom.dll
|   |   ├── wxmsw314ud_aui_gcc_custom.dll
|   |   ├── wxmsw314ud_core_gcc_custom.dll
|   |   ├── wxmsw314ud_gl_gcc_custom.dll
|   |   ├── wxmsw314ud_html_gcc_custom.dll
|   |   ├── wxmsw314ud_media_gcc_custom.dll
|   |   ├── wxmsw314ud_propgrid_gcc_custom.dll
|   |   ├── wxmsw314ud_ribbon_gcc_custom.dll
|   |   ├── wxmsw314ud_richtext_gcc_custom.dll
|   |   ├── wxmsw314ud_stc_gcc_custom.dll
|   |   ├── wxmsw314ud_webview_gcc_custom.dll
|   |   └── wxmsw314ud_xrc_gcc_custom.dll
|   ├── Win64/
|   |   └── ...
├── include/
|   ├── wx/
|   |   └── ...
├── lib/
|   ├── Win32/
|   |   ├── Debug/
|   |   |   ├── mswud/
|   |   |   ├── libwxbase31ud.a
|   |   |   └── ...
|   |   └── Release/
|   |   |   ├── mswu/
|   |   |   ├── libwxbase31u.a
|   |   |   └── ...
|   ├── Win64/
|   |   └── ...
└── simpleWindow/
    ├── makefile.Win32Debug
    ├── makefile.Win32Release
    └── simpleWindow.cpp

simpleWindow

So let's test our toolchain by building a simple application.
Create the folder simpleWindow in the project folder and place the following files in it. Don't forget to replace the indenting whitespaces in the makefiles with actual tabs!
File: simpleWindow.cpp
#include <wx/wx.h>

class MainWindow : public wxFrame
{
public:
    MainWindow()
        : wxFrame(nullptr, wxID_ANY, _("Test"))
    {}
};

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        // wxFrames call Destroy() in the default close handler and thus get deleted automatically
        // Note: wxDialogs need manual deletion since they are often instantiated on the stack
        // see https://docs.wxwidgets.org/trunk/overview_windowdeletion.html for more information
        MainWindow *mainWindow = new MainWindow();
        mainWindow->Show();
        return true;
    }
};
IMPLEMENT_APP(MyApp)
File: makefile.Win32Debug
OUTPUT=../bin/Win32/simpleWindow_Debug.exe

CXXPREFIX=i686-w64-mingw32-
CXXFLAGS=-Os -g -mthreads
CXXINCLUDES=-I../include -I../lib/Win32/Debug/mswud
CXXLIBS=-L../lib/Win32/Debug  -lwxmsw31ud_core -lwxbase31ud

${OUTPUT}: simpleWindow.cpp
    ${CXXPREFIX}g++ ${CXXFLAGS} ${CXXINCLUDES} -o $@ $^ ${CXXLIBS}

clean:
    rm "${OUTPUT}"
File: makefile.Win32Release
OUTPUT=../bin/Win32/simpleWindow.exe

CXXPREFIX=i686-w64-mingw32-
CXXFLAGS=-Os -s -mthreads -static -static-libgcc -static-libstdc++ -mwindows
CXXINCLUDES=-I../include -I../lib/Win32/Release/mswu
WXLIBS=-L../lib/Win32/Release -lwxmsw31u_core -lwxbase31u -lwxpng -lwxzlib -lwxtiff -lwxjpeg -lwxregexu -lwxmsw31u_aui -lwxmsw31u_adv
WINLIBS=-lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lcomctl32 -lwsock32 -lodbc32 -lshlwapi -lversion -loleacc -luxtheme

${OUTPUT}: simpleWindow.cpp
    ${CXXPREFIX}g++ ${CXXFLAGS} ${CXXINCLUDES} -o $@ $^ ${WXLIBS} ${WINLIBS}

clean:
    rm "${OUTPUT}"

Especially the linking process for the static build can be quite error-prone since the order of the libraries actually matters with gcc! So if you get a bunch of undefined reference errors, that's one of the most common culprits. On a side-note, if you want to use std::thread (and other std features that require threads) in your code with the win32 thread version of MinGW, use https://github.com/meganz/mingw-std-threads.

Now let's compile the program:
set PATH=C:\MinGW\i686-8.1.0-win32-sjlj-rt_v6-rev0\mingw32\bin;%PATH%
mingw32-make -f makefile.Win32Debug
mingw32-make -f makefile.Win32Release

If everything went alright, you should now have the binaries simpleWindow_Debug.exe and simpleWindow.exe in the bin\Win32 directory. To cross-compile, just put that whole wxprojects folder on your Linux box, install mingw through your package manager and use the same makefiles.

Should you have problems with code completion in your favorite IDE or editor (e.g. vscode), try adding "__WXMSW__=1" (or "__WXGTK__=1" on Linux) to the defines in c_cpp_properties.json.
File: .vscode\c_cpp_properties.json

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "${workspaceFolder}/../lib/Win32/Release/mswu",
                "${workspaceFolder}/../include/**"
            ],
            "defines": ["__WXGTK__=1"],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c11",
            "cppStandard": "c++14",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}