OSXHorrors » History » Version 25
Version 24 (Chris Cannam, 2011-03-24 03:03 PM) → Version 25/29 (Chris Cannam, 2011-03-24 03:05 PM)
h1. OS/X version, hardware platform, and Qt version compatibility
{{>toc}}
*Executive summary:* For a program like EasyHg that demands the widest possible compatibility, we currently want to hit the following targets:
* 10.4 PPC 32-bit Carbon
* 10.4 Intel 32-bit Carbon
* 10.6 Intel 64-bit Cocoa
To do this, we currently need at least two builds of Qt:
* 10.6 gcc-4.2 Cocoa x86_64
* 10.4 gcc-4.0 Carbon PPC and i386
And we need at least three builds of our PyQt (Python plus C++) modules:
* Python 2.6 gcc-4.2 Cocoa x86_64
* Python 2.6 gcc-4.0 Carbon PPC and i386
* Python 2.5 gcc-4.0 Carbon PPC and i386
Our Qt builds need to be custom rather than stock distribution (see Qt Plugins below).
Note it is not possible to cover all platforms in a single build step, we always need to do at least two separate builds plus lipo.
If we are going to make a 3-way universal binary, we need to ensure the 10.4 build gets selected for i386 -- i.e. to pull only the x86_64 architecture from any 10.5 or 10.6 SDK build we do. The inability to select between different i386 versions from a single universal binary is a strong incentive to stick to a single 10.4+ Carbon build for all 32-bit platforms.
So our bundle layout will look like this (beneath the Contents directory):
<pre>
Contents/
+- Frameworks/
| +- QtCore [ppc] [i386] [x86_64]
| +- QtGui [ppc-carbon] [i386-carbon] [x86_64-cocoa]
| `- QtNetwork [ppc] [i386] [x86_64]
+- MacOS/
+- Py2.5/
| +- sip.so [ppc] [i386]
| +- PyQt4
| +- Qt.so [ppc] [i386]
| +- QtCore.so [ppc] [i386]
| `- QtGui.so [ppc-carbon] [i386-carbon]
+- Py2.6/
| +- sip.so [ppc] [i386] [x86_64]
| +- PyQt4
| +- Qt.so [ppc] [i386] [x86_64]
| +- QtCore.so [ppc] [i386] [x86_64]
| `- QtGui.so [ppc-carbon] [i386-carbon] [x86_64-cocoa]
+- EasyMercurial [ppc-carbon] [i386-carbon] [x86_64-cocoa]
</pre>
We also need to make sure the dependency paths are correct. Beware! It appears the dependency paths are stored separately for each architecture in a fat binary, but otool won't report them separately if you run it on a single fat binary with multiple architectures. We need to ensure each architecture is correct before we can lipo them together.
h2. Qt Plugins
Qt will by default load any plugins for e.g. image format support that it finds in the system. There are various ways to block plugin loading (e.g. through build key) but I can't see any way that will prevent Qt from at least dlopen()ing the shared object and rifling through its drawers.
This is a problem, because the plugins have dependencies on the Qt framework components, so we may end up with two different versions of Qt (the one in our bundle and the system one) being loaded at once. Result: mysterious crashes.
For the moment we "fix" this by applying the following elegant patch to our Qt build:
<pre>
--- src/corelib/plugin/qfactoryloader.cpp_ 2011-03-14 12:21:11.000000000 +0000
+++ src/corelib/plugin/qfactoryloader.cpp 2011-03-14 12:22:04.000000000 +0000
@@ -104,7 +104,7 @@
void QFactoryLoader::update()
{
-#ifdef QT_SHARED
+#ifdef NO_WE_DONT_WANT_PLUGINS_THANKS
Q_D(QFactoryLoader);
QStringList paths = QCoreApplication::libraryPaths();
QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
--- src/corelib/plugin/qpluginloader.cpp_ 2011-03-14 12:23:31.000000000 +0000
+++ src/corelib/plugin/qpluginloader.cpp 2011-03-14 12:23:53.000000000 +0000
@@ -285,7 +285,7 @@
*/
void QPluginLoader::setFileName(const QString &fileName)
{
-#if defined(QT_SHARED)
+#if defined(NO_WE_DONT_WANT_PLUGINS_THANKS)
QLibrary::LoadHints lh;
if (d) {
lh = d->loadHints;
</pre>
h2. Python versions
Python versioning and compatibility is a bit of a nightmare. We need to load a Python module with C components (using PyQt) in Mercurial, so we need to take into account both the Python version and the architecture.
The system Python is 2.5 on 10.4/10.5 and 2.6 on 10.6. PyQt compiled for 2.5 will not work in 2.6, and vice versa. This makes it very difficult to provide a single bundle for all platforms, as we have no easy way (without an installer script) to ensure that the PyQt modules for the right version of Python get loaded. It's quite likely that users of older (10.4) OS/X will have updated their Python to 2.6 as well, since that's advisable for installing Mercurial.
On 10.6, Python is 32-/64-bit universal which runs in 64-bit by default on a 64-bit system, so any modules need to be available both ways as well (troubleshooting this when it goes wrong is quite tricky). There is an environment variable @VERSIONER_PYTHON_PREFER_32_BIT@ which you can set to cause it always to run in 32-bit.
Where things get complicated is when users install additional versions of Python from other ports repositories; this seems to be quite common around these parts. Then your user-installed Python is likely to get picked up before the system one, and you don't know whether it's going to be 32- or 64-bit, and it won't support the versioning environment variable. My impression is that people get custom Python installs dragged in as dependencies of other packages, and that tends to break quite a lot of things.
Some 10.6 system Pythons have a problem loading modules, apparently something getting broken during the upgrade from 10.5 -- this affects my home machine, but I haven't had time to look into it properly yet.
h2. OS/X versions
h3. 10.6
h4. As target
* By far the most common version as of Feb 2011 (apparently >80%)
* Not supported on PPC
* Runs in 64-bit mode by default where possible
* Note Python is also 64-bit by default, so PyQt needs to be as well
* Is _not_ always 64-bit -- it is supported on 32-bit-only hardware such as Core Duo (first Intel Macs)
h4. As build host
* Builds 64-bit by default
* Can be used to do 32-bit Intel and PPC builds
h3. 10.5
h4. As target
* Not all that much more widely used than 10.4 -- if we were dropping 10.4, we probably might as well drop 10.5 as well
* Last version supported for PPC platforms
* Runs in 32-bit mode by default
* Can build for it from 10.5, 10.6
* Requires SDK @/Developer/SDKs/MacOSX10.5.SDK@
* First version to support Objective-C 2.0
* Qt Cocoa supported
h4. As build host
* Builds 32-bit by default
* Can be used to do 64-bit builds
h3. 10.4
h4. As target
* Oldest version still apparently in use as of Feb 2011: not very widespread (low single digit %age of Mac users), but at least two researchers here use it
* Appears in PPC and i386 systems
* Runs in 32-bit mode only
* Can build for it from 10.4, 10.5, 10.6
* Requires SDK @/Developer/SDKs/MacOSX10.4u.SDK@
* Requires @-mmacosx-version-min=10.4@ on 10.5+
* Requires gcc-4.0 to be requested explicitly on 10.6
* Does not support Objective-C 2.0
* Not a supported target for Qt's Cocoa builds, Qt Carbon needed
h4. As build host
* Does not support Objective-C 2.0
* 10.4u SDK can be used to build 64-bit executables of simple C/C++ programs such as plugins, but not of GUIs or anything using Core frameworks
{{>toc}}
*Executive summary:* For a program like EasyHg that demands the widest possible compatibility, we currently want to hit the following targets:
* 10.4 PPC 32-bit Carbon
* 10.4 Intel 32-bit Carbon
* 10.6 Intel 64-bit Cocoa
To do this, we currently need at least two builds of Qt:
* 10.6 gcc-4.2 Cocoa x86_64
* 10.4 gcc-4.0 Carbon PPC and i386
And we need at least three builds of our PyQt (Python plus C++) modules:
* Python 2.6 gcc-4.2 Cocoa x86_64
* Python 2.6 gcc-4.0 Carbon PPC and i386
* Python 2.5 gcc-4.0 Carbon PPC and i386
Our Qt builds need to be custom rather than stock distribution (see Qt Plugins below).
Note it is not possible to cover all platforms in a single build step, we always need to do at least two separate builds plus lipo.
If we are going to make a 3-way universal binary, we need to ensure the 10.4 build gets selected for i386 -- i.e. to pull only the x86_64 architecture from any 10.5 or 10.6 SDK build we do. The inability to select between different i386 versions from a single universal binary is a strong incentive to stick to a single 10.4+ Carbon build for all 32-bit platforms.
So our bundle layout will look like this (beneath the Contents directory):
<pre>
Contents/
+- Frameworks/
| +- QtCore [ppc] [i386] [x86_64]
| +- QtGui [ppc-carbon] [i386-carbon] [x86_64-cocoa]
| `- QtNetwork [ppc] [i386] [x86_64]
+- MacOS/
+- Py2.5/
| +- sip.so [ppc] [i386]
| +- PyQt4
| +- Qt.so [ppc] [i386]
| +- QtCore.so [ppc] [i386]
| `- QtGui.so [ppc-carbon] [i386-carbon]
+- Py2.6/
| +- sip.so [ppc] [i386] [x86_64]
| +- PyQt4
| +- Qt.so [ppc] [i386] [x86_64]
| +- QtCore.so [ppc] [i386] [x86_64]
| `- QtGui.so [ppc-carbon] [i386-carbon] [x86_64-cocoa]
+- EasyMercurial [ppc-carbon] [i386-carbon] [x86_64-cocoa]
</pre>
We also need to make sure the dependency paths are correct. Beware! It appears the dependency paths are stored separately for each architecture in a fat binary, but otool won't report them separately if you run it on a single fat binary with multiple architectures. We need to ensure each architecture is correct before we can lipo them together.
h2. Qt Plugins
Qt will by default load any plugins for e.g. image format support that it finds in the system. There are various ways to block plugin loading (e.g. through build key) but I can't see any way that will prevent Qt from at least dlopen()ing the shared object and rifling through its drawers.
This is a problem, because the plugins have dependencies on the Qt framework components, so we may end up with two different versions of Qt (the one in our bundle and the system one) being loaded at once. Result: mysterious crashes.
For the moment we "fix" this by applying the following elegant patch to our Qt build:
<pre>
--- src/corelib/plugin/qfactoryloader.cpp_ 2011-03-14 12:21:11.000000000 +0000
+++ src/corelib/plugin/qfactoryloader.cpp 2011-03-14 12:22:04.000000000 +0000
@@ -104,7 +104,7 @@
void QFactoryLoader::update()
{
-#ifdef QT_SHARED
+#ifdef NO_WE_DONT_WANT_PLUGINS_THANKS
Q_D(QFactoryLoader);
QStringList paths = QCoreApplication::libraryPaths();
QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
--- src/corelib/plugin/qpluginloader.cpp_ 2011-03-14 12:23:31.000000000 +0000
+++ src/corelib/plugin/qpluginloader.cpp 2011-03-14 12:23:53.000000000 +0000
@@ -285,7 +285,7 @@
*/
void QPluginLoader::setFileName(const QString &fileName)
{
-#if defined(QT_SHARED)
+#if defined(NO_WE_DONT_WANT_PLUGINS_THANKS)
QLibrary::LoadHints lh;
if (d) {
lh = d->loadHints;
</pre>
h2. Python versions
Python versioning and compatibility is a bit of a nightmare. We need to load a Python module with C components (using PyQt) in Mercurial, so we need to take into account both the Python version and the architecture.
The system Python is 2.5 on 10.4/10.5 and 2.6 on 10.6. PyQt compiled for 2.5 will not work in 2.6, and vice versa. This makes it very difficult to provide a single bundle for all platforms, as we have no easy way (without an installer script) to ensure that the PyQt modules for the right version of Python get loaded. It's quite likely that users of older (10.4) OS/X will have updated their Python to 2.6 as well, since that's advisable for installing Mercurial.
On 10.6, Python is 32-/64-bit universal which runs in 64-bit by default on a 64-bit system, so any modules need to be available both ways as well (troubleshooting this when it goes wrong is quite tricky). There is an environment variable @VERSIONER_PYTHON_PREFER_32_BIT@ which you can set to cause it always to run in 32-bit.
Where things get complicated is when users install additional versions of Python from other ports repositories; this seems to be quite common around these parts. Then your user-installed Python is likely to get picked up before the system one, and you don't know whether it's going to be 32- or 64-bit, and it won't support the versioning environment variable. My impression is that people get custom Python installs dragged in as dependencies of other packages, and that tends to break quite a lot of things.
Some 10.6 system Pythons have a problem loading modules, apparently something getting broken during the upgrade from 10.5 -- this affects my home machine, but I haven't had time to look into it properly yet.
h2. OS/X versions
h3. 10.6
h4. As target
* By far the most common version as of Feb 2011 (apparently >80%)
* Not supported on PPC
* Runs in 64-bit mode by default where possible
* Note Python is also 64-bit by default, so PyQt needs to be as well
* Is _not_ always 64-bit -- it is supported on 32-bit-only hardware such as Core Duo (first Intel Macs)
h4. As build host
* Builds 64-bit by default
* Can be used to do 32-bit Intel and PPC builds
h3. 10.5
h4. As target
* Not all that much more widely used than 10.4 -- if we were dropping 10.4, we probably might as well drop 10.5 as well
* Last version supported for PPC platforms
* Runs in 32-bit mode by default
* Can build for it from 10.5, 10.6
* Requires SDK @/Developer/SDKs/MacOSX10.5.SDK@
* First version to support Objective-C 2.0
* Qt Cocoa supported
h4. As build host
* Builds 32-bit by default
* Can be used to do 64-bit builds
h3. 10.4
h4. As target
* Oldest version still apparently in use as of Feb 2011: not very widespread (low single digit %age of Mac users), but at least two researchers here use it
* Appears in PPC and i386 systems
* Runs in 32-bit mode only
* Can build for it from 10.4, 10.5, 10.6
* Requires SDK @/Developer/SDKs/MacOSX10.4u.SDK@
* Requires @-mmacosx-version-min=10.4@ on 10.5+
* Requires gcc-4.0 to be requested explicitly on 10.6
* Does not support Objective-C 2.0
* Not a supported target for Qt's Cocoa builds, Qt Carbon needed
h4. As build host
* Does not support Objective-C 2.0
* 10.4u SDK can be used to build 64-bit executables of simple C/C++ programs such as plugins, but not of GUIs or anything using Core frameworks