MinGW Gotchas
You carefully crafted, tested, and documented something written in C for Linux. It will compile and “just work” in MinGW, right? It turns out MinGW does not just use glibc wholesale as I somehow expected. It uses a mix of code from glibc, Windows’ built-in POSIX libraries (believe it or not, Windows used to be POSIX compliant), and probably some stuff they developed themselves. As such, it’s not really POSIX-compliant and there are a few things you should know about before trying to compile at a MinGW target.
Regex
error: regcomp() not found. Yep, POSIX specifies
regex.h and the functions therein, and it’s in glibc, but
MinGW doesn’t provide for it. Look into the matter and you will no doubt
stumble upon a
stackoverflow answer. Out of the two regex libs listed there,
libgnurx seems to be the one that works. But only halfway.
I can’t speak for actually using MinGW on Windows, but the
cross-compilation package of libgnux provided for openSUSE
(and Ubuntu, I believe) only works when linking dynamically. The
statically linked version will statically link something into
the binary, but it still needs the dll to function. I also tried
compiling libgnurx from source, and it generates a static
library very similar to the one in openSUSE’s package – it still
complains about a missing dll when running the binary. I am not sure
how, but Fedora worked around the issue, and the libregex.a
library they provide will properly statically link libgnurx. You can
find a link to the rpm for libgnurx-static from pkgs.org then simply
extract libregex.a, including it as you see fit.
fopen
It will seem like fread is broken:
16338 bytes expected, 473 bytes read. I thought at first
that non-blocking I/O (in which you must call fread in a
loop until all data has been read) was being used by default. This is
incorrect. Rather, if the file being read is binary, Windows is
detecting some random string of bytes as an EOF indicator. It’s safe to
blame newlines in part as well. Solution? Use the rb mode
when fopen’ing the file instead of r. The
fopen(3p) manpage calls rb obsolete and
identical in function to r, but on Windows it lets it know
that the file is binary and to not stop reading prematurely.
But wait, there’s more!
No, I’m not talking about using wb as the mode when
writing (though you should do that too), but rather
freopen! While trying to read then subsequently truncate
and write to a file, I called freopen(NULL, "wb", my_fh)
only for it to spit an indeterminite error at me. The POSIX manpage
freopen(3p) claims:
If pathname is a null pointer, the freopen() function shall attempt to change the mode of the stream to that specified by mode, as if the name of the file currently associated with the stream had been used. if the call to freopen() succeeds. It is implementation-defined which changes of mode are permitted (if any), and under what circumstances.
Clear enough. But read further:
Since implementations are not required to support any stream mode changes when the pathname argument is NULL, portable applications cannot rely on the use of freopen() to change the stream mode, and use of this feature is discouraged.
Indeed, freopen without a pathname fails on Windows.
Always be sure to pass the pathname as the first argument and you
shouldn’t run into issues.
64-bit printf tokens
printf("%llu", my_long_long_unsigned_integer_with_an_equally_long_name)
is ugly as hell but it should work. The Windows libc
implementation fails on this front, and you will find conflicting
information on the internet about how MinGW handle this. There seems to
be a concensus that MinGW used to default to using the Windows
implementation but has begun to use their own implementation in recent
versions. This is not the case. Even with MinGW 8 in
October 2018 it uses the Windows printf implementation by
default and will not work right for 64-bit integers. To use the MinGW
implementation, which properly supports these special substitutions, you
must define the __USE_MINGW_ANSI_STDIO preprocessor
variable before including stdio.h. MinGW GCC will warn you
if you use any unsupported patterns, which is when you know you will
need this.
#define __USE_MINGW_ANSI_STDIO
#include <stdio.h>
int main() {
printf("I have %llu donuts", 12345678901234567890LL);
}