build: Set macOS minimum version to Qt min version
authorShane Synan <digitalcircuit36939@gmail.com>
Wed, 25 Nov 2020 08:19:17 +0000 (03:19 -0500)
committerManuel Nickschas <sputnick@quassel-irc.org>
Fri, 4 Dec 2020 07:56:05 +0000 (08:56 +0100)
Set the macOS Info.plist minimum version to the minimum version
supported by Qt for macOS.

Determine the minimum macOS version for Qt through querying qmake.
Unfortunately, this involves a somewhat roundabout process of a fake
project file and searching qmake's output.

See https://code.qt.io/cgit/pyside/pyside-setup.git/tree/qtinfo.py?h=5.6

Switch to HFS+ (from APFS) so older macOS versions can parse the app
bundle and display the "OS version too old" warning, instead of just
warning about a corrupt image file.

Credit to freenode/Deas for point this out and providing screenshots!

See https://doc.qt.io/qt-5/macos.html
And https://doc.qt.io/qt-5/macos-deployment.html

scripts/build/Info.plist
scripts/build/macosx_DeployApp.py
scripts/build/macosx_makePackage.sh
scripts/build/macosx_makebundle.py
scripts/build/macosx_qt.py [new file with mode: 0755]

index b36abf4..c773a3f 100644 (file)
@@ -34,5 +34,7 @@
        <string>© 2005-2020, Quassel IRC Team</string>
        <key>NSSupportsAutomaticGraphicsSwitching</key>
        <true/>
        <string>© 2005-2020, Quassel IRC Team</string>
        <key>NSSupportsAutomaticGraphicsSwitching</key>
        <true/>
+       <key>LSMinimumSystemVersion</key>
+       <string>%(QT_MACOSX_DEPLOYMENT_TARGET)s</string>
 </dict>
 </plist>
 </dict>
 </plist>
index 4ca72b4..edcb13c 100755 (executable)
@@ -19,6 +19,9 @@ import os.path
 
 from subprocess import Popen, PIPE
 
 
 from subprocess import Popen, PIPE
 
+# Handling Qt properties
+import macosx_qt
+
 # ==============================
 #  Constants
 # ==============================
 # ==============================
 #  Constants
 # ==============================
@@ -55,40 +58,11 @@ class InstallQt(object):
         if not skipInstallQtConf:
             self.installQtConf()
 
         if not skipInstallQtConf:
             self.installQtConf()
 
-    def qtProperty(self, qtProperty):
-        """
-        Query persistent property of Qt via qmake
-        """
-        VALID_PROPERTIES = ['QT_INSTALL_PREFIX',
-                            'QT_INSTALL_DATA',
-                            'QT_INSTALL_DOCS',
-                            'QT_INSTALL_HEADERS',
-                            'QT_INSTALL_LIBS',
-                            'QT_INSTALL_BINS',
-                            'QT_INSTALL_PLUGINS',
-                            'QT_INSTALL_IMPORTS',
-                            'QT_INSTALL_TRANSLATIONS',
-                            'QT_INSTALL_CONFIGURATION',
-                            'QT_INSTALL_EXAMPLES',
-                            'QT_INSTALL_DEMOS',
-                            'QMAKE_MKSPECS',
-                            'QMAKE_VERSION',
-                            'QT_VERSION'
-                            ]
-        if qtProperty not in VALID_PROPERTIES:
-            return None
-
-        qmakeProcess = Popen('qmake -query %s' % qtProperty, shell=True, stdout=PIPE, stderr=PIPE)
-        result = qmakeProcess.stdout.read().strip()
-        qmakeProcess.stdout.close()
-        qmakeProcess.wait()
-        return result
-
     def findFrameworkPath(self):
     def findFrameworkPath(self):
-        self.sourceFrameworkPath = self.qtProperty('QT_INSTALL_LIBS')
+        self.sourceFrameworkPath = macosx_qt.qtProperty('QT_INSTALL_LIBS')
 
     def findPluginsPath(self):
 
     def findPluginsPath(self):
-        self.sourcePluginsPath = self.qtProperty('QT_INSTALL_PLUGINS')
+        self.sourcePluginsPath = macosx_qt.qtProperty('QT_INSTALL_PLUGINS')
 
     def findPlugin(self, pluginname):
         qmakeProcess = Popen('find %s -name %s' % (self.sourcePluginsPath, pluginname), shell=True, stdout=PIPE, stderr=PIPE)
 
     def findPlugin(self, pluginname):
         qmakeProcess = Popen('find %s -name %s' % (self.sourcePluginsPath, pluginname), shell=True, stdout=PIPE, stderr=PIPE)
