Search code examples
c++visual-studio-codevisual-c++wxwidgets

How do I correctly compile wxWidgets with VS Code and MSVC?


I am currently working on developing a GUI front-end for a console-based C++ application using wxWidgets (I decided against dancing with Qt's licensing conditions). I've been doing all the work for this app thus far in VS Code, so I tried my hand at adapting the instructions detailed in the provided docs/msw/install.md file, with no luck. As kind of a reference point, I'm trying to compile the "Hello World" example code on wxWidget's website here (copy-pasted into my own file). Whenever I try to compile, I get a slew of LNK2019 and LNK2001 errors (literally hundreds of them). Here's the first 3 for reference:

main.obj : error LNK2019: unresolved external symbol "void __cdecl wxOnAssert(char const *,int,char const *,char const *,char const *)" (?wxOnAssert@@YAXPBDH000@Z) referenced in function "public: __thiscall wxEventFunctorMethod<class wxEventTypeTag<class wxCommandEvent>,class MyFrame,class wxCommandEvent,class MyFrame>::wxEventFunctorMethod<class wxEventTypeTag<class wxCommandEvent>,class MyFrame,class wxCommandEvent,class MyFrame>(void (__thiscall MyFrame::*)(class wxCommandEvent &),class MyFrame *)" (??0?$wxEventFunctorMethod@V?$wxEventTypeTag@VwxCommandEvent@@@@VMyFrame@@VwxCommandEvent@@V2@@@QAE@P8MyFrame@@AEXAAVwxCommandEvent@@@ZPAV1@@Z)
main.obj : error LNK2019: unresolved external symbol "struct wxPrivate::UntypedBufferData * __cdecl wxPrivate::GetUntypedNullData(void)" (?GetUntypedNullData@wxPrivate@@YAPAUUntypedBufferData@1@XZ) referenced in function "protected: static struct wxScopedCharTypeBuffer<char>::Data * __cdecl wxScopedCharTypeBuffer<char>::GetNullData(void)" (?GetNullData@?$wxScopedCharTypeBuffer@D@@KAPAUData@1@XZ)
main.obj : error LNK2019: unresolved external symbol "class wxMBConv * __cdecl wxGet_wxConvLibcPtr(void)" (?wxGet_wxConvLibcPtr@@YAPAVwxMBConv@@XZ) referenced in function "class wxMBConv & __cdecl wxGet_wxConvLibc(void)" (?wxGet_wxConvLibc@@YAAAVwxMBConv@@XZ)

Ending with (directory censored):

...\main.exe : fatal error LNK1120: 339 unresolved externals

I have tried searching for any clues as to how I should go about doing this, but any resources I find seems to be either for GCC or Visual Studio or just downright not the issue I'm having. So, my question is, what is the "correct" way to compile wxWidgets apps in VS Code and the MSVC (cl.exe) compiler? How should I have the include and linker paths set up in the compiler flags? And are there any preprocessor directives that I'm just straightup missing?

This is how I currently have my compiler options set up:

tasks.json

{
    "tasks": [
        {
            "type": "shell",
            "label": "C/C++: cl.exe build active file",
            "command": "cl.exe",
            "args": [
                "/Zi",
                "/EHsc",
                "/nologo",
                "/Fe${fileDirname}\\${fileBasenameNoExtension}.exe",
                "/IC:\\wxWidgets-3.2.2.1\\lib\\vc_lib\\mswu",
                "/IC:\\wxWidgets-3.2.2.1\\include",
                "${file}",
                "/link",
                "/LIBPATH:C:\\wxWidgets-3.2.2.1\\lib\\vc_lib"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$msCompile"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        }
    ],
    "version": "2.0.0",
    "windows": {
        "options": {
            "shell": {
                "executable": "cmd.exe",
                "args": [
                    "/C",
                    "\"C:/Program Files/Microsoft Visual Studio/2022/Community/Common7/Tools/VsDevCmd.bat\"",
                    "&&"
                ]
            }
        }
    }
}

c_cpp_properties.json

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}/**",
                "C:/wxWidgets-3.2.2.1/include",
                "C:/wxWidgets-3.2.2.1/lib/vc_lib/mswu"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "NDEBUG"
            ],
            "windowsSdkVersion": "10.0.19041.0",
            "compilerPath": "cl.exe",
            "cStandard": "c17",
            "intelliSenseMode": "windows-msvc-x64"
        }
    ],
    "version": 4
}

Admittedly these files are a little sloppily put-together. They're just retrofitted relics from old one-off projects, but up till now they've served their purpose pretty well. To my knowledge, the include paths and library path in tasks.json should be correct, but this is my first time using wxWidgets (be gentle) and I have pretty limited experience with playing around with MSVC flags directly. Any adjustments I try to make to the paths produce different errors (obviously), like not being able to find wxbase32u.lib, but I'm under the impression that's fixable by... well... actually using the correct paths. From here, I'm pretty well stuck.

I did compile my own binaries per the instructions, both debug and release (side note: I am in fact trying to use release binaries here), and I did successfully compile and run the "minimal" sample code provided with the wxWidgets source, so I'm pretty sure the binaries themselves are not problematic. I tried disecting the Makefile used for the sample for hints, but the formatting admittedly mystifies me, and I'd be even at more of a loss trying to refactor that into VS Code's configurations.

Now, I do realize that I could fairly easily, with just a little bit of time, migrate my app development to Visual Studio or figure out how to put everything into a Makefile or use GCC, but I'm going to try to make this an opportunity to learn a little bit about the linker. So, if say someone were unable to use any editor/compiler combo besides VS Code and cl.exe, or just too stubborn to switch to something else, how could they go about doing it?


Solution

  • After coming back to this with a fresh mindset in the morning, I found my problem. Well, I actually had a couple problems. For everybody else's reference, I'm leaving the original post unmodified.

    First and foremost, the problem that actually started this post: I had a major typo in tasks.json. The include path that leads to C:\\wxWidgets-3.2.2.1\\lib\\vc_lib\\mswu should be C:\\wxWidgets-3.2.2.1\\include\\msvc. That wasn't me misunderstanding the setup instructions for wxWidgets, that was just me being tired and hand-typing that path in a few dozen times instead of just copy-pasting.

    But, that generated a new set of errors on top of the LNK2019/2001 errors, namely LNK4286, LNK4217, and (most importantly) a bunch of LNK2038 errors:

    error LNK2038: mismatch detected for 'RuntimeLibrary' : value 'MD_DynamicRelease' doesn't match value 'MT_StaticRelease' in main.obj
    

    Took me 5 seconds to add /MD to the flags in tasks.json, and now it compiles perfectly fine now.

    Now, why the linker is expecting a dynamic release and not a static release even though I'm using static libraries, I have no clue. I moved the final .exe around on my computer and obfuscated the library path with no runtime issues, as far as I know, it behaves like a statically-linked library. So, if anyone who knows why the linker is behaving like this, feel free to comment. In the meantime, since I was able to solve my problem, I'm accepting this as the answer.