Hi all,
I've been upgrading my IDE to use wxwidgets 2.9.5 and, since wxwidgets of this version is not available as a Linux package, would need to compile and package it myself. As I've been doing this, I thought it would be great to have one package I could run on as many Linux distros as possible (32bit/64bit question aside), so I decide to take a stub at this. Based on my testing it seems like this may work, but I applied several "hacks" related to libpng that I'd like to review here.
First step was to compile with sufficiently old libc. I compiled on Ubuntu 10.04, which allows me to use the binaries on recent Debian, Ubuntu, Fedora 18, and ArchLinux Linuxes.
Second step was to decide what to do with libpng. The problem with libpng on Linux is that GTK (also) depends on libpng and pulls in its own version. So, there are three options:
1. Not include libpng at all; it will still be pulled in by GTK, but none of the PNG-related code is compiled in, so wxwidgets doesn't know anything about PNG type at all. Not a good option for me.
3. Reference system library. This works, but only as long as the version of libpng this is linked against is available. So, if I build on Ubuntu 10.04, which has libpng 1.2.42, it won't run on Fedora 18 or ArchLinux, which both have libpng 1.5.x as wx.so is looking for libpng 1.2. It's possible to add a symlink to libpng.so, trying to trick wx.so into using 1.5 libpng, but unfortunately all the symbols are linked against PNG12_0 version, so this doesn't work either.
At this point I felt stuck, but decided to try Vadim's suggestion from the earlier discussion about building with libpng=builtin, but not linking it (and using --allow-shlib-undefined, but this wasn't needed as this seems to be a default setting). This worked, as all png symbols were shown as unresolved and without any version numbers, which was exactly what I wanted. (I also used zlib=sys as it would be pulled in by system libpng.)
With that change I could use the generated binaries, because GTK would pull in libpng and my application would simply use it. Unfortunately, this wasn't the end of it and two more changes were needed.
When wxwidgets is compiled with libpng=builtin, it stores the version of libpng it's packaged with, which in my case 1.5.x. When it runs on a system with any other minor version (png only checks for maj/min version numbers), it complaints that the versions are different and refuses to do any useful work. This is where my first "fix" comes in. I replaced '#define PNG_LIBPNG_VER_STRING "1.5.7"' with '#define PNG_LIBPNG_VER_STRING (png_get_header_ver(NULL))' to always pass the check. I know, this is not ideal, but if I *do* want to run on systems with different libpng versions, this is exactly what I need. And I also tested libpng 1.2 to 1.5 to be compatible enough for my purposes.
The binaries now worked as expected on 32bit systems. But there was still a problem on 64bit Linuxes. The images would not load and I was getting "Couldn't load a PNG image - file is corrupted or not enough memory." messages. When I checked what was causing this, it turned out that png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL ); call in wxPNGHandler::LoadFile was returning zero width (the height was correct). This was puzzling as exactly the same code was working correctly on 32bit system. I suspected some sort of data truncation, but couldn't figure out what could be causing it.
Then I looked at libpng headers. LoadFile from wxwidgets defines width as png_uint_32, but its definition has changed between libpng 1.2 I was using it with and libpng 1.5 it was compiled with. In libpng 1.2 it's defined as
typedef unsigned long png_uint_32;
typedef long png_int_32;
but in libpng 1.5 it's defined as
#if defined(INT_MAX) && (INT_MAX > 0x7ffffffeL)
typedef unsigned int png_uint_32;
typedef int png_int_32;
#else
typedef unsigned long png_uint_32;
typedef long png_int_32;
#endif
This means that libpng 1.2 attempts to return width as unsigned long on 64bit system, but wxwidgets code only expects unsigned int. My "fix" was to disable the check and always use unsigned long, which allowed LoadFile (and SaveFile) code to work with both 1.2 and 1.5 versions of libpng.
Now, I'm not absolutely sure about my two libpng "fixes" and would welcome feedback on why this may be wrong or what a better way to do this may be. This has been tested so far on 32bit Ubuntu 10.04 and 12.04, Fedora 18 and ArchLinux, as well as on 64bit Ubuntu 10.04, 12.04 and Fedora 18 without any noticeable issues (with libpng 1.2 and libpng 1.5).
Paul.