mkdir build
cd build && cmake $GITHUB_WORKSPACE \
-GNinja \
+ -DWANT_CORE=ON \
+ -DWANT_QTCLIENT=ON \
+ -DWANT_MONO=ON \
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \
+ -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/bundles \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=ON \
-DFATAL_WARNINGS=OFF \
- -DDEPLOY=ON \
- -DENABLE_SHARED=OFF
+ -DENABLE_SHARED=OFF \
+ -DBUNDLE=ON \
- name: Build
- run: |
- # Deploy scripts require qmake in the path
- export PATH=$PATH:/usr/local/opt/qt5/bin
- cd build && ninja
+ run: cd build && ninja
- name: Run tests
run: cd build && ctest
- name: Install
- run: cd build && DESTDIR=$GITHUB_WORKSPACE/image ninja install
+ run: cd build && ninja install
- name: Print ccache stats
run: ccache -s
uses: actions/upload-artifact@v2
with:
name: macOS
- path: ${{ github.workspace }}/build/*.dmg
+ path: ${{ github.workspace }}/bundles/*.dmg
# ------------------------------------------------------------------------------------------------------------------------------------------
build-windows:
find_library(CARBON_LIBRARY Carbon)
mark_as_advanced(CARBON_LIBRARY)
link_libraries(${CARBON_LIBRARY})
+
+ # Whether to enable the creation of bundles and DMG images
+ cmake_dependent_option(BUNDLE "Create bundles and DMG images" OFF "APPLE" OFF)
+ add_feature_info(BUNDLE BUNDLE "Create bundles and DMG images")
endif()
# Always embed on Windows or OSX; never embed when enabling KDE integration
set(EMBED_DEFAULT ON)
endif()
cmake_dependent_option(EMBED_DATA "Embed icons and translations into the binaries instead of installing them" ${EMBED_DEFAULT}
- "NOT WIN32;NOT WITH_KDE" ${EMBED_DEFAULT})
+ "NOT WIN32;NOT WITH_KDE" ${EMBED_DEFAULT})
if (NOT EMBED_DEFAULT)
add_feature_info(EMBED_DATA EMBED_DATA "Embed icons and translations in the binaries instead of installing them")
endif()
-# The following options are not for end-user consumption, so don't list them in the feature summary
+# The following option is not for end-user consumption, so don't list it in the feature summary
option(FATAL_WARNINGS "Make compile warnings fatal (most useful for CI builds)" OFF)
-cmake_dependent_option(DEPLOY "Add required libs to bundle resources and create a dmg" OFF "APPLE" OFF)
# List of authenticators and the cmake flags to build them
# (currently that's just LDAP, but more can be added here).
* Support building shared libraries via the ENABLE_SHARED CMake option (defaults to on)
* Introduce support for (and a small selection of) unit tests via the BUILD_TESTING CMake option
* Use Github Actions as CI system, replacing Travis and Appveyor
+* Revamp bundle/DMG creation on macOS
* Many smaller fixes
* Improve documentation and UI help
* Update translations
--- /dev/null
+include(BundleUtilities)
+
+# After the relevant targets, support files, as well as plugins have already been installed into the bundle structure,
+# the bundle must still be made standalone by copying the required frameworks and making them position-independent.
+# This is generally called "fixing up" the bundle.
+#
+# Principally there are two ways to do that: Qt's official macdeployqt tool, and CMake's BundleUtilities.
+#
+# Some frameworks, in particular QtWebEngineCore, come with nested bundles. In order for them to work correctly, the Frameworks directory
+# from the main bundle must be symlinked into the nested bundle, otherwise dependencies cannot be resolved.
+# Neither macdeployqt (shockingly) nor BundleUtilities can handle this properly. The former simply ignores nested bundles and thus
+# does not fix them up at all. The latter scans for additional binaries and tries fixing them up, but there is no way to inject
+# creation of the Frameworks symlink between the copy and the fixup steps.
+#
+# The working solution implemented here is to first run macdeployqt (which also handles some Qt-specific quirks), then symlink Frameworks
+# into the nested bundles (if any), then use BundleUtilities to perform the remaining fixups and verify the bundle.
+
+# Since we're in the install phase, DESTDIR might be set
+set(BUNDLE_PATH "$ENV{DESTDIR}@BUNDLE_PATH@")
+set(DMG_PATH "$ENV{DESTDIR}@DMG_PATH@")
+
+# First, use Qt's official tool, macdeployqt, for deploying the needed Qt Frameworks into the bundle
+message(STATUS "Deploying Qt Frameworks in bundle \"${BUNDLE_PATH}\"")
+execute_process(
+ # Don't deploy plugins - we've already installed the selection relevant for our target!
+ COMMAND @MACDEPLOYQT_EXECUTABLE@ "${BUNDLE_PATH}" -verbose=1 -no-plugins
+ RESULT_VARIABLE result
+)
+if(NOT result EQUAL 0)
+ message(FATAL_ERROR "Deploying Qt Frameworks failed.")
+endif()
+
+# Scan for nested bundles and symlink the main bundle's Frameworks directory into them
+message(STATUS "Checking for nested bundles")
+execute_process(
+ COMMAND find "${BUNDLE_PATH}" -mindepth 1 -type d -name "*.app"
+ OUTPUT_VARIABLE nested_bundles
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+if(nested_bundles)
+ string(REPLACE "\n" ";" nested_bundles ${nested_bundles})
+ foreach(nested_bundle IN LISTS nested_bundles)
+ message(STATUS "Symlinking Frameworks into nested bundle \"${nested_bundle}\"")
+ file(RELATIVE_PATH path "${nested_bundle}/Contents" "${BUNDLE_PATH}/Contents/Frameworks")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${path}" "${nested_bundle}/Contents/Frameworks")
+ endforeach()
+else()
+ message("Checking for nested bundles - none found")
+endif()
+
+# Now fixup the whole thing using CMake's own tooling, which (unlike macdeployqt) will take care of any additional internal executables
+message(STATUS "Fixing up bundle...")
+fixup_bundle("${BUNDLE_PATH}" "" "")
+
+# Create the DMG image
+message(STATUS "Creating DMG image...")
+execute_process(
+ COMMAND hdiutil create "${DMG_PATH}" -srcfolder "${BUNDLE_PATH}" -format "UDBZ" -fs "HFS+" -volname "Quassel IRC"
+ RESULT_VARIABLE result
+)
+if(NOT result EQUAL 0)
+ message(FATAL_ERROR "Creating DMG image failed.")
+endif()
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
- <string>%(BUNDLE_NAME)s</string>
+ <string>@BUNDLE_NAME@</string>
<key>CFBundleGetInfoString</key>
<string>Quassel IRC Client</string>
<key>CFBundleIconFile</key>
- <string>%(ICON_FILE)s</string>
+ <string>quassel.icns</string>
<key>CFBundleIdentifier</key>
<string>org.quassel-irc.client</string>
<key>CFBundleInfoDictionaryVersion</key>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>%(BUNDLE_VERSION)s</string>
+ <string>@QUASSEL_MAJOR@.@QUASSEL_MINOR@.@QUASSEL_PATCH@</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>%(BUNDLE_VERSION)s</string>
+ <string>@QUASSEL_MAJOR@.@QUASSEL_MINOR@.@QUASSEL_PATCH@</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>@QMAKE_MACOSX_DEPLOYMENT_TARGET@</string>
<key>LSRequiresCarbon</key>
<true/>
- <key>NSPrincipalClass</key>
- <string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>© 2005-2020, Quassel IRC Team</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
- <key>LSMinimumSystemVersion</key>
- <string>%(QT_MACOSX_DEPLOYMENT_TARGET)s</string>
</dict>
</plist>
set(TARGET ${target} PARENT_SCOPE)
endfunction()
+###################################################################################################
+# Adds an executable target for Quassel.
+#
+# quassel_add_executable(<target> COMPONENT <Core|Client|Mono> [SOURCES src1 src2...] [LIBRARIES lib1 lib2...])
+#
+# This function supports the creation of either of the three hardcoded executable targets: Core, Client, and Mono.
+# Given sources and libraries are added to the target.
+#
+# On macOS, the creation of bundles and corresponding DMG files is supported and can be enabled by setting the
+# BUNDLE option to ON.
+#
+function(quassel_add_executable _target)
+ set(options)
+ set(oneValueArgs COMPONENT)
+ set(multiValueArgs SOURCES LIBRARIES)
+ cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ # Set up some hard-coded data based on the component to be built
+ if(ARG_COMPONENT STREQUAL "Core")
+ set(DEFINE BUILD_CORE)
+ set(WIN32 FALSE)
+ set(BUNDLE_NAME "Quassel Core")
+ elseif(ARG_COMPONENT STREQUAL "Client")
+ set(DEFINE BUILD_QTUI)
+ set(WIN32 TRUE)
+ set(BUNDLE_NAME "Quassel Client")
+ elseif(ARG_COMPONENT STREQUAL "Mono")
+ set(DEFINE BUILD_MONO)
+ set(WIN32 TRUE)
+ set(BUNDLE_NAME "Quassel")
+ else()
+ message(FATAL_ERROR "quassel_executable requires a COMPONENT argument with one of the values 'Core', 'Client' or 'Mono'")
+ endif()
+
+ add_executable(${_target} ${ARG_SOURCES})
+ set_property(TARGET ${_target} APPEND PROPERTY COMPILE_DEFINITIONS ${DEFINE})
+ set_target_properties(${_target} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
+ WIN32_EXECUTABLE ${WIN32} # Ignored on non-Windows platforms
+ )
+ target_link_libraries(${_target} PUBLIC ${ARG_LIBRARIES}) # Link publicly, so plugin detection for bundles work
+
+ # Prepare bundle creation on macOS
+ if(APPLE AND BUNDLE)
+ set(BUNDLE_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}.app")
+ set(DMG_PATH "${CMAKE_INSTALL_PREFIX}/Quassel${ARG_COMPONENT}_MacOSX-x86_64_${QUASSEL_VERSION_STRING}.dmg")
+
+ # Generate an appropriate Info.plist
+ set(BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info_${ARG_COMPONENT}.plist")
+ configure_file(${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in ${BUNDLE_INFO_PLIST} @ONLY)
+
+ # Set some bundle-specific properties
+ set_target_properties(${_target} PROPERTIES
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST "${BUNDLE_INFO_PLIST}"
+ OUTPUT_NAME "${BUNDLE_NAME}"
+ )
+ endif()
+
+ # Install main target; this will also create an initial bundle skeleton if appropriate
+ install(TARGETS ${_target}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX} # Ignored when not creating a bundle
+ COMPONENT ${ARG_COMPONENT}
+ )
+
+ # Once the bundle skeleton has been created and the main executable installed, finalize bundle creation and build DMGs
+ if(APPLE AND BUNDLE)
+ # We cannot rely on Qt's macdeployqt for deploying plugins, because it will unconditionally install a bunch of unneeded ones,
+ # dragging in unwanted dependencies.
+ # Instead, transitively determine all Qt modules that the Quassel executable links against, and deploy only the plugins belonging
+ # to those modules.
+ # macdeployqt will take care of fixing up dependencies afterwards.
+ function(find_transitive_link_deps target var)
+ if(TARGET ${target})
+ get_target_property(libs ${target} LINK_LIBRARIES)
+ if(libs)
+ foreach(lib IN LISTS libs)
+ if(NOT lib IN_LIST ${var})
+ list(APPEND ${var} ${lib})
+ find_transitive_link_deps(${lib} ${var})
+ endif()
+ endforeach()
+ endif()
+ set(${var} ${${var}} PARENT_SCOPE)
+ endif()
+ endfunction()
+
+ find_transitive_link_deps(${_target} link_deps)
+ # TODO CMake 3.6: use list(FILTER...)
+ foreach(dep IN LISTS link_deps)
+ if(${dep} MATCHES "^Qt5::.*")
+ list(APPEND qt_deps ${dep})
+ endif()
+ endforeach()
+
+ foreach(module IN LISTS qt_deps)
+ string(REPLACE "::" "" module ${module})
+ foreach(plugin ${${module}_PLUGINS})
+ install(
+ FILES $<TARGET_PROPERTY:${plugin},LOCATION>
+ DESTINATION ${BUNDLE_PATH}/Contents/PlugIns/$<TARGET_PROPERTY:${plugin},QT_PLUGIN_TYPE>
+ COMPONENT ${ARG_COMPONENT}
+ )
+ endforeach()
+ endforeach()
+
+ # Generate iconset and deploy it as well as a qt.conf enabling plugins
+ add_dependencies(${_target} MacOsIcons)
+ install(
+ FILES ${CMAKE_SOURCE_DIR}/data/qt.conf ${CMAKE_BINARY_DIR}/pics/quassel.icns
+ DESTINATION ${BUNDLE_PATH}/Contents/Resources
+ COMPONENT ${ARG_COMPONENT}
+ )
+
+ # Determine the location of macdeployqt. Not available directly via CMake, so look for it in qmake's bindir...
+ get_target_property(QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
+ get_filename_component(qt_bin_dir ${QMAKE_EXECUTABLE} DIRECTORY)
+ find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS ${qt_bin_dir} REQUIRED)
+
+ # Generate and invoke post-install script, finalizing the bundle and creating a DMG image
+ #set(BUNDLE_PATH $ENV{DESTDIR}/${BUNDLE_PATH})
+ configure_file(${CMAKE_SOURCE_DIR}/cmake/FinalizeBundle.cmake.in ${CMAKE_BINARY_DIR}/FinalizeBundle_${ARG_COMPONENT}.cmake @ONLY)
+ install(CODE "
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/FinalizeBundle_${ARG_COMPONENT}.cmake
+ RESULT_VARIABLE result
+ )
+ if(NOT result EQUAL 0)
+ message(FATAL_ERROR \"Finalizing bundle failed.\")
+ endif()
+ "
+ COMPONENT ${ARG_COMPONENT}
+ )
+ endif()
+endfunction()
+
###################################################################################################
# Provides a library that contains data files as a Qt resource (.qrc).
#
--- /dev/null
+[Paths]
+Plugins = PlugIns
+Imports = Resources/qml
+Qml2Imports = Resources/qml
+++ /dev/null
-#!/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 sys
-import os
-import os.path
-
-from subprocess import Popen, PIPE
-
-# Handling Qt properties
-import macosx_qt
-
-# ==============================
-# Constants
-# ==============================
-QT_CONFIG = """[Paths]
- Plugins = plugins
-"""
-
-QT_CONFIG_NOBUNDLE = """[Paths]
- Prefix = ../
- Plugins = plugins
-"""
-
-
-class InstallQt(object):
- def __init__(self, appdir, bundle=True, requestedPlugins=[], skipInstallQtConf=False):
- self.appDir = appdir
- self.bundle = bundle
- self.frameworkDir = self.appDir + "/Frameworks"
- self.pluginDir = self.appDir + "/plugins"
- self.executableDir = self.appDir
- if bundle:
- self.executableDir += "/MacOS"
-
- self.installedFrameworks = set()
-
- self.findFrameworkPath()
-
- executables = [self.executableDir + "/" + executable for executable in os.listdir(self.executableDir)]
- for executable in executables:
- self.resolveDependancies(executable)
-
- self.findPluginsPath()
- self.installPlugins(requestedPlugins)
- if not skipInstallQtConf:
- self.installQtConf()
-
- def findFrameworkPath(self):
- self.sourceFrameworkPath = macosx_qt.qtProperty('QT_INSTALL_LIBS')
-
- def findPluginsPath(self):
- 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)
- result = qmakeProcess.stdout.read().strip()
- qmakeProcess.stdout.close()
- qmakeProcess.wait()
- if not result:
- raise OSError
- return result
-
- def installPlugins(self, requestedPlugins):
- try:
- os.mkdir(self.pluginDir)
- except:
- pass
-
- for plugin in requestedPlugins:
- if not plugin.isalnum():
- print "Skipping library '%s'..." % plugin
- continue
-
- pluginName = "lib%s.dylib" % plugin
- pluginSource = ''
- try:
- pluginSource = self.findPlugin(pluginName)
- except OSError:
- print "WARNING: Requested library does not exist: '%s'" % plugin
- continue
-
- pluginSubDir = os.path.dirname(pluginSource)
- pluginSubDir = pluginSubDir.replace(self.sourcePluginsPath, '').strip('/')
- try:
- os.mkdir("%s/%s" % (self.pluginDir, pluginSubDir))
- except OSError:
- pass
-
- os.system('cp "%s" "%s/%s"' % (pluginSource, self.pluginDir, pluginSubDir))
-
- self.resolveDependancies("%s/%s/%s" % (self.pluginDir, pluginSubDir, pluginName))
-
- def installQtConf(self):
- qtConfName = self.appDir + "/qt.conf"
- qtConfContent = QT_CONFIG_NOBUNDLE
- if self.bundle:
- qtConfContent = QT_CONFIG
- qtConfName = self.appDir + "/Resources/qt.conf"
-
- qtConf = open(qtConfName, 'w')
- qtConf.write(qtConfContent)
- qtConf.close()
-
- def resolveDependancies(self, obj):
- # obj must be either an application binary or a framework library
- # print "resolving deps for:", obj
- for framework, lib in self.determineDependancies(obj):
- self.installFramework(framework)
- self.changeDylPath(obj, framework, lib)
-
- def installFramework(self, framework):
- # skip if framework is already installed.
- if framework in self.installedFrameworks:
- return
-
- self.installedFrameworks.add(framework)
-
- # if the Framework-Folder is a Symlink we are in a Helper-Process ".app" (e.g. in QtWebEngine),
- # in this case skip copying/installing on existing folders
- skipExisting = False;
- if os.path.islink(self.frameworkDir):
- skipExisting = True;
-
- # ensure that the framework directory exists
- try:
- os.mkdir(self.frameworkDir)
- except:
- pass
-
- if not framework.startswith('/'):
- framework = "%s/%s" % (self.sourceFrameworkPath, framework)
-
- frameworkname = framework.split('/')[-1]
- localframework = self.frameworkDir + "/" + frameworkname
-
- # Framework already installed in previous run ... see above
- if skipExisting and os.path.isdir(localframework):
- return
-
- # Copy Framework
- os.system('cp -R "%s" "%s"' % (framework, self.frameworkDir))
-
- # De-Myllify
- os.system('find "%s" -name *debug* -exec rm -f {} \;' % localframework)
- os.system('find "%s" -name Headers -exec rm -rf {} \; 2>/dev/null' % localframework)
-
- # Install new Lib ID and Change Path to Frameworks for the Dynamic linker
- for lib in os.listdir(localframework + "/Versions/Current"):
- lib = "%s/Versions/Current/%s" % (localframework, lib)
- otoolProcess = Popen('otool -D "%s"' % lib, shell=True, stdout=PIPE, stderr=PIPE)
- try:
- libname = [line for line in otoolProcess.stdout][1].strip()
- except:
- libname = ''
- otoolProcess.stdout.close()
- if otoolProcess.wait() == 1: # we found some Resource dir or similar -> skip
- continue
- frameworkpath, libpath = libname.split(frameworkname)
- if self.bundle:
- newlibname = "@executable_path/../%s%s" % (frameworkname, libpath)
- else:
- newlibname = "@executable_path/%s%s" % (frameworkname, libpath)
- # print 'install_name_tool -id "%s" "%s"' % (newlibname, lib)
- os.system('chmod +w "%s"' % (lib))
- os.system('install_name_tool -id "%s" "%s"' % (newlibname, lib))
-
- self.resolveDependancies(lib)
-
- def determineDependancies(self, app):
- otoolPipe = Popen('otool -L "%s"' % app, shell=True, stdout=PIPE).stdout
- otoolOutput = [line for line in otoolPipe]
- otoolPipe.close()
- libs = [line.split()[0] for line in otoolOutput[1:] if ("Qt" in line or "phonon" in line) and "@executable_path" not in line]
- frameworks = [lib[:lib.find(".framework") + len(".framework")] for lib in libs]
- frameworks = [framework[framework.rfind('/') + 1:] for framework in frameworks]
- return zip(frameworks, libs)
-
- def changeDylPath(self, obj, framework, lib):
- newlibname = framework + lib.split(framework)[1]
- if self.bundle:
- newlibname = "@executable_path/../Frameworks/%s" % newlibname
- else:
- newlibname = "@executable_path/Frameworks/%s" % newlibname
-
- # print 'install_name_tool -change "%s" "%s" "%s"' % (lib, newlibname, obj)
- os.system('chmod +w "%s"' % (lib))
- os.system('chmod +w "%s"' % (obj))
- os.system('install_name_tool -change "%s" "%s" "%s"' % (lib, newlibname, obj))
-
-if __name__ == "__main__":
- if len(sys.argv) < 2:
- print "Wrong Argument Count (Syntax: %s [--nobundle] [--plugins=plugin1,plugin2,...] $TARGET_APP)" % sys.argv[0]
- sys.exit(1)
- else:
- bundle = True
- plugins = []
- offset = 1
-
- while offset < len(sys.argv) and sys.argv[offset].startswith("--"):
- if sys.argv[offset] == "--nobundle":
- bundle = False
-
- if sys.argv[offset].startswith("--plugins="):
- plugins = sys.argv[offset].split('=')[1].split(',')
-
- offset += 1
-
- targetDir = sys.argv[offset]
- if bundle:
- targetDir += "/Contents"
-
- InstallQt(targetDir, bundle, plugins)
-
- if bundle:
- webenginetarget = targetDir + '/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents'
-
- if os.path.isdir(webenginetarget):
- os.system('ln -s ../../../../../../ "%s"/Frameworks' % webenginetarget)
- InstallQt(webenginetarget, bundle, [], True)
+++ /dev/null
-#!/bin/bash
-# Don't consider packaging a success if any commands fail
-# See http://redsymbol.net/articles/unofficial-bash-strict-mode/
-set -euo pipefail
-
-myname=$0
-if [ -s "$myname" ] && [ -x "$myname" ]; then
- # $myname is already a valid file name
-
- mypath=$myname
-else
- case "$myname" in
- /*) exit 1;; # absolute path - do not search PATH
- *)
- # Search all directories from the PATH variable. Take
- # care to interpret leading and trailing ":" as meaning
- # the current directory; the same is true for "::" within
- # the PATH.
-
- # Replace leading : with . in PATH, store in p
- p=${PATH/#:/.:}
- # Replace trailing : with .
- p=${p//%:/:.}
- # Replace :: with :.:
- p=${p//::/:.:}
- # Temporary input field separator, see FAQ #1
- OFS=$IFS IFS=:
- # Split the path on colons and loop through each of them
- for dir in $p; do
- [ -f "$dir/$myname" ] || continue # no file
- [ -x "$dir/$myname" ] || continue # not executable
- mypath=$dir/$myname
- break # only return first matching file
- done
- # Restore old input field separator
- IFS=$OFS
- ;;
- esac
-fi
-
-if [ ! -f "$mypath" ]; then
- echo >&2 "cannot find full path name: $myname"
- exit 1
-fi
-
-SCRIPTDIR=$(dirname $mypath)
-QUASSEL_VERSION=$(git describe)
-BUILDTYPE=$1
-
-# check the working dir
-# Default to "." using Bash default-value syntax
-WORKINGDIR="${2:-.}"
-WORKINGDIR="${WORKINGDIR}/"
-PACKAGETMPDIR="${WORKINGDIR}PACKAGE_TMP_DIR_${BUILDTYPE}"
-QUASSEL_DMG="Quassel${BUILDTYPE}_MacOSX-x86_64_${QUASSEL_VERSION}.dmg"
-
-# Default to null string
-if [[ -z ${3:-} ]]; then
- ADDITIONAL_PLUGINS=""
-else
- # Options provided, append to list
- ADDITIONAL_PLUGINS=",$3"
-fi
-
-echo "ADDITIONAL_PLUGINS: ${ADDITIONAL_PLUGINS}"
-
-mkdir $PACKAGETMPDIR
-case $BUILDTYPE in
-"Client")
- cp -r ${WORKINGDIR}Quassel\ Client.app ${PACKAGETMPDIR}/
- ${SCRIPTDIR}/macosx_DeployApp.py --plugins=qcocoa,qgenericbearer,qcorewlanbearer,qmacstyle${ADDITIONAL_PLUGINS} "${PACKAGETMPDIR}/Quassel Client.app"
- ;;
-"Core")
- cp ${WORKINGDIR}quasselcore ${PACKAGETMPDIR}/
- ${SCRIPTDIR}/macosx_DeployApp.py --nobundle --plugins=qsqlite,qsqlpsql${ADDITIONAL_PLUGINS} ${PACKAGETMPDIR}
- ;;
-"Mono")
- cp -r ${WORKINGDIR}Quassel.app ${PACKAGETMPDIR}/
- ${SCRIPTDIR}/macosx_DeployApp.py --plugins=qsqlite,qsqlpsql,qcocoa,qgenericbearer,qcorewlanbearer,qmacstyle${ADDITIONAL_PLUGINS} "${PACKAGETMPDIR}/Quassel.app"
- ;;
-*)
- echo >&2 "Valid parameters are \"Client\", \"Core\", or \"Mono\"."
- rmdir ${PACKAGETMPDIR}
- exit 1
- ;;
-esac
-
-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.
-#
-# If you've come here to see why on earth your macOS build is failing despite
-# making changes entirely unrelated to macOS, you have my sympathy.
-#
-# There are two main approaches:
-#
-# 1. Let hdiutil calculate a size automatically
-#
-# 2. Separately calculate the size with a margin of error, then specify this
-# to hdiutil during disk image creation.
-#
-# Both seem to have caused issues, but in recent tests, option #1 seemed more
-# reliable.
-#
-# Option 1:
-
-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.
-#
-# Option 2:
-#
-#PACKAGESIZE_MARGIN="1.1"
-#PACKAGESIZE=$(echo "$(du -ms ${PACKAGETMPDIR} | cut -f1) * $PACKAGESIZE_MARGIN" | bc)
-#echo "PACKAGESIZE: $PACKAGESIZE MB"
-#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
-rm -rf ${PACKAGETMPDIR}
+++ /dev/null
-#!/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
-import os.path
-import sys
-import commands
-
-# Handling Qt properties
-import macosx_qt
-
-# ==============================
-# Constants
-# ==============================
-if len(sys.argv) < 2:
- sys.exit(1)
-
-SOURCE_DIR = sys.argv[1]
-
-if len(sys.argv) < 4:
- BUNDLE_NAME = "Quassel Client"
- EXE_NAME = "quasselclient"
-else:
- EXE_NAME = sys.argv[3]
- BUNDLE_NAME = sys.argv[2]
-
-# make the dir of the exe the target dir
-if(os.path.dirname(EXE_NAME)):
- CONTENTS_DIR = os.path.dirname(EXE_NAME) + "/"
-CONTENTS_DIR += BUNDLE_NAME + ".app/Contents/"
-
-BUNDLE_VERSION = commands.getoutput("git --git-dir=" + SOURCE_DIR + "/.git/ describe")
-ICONSET_FOLDER = "pics/iconset/"
-
-
-def createBundle():
- try:
- os.makedirs(CONTENTS_DIR + "MacOS")
- os.makedirs(CONTENTS_DIR + "Resources")
- except:
- pass
-
-
-def copyFiles(exeFile, iconset):
- os.system("cp %s %sMacOs/%s" % (exeFile, CONTENTS_DIR.replace(' ', '\ '), BUNDLE_NAME.replace(' ', '\ ')))
- os.system("cp -r %s/%s %s/Resources/quassel.iconset/" % (SOURCE_DIR, iconset, CONTENTS_DIR.replace(' ', '\ ')))
-
-
-def createPlist(bundleName, bundleVersion):
- templateFile = file(SOURCE_DIR + "/scripts/build/Info.plist", 'r')
- 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",
- "BUNDLE_VERSION": bundleVersion,
- "QT_MACOSX_DEPLOYMENT_TARGET": QT_MACOSX_DEPLOYMENT_TARGET})
- plistFile.close()
-
-def convertIconset():
- os.system("iconutil -c icns %s/Resources/quassel.iconset" % CONTENTS_DIR.replace(' ', '\ '))
- os.system("rm -R %s/Resources/quassel.iconset" % CONTENTS_DIR.replace(' ', '\ '))
-
-if __name__ == "__main__":
- createBundle()
- createPlist(BUNDLE_NAME, BUNDLE_VERSION)
- copyFiles(EXE_NAME, ICONSET_FOLDER)
- convertIconset()
+++ /dev/null
-#!/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
add_subdirectory(common)
-add_subdirectory(main)
if (BUILD_CORE)
add_subdirectory(core)
endif()
if (BUILD_TESTING)
add_subdirectory(test)
endif()
+
+add_subdirectory(main)
-# Convenience function to avoid boilerplate
-function(setup_executable _target _define)
- set_target_properties(${_target} PROPERTIES
- COMPILE_FLAGS ${_define}
- RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
- )
- target_link_libraries(${_target} PRIVATE ${ARGN})
- install(TARGETS ${_target} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
-endfunction()
-
if (HAVE_UMASK)
set_property(SOURCE main.cpp APPEND PROPERTY COMPILE_DEFINITIONS HAVE_UMASK)
endif()
endif()
endif()
-
# Build the executables
if (WANT_CORE)
- add_executable(quasselcore main.cpp ${WIN_RC})
- setup_executable(quasselcore -DBUILD_CORE Qt5::Core Quassel::Core)
+ quassel_add_executable(quasselcore COMPONENT Core SOURCES main.cpp ${WIN_RC} LIBRARIES Qt5::Core Quassel::Core)
endif()
if (WANT_QTCLIENT)
- add_executable(quasselclient WIN32 main.cpp ${WIN_RC})
- setup_executable(quasselclient -DBUILD_QTUI Qt5::Core Qt5::Gui Quassel::QtUi)
+ set(libs Qt5::Core Qt5::Gui Quassel::QtUi)
if (WITH_KDE)
- target_link_libraries(quasselclient PRIVATE KF5::CoreAddons)
+ list(APPEND libs KF5::CoreAddons)
endif()
+ quassel_add_executable(quasselclient COMPONENT Client SOURCES main.cpp ${WIN_RC} LIBRARIES ${libs})
endif()
if (WANT_MONO)
- add_executable(quassel WIN32 main.cpp monoapplication.cpp ${WIN_RC})
- setup_executable(quassel -DBUILD_MONO Qt5::Core Qt5::Gui Quassel::Core Quassel::QtUi)
+ set(libs Qt5::Core Qt5::Gui Quassel::Core Quassel::QtUi)
if (WITH_KDE)
- target_link_libraries(quassel PRIVATE KF5::CoreAddons)
- endif()
-endif()
-
-# Build bundles for MacOSX
-if (APPLE)
- if (WANT_QTCLIENT)
- add_custom_command(TARGET quasselclient POST_BUILD
- COMMAND ${CMAKE_SOURCE_DIR}/scripts/build/macosx_makebundle.py
- ${CMAKE_SOURCE_DIR} "Quassel Client" ${CMAKE_BINARY_DIR}/quasselclient)
- endif()
- if (WANT_MONO)
- add_custom_command(TARGET quassel POST_BUILD
- COMMAND ${CMAKE_SOURCE_DIR}/scripts/build/macosx_makebundle.py
- ${CMAKE_SOURCE_DIR} "Quassel" ${CMAKE_BINARY_DIR}/quassel)
- endif()
-
- if (DEPLOY)
- if (WANT_QTCLIENT)
- add_custom_command(TARGET quasselclient POST_BUILD WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- COMMAND ${CMAKE_SOURCE_DIR}/scripts/build/macosx_makePackage.sh Client ${CMAKE_BINARY_DIR} qsvgicon)
- endif()
- if (WANT_CORE)
- add_custom_command(TARGET quasselcore POST_BUILD WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- COMMAND ${CMAKE_SOURCE_DIR}/scripts/build/macosx_makePackage.sh Core ${CMAKE_BINARY_DIR})
- endif()
- if (WANT_MONO)
- add_custom_command(TARGET quassel POST_BUILD WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- COMMAND ${CMAKE_SOURCE_DIR}/scripts/build/macosx_makePackage.sh Mono ${CMAKE_BINARY_DIR} qsvgicon)
- endif()
+ list(APPEND libs KF5::CoreAddons)
endif()
+ quassel_add_executable(quassel COMPONENT Mono SOURCES main.cpp monoapplication.cpp ${WIN_RC} LIBRARIES ${libs})
endif()