index a359ecd..e9342dd 100755 (executable)
@@ -87,6 +87,14 @@ esac
 
 echo "Creating macOS disk image with hdiutil: 'Quassel ${BUILDTYPE} - ${QUASSEL_VERSION}'"
 
 
 echo "Creating macOS disk image with hdiutil: 'Quassel ${BUILDTYPE} - ${QUASSEL_VERSION}'"
 
+# Modern macOS versions support APFS, however default to HFS+ for now in order
+# to ensure old macOS versions can parse the package and display the warning
+# about being out of date.  This mirrors the approach taken by Qt's macdeployqt
+# tool.  In the future if this isn't needed, just remove "-fs HFS+" to revert
+# to default.
+#
+# See https://doc.qt.io/qt-5/macos-deployment.html
+
 # hdiutil seems to have a bit of a reputation for failing to create disk images
 # for various reasons.
 #
 # hdiutil seems to have a bit of a reputation for failing to create disk images
 # for various reasons.
 #
@@ -105,7 +113,7 @@ echo "Creating macOS disk image with hdiutil: 'Quassel ${BUILDTYPE} - ${QUASSEL_
 #
 # Option 1:
 
 #
 # Option 1:
 
-hdiutil create -srcfolder ${PACKAGETMPDIR} -format UDBZ -volname "Quassel ${BUILDTYPE} - ${QUASSEL_VERSION}" "${WORKINGDIR}${QUASSEL_DMG}" >/dev/null
+hdiutil create -srcfolder ${PACKAGETMPDIR} -format UDBZ -fs HFS+ -volname "Quassel ${BUILDTYPE} - ${QUASSEL_VERSION}" "${WORKINGDIR}${QUASSEL_DMG}" >/dev/null
 
 # If hdiutil changes over time and fails often, you can try the other option.
 #
 
 # If hdiutil changes over time and fails often, you can try the other option.
 #
@@ -114,7 +122,7 @@ hdiutil create -srcfolder ${PACKAGETMPDIR} -format UDBZ -volname "Quassel ${BUIL
 #PACKAGESIZE_MARGIN="1.1"
 #PACKAGESIZE=$(echo "$(du -ms ${PACKAGETMPDIR} | cut -f1) * $PACKAGESIZE_MARGIN" | bc)
 #echo "PACKAGESIZE: $PACKAGESIZE MB"
 #PACKAGESIZE_MARGIN="1.1"
 #PACKAGESIZE=$(echo "$(du -ms ${PACKAGETMPDIR} | cut -f1) * $PACKAGESIZE_MARGIN" | bc)
 #echo "PACKAGESIZE: $PACKAGESIZE MB"
-#hdiutil create -srcfolder ${PACKAGETMPDIR} -format UDBZ -size ${PACKAGESIZE}M -volname "Quassel ${BUILDTYPE} - ${QUASSEL_VERSION}" "${WORKINGDIR}${QUASSEL_DMG}" >/dev/null
+#hdiutil create -srcfolder ${PACKAGETMPDIR} -format UDBZ -fs HFS+ -size ${PACKAGESIZE}M -volname "Quassel ${BUILDTYPE} - ${QUASSEL_VERSION}" "${WORKINGDIR}${QUASSEL_DMG}" >/dev/null
 
 
 # Regardless of choice, clean up the packaging temporary directory
 
 
 # Regardless of choice, clean up the packaging temporary directory
index e313a07..85742a0 100755 (executable)
@@ -18,6 +18,9 @@ import os.path
 import sys
 import commands
 
 import sys
 import commands
 
+# Handling Qt properties
+import macosx_qt
+
 # ==============================
 #  Constants
 # ==============================
 # ==============================
 #  Constants
 # ==============================
@@ -60,10 +63,20 @@ def createPlist(bundleName, bundleVersion):
     template = templateFile.read()
     templateFile.close()
 
     template = templateFile.read()
     templateFile.close()
 
+    # Get the minimum macOS deployment version
+    QT_MACOSX_DEPLOYMENT_TARGET = macosx_qt.qtMakespec('QMAKE_MACOSX_DEPLOYMENT_TARGET')
+    # Keep in sync with QMAKE_MACOSX_DEPLOYMENT_TARGET
+    # See https://doc.qt.io/qt-5/macos.html
+    if QT_MACOSX_DEPLOYMENT_TARGET is None:
+        # Something went wrong
+        sys.exit("Could not determine 'QMAKE_MACOSX_DEPLOYMENT_TARGET', check build scripts")
+    print("Qt macOS deployment target (minimum version): %s" % QT_MACOSX_DEPLOYMENT_TARGET)
+
     plistFile = file(CONTENTS_DIR + "Info.plist", 'w')
     plistFile.write(template % {"BUNDLE_NAME": bundleName,
                                 "ICON_FILE": "quassel.icns",
     plistFile = file(CONTENTS_DIR + "Info.plist", 'w')
     plistFile.write(template % {"BUNDLE_NAME": bundleName,
                                 "ICON_FILE": "quassel.icns",
-                                "BUNDLE_VERSION": bundleVersion})
+                                "BUNDLE_VERSION": bundleVersion,
+                                "QT_MACOSX_DEPLOYMENT_TARGET": QT_MACOSX_DEPLOYMENT_TARGET})
     plistFile.close()
 
 def convertIconset():
     plistFile.close()
 
 def convertIconset():
