go-sdl2 icon indicating copy to clipboard operation
go-sdl2 copied to clipboard

Remove CGo for Windows

Open gonutz opened this issue 5 years ago • 17 comments

I have started an implementation of this library using the SDL2.dll on Windows directly, thus removing the necessity for a C-compiler.

What I have done so far is: I went through the veandco docs, copied the API with comments from there. This resulted in more or less the same docs for my version. So far there are still some TODOs left in my code, mainly concerning callbacks and some questions I have regarding 32 bit and 64 bit DLLs (note that this port allows us to use 64 bit DLLs on Windows where right now, for the veandco version, we get a compiler error stating that 64 bit support is not yet here).

There is still work to do but I wanted to start the discussion on this early, that is why I am creating this issue.

Here is a list of things that I am working on/thinking about right now.

I want to make my port API compatible with the original to be able to eventually create a pull request and merge it as an alternative way to use the library on Windows. This means that future updates would need to consider the DLL-port besides the CGo version of the code. This should however be no problem as the porting follows the same pattern always.

The other libraries (gfx, img, etc.) should be ported as well eventually.

I have yet to test all of my code, right now I have run some of my apps with it and they work but they only use a small bit of the API. I will add my tests to the repo to be run as automatic as possible. Any effort put into this will also be useful for the veandco repo, even if you would not merge my changes for any reason.

Right now my code loads SDL2.dll using syscall.NewLazyDLL on init time. I am thinking about adding a function like LoadDLL(file string) where you can specify your DLL path and name. Still, the default should be that you do not need to call this function and it will load SDL2.dll by default. This means it must reside either next to your .exe or in the Windows system folders.

Thinking about the future of this, I would suggest making it an option for the developer to chose between the CGo and the DLL version of this library on Windows. I am not quite sure what the right way to do this is, however. Put it in a subpackage github.com/veandco/go-sdl2/sdl/dll/sdl? This would require users to change their package import paths. Another way is to have a build tag dll, toggling between the versions. This would probably be nicer to use.

The reason is did this is because I have gone through the hassle of installing MinGW and Cygwin in the past and it is a terrible user experience. On Linux, using CGo is easy, no problem whatsoever, just a few apt installs and you have your SDL2 libraries and everything. On Windows, I like my Go code to work without a C-compiler, I have been creating a couple of libraries that do this. GCCs on Windows are a horror to set up, especially for newcomers just learning to program, and the Visual Studio compiler is not supported by Go. Also, installing that is an undertaking of its own as well. C-compilers on Windows... that's why I love using Go.

I hope we can make this work. I love the fact that you can now use the static option to create a dependency-free executable, that was a great step. Improving the usability, easy of development and easy of deployment are very high on my priority list. Great job on this SDL2 Go port, I love contributing to it. Looking forward to your comments.

gonutz avatar Feb 16 '19 19:02 gonutz

I have referenced a couple of other Windows related issues to this one to get a conversation started with people who have had problems with this in the past.

gonutz avatar Feb 16 '19 19:02 gonutz

Hi @gonutz, that sounds like a really nice alternative that would make this package so much more usable! I can see that even on Linux and other platforms that it can be beneficial. For example, we could statically cross-compile the binary, copy it into another machine, and let it load the libraries at run time.

veeableful avatar Feb 17 '19 07:02 veeableful

Being able to compile without cgo would definitely make me gonutz! what are the downsides of loading libraries in runtime versus dynamic linking? is there any performance hit?

Instead of starting a whole different project to eventually try to merge in this, wouldn't it be easier to start directly from this one, look for every file with import "C" and mark it with // build !dll and try to make everything work from there (by having separate replacement files with // build dll that would expose same API but using loaded functions?

I would definitely embrace that work as well, supporting OSX/Linux

fopina avatar Mar 13 '19 16:03 fopina

I have not evaluated this but I think there are no performance disadvantages. Using CGo is probably the same overhead as calling into a DLL but that is just a feeling. Maybe someone could do a comparison. Then again it does not really matter, the function call overhead is not important in reality. How much time is a couple of nanoseconds to invoke a function versus blitting a bitmap? Nothing really.

Actually I have the API complete, all that is missing now are tests. There are about five to ten TODOs left where I am not sure whether the DLL version really works correctly. One such thing I am grinding my teeth on right now is that a joystick has a GUID which is a struct of 16 bytes. This is the only thing that is passed by value in SDL2 while being larger than 32/64 bit. Besides this most of the things should work already.

Anyway, what you described is basically what I did, only instead of mirroring the file structure of this repo I use one file for everything. This is more of a personal preference, I like to find my stuff faster.

I think a library without CGo is not really that important on Linux since every Linux I have ever used comes with gcc. I have no experience with Mac, never had one, but someone else might be able to pull something similar off for OSX.

gonutz avatar Mar 13 '19 19:03 gonutz

Yes I mentioned performance as it was the only thing that came to mind, not that it was important. I was really wondering about other cons of runtime loading, as I don’t know any (besides compile-time validations that can be covered with unit tests I guess).

Not really sure I understand your last statement. Most Linux distros come with gcc, that’s true (except for minimal ones, common in docker images, alpine doesn’t even use glibc), OSX also. But is that your point to remove cgo dependency for windows? That it doesn’t have gcc pre-installed? Because it certainly doesn’t have go toolchain either :) and nowadays installing gcc in windows is as trivial as in Linux..

So my point for removing cgo is basically cross compilation. Go brought a world of improvements for current days: easier threading, faster/lighter Async routines and also, out of the box, ability to build static and packed binaries and cross compile to any os/arch with the same toolchain. That doesn’t happen in C. You need a different toolchain for every arch. If you use other libs and link to them (either dynamically or statically) you need to build them as well with all those toolchains. Removing cgo dependency (and the need to even have the libraries installed in the build machine) is awesome for that, we go back to only requiring one toolchain: the mighty go one.

So yes, there is a good advantage for Linux and OSX, regardless of having gcc pre-installed :)

fopina avatar Mar 13 '19 20:03 fopina

Just to add, you can load DLL like that only in Windows, via syscall. In Linux, that is the job of C lib (usually glibc), even plugins just use libdl and cgo.

gen2brain avatar Mar 13 '19 20:03 gen2brain

Crap, good to know @gen2brain cheers. So multiple toolchains still would be required, just not building all dependent libraries with them (as the only requirement would be libdl which is part of any of these toolchains I suppose).

fopina avatar Mar 13 '19 22:03 fopina

You are of course right, it would be great to remove CGo on every platform. Cross-compilation is a great feature, it is really sad that it is seemingly impossible to remove it in Linux. Still the main reason I even started doing everything with DLLs on Windows was in fact that I found C compilers annoying to set up. I sometimes teach students programming and I use my little prototying game library for it. In the beginning when I created it it used CGo which at the time was fine for me because I had MinGW installed on my machine. The problem was when students wanted to install it on their own laptops, to be able to keep programming at home. Setting up MinGW was a catastrophy when I last did it two years ago, I don't know if it has changed since then. I remember a thousand questions during the setup about stuff that a beginner has no idea of how to answer. Then came a Windows version of the Synaptic package manager. You had to know your way around it to even be able to select the right things to install. After everything you of course had to add both the msys and mingw bin directories to your path. Arcane is what I would call the process.

Eventually, after writing a huge instruction in my repo, I decided to just implement a Windows version of the library that uses DLLs directly, I now have a win api, Direct3D9 and DirectSound8 wrapper. Now I just tell people to download and install Go and we go from there. They finally removed GOPATH so usability is now at its highest it has ever been. Sorry for the long rant :-)

The way I would probably go when building e.g. a Ludum Dare game in 48 hours is to use Linux, compile the Linux binary with CGo and have a script to cross compile the Windows version from there. This way it works, cross compiling from Windows to Linux requires MinGW or Cygwin so a pure Go Linux library would be awesome.

gonutz avatar Mar 14 '19 10:03 gonutz

I was highlighting the cross-compiling and ignoring the tricky parts of mingw in Windows because I am fighting with the cross-compiling to ARM (RPi), sorry.

But I do get your point, Go (as anything higher-level than C, such as python as well) is easier to setup in Windows than GCC... Specially if it's GCC+CGo... Even though Travis-CI supports windows machine, I ended up using Linux one with cross-compilation to produce the windows executable, it just works...

I guess you should start your programming classes with Installing VirtualBox and using this Linux VM pre-configured for CGo and cross-compilation to Windows :)

fopina avatar Mar 14 '19 11:03 fopina

Mabe github.com/rainycape/dl is what you need for a dynamic loading on linux:

https://github.com/golang/go/issues/30162#issuecomment-462346856

lkppo avatar Aug 10 '19 10:08 lkppo

@lkppo Hey, thanks for the hint. I took a look at github.com/rainycape/dl and it uses CGo internally. This is what we want to avoid ideally. The point of this port is to remove CGo from the process because on Windows, installing a C-compiler is a pain. On Linux I do not see a problem with using CGo, most Linux Distros come with GCC. Of course we could also build something using C.dlopen and the .so file directly but in the case of Linux that will not save the developer much trouble.

gonutz avatar Aug 10 '19 10:08 gonutz

Hi gonutz,

My point was you could cross-compile and provide this thin wrapper for linux as binary and change only your main function to use it on linux.

This could avoid two ways of writing the binding. Since I did not do this before, did I say something silly?

lkppo avatar Aug 10 '19 11:08 lkppo

Oh, I see. Do you mean cross-compiling from Windows to Linux or vice versa?

Removing CGo from the Windows thing will allow you to cross-compile from Linux to Windows easily.

Cross-compiling from Windows to Linux would still require you to have a GCC installed on Windows, since as I said the library you suggested uses CGo for dlopen as well. If we could however get CGo out of the way and use dlopen from pure Go, similar things might be possible for the Linux version. In that case the cross-compilation from Windows to Linux could be made easy as well.

gonutz avatar Aug 10 '19 13:08 gonutz

Yes it's what I mean. Using Linux the first time for compiling a dl package for Linux. Adapt the binding to use the native windows loader or dl loader for Linux. After that use the dl binary package to cross-compile for Linux from windows, or compile on Linux. When we have a binary package of dl. Would it be possible to do that?

lkppo avatar Aug 10 '19 15:08 lkppo

I am not really sure what you mean by "binary package of dl". As I said, we would have to be able to call dlopen without CGo, see Go issue #18296.

gonutz avatar Aug 10 '19 19:08 gonutz

Just tested https://github.com/gonutz/go-sdl2 and this worked great! Keep up the great work!

5k3105 avatar Nov 03 '19 16:11 5k3105

@5k3105 Thank you very much, there are some issues left, however. I have not worked on this fork for some time now and the status I left it in was testing if everything works. Unfortunately the SDL 2 API is pretty large and I cannot really test everything (e.g. some game controller functionality) which is why I have not yet created a pull request for the main repo.

gonutz avatar Nov 04 '19 11:11 gonutz