Ekaitz's tech blog:
I make stuff at ElenQ Technology and I talk about it

Support Windows not supporting Windows

I hate Windows. I don’t like it, I don’t support the software practices of Microsoft and I probably never will. That doesn’t mean I don’t live in this world, where unfortunately most of the people uses Windows. Many people have no other choice than using Windows and they still deserve to have some good free software in their computers.

Of course, I always try to encourage my clients (and everyone around me!) to use free software and stop using Windows, but sometimes it’s impossible, and it’s always better for them to use Windows with some free software made by me1 than using Windows with more proprietary bloatware done by any garbage corporation that doesn’t care about their freedom.

Since I started with ElenQ Technology I always had this issue in mind, and the time to tackle it has come, so:

How could I make software for Windows users if I don’t use Windows, I don’t have any machine that runs Windows and I don’t support Windows in any way?

Until recently, most of my clients asked me for Web based tools, so I dodged that ball without even realizing it, but I always had that the impression I would have to tackle the issue someday and I was just delaying the moment to make some research.

During the last couple of weeks, in my spare time, I made that research a little bit and this is a simple high-level result of that research.

Needless to say this research is for myself and I have a very strong background to take in consideration (read the blog and you’ll see!), so it probably won’t fit your needs in any way, but it does fit mine and probably some of my near colleagues’.

  1. Web-based
    1. Web Extensions
  2. Java Virtual Machine
    1. GUIs
    2. Interesting JVM languages
      1. Clojure
      2. Kawa
    3. Distribution: JAR files and UberJAR
  3. Native Binaries
    1. MinGW
    2. Zig
    3. Distribution: statically built binaries
    4. GUIs
  4. Distribution with Windows Installers
  5. Conclusion
  6. Final words

Web-based

Most of the times I am asked to do Web stuff, because most of the people just use Webs for everything2.

Clients are used to work with websites, the UIs are easy to make, they work in any device… They are cool for many things, but when you need to make any interesting native operation they don’t make any sense (i.e. reading or creating files locally) and they require deployment, one way or another, and that may carry extra costs or efforts and maintenance.

Web Extensions

Another interesting option are Web Extensions (browser extensions). They are kinda easy to make and making them work in several browsers3 is almost no effort, they don’t require deployment (no servers, no pain) and they have more permissions than a regular website.

The problem they have is the browser is still a constrained environment, and you might not be able to do anything you’d like in there and they force the users to have the browser open in order to run them.

JVM runs everywhere!

I’ve never been a Java fan. I don’t really like the language and the fact that it supposes you are using an IDE to code in it, but I have to say the JVM is a really interesting environment.

The main problem it has is it’s pretty large, but it’s not a huge deal to tell my clients to install it (if they don’t have it already) and it provides most of the functionality I’d ever need out of the box.

GUI

It comes with GUI stuff by default (Swing and AWT4) and there are more modern ways to make GUIs like JavaFX, which I didn’t manage to make it work in my Guix machine.

Interesting JVM languages

The best thing about the Java Virtual Machine is you don’t need to use Java for it. There are some cool languages full of parenthesis you can use in it.

Clojure

I have some Clojure past. It’s a language I love. It had a couple of things I didn’t like about it though:

  • The startup time of Clojure made me feel uncomfortable.
  • I was worried about the size of the JVM.
  • Most of my code relied too heavily in Leiningen for everything and I didn’t know very well what was going on internally or which libraries were being used (a little bit of an NPM effect), and I was worried about the maintenance of the software if I was asked to make changes in the future5.
  • The Java interaction is really well designed at a language level, but integrating Clojure’s functional programming with heavily imperative Java code (like GUIs) feels uncomfortable.

I have to say I don’t code in Clojure for a long time and I wasn’t that good programmer at the time I played around with it. Probably I would make a way better use of it right now, and things that I felt weird may feel way more comfortable.

None of this issues is a big deal anyway. For these kind of projects for Windows, it might be a great choice, as most of the problems don’t mean a lot any more in this context. I may give Clojure another go.

Kawa

Recently I discovered Kawa, and it looks great. Kawa programs are easy to build with no additional tools, it’s a scheme, it’s fast, and its Java interaction feels natural.

Of course, there are almost no libraries written in Kawa, no tutorials, no learning resources further than the documentation (which is very good, by the way).

It’s minimalistic, it’s easy to set up and is really fast: it might be a good choice for many projects.

Distribution: JAR files and UberJARs

The Java world might be “too enterprisey” for someone like me, but it has interesting features. The jar files are just zip files that have Java bytecode and resources inside.

In any machine with Java installed they are launched automatically when they are clicked. There’s no need to unpack them or anything like that.

There are several ways to make those jar files, and one is simply insert all the dependencies of the Java application inside of the jar. That’s called the UberJAR.

Doing this makes you sure to have all the dependencies and resource files (such as icons and stuff like that) inside of a file that you can distribute and will always run if there’s a JVM installed in the target machine. Simple distribution!

The only problem they have is they are not located in the correct system folder to appear in the application launchers6.

Native binaries

Sharing prebuilt binaries is also feasible if you are using the right tools, but it might be tricky to distribute.

The first thing you have to do if you want to share binaries is cross-compile them for windows. I found a couple of tools that fit very well with my style here: MinGW and Zig.

MinGW

Recently I discovered this and it happens to be great. Simply put, MinGW a cross-compiler toolchain for Windows. It has everything you need to build your C/C++ software for Windows: gcc, binutils, libraries and header files.

Pretty straightforward.

Guix also supports this as a target so I can even guix build --target= with it and have all the fun.

Zig

Zig is a great programming language and the tooling around it is absolutely fantastic. It’s designed to be easy to cross-compile and don’t need anything else than the Zig compiler itself to be able to build your Zig, C or C++ software for a Windows machine. Just change the target and boom, works!

Zig also comes with a build-system, that lets you describe how to build your whole project and build it with a simple command. No need to use external tools like GNU Autotools, Make, CMake, Meson or anything like that. One Zig installation comes with everything you need.

Another advantage of Zig is that I love the language and I’m looking for an excuse to learn it. I think it’s very well designed and I love the community it has. I don’t like the syntax that much but I think I’ll get used to it.

Distribution: statically built binaries

In order to distribute the binaries, there only obvious choice is to statically link everything and give it in one .exe file to my clients. I can’t really trust non-tech users to install all the dependencies in place and I can’t guide them in the process of how to do it because I don’t know how to do it myself, and I can’t try as I don’t own any Windows machine.

Statically built binaries require no installation so that’s great, but they are not set in the correct folder of the system and they don’t support resources as icons and stuff like that7. They might be ok for many things but they are not a perfect solution either.

GUIs

This kind of clients require GUIs most of the time. I don’t imagine them running a script from the shell.

There are many GUI libraries I could use but I’d like to use anything that is small and easy to build for any target. That leaves most of them out.

  • I tried to build IUP myself but the build process is basically broken. It looks good and uses native GUI components, but if the build process is broken I can’t really trust it. I could just use the binaries they provide but I don’t like that.

  • I built FLTK successfully without problems and I even packaged it in my personal Guix channel. It’s not beautiful, but it works, and I don’t expect it to be hard to build for Windows either.

  • I could go for something like Dear ImGUI, but ImGUIs are better suited for programs that are being rendered continuously like games and such.

  • Qt, GTK, wxWidgets and those are great too, but probably too much for a simple man like me8.

Distribution with Windows installers

Software distribution can be eased using a Windows Installer like MSI or MSIX. Those packages know where to install everything and they do automagically, as Windows users are used to.

They require extra tools but they might be simple enough to deal with and help a lot removing the downsides of the distribution methods described previously.

  • GNOME project has a tool called msitools, which exposes a similar interface to WixToolset, a popular Windows installer generator. I can use that to build and inspect MSI installers.

  • Microsoft also provides a tool for MSIX installers that is Open Source but it happens to insert telemetry (what a surprise!).

  • There’s a Python package called msicreator that simplifies the use of msitools with a simpler approach that might be more than enough for my needs.

There are also some language-specific tools like PyInstaller but that forces me to use Python, which I like but I don’t know if I want to keep using for everything. Also, it includes the interpreter in the installer, which feels like a little bit too much.

EDIT: Someone mentioned the existence of NSIS, which looks pretty promising. I add it here for future reference. It’s packaged in Guix so it might be a good idea to give it a go.

Conclusion

Each choice comes with its downsides and shines in specific scenarios. Working in a classic C/C++ setup with MinGW might be great but I have to make sure I don’t use a complex dependency tree, as everything might fail to compile or distribute for Windows.

I want to learn Zig. It’s really cool and I think it will ease the process significantly. I’m not sure about the C integration but I need to give it a go first. It might become my go-to language for these kind of applications.

On these cases I need still to find the best GUI library to use (suggestions welcome!).

The JVM case is also interesting. It comes batteries included and has Swing by default, which is horrible but it’s something. For faster development I could use some flexible language like Clojure or Kawa there, being the second way faster than the first but also way less know (which shouldn’t be a problem as I don’t want to rely in many external libraries).

All these options look feasible so I could just go for any of them. Obviously, some have way better performance than others (C/C++/Zig vs Clojure) but the ease of development is also something I have to take in account. That I’d need to think about when the projects come.

We’ll see…9

Final words

It’s obvious that I left many options out and I can’t wait to get some emails of people recommending me to learn Go or Rust10, but this non-exhaustive research is mostly based on my personal (and current) preference.

Surely you’ll think I’m making it way more difficult than it actually is: “just install a virtual machine and build there! Buy a Windows machine if you really want to solve this issue!!” and you’d probably be right, but it doesn’t feel right to me. So I won’t.

If you have other ideas or success stories that fit this line of thinking don’t hesitate to contact me.

Stay safe!


  1. And I can earn some money in the process. 

  2. Often times what the clients want or they ask for is not what they need, so be careful with that. 

  3. API support in all the browsers is not the same, be careful with that. 

  4. Welcome back to 2001. 

  5. Guix can help here! 

  6. The Java ecosystem provides a tool to solve this called javapackage but unfortunately it doesn’t cross-compile (Wine for the win?) and it would add a full Java runtime to the installer. Maybe it’s too much. 

  7. The problem with the resources can be bypassed by a self-expanding executable: a program that unpacks its binary contents in the current folder. They can be made by 7zip and other tools like this, but I don’t really like it, as they pollute the current folder and you might not expect that to happen. Some games are distributed like this. 

  8. Probably you didn’t know but my first free software contribution was for KDE and I had to deal with Qt. It was right in the migration from Qt4 to Qt5. Good times. 

  9. I can always chicken out and go for PyInstaller + PyQt when the projects come. 😐 

  10. I don’t like Go but I’m open to learn Rust even if its a little bit more complex than I’d like it to be (and I don’t like the syntax). It would require me to allocate a long time for it, which is not a problem, but I need to be sure that I will be able to get some benefit from it.