diff --git a/scripts/build/macosx_qt.py b/scripts/build/macosx_qt.py
new file mode 100755 (executable)
index 0000000..3955364
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-1 -*-
+
+################################################################################
+#                                                                              #
+# 2008 June 27th by Marcus 'EgS' Eggenberger <egs@quassel-irc.org>             #
+#                                                                              #
+# The author disclaims copyright to this source code.                          #
+# This Python Script is in the PUBLIC DOMAIN.                                  #
+#                                                                              #
+################################################################################
+
+# ==============================
+#  Imports
+# ==============================
+import os
+from subprocess import Popen, PIPE
+
+# ==============================
+#  Global Functions
+# ==============================
+def qtProperty(qtProperty):
+    """
+    Query persistent property of Qt via qmake
+    """
+    VALID_PROPERTIES = ['QT_INSTALL_PREFIX',
+                        'QT_INSTALL_DATA',
+                        'QT_INSTALL_DOCS',
+                        'QT_INSTALL_HEADERS',
+                        'QT_INSTALL_LIBS',
+                        'QT_INSTALL_BINS',
+                        'QT_INSTALL_PLUGINS',
+                        'QT_INSTALL_IMPORTS',
+                        'QT_INSTALL_TRANSLATIONS',
+                        'QT_INSTALL_CONFIGURATION',
+                        'QT_INSTALL_EXAMPLES',
+                        'QT_INSTALL_DEMOS',
+                        'QMAKE_MKSPECS',
+                        'QMAKE_VERSION',
+                        'QT_VERSION'
+                        ]
+    if qtProperty not in VALID_PROPERTIES:
+        return None
+
+    qmakeProcess = Popen('qmake -query %s' % qtProperty, shell=True, stdout=PIPE, stderr=PIPE)
+    result = qmakeProcess.stdout.read().strip()
+    qmakeProcess.stdout.close()
+    qmakeProcess.wait()
+    return result
+
+def qtMakespec(qtMakespec):
+    """
+    Query a Makespec value of Qt via qmake
+    """
+
+    VALID_PROPERTIES = ['QMAKE_MACOSX_DEPLOYMENT_TARGET',
+                        ]
+    if qtMakespec not in VALID_PROPERTIES:
+        return None
+
+    # QMAKE_MACOSX_DEPLOYMENT_TARGET sadly cannot be queried in the traditional way
+    #
+    # Inspired by https://code.qt.io/cgit/pyside/pyside-setup.git/tree/qtinfo.py?h=5.6
+    # Simplified, no caching, etc, as we're just looking for the macOS version.
+    # If a cleaner solution is desired, look into license compatibility in
+    # order to simply copy the above code.
+
+    current_dir = os.getcwd()
+    qmakeFakeProjectFile = os.path.join(current_dir, "qmake_empty_project.txt")
+    qmakeStashFile = os.path.join(current_dir, ".qmake.stash")
+    # Make an empty file
+    open(qmakeFakeProjectFile, 'a').close()
+
+    qmakeProcess = Popen('qmake -E %s' % qmakeFakeProjectFile, shell=True, stdout=PIPE, stderr=PIPE)
+    result = qmakeProcess.stdout.read().strip()
+    qmakeProcess.stdout.close()
+    qmakeProcess.wait()
+
+    # Clean up temporary files
+    try:
+        os.remove(qmakeFakeProjectFile)
+    except OSError:
+        pass
+    try:
+        os.remove(qmakeStashFile)
+    except OSError:
+        pass
+
+    # Result should be like this:
+    # PROPERTY = VALUE\n
+    result_list = result.splitlines()
+    # Clear result so if nothing matches, nothing is returned
+    result = None
+    # Search keys
+    for line in result_list:
+        if not '=' in line:
+            # Ignore lines without '='
+            continue
+
+        # Find property = value
+        parts = line.split('=', 1)
+        prop = parts[0].strip()
+        value = parts[1].strip()
+        if (prop == qtMakespec):
+            result = value
+            break
+
+    return result