Make SDL_main a header-only lib
Make SDL_main a header-only lib
Right now using SDL_main can be painful, especially with MinGW: There you have to link
-lmingw32 -lSDL2main -lSDL2in that exact order (you can add other libs in between, but libmingw32 must be before libSDL2main, and libSDL2main must be before libSDL2) - especially getting libmingw32 there isn't trivial, see https://github.com/libsdl-org/SDL/blob/1bfefb5b4d5fbd9899fca460442240dad2bd4510/sdl2-config.cmake.in#L27-L41 I think this happens becausemain()(orWinMain()or whatever) being in a static lib (instead of a source file that gets compiled as part of the build, or a .o/.obj) seems to confuse MinGW, which otherwise would automagically link libmingw32.Another (also Windows-specific) problem is that .lib files generated by MinGW don't work with modern MSVC versions ("module unsafe for SAFESEH"). For the SDL2.lib only used for linking the DLL it's not that bad, because it can relatively easily be generated from SDL2.dll (create a .def with mingw's
gendef SDL2.dlland then uselib.exe /machine:x86 /def:SDL2.def /out:SDL2.libin the VS developer console to generate the lib) - but that doesn't help for SDL2main.lib, which is a proper static lib and not just this stub-thing used on Windows to link DLLs. So binary builds of SDL2 for Windows that have been built with MinGW aren't really compatible with MSVC, mostly because of SDL2main.These problems would vanish if SDL2main was a header-only lib that you just
#includein your source file that implementsint main(int argc, char** argv)(and only there). It could still do its#define main SDL_maintrick, and it could contain the platform-specific SDL_main code (or nothing at all for most of the platforms which don't use it). SDL.h wouldn't#include <SDL_main.h>anymore (maybe it shouldn't do that either way). LikeSDL_config.hit would be generated when SDL is built (or probably just copied from some dir in src/ to include/).As a nice side-effect, it would make it clearer when it is used an when not: Right now if you don't want to use it on e.g. Windows, you need to
#define SDL_MAIN_HANDLEDbefore#include <SDL.h>(or in your build process), which is not obvious - with SDL_main as a header-only lib, you'd just do nothing, and do#include <SDL_main.h>if you want to use it. Also I think that right now if you have any variable (or function, enum member, ...) calledmain- in any file that just includes SDL.h, not just the one withmain()in it! - and that#define main SDL_mainis active on your platform, that variable will be renamed as well, which could lead to confusion..Furthermore, it would make it easier to copy the SDL_main implementation into your own source and adjust it to your needs.
Originally posted by @DanielGibson in https://github.com/libsdl-org/SDL/issues/3519#issuecomment-1014960548
Windows is the only platform where you could conceivably do this. Windows RT / Xbox, iOS, PSP, etc. all require you to have a separate file with the main implementation. iOS actually requires you to have the main implementation in an Objective C file included with your application - you can't use a static library at all.
What you're proposing is essentially including SDL_windows_main.c in a header that you include, but only on Windows. At that point you might as well include SDL_windows_main.c as a source file in your project. That file was put in the public domain in 1998 for just that purpose.
I completely agree with your reasoning, and agree with the benefits, but I don't see an elegant way to do that and still support other platforms.
@DanielGibson, feel free to submit a PR which solves this.
Windows RT / Xbox, iOS, PSP, etc. all require you to have a separate file with the main implementation
That sucks, and the only of these platforms I can even develop for and test on is Win RT.
I'll look into it anyway.
A middle ground might be to provide an empty dummy libSDL3main on platforms that can use a header-only solution, and a real libSDL3main only for those that need it (because it needs to be implemented in obj-c or c++ or whatever) and use the header-only solution?
Then building SDL3 apps could work the same everywhere: You #include <SDL3/SDL_main.h> in your source file that implements main() and link against libSDL3main (even though that's a no-op on most platforms) - though in case you have to handle different operating systems differently in your build system anyway (or don't care about the platforms that need it as a lib), you can also skip the linking part of course.
(Admittedly, this is pretty much the same as it works now, with the difference that we get the header-only benefits on Windows and similar platforms that need the SDL_main functionality and allow implementing it in C)
Regarding:
Windows RT / Xbox, iOS, PSP, etc. all require you to have a separate file with the main implementation
Why iOS and PSP? They both seem to implement their SDL main stuff in plain C, so it could be in a header-only lib - or am I missing anything?
Of course it seems to be different for Haiku, ngage and WinRT, those three use C++ (though I wonder if any of those platforms would support putting the SDL_main C++ code into the dynamic libSDL and call that from a possibly-C-header-only SDLmain)
Ah, you're right, I was thinking that iOS was Objective C.
iOS is particularly painful in that you have to have the code in your application, you can't link to a library to pick up the main entry point.
I'm willing to entertain a new approach in SDL3, as long as it works well across the different platforms.
Some remarks and questions about the C++ platforms:
WinRT
If I understand https://github.com/libsdl-org/SDL/blob/main/docs/README-winrt.md correctly, there is no static SDLmain lib for WinRT, instead apps need to add SDL_winrt_main_NonXAML.cpp to their build and compile it as part of their app, because
WinRT uses a different signature for each app's main() function. SDL-based apps that use this port must compile in SDL_winrt_main_NonXAML.cpp (in SDL\src\main\winrt) directly in order for their C-style main() functions to be called.
I think this means that moving that code into a header-only SDL_main_winrt.hpp that must be #included from a .cpp file of the project would make usage slightly easier (no need to copy a source file from the SDL sourcetree)? (And if for some reason that's a bad idea, we can still ignore WinRT for SDL_main considerations because there is no libSDLmain for WinRT to link against either way)
Haiku
Disclaimer: Haiku isn't documented in docs/ and I have no experience with Haiku/BeOS development at all so I can only make deductions from the source code
As far as I see it, there is no main()-like function in src/main/haiku/.
Apparently the way it works is that you have a "normal" main() function and code, and when you call SDL_Init() and then through SDL_VideoInit() HAIKU_VideoInit() (from src/video/haiku/SDL_bvideo.cc) is called which calls SDL_InitBeApp() (a similar path exists via SDL_InitAudio()). And then there's the counter-part, HAIKU_VideoQuit() calling SDL_QuitBeApp().
Anyway, SDL_InitBeApp() and SDL_QuitBeApp() are implemented in SDL_main.
So question for people who know about Haiku: Is that really necessary?
As apparently everything that happens at startup, including linking/loading libSDL, main() and calling SDL_Init() works without the code in SDL_main, does that code really have to be in a separate, static library? Couldn't SDL_InitBeApp() and SDL_QuitBeApp() (and the class SDL_BApp that's created/destroyed by those functions) also be implemented in the dynamic libSDL3 itself?
ngage
Another platform I know nothing about :)
I see extern "C" int main(int argc, char *argv[]); (which then is not referenced/used again - does it have a specific purpose?)
and TInt E32Main() which seems to contain relevant code and eventually calls SDL_main and uses C++-constructs.
So, question for people who know about ngage development: Couldn't we move the code from E32Main() into int SDL_E32MainImpl() in libSDL itself, and let the SDLmain part be pure C code that just calls that?
(Maybe int SDL_E32MainImpl(SDL_main_func mainfunc) to pass the users main function as a function pointer that's called there)
Android
Bonus-question (as this doesn't use C++): src/main/android/SDL_android_main.c is an empty file with a As of SDL 2.0.6 this file is no longer necessary. comment - maybe it should just be removed?
One more thing: SDL_main.h currently serves multiple purposes:
- Decide
SDL_MAIN_NEEDEDandSDL_MAIN_AVAILABLE- also used by SDLs implementation code #define main SDL_mainso the libSDLmain code can call SDL_main() instead of main()- Declare some platform-specific functions like
SDL_RegisterApp()andSDL_WinRTRunApp(),SDL_UIKitRunApp()etc that users may wanna call in their own main() code
So when turning SDL_main.h into a header-only lib that (on platforms that use some kind of SDL_main functionality, basically SDL_MAIN_AVAILABLE I guess) implements the main() magic that in SDL2 lives in libSDLmain, we have (at least) two options how to handle this:
- Split it in two headers: One with the main-#define and implementation, the other setting
SDL_MAIN_NEEDEDandSDL_MAIN_AVAILABLEand declaring platform-specific main-related functions, if any. For exampleSDL_main_impl.h(for the header-only lib with the implementation and the main-#define) andSDL_main.hfor the rest - Keep it in one header, but put
#ifndef SDL_MAIN_HANDLEDaround the implementation parts
I started implementing this, so far only for ("classic") Windows. You can see the WIP at https://github.com/libsdl-org/SDL/pull/6750
Now that #6750 is merged, this can probably be closed :)
@madebr: As you seem to be very involved with SDLs CMake build, do you think it would now be feasible to do some magic in SDL3Config.cmake (or whatever is shipped with SDL3 builds) to make it possible to use MinGW builds with MSVC?
As I wrote above:
For the SDL2.lib only used for linking the DLL it's not that bad, because it can relatively easily be generated from SDL2.dll (create a .def with mingw's gendef SDL2.dll and then use lib.exe /machine:x86 /def:SDL2.def /out:SDL2.lib in the VS developer console to generate the lib) - but that doesn't help for SDL2main.lib, which is a proper static lib and not just this stub-thing used on Windows to link DLLs.
As the SDLmain part of the problem is now solved, it should be possible to ship SDL3.def with the cmake scripts, and when they're used with MSVC (and the SDL3.dll was built with MinGW), the required SDL3.lib could be created from SDL3.def with the lib.exe of the users machine
Just a wild idea ;)
That's correct for SDLx.dll, but the MSVC and MinGW sdk's also ship static libraries.
The SDL2 MinGW sdk also contain libSDL2.a and libSDL2_test.a.
The SDL2 VC sdk also contains SDL2test.lib.
The static SDLx libraries depends on the c library it is compiled with. So I am not 100% sure this will work for these.
Last time I tried, I was able to use the VC sdk with a MinGW toolchain (using a shared SDL). So I think generating an import library is not needed in this case. I haven't tested the reverse case (a MinGW SDK with a VC toolchain).
Using an SDL SDK built with MSVC with MinGW works (MinGW can use the .lib), but the other way around doesn't work because MSVC can't use libSDL2.dll.a or however it's called (not the full static lib but the equivalent of the glue .lib for DLLs). And even though MinGW can generate .lib files, those are incompatible with modern versions of MSVC ("module unsafe for SAFESEH").
Yeah, full static libs built with MinGW still wouldn't work with MSVC, but having the dynamic lib work might be nice to have (people aren't supposed to link SDL statically anyway :-p)
Yeah, full static libs built with MinGW still wouldn't work with MSVC, but having the dynamic lib work might be nice to have (people aren't supposed to link SDL statically anyway :-p)
Speaking of which, @madebr, can we default SDL static off in CMake?
Speaking of which, @madebr, can we default SDL static off in CMake?
Sure, I will include it in the next cmake pr.
Using an SDL SDK built with MSVC with MinGW works (MinGW can use the .lib), but the other way around doesn't work because MSVC can't use libSDL2.dll.a or however it's called (not the full static lib but the equivalent of the glue .lib for DLLs). And even though MinGW can generate .lib files, those are incompatible with modern versions of MSVC ("module unsafe for SAFESEH").
I don't see a SAFESEH warning when linking against a libSDL3.dll.a, created by a mingw64 toolchain from msys2 (Using gcc 11.2.0 and cl 19.29.30147 (=MSVC2019))
Perhaps current toolchains have fixed this?
I created this branch on my sdl2 fork that will recreate a sdl2 import library, but I don't see any benefits and don't really like it.
I think that warning happened with older import libraries. For a retro project, I link to ancient directx7 sdk import libraries (downloaded from archive.org) where I remember seeing similar warnings.
Speaking of which, @madebr, can we default SDL static off in CMake?
You can currently do that by configuring with -DBUILD_SHARED_LIBS=ON.
This has been implemented, thanks!