#
# CMakeLists.txt is part of Brewtarget, and is Copyright the following
# authors 2009-2022
# - Chris Pavetto <chrispavetto@gmail.com>
# - Dan Cavanagh <dan@dancavanagh.com>
# - Daniel Moreno <danielm5@users.noreply.github.com>
# - Daniel Pettersson <pettson81@gmail.com>
# - Kregg Kemper <gigatropolis@yahoo.com>
# - Matt Young <mfsy@yahoo.com>
# - Maxime Lavigne (malavv) <duguigne@gmail.com>
# - Mik Firestone <mikfire@gmail.com>
# - Philip Greggory Lee <rocketman768@gmail.com>
# - Robby Workman <rworkman@slackware.com>
# - Théophane Martin <theophane.m@gmail.com>
#
# Brewtarget is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Brewtarget is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
# Creates a Makefile in the build directory, from where you can do builds and installs.
#
# NOTE: cmake . -DCMAKE_INSTALL_PREFIX=/tmp/blah && make DESTDIR=/foo
# will install to /foo/tmp/blah.
#
# See also - src/CMakeLists.txt
# - QtDesignerPlugins/CMakeLists.txt
#
# Standard make targets:
# * make - Regenerate the makefile if necessary and compile the source code. (On Linux, also
# converts the markdown list of changes (CHANGES.markdown) to Debian package format
# changelog.)
#
# NB: Other make targets will NOT regenerate the makefile from this CMakeLists.txt file.
# That means that, if you make a change that affects "make package" or "make clean" etc,
# you must run "make" before you run "make package" or "make clean" etc, otherwise your
# change will not be picked up. (If necessary, you can interrupt the build that "make"
# kicks off, as, by that point, the makefile will be updated.)
#
# * make clean - Delete compiled objects so next build starts from scratch
# * sudo make install - Install locally
# * make test - Run unit tests via CTest
# * make package - Makes .deb, .rpm, NSIS Installer, and .tar.bz2 binary packages. (TBD What about Mac?)
#
# On Linux, after make package, it's one of the following to install, if you want to
# install from the package rather than direct from the build tree (with sudo make install).
# (This would typically be for installing the package on a different machine then where the
# build was done.)
# $ sudo dpkg -i brewtarget*.deb
# $ sudo rpm -i brewtarget*.rpm
#
# On Mac and Windows environments, the `package` target will create an installer
# that may be executed to finish the installation.
#
# * make package_source - Makes a .tar.bz2 source package.
#
# Custom make targets:
# * make plugins - Builds the Qt Designer plugins
# * make source_doc - Makes Doxygen HTML documentation of the source in doc/html
# * make install-data
# * make install-runtime
# * make package_lint - Runs lintian and rpmlint on packages
#
#
# CMake options
# * CMAKE_INSTALL_PREFIX - /usr/local by default. Set this to /usr on Debian-based systems like Ubuntu.
# * DO_RELEASE_BUILD - OFF by default. If ON, will do a release build. Otherwise, debug build.
# * NO_MESSING_WITH_FLAGS - OFF by default. ON means do not add any build flags whatsoever. May override other options.
# NOTE: You need to run CMake to change the options (you can't change them just by running make, not even by running
# make clean. Eg, in the build directory, run the following to switch to debug builds:
# cmake -DDO_RELEASE_BUILD=OFF ..
#
# Uncomment the next line for slightly verbose build output
# Alternatively, for very verbose output, invoke make as follows:
# $ make VERBOSE=1
#
#set(CMAKE_VERBOSE_MAKEFILE ON)
#=======================================================================================================================
#================================================= CMake Configuration =================================================
#=======================================================================================================================
cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16)
# Ensure we are using modern behaviour of the project command (introduced in CMake 3.0) which enables setting version
# numbers via it (rather than manually setting individual major/minor/patch vars).
cmake_policy(SET CMP0048 NEW)
#=======================================================================================================================
#============================================== Project name and Version ===============================================
#=======================================================================================================================
# It's simplest to keep the project name all lower-case as it means we can use a lot more of the default settings for
# Linux packaging (where directory names etc are expected to be all lower-case)
project(brewtarget VERSION 3.0.3 LANGUAGES CXX)
message(STATUS "Building ${PROJECT_NAME} version ${PROJECT_VERSION}")
message(STATUS "PROJECT_SOURCE_DIR is ${PROJECT_SOURCE_DIR}")
# Sometimes we do need the capitalised version of the project name
set(capitalisedProjectName Brewtarget)
#=======================================================================================================================
#======================================================= Options =======================================================
#=======================================================================================================================
option(DO_RELEASE_BUILD "If on, will do a release build. Otherwise, debug build." OFF)
option(NO_MESSING_WITH_FLAGS "On means do not add any build flags whatsoever. May override other options." OFF)
#=======================================================================================================================
#===================================================== Directories =====================================================
#=======================================================================================================================
#================================================== Source directories =================================================
#
# ${repoDir} = the directory containing this (CMakeLists.txt) file
# ${repoDir}/src = C++ source code (with the exception of Qt Designer Plugins)
# ${repoDir}/QtDesignerPlugins = Qt Designer Plugins
# ${repoDir}/ui = QML UI layout files
# ${repoDir}/data = Binary files, including sounds and default database
# ${repoDir}/translations = Translated texts
# ${repoDir}/mac = Mac-specific files (desktop icon)
# ${repoDir}/win = Windows-specific files (desktop icon)
# ${repoDir}/packaging = Packaging-related config
#
set(repoDir "${CMAKE_CURRENT_SOURCE_DIR}")
# AFAICT this is used only in cmake/modules/NSIS.template.in
set(WINDIR "${repoDir}/win")
# Location of custom CMake modules. (At the moment, there is only one, which is used for Windows packaging
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
#================================================= Install directories =================================================
# Note that WIN32 is true "when the target system is Windows, including Win64" (see
# https://cmake.org/cmake/help/latest/variable/WIN32.html), so this is ALL versions of Windows, not just 32-bit.
# UNIX is true when the "the target system is UNIX or UNIX-like", so it is set when we're building for Mac and for
# Linux. There is a separate flag (APPLE) when we're building for Mac, but, AFAICT, no specific flag for Linux.
#
if(UNIX AND NOT APPLE)
#============================================= Linux Install Directories ============================================
# By default, CMAKE_INSTALL_PREFIX is:
# - /usr/local on Linux (& Mac)
# - c:/Program Files/${PROJECT_NAME} on Windows
#
# On a lot of Linux distros, including Debian and derived systems (such as Ubuntu), it's more normal for pretty much
# all user-installed apps to go in /usr/bin rather than /usr/local/bin, so CMAKE_INSTALL_PREFIX can be overridden on
# the command line via "--prefix usr"
#
# (See http://lists.busybox.net/pipermail/busybox/2010-December/074114.html for a great, albeit slightly depressing,
# explanation of why there are so many places for binaries to live on Unix/Linux systems. FWIW, the current
# "standards" for Linux are at https://refspecs.linuxfoundation.org/fhs.shtml but these are open to interpretation.)
#
# .:TBD: We also allow -DEXEC_PREFIX=/usr (used in .github/workflows/linux-ubuntu.yml) but I'm not sure if this is
# needed or could be replaced by "--prefix usr"
#
# Debian standard directories.
if(NOT EXEC_PREFIX)
set(EXEC_PREFIX ${CMAKE_INSTALL_PREFIX})
endif()
set(installSubDir_data "share/${CMAKE_PROJECT_NAME}")
set(installSubDir_doc "share/doc/${CMAKE_PROJECT_NAME}")
set(installSubDir_bin "bin")
elseif(WIN32)
#============================================ Windows Install Directories ===========================================
# For some damn reason, for the NSIS installer,
# the prefix needs to be empty. Also, seems that the .exe
# needs to be in bin/. Fucking piece of shit CPack...
# Can anybody shed some light on this situation?
#set(CMAKE_INSTALL_PREFIX "")
set(CPACK_INSTALL_PREFIX "")
set(installSubDir_data "data")
set(installSubDir_doc "doc")
set(installSubDir_bin "bin")
elseif(APPLE)
#============================================== Mac Install Directories =============================================
set(installSubDir_data "Contents/Resources")
set(installSubDir_doc "Contents/Resources/en.lproj")
set(installSubDir_bin "Contents/MacOS")
endif()
#=======================================================================================================================
#====================================================== File Names =====================================================
#=======================================================================================================================
if(APPLE)
# Use capital letters. Don't question the APPLE.
set(fileName_executable "${capitalisedProjectName}")
else()
set(fileName_executable "${PROJECT_NAME}")
endif()
set(fileName_unitTestRunner "${PROJECT_NAME}_tests")
#=======================================================================================================================
#=================================================== General Settings ==================================================
#=======================================================================================================================
# This is needed to enable the add_test() command
enable_testing()
#=======================================================================================================================
#=============================================== Installation Components ===============================================
#=======================================================================================================================
# For architecture-independent data
set(DATA_INSTALL_COMPONENT "Data")
# For architecture-dependent binaries
set(RUNTIME_INSTALL_COMPONENT "Runtime")
#=======================================================================================================================
#================================================= Mac Bundle Settings =================================================
#=======================================================================================================================
set(MACOSX_BUNDLE_BUNDLE_NAME "${capitalisedProjectName}")
#set(MACOSX_BUNDLE_GUI_IDENTIFIER)
#set(MACOSX_BUNDLE_INFO_STRING)
#set(MACOSX_BUNDLE_BUNDLE_VERSION)
#set(MACOSX_BUNDLE_SHORT_VERSION_STRING)
set(MACOSX_BUNDLE_LONG_VERSION_STRING ${PROJECT_VERSION})
set(MACOSX_BUNDLE_ICON_FILE "BrewtargetIcon.icns")
set(MACOSX_BUNDLE_COPYRIGHT "GPLv3")
#=======================================================================================================================
#============================================== Compiler settings & flags ==============================================
#=======================================================================================================================
# We use different compilers on different platforms. Where possible, we want to let CMake handle the actual compiler
# settings
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# We need C++17 or later for nested namespaces (and C++11 or later for lambdas)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include_directories(${repoDir}/src)
include_directories("${CMAKE_BINARY_DIR}/src") # In case of out-of-source build.
include_directories("${CMAKE_BINARY_DIR}/QtDesignerPlugins")
# GCC-specific flags
if(NOT ${NO_MESSING_WITH_FLAGS})
if(CMAKE_COMPILER_IS_GNUCXX)
#
# We would like to avoid having an executable stack, partly as a good thing in itself, and partly because, by
# default, rpmlint with throw a missing-PT_GNU_STACK-section error if we don't.
#
# In theory, the compiler should work out automatically whether we need an executable stack, decide the answer is
# "No" and pass all the right options to the linker. In practice, it seems this doesn't happen for reasons I
# have, as yet, to discover.
#
# We attempt here to to assert manually that the stack should not be executable. The "-z noexecstack" should
# get passed through by gcc the linker (see https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options) and
# the GNU linker (https://sourceware.org/binutils/docs/ld/Options.html) should recognise "-z noexecstack" as
# "Marks the object as not requiring executable stack".
#
# However, this is not sufficient. So, for the moment, we suppress the rpmlint error (see
# packaging/rpmLintFilters.toml).
#
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -ansi -pedantic -Wno-long-long -O2 -z noexecstack")
#
# -g3 should give even more debugging information thatn -g (which is equivalent to -g2)
# -no-pie -fno-pie -rdynamic are needed for Boost stacktrace to work properly - at least according to comments
# at https://stackoverflow.com/questions/52583544/boost-stack-trace-not-showing-function-names-and-line-numbers
#
# But, for some reason, gcc on Windows does not accept -rdynamic
#
if(NOT WIN32)
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -no-pie -fno-pie -rdynamic")
else()
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -no-pie -fno-pie")
endif()
endif()
# Speed up compilation if using gcc.
if(UNIX AND NOT APPLE)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -pipe")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pipe")
endif()
endif()
# Windows-specific compilation settings
if(WIN32)
# See https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Link-Options.html#Link-Options for more on GCC linker options
# In theory, this statically links all the GCC and MinGW libraries, so we don't have to ship DLLs for them. In
# practice, it seems we still need a fair few other DLLs.
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")
endif()
# Mac-specific compilation settings
if(APPLE AND NOT CMAKE_OSX_ARCHITECTURES)
set(CMAKE_OSX_ARCHITECTURES x86_64) # Build intel 64-bit binary.
#set(CMAKE_OSX_ARCHITECTURES i386 x86_64) # Build intel binary.
#set(CMAKE_OSX_ARCHITECTURES ppc i386 ppc64 x86_64) # Build universal binary.
endif()
if(APPLE)
# As explained at https://stackoverflow.com/questions/5582211/what-does-define-gnu-source-imply, defining _GNU_SOURCE
# gives access to various non-standard GNU/Linux extension functions and changes the behaviour of some POSIX
# functions.
#
# .:TBD:. Not sure exactly why we need this set on Mac builds
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE")
endif()
#======Speed up compilation by using precompiled headers (PCH) for development======
# (ADD_PCH_RULE _header_filename _src_list)
# Version 7/26/2010
#
# use this macro before "add_executable"
#
# _header_filename
# header to make a .gch
#
# _src_list
# the variable name (do not use ${..}) which contains a
# a list of sources (a.cpp b.cpp c.cpp ...)
# This macro will append a header file to it, then this src_list can be used in
# "add_executable..."
#
#
# Now a .gch file should be generated and gcc should use it.
# (add -Winvalid-pch to the cpp flags to verify)
#
# make clean should delete the pch file
#
# example : ADD_PCH_RULE(headers.h myprog_SRCS)
MACRO (ADD_PCH_RULE _header_filename _src_list)
set(_gch_filename "${CMAKE_CURRENT_BINARY_DIR}/${_header_filename}.gch")
set(_header "${CMAKE_CURRENT_SOURCE_DIR}/${_header_filename}")
LIST(APPEND ${_src_list} ${_gch_filename})
SET (_args ${CMAKE_CXX_FLAGS})
LIST(APPEND _args -c ${_header} -o ${_gch_filename})
GET_DIRECTORY_PROPERTY(DIRINC include_directories)
FOREACH(_inc ${DIRINC})
LIST(APPEND _args "-I" ${_inc})
ENDFOREACH(_inc ${DIRINC})
SEPARATE_ARGUMENTS(_args)
ADD_CUSTOM_COMMAND(OUTPUT ${_gch_filename}
COMMAND rm -f ${_gch_filename}
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args}
DEPENDS ${_header})
ENDMACRO(ADD_PCH_RULE _src_list _header_filename)
# See discussion at https://stackoverflow.com/questions/1138301/precompiled-headers-do-we-really-need-them about the
# pros and cons of using pre-compiles headers. For now we keep them turned off.
#if( UNIX AND NOT APPLE )
# SET( precompiled_h "equipment.h" "fermentable.h" "hop.h" "instruction.h" "mash.h" "mashstep.h" "misc.h" "recipe.h" "style.h" "unit.h" "water.h" "yeast.h" "database.h" "brewtarget.h" )
# FOREACH( header ${precompiled_h} )
# ADD_PCH_RULE( ${header} brewtarget_SRCS )
# ENDFOREACH()
#endif()
#=======================================================================================================================
#=================================================== Set build type ====================================================
#=======================================================================================================================
# We might always to tell the compiler to include debugging information (eg via the -g option on gcc). It makes the
# binaries slightly bigger on Linux, but helps greatly in analysing core dumps etc. (In closed-source projects people
# sometimes turn it off for release builds to make it harder to reverse engineer the software, but obviously that's not
# an issue for us.)
#
# However, setting CMAKE_BUILD_TYPE to "Debug", also changes other things, such as the default location for config
# files, which we don't want on a release build, so we would probably need to set compiler flags directly.
#
# .:TBD:. Investigate whether setting CMAKE_BUILD_TYPE to "RelWithDebInfo" does what we want.
#
if(${DO_RELEASE_BUILD})
set(CMAKE_BUILD_TYPE "Release")
else()
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_ENABLE_EXPORTS true)
endif()
message(STATUS "Doing ${CMAKE_BUILD_TYPE} build (DO_RELEASE_BUILD = ${DO_RELEASE_BUILD})")
#=======================================================================================================================
#========================================= Find various libraries we depend on =========================================
#=======================================================================================================================
#======================================================= Find Qt =======================================================
# We need not just the "core" bit of Qt but various "optional" elements.
#
# We try to keep the minimum Qt version we need as low as we can.
#
# Note that if you change the minimum Qt version, you need to make corresponding changes to the .github/workflows/*.yml
# files so that GitHub uses the appropriate version of Qt for the automated builds.
#
# For the moment, max version we can have here is 5.9.5, because that's what Ubuntu 18.04 topped out at
#
set(QT5_MIN_VERSION 5.9.5)
# Set the AUTOMOC property on all targets. This tells CMake to automatically handle the Qt Meta-Object Compiler (moc)
# preprocessor (ie the thing that handles Qt's C++ extensions), without having to use commands such as QT4_WRAP_CPP(),
# QT5_WRAP_CPP(), etc. In particular, it also means we no longer have to manually identify which headers have Q_OBJECT
# declarations etc.
set(CMAKE_AUTOMOC ON)
# Set the AUTOUIC property on all targets. This tells CMake to automatically handle the Qt uic code generator, without
# having to use commands such as QT4_WRAP_UI(), QT5_WRAP_UI(), etc.
set(CMAKE_AUTOUIC ON)
# This tells CMake where to look for .ui files when AUTOUIC is on
set(CMAKE_AUTOUIC_SEARCH_PATHS ${repoDir}/ui)
# Set the AUTORCC property on all targets. This tells CMake to automatically handle the Qt Resource Compiler (rcc),
# without having to use commands such as QT4_ADD_RESOURCES(), QT5_ADD_RESOURCES(), etc.
# Note that you need to add your .qrc file(s) as sources to the target you are building
set(CMAKE_AUTORCC ON)
# Name of FOLDER for *_autogen targets that are added automatically by CMake for targets for which AUTOMOC is enabled.
# Note that this replaces AUTOMOC_TARGETS_FOLDER.
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER ${CMAKE_CURRENT_BINARY_DIR}/autogen)
# Directory where AUTOMOC, AUTOUIC and AUTORCC generate files for the target.
set_property(GLOBAL PROPERTY AUTOGEN_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/autogen)
# As moc files are generated in the binary dir, tell CMake
# to always look for includes there:
set(CMAKE_INCLUDE_CURRENT_DIR ON)
#
# Although it's possible to do individual find_package commands for each bit of Qt we want to use (Qt5Core, Qt5Widgets,
# Qt5Sql, etc), the newer, and more compact, way of doing things (per
# https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html and https://doc.qt.io/qt-5/cmake-get-started.html) is to do
# one find for Qt as a whole and to list the components that we need. Depending on what versions of CMake and Qt you
# have installed, the way to find out what components exist varies a bit, but there is a relatively recent list at
# https://stackoverflow.com/questions/62676472/how-to-list-all-cmake-components-of-qt5
#
set(qtCommonComponents
Core
Gui
Multimedia
Network
PrintSupport
Sql
Svg # Required to make the deploy scripts pick up the svg plugins
Widgets
Xml # TBD: Not sure we need this any more
)
set(qtTestComponents
Test
)
set(qtToolsComponents
LinguistTools
)
list(APPEND qtAllComponents ${qtCommonComponents} ${qtToolsComponents} ${qtTestComponents})
find_package(Qt5 ${QT5_MIN_VERSION} COMPONENTS ${qtAllComponents} REQUIRED)
# Each package has its own include directories that we need to make sure the compiler knows about
foreach(qtComponent IN LISTS qtAllComponents)
# Sometimes it's useful that part of a variable name can be specified by expanding another variable!
include_directories(${Qt5${qtComponent}_INCLUDE_DIRS})
endforeach()
# Qt wants position independent code in certain circumstances - specifically "You must build your code with position
# independent code if Qt was built with -reduce-relocations. Compile your code with -fPIC (and not with -fPIE)."
if(Qt5_POSITION_INDEPENDENT_CODE)
# This will initialize the POSITION_INDEPENDENT_CODE property on all the targets
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
# There's apparently a whole bunch of extra work we need to do to use Qt on Windows
if(WIN32)
# .:TBD:. Not sure whether/why we need these additional Qt components on Windows
find_package(Qt5MultimediaWidgets REQUIRED)
find_package(Qt5OpenGL REQUIRED)
get_target_property(QtCore_location Qt5::Core LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtGui_location Qt5::Gui LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtMultimedia_location Qt5::Multimedia LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtMultimediaWidgets_location Qt5::MultimediaWidgets LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtNetwork_location Qt5::Network LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtOpenGL_location Qt5::OpenGL LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtPrintSupport_location Qt5::PrintSupport LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQgif_location Qt5::QGifPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQico_location Qt5::QICOPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQjpeg_location Qt5::QJpegPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQsvgIcon_location Qt5::QSvgIconPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQsvg_location Qt5::QSvgPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQtiff_location Qt5::QTiffPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQWindows_location Qt5::QWindowsIntegrationPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtSqliteDriver_location Qt5::QSQLiteDriverPlugin LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtSql_location Qt5::Sql LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtSvg_location Qt5::Svg LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtWidgets_location Qt5::Widgets LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtXml_location Qt5::Xml LOCATION_${CMAKE_BUILD_TYPE})
# .:TBD:. Not clear whether/where these xxx_DLLs variables get used
set(Qt_DLLs ${QtCore_location}
${QtGui_location}
${QtMultimedia_location}
${QtMultimediaWidgets_location}
${QtNetwork_location}
${QtOpenGL_location}
${QtPrintSupport_location}
${QtSql_location}
${QtSvg_location}
${QtWebKit_location}
${QtWebKitWidgets_location}
${QtWidgets_location}
${QtXml_location})
set(SQL_Drivers_DLLs ${QtSqliteDriver_location})
set(Image_Formats_DLLs ${QtQgif_location}
${QtQico_location}
${QtQjpeg_location}
${QtQmng_location}
${QtQsvg_location}
${QtQtiff_location})
set(Icon_Engines_DLLs ${QtQsvgIcon_location})
set(Platform_DLLs ${QtQWindows_location})
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(QT5_BIN_DIR "${_qmake_executable}" DIRECTORY)
message("QT5_BIN_DIR = ${QT5_BIN_DIR}")
#
# Per https://doc.qt.io/qt-6/windows-deployment.html, the windeployqt executable creates all the necessary folder
# tree "containing the Qt-related dependencies (libraries, QML imports, plugins, and translations) required to run
# the application from that folder".
#
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT5_BIN_DIR}")
if(EXISTS ${WINDEPLOYQT_EXECUTABLE})
add_executable(Qt5::windeployqt IMPORTED)
set_target_properties(Qt5::windeployqt PROPERTIES IMPORTED_LOCATION ${WINDEPLOYQT_EXECUTABLE})
endif()
# International Components for Unicode
file(GLOB IcuDlls "${QT5_BIN_DIR}/libicu*.dll")
endif()
# Uncomment the following line to show what version of Qt is actually being used
message(STATUS "Using Qt version " ${Qt5Core_VERSION})
#===================================================== Find Boost ======================================================
# Boost is a collection of separate libraries, some, but not all, of which are header-only. We only specify the Boost
# libraries that we actually use.
#
# On Linux, there are cases where we need a more recent version of a Boost library than is readily-available in system-
# supplied packages. I haven't found a slick way to solve this in CMake, though https://github.com/Orphis/boost-cmake
# looks promising. (For header-only Boost libraries, you might think it would be relatively painless to pull them in
# from where they are hosted on GitHub (see https://github.com/boostorg), but this is not the case. AFAICT you can't
# easily pull a specific release, and just pulling master doesn't guarantee that everything compiles.) So, anyway, on
# Debian-based distros of Linux, such as Ubuntu, you need to do the following to install Boost 1.79 in place of whatever
# (if anything) is already installed:
#
# $ sudo apt remove boost-all-dev
# $ cd ~
# $ mkdir boost-tmp
# $ cd boost-tmp
# $ wget https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2
# $ tar --bzip2 -xf boost_1_79_0.tar.bz2
# $ cd boost_1_79_0
# $ ./bootstrap.sh --prefix=/usr
# $ sudo ./b2 install
# $ cd ../..
# $ sudo rm -rf boost-tmp
#
# (Obviously if you want to make the necessary change to install an even more recent version than Boost 1.79 then that
# should be fine.)
#
# We do the same in .github/workflows/linux-ubuntu.yml to make GitHub automated builds work.
#
# Note that this means we want to _statically_ link Boost rather than force end users to have to do all the palava above
#
# ************************
# *** Boost Stacktrace ***
# ************************
#
# We use this for diagnostics. In certain error cases it's very helpful to be able to log the call stack.
#
# On Windows, using MSYS2, the mingw-w64-boost packages do not include libboost_stacktrace_backtrace, but
# https://www.boost.org/doc/libs/1_76_0/doc/html/stacktrace/configuration_and_build.html suggests it is not required
# (because on Windows, if you have libbacktrace installed, you can set BOOST_STACKTRACE_USE_BACKTRACE in header-only
# mode).
#
# .:TODO:. Not sure how to get libboost_stacktrace_backtrace installed on Mac. It doesn't seem to be findable by
# CMake after installing Boost via Homebrew (https://brew.sh/). For the moment, skip trying to use
# libboost_stacktrace_backtrace on Mac
#
# .:TODO:. So far don't have stacktraces working properly on Windows (everything shows as register_frame_ctor), so
# that needs some more investigation. (It could be that it's a bug in Boost, at least according to
# https://stackoverflow.com/questions/54333608/boost-stacktrace-not-demangling-names-when-cross-compiled)
set(Boost_USE_STATIC_LIBS ON)
if(WIN32)
find_package(Boost REQUIRED)
elseif(APPLE)
find_package(Boost REQUIRED)
else()
# TBD Some users report problems getting CMake to find libboost_stacktrace_backtrace on Ubuntu and Gentoo, so disable it
# for now and fallback to the header-only version
#find_package(Boost REQUIRED COMPONENTS stacktrace_backtrace)
find_package(Boost REQUIRED)
endif()
include_directories(${Boost_INCLUDE_DIRS})
# Uncomment the next two lines if you want to find where Boost headers and DLLs are on your system
message( "Boost include directories: " ${Boost_INCLUDE_DIRS} )
message( "Boost libraries: " ${Boost_LIBRARIES} )
#
# Extra requirements for Boost Stacktrace
#
# Per https://www.boost.org/doc/libs/1_76_0/doc/html/stacktrace/configuration_and_build.html, by default
# Boost.Stacktrace is a header-only library. However, you get better results by linking (either statically or
# dynamically) with a helper library. Of the various options, it seems like boost_stacktrace_backtrace gives the most
# functionality over the most platforms. This has dependencies on:
# - libdl on POSIX platforms -- but see note below
# - libbacktrace
# The latter is an external library on Windows. On POSIX plaforms it's typically already either installed on the system
# (eg see https://man7.org/linux/man-pages/man3/backtrace.3.html) or built in to the compiler. Fortunately, CMake knows
# how to do the right thing in either case, thanks to https://cmake.org/cmake/help/latest/module/FindBacktrace.html.
#
# Just to make things extra fun, in 2021, the GNU compilers did away with libdl and incorporated its functionality into
# libc, per the announcement of GNU C Library v2.3.4 at
# https://sourceware.org/pipermail/libc-alpha/2021-August/129718.html. This means, if we're using the GNU tool chain
# and libc is v2.3.4 or newer, then we should NOT look for libdl, as we won't find it!
#
# Fortunately, CMake has a special variable, CMAKE_DL_LIBS, that is, essentially "whatever library you need to link to
# for dlopen and dlclose", so we don't need to worry about libc versions.
#
if(NOT WIN32)
set(DL_LIBRARY ${CMAKE_DL_LIBS})
endif()
find_package(Backtrace REQUIRED)
# For the moment, leave default settings for Mac as can't work out how to get the backtrace version of stacktrace
# working. (Should still get stack traces on Mac, but might not get as much info in them as we'd like.)
if(NOT APPLE)
if(NOT WIN32)
# TBD Some users report problems getting CMake to find libboost_stacktrace_backtrace on Ubuntu and Gentoo, so disable it
# for now and fallback to the header-only version
# add_compile_definitions(BOOST_STACKTRACE_DYN_LINK)
endif()
# add_compile_definitions(BOOST_STACKTRACE_USE_BACKTRACE)
endif()
message("Backtrace libs " ${DL_LIBRARY} " | " ${Backtrace_LIBRARIES} " | " ${Boost_LIBRARIES})
#=================================================== Find Xerces-C++ ===================================================
# CMake already knows how to find and configure Xerces-C++, see
# https://cmake.org/cmake/help/latest/module/FindXercesC.html
find_package(XercesC REQUIRED)
include_directories(${XercesC_INCLUDE_DIRS})
# Uncomment the next two lines if you want to find where Xerces headers and DLLs are on your system
#message("Xerces-C++ include directories: " ${XercesC_INCLUDE_DIRS})
#message("Xerces-C++ libraries: " ${XercesC_LIBRARIES})
#==================================================== Find Xalan-C++ ===================================================
# Same comments apply here as for Xerces above
find_package(XalanC REQUIRED)
include_directories(${XalanC_INCLUDE_DIRS})
# Uncomment the next two lines if you want to find where Xalan headers and DLLs are on your system
#message("Xalan-C++ include directories: " ${XalanC_INCLUDE_DIRS})
#message("Xalan-C++ libraries: " ${XalanC_LIBRARIES})
#========================================= Find MinGW (only needed on Windows) =========================================
if(WIN32)
# This is to detect whether we're using MSYS2 and/or MinGW
if(WIN32)
execute_process(COMMAND uname OUTPUT_VARIABLE uname)
message(STATUS "Uname is " ${uname})
if(uname MATCHES "^MSYS" OR uname MATCHES "^MINGW")
message(STATUS "Running on MSYS/MinGW")
set(MINGW true)
endif()
endif()
# Find extra MinGW-specific dlls.
if(MINGW)
if(NOT MINGW_BIN_DIR)
# Yes, it's mingw32-make.exe even on 64-bit systems
FIND_PATH(MINGW_BIN_DIR "mingw32-make.exe")
endif()
if(NOT EXISTS ${MINGW_BIN_DIR})
message(FATAL_ERROR "MinGW bin dir not found. Run cmake again with the option -DMINGW_BIN_DIR=c:/path/to/mingw/bin")
else()
get_filename_component(Mingw_Path ${CMAKE_CXX_COMPILER} PATH)
endif()
message(STATUS "MINGW_BIN_DIR " ${MINGW_BIN_DIR})
endif()
endif()
# Shows all the places we are looking for headers
message(STATUS "CMAKE_SYSTEM_INCLUDE_PATH: ${CMAKE_SYSTEM_INCLUDE_PATH}")
message(STATUS "CMAKE_INCLUDE_PATH: ${CMAKE_INCLUDE_PATH}")
include(InstallRequiredSystemLibraries)
#=======================================================================================================================
#=========================================== Generate config.h from config.in ==========================================
#=======================================================================================================================
# Taking src/config.in as input, we generate (in the build subdirectory only) config.h. This is a way to inject CMake
# variables into the code.
#
# All variables written as "${VAR}" in config.in will be replaced by the value of VAR in config.h.
# Eg "#define CONFIG_DATA_DIR ${CONFIG_DATA_DIR}" in config.in will be replaced by the below corresponding value in
# ${CONFIG_DATA_DIR} below when configure_file() is called.
#
set(CONFIG_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${installSubDir_data}/")
string(TIMESTAMP BUILD_TIMESTAMP "%Y-%m-%d %H:%M:%S (UTC)" UTC)
configure_file(src/config.in src/config.h)
#=======================================================================================================================
#=============================================== Embedded Resource Files ===============================================
#=======================================================================================================================
# We don't need to list the embedded resource files themselves here, just the "Resource Collection File" that lists what
# they are.
set(filesToCompile_qrc "${CMAKE_CURRENT_SOURCE_DIR}/brewtarget.qrc")
#=======================================================================================================================
#=========================================== Files included with the program ===========================================
#=======================================================================================================================
# These are files that actually ship/install as real files, rather than Qt resources
#
# List of documentation files to be installed. Note that ${repoDir}/COPYRIGHT is NOT included here as it needs special
# case handling below.
set(filesToInstall_docs ${repoDir}/README.markdown)
# List of data files to be installed.
set(filesToInstall_data ${repoDir}/data/default_db.sqlite
${repoDir}/data/DefaultData.xml
# Yes, I know this is 'documentation', but Debian policy suggests it should be
# with the data (see section 12.3 of the policy manual).
${repoDir}/doc/manual-en.pdf)
# Desktop files to install.
set(filesToInstall_desktop ${repoDir}/brewtarget.desktop)
# Icon files to install.
set(filesToInstall_icons ${repoDir}/images/brewtarget.svg)
# This is the list of translation files to update (from translatable strings in the source code) and from which the
# binary .qm files will be generated and shipped. Note that src/OptionDialog.cpp controls which languages are shown to
# the user as options for the UI
set(translationSourceFiles ${repoDir}/translations/bt_ca.ts # Catalan
${repoDir}/translations/bt_cs.ts # Czech
${repoDir}/translations/bt_de.ts # German
${repoDir}/translations/bt_en.ts # English
${repoDir}/translations/bt_el.ts # Greek
${repoDir}/translations/bt_es.ts # Spanish
${repoDir}/translations/bt_et.ts # Estonian
${repoDir}/translations/bt_eu.ts # Basque
${repoDir}/translations/bt_fr.ts # French
${repoDir}/translations/bt_gl.ts # Galician
${repoDir}/translations/bt_nb.ts # Norwegian Bokmal
${repoDir}/translations/bt_it.ts # Italian
${repoDir}/translations/bt_lv.ts # Latvian
${repoDir}/translations/bt_nl.ts # Dutch
${repoDir}/translations/bt_pl.ts # Polish
${repoDir}/translations/bt_pt.ts # Portuguese
${repoDir}/translations/bt_hu.ts # Hungarian
${repoDir}/translations/bt_ru.ts # Russian
${repoDir}/translations/bt_sr.ts # Serbian
${repoDir}/translations/bt_sv.ts # Swedish
${repoDir}/translations/bt_tr.ts # Turkish
${repoDir}/translations/bt_zh.ts) # Chinese
set(filesToInstall_windowsIcon ${repoDir}/win/icon.rc)
set(filesToInstall_sounds ${repoDir}/data/sounds/45minLeft.wav
${repoDir}/data/sounds/addFuckinHops.wav
${repoDir}/data/sounds/aromaHops.wav
${repoDir}/data/sounds/beep.wav
${repoDir}/data/sounds/bitteringHops.wav
${repoDir}/data/sounds/checkBoil.wav
${repoDir}/data/sounds/checkFirstRunnings.wav
${repoDir}/data/sounds/checkGravity.wav
${repoDir}/data/sounds/checkHydrometer.wav
${repoDir}/data/sounds/checkMashTemps.wav
${repoDir}/data/sounds/checkTemp.wav
${repoDir}/data/sounds/clarifyingAgent.wav
${repoDir}/data/sounds/cleanup.wav
${repoDir}/data/sounds/closeFuckinValves.wav
${repoDir}/data/sounds/closeValves.wav
${repoDir}/data/sounds/doughIn.wav
${repoDir}/data/sounds/drinkAnotherHomebrew.wav
${repoDir}/data/sounds/drinkHomebrew.wav
${repoDir}/data/sounds/emptyMashTun.wav
${repoDir}/data/sounds/extraPropane.wav
${repoDir}/data/sounds/flameout.wav
${repoDir}/data/sounds/flavorHops.wav
${repoDir}/data/sounds/heatWater.wav
${repoDir}/data/sounds/mashHops.wav
${repoDir}/data/sounds/pitchYeast.wav
${repoDir}/data/sounds/sanitize.wav
${repoDir}/data/sounds/sparge.wav
${repoDir}/data/sounds/startBurner.wav
${repoDir}/data/sounds/startChill.wav
${repoDir}/data/sounds/stirMash.wav)
# We mostly don't need to explicitly specify the .ui files because AUTOUIC will find them all for us. However, I
# haven't found a way to connect AUTOUIC with the translation stuff below, so grab a list of all the .ui files here for
# that.
file(GLOB_RECURSE filesToCompile_ui "${repoDir}/ui/*.ui")
set(filesToInstall_macPropertyList "${repoDir}/mac/Info.plist")
set(filesToInstall_macIcons "${repoDir}/mac/BrewtargetIcon.icns")
set(filesToInstall_changeLogUncompressed "${repoDir}/CHANGES.markdown")
# See below for how this one gets created from filesToInstall_changeLogUncompressed
set(filesToInstall_changeLogCompressed "${CMAKE_CURRENT_BINARY_DIR}/changelog.gz")
#=======================================================================================================================
#=========================================== Process other CMakeList.txt files =========================================
#=======================================================================================================================
# We try to restrict QtDesignerPlugins/CMakeLists.txt and src/CMakeLists.txt to just holding lists of source files,
# otherwise the dependencies and interactions between those files and this one get a bit hard to follow.
include(QtDesignerPlugins/CMakeLists.txt)
include(src/CMakeLists.txt)
#=======================================================================================================================
#==================================================== Translations =====================================================
#=======================================================================================================================
#
# We need to do two processes with Translation Source (.ts) XML files:
# - Update them from the source code, ie to ensure they have all the tr(), QObject::tr() etc calls from the .cpp files
# and all the translatable strings from the .ui files -- which can be done manually from the command line with
# lupdate
# - Generate the binary .qm files that ship with the application and are used at run time -- which can be done
# manually from the command line with lrelease
#
# Note that qt5_add_translation() _only_ does the latter and it is only qt5_create_translation() which does both.
# HOWEVER, there is a longstanding bug in qt5_create_translation() that means it adds all the .ts files to the list of
# files that get deleted when you invoke "make clean". (See https://bugreports.qt.io/browse/QTBUG-31860,
# https://bugreports.qt.io/browse/QTBUG-41736, https://bugreports.qt.io/browse/QTBUG-76410,
# https://bugreports.qt.io/browse/QTBUG-96549.)
#
# There are various workarounds proposed on the internet -- eg setting the CLEAN_NO_CUSTOM property on the
# ${repoDir}/translations directory (something which itself requires jumping through a few hoops) -- but I have not had
# success with any of them. Instead, taking inspiration from
# https://codereview.qt-project.org/c/qt/qttools/+/261912/1/src/linguist/Qt5LinguistToolsMacros.cmake, we run lupdate
# manually and then let qt5_add_translation invoke lrelease.
#
# Of course we have to do declare everything backwards for the dependencies:
# - The executable will depend on translationsTarget
# - translationsTarget depends on the binary .qm files, as generated by qt5_add_translation
# - But before translationsTarget is built, we always run lupdate
#
qt5_add_translation(QM_FILES ${translationSourceFiles})
# Add a target for the QM_FILES so that we can add the translations as a dependency for the executable later.
add_custom_target(translationsTarget DEPENDS ${QM_FILES})
add_custom_command(TARGET translationsTarget PRE_BUILD
COMMAND ${Qt5_LUPDATE_EXECUTABLE}
ARGS ${filesToCompile_cpp} ${filesToCompile_ui} -I ${repoDir}/src -ts ${translationSourceFiles}
VERBATIM)
#============================Icon for Windows==================================
set(desktopIcon "")
if(WIN32 AND MINGW)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/src/icon.o
COMMAND windres.exe -I${CMAKE_CURRENT_SOURCE_DIR}
-i${filesToInstall_windowsIcon}
-o${CMAKE_BINARY_DIR}/src/icon.o
DEPENDS ${filesToInstall_windowsIcon}
)
set(desktopIcon ${CMAKE_BINARY_DIR}/src/icon.o)
elseif(WIN32)
set(desktopIcon ${filesToInstall_windowsIcon})
endif()
#===========================File ownership==================================
#
# When you do "make install", the last thing CMake does is generate a file called install_manifest.txt in the build
# output directory containing a list of all the files that were installed. On Linux, since we need to run make install
# as root (eg via "sudo make install"), this file would get created with root:root ownership. That's a problem because
# you then when you run "make package" as a non-root user, you get an error "file failed to open for writing (Permission
# denied)". The workaround is to create the install_manifest.txt file as a normal user when "make" is run, so that
# "sudo make install" is just updating an existing file rather than creating it from scratch.
#
file(TOUCH ${CMAKE_BINARY_DIR}/install_manifest.txt)
#===========================Create the binary==================================
# This intermediate library target simplifies building the main app and the test app from largely the same sources
# Note that using this is why we can't include src/main.cpp in filesToCompile_cpp.
add_library(btobjlib
OBJECT
${filesToCompile_cpp}
${filesToCompile_qrc})
set_source_files_properties(${filesToInstall_macIcons}
PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources")
set_source_files_properties(${filesToInstall_data}
PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources")
set_source_files_properties(${filesToInstall_docs}
PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources/en.lproj")
set_source_files_properties(${filesToInstall_sounds}
PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources/sounds")
set_source_files_properties(${QM_FILES}
PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources/translations_qm")
if(APPLE)
add_executable(${fileName_executable}
MACOSX_BUNDLE
${repoDir}/src/main.cpp
${translationSourceFiles}
${QM_FILES}
${filesToInstall_macIcons}
${filesToInstall_data}
${filesToInstall_docs}
${filesToInstall_sounds}
$<TARGET_OBJECTS:btobjlib>)
else()
add_executable(${fileName_executable}
${repoDir}/src/main.cpp
${translationSourceFiles}
${QM_FILES}
${desktopIcon}
$<TARGET_OBJECTS:btobjlib>)
endif()
# Windows-specific library linking
if(WIN32 AND MINGW)
############################################################################
# Need to set some linker flags that I don't know how to get
# automatically.
############################################################################
# MinGW-specific flags.
# '-Wl,-subsystem,windows' supresses the output command window.
# '-Wl,-s' strips the executable of symbols.
set_target_properties(
${fileName_executable}
PROPERTIES
LINK_FLAGS "-Wl,-enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -mthreads -Wl,-subsystem,windows"
)
# This is from https://stackoverflow.com/questions/41193584/deploy-all-qt-dependencies-when-building
if(TARGET Qt5::windeployqt)
# First, execute windeployqt in a temporary directory after the build to create the directory structure and files
# that Qt needs on Windows...
add_custom_command(TARGET ${fileName_executable}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt"
COMMAND Qt5::windeployqt --dir "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" --no-translations --compiler-runtime "$<TARGET_FILE_DIR:${fileName_executable}>/$<TARGET_FILE_NAME:${fileName_executable}>"
)
# ...then we copy that directory tree to (a) the installation directory (when "make install" is run) and (b)
# the directory where we run the test executable (straight away)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/windeployqt/" DESTINATION bin)
add_custom_target(
copy-runtime-files ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_BINARY_DIR}/windeployqt/" "${CMAKE_BINARY_DIR}/bin"
DEPENDS ${fileName_executable}
)
endif()
endif()
add_dependencies(${fileName_executable} translationsTarget)
# All the libraries (except the Qt ones we add immediately below) that are used by both the main app and the unit
# testing app
set(appAndTestCommonLibraries
${Backtrace_LIBRARIES}
${Boost_LIBRARIES}
${DL_LIBRARY}
${XalanC_LIBRARIES}
${XercesC_LIBRARIES}
)
foreach(qtComponent IN LISTS qtCommonComponents)
list(APPEND appAndTestCommonLibraries "Qt5::${qtComponent}")
endforeach()
message("appAndTestCommonLibraries: ${appAndTestCommonLibraries}")
target_link_libraries(${fileName_executable} ${appAndTestCommonLibraries})
#=================================Tests========================================
# We build the unit test executable in the bin subdirectory because, on Windows, we're going to copy a lot of other
# files in there (see below).
add_executable(${fileName_unitTestRunner}
${repoDir}/src/unitTests/Testing.cpp
${testing_MOC_SRCS}
$<TARGET_OBJECTS:btobjlib>)
set_target_properties(${fileName_unitTestRunner} PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin)
# Test app needs all the same libraries as the main app, plus Qt5::Test
target_link_libraries(${fileName_unitTestRunner} ${appAndTestCommonLibraries} Qt5::Test)
add_test(
NAME pstdintTest
COMMAND bin/${fileName_unitTestRunner} pstdintTest
)
add_test(
NAME recipeCalcTest_allGrain
COMMAND bin/${fileName_unitTestRunner} recipeCalcTest_allGrain
)
add_test(
NAME postBoilLossOgTest
COMMAND bin/${fileName_unitTestRunner} postBoilLossOgTest
)
add_test(
NAME testUnitConversions
COMMAND bin/${fileName_unitTestRunner} testUnitConversions
)
add_test(
NAME testLogRotation
COMMAND bin/${fileName_unitTestRunner} testLogRotation
)
#=======================================================================================================================
#============================================== Debian-friendly ChangeLog ==============================================
#=======================================================================================================================
#
# This is to create a compressed changelog in a Debian-friendly format
#
# NB: Even though this is a target you build (with make changelog) before doing make package, this section needs to
# appear below the stuff for make package so that we can reuse variables such as CPACK_DEBIAN_PACKAGE_MAINTAINER
#
# Our change log (CHANGES.markdown) uses markdown format, with the following raw structure:
# ## v1.2.3
#
# Optional one-line description of the release.
#
# ### New Features
#
# * Blah blah blah
# * etc
#
# ### Bug Fixes
#
# * Blah blah blah
# * etc
#
# ### Incompatibilities
#
# None
#
# ### Release Timestamp
# Sun, 06 Feb 2022 12:02:58 +0100
#
# However, per https://www.debian.org/doc/debian-policy/ch-source.html#debian-changelog-debian-changelog, Debian
# change logs need to be in the following format:
# package (version) distribution(s); urgency=urgency
# [optional blank line(s), stripped]
# * change details
# more change details
# [blank line(s), included in output of dpkg-parsechangelog]
# * even more change details
# [optional blank line(s), stripped]
# -- maintainer name <email address>[two spaces] date
#
# We are being a bit fast-and-loose in hard-coding the same maintainer name for each release, but I don't thing it's a
# huge issue.
#
# Note that, to keep us on our toes, Debian change log lines are not supposed to be more than 80 characters long. This
# is non-trivial, but the ghastly bit of awk below gets us most of the way there.
#
if(UNIX AND NOT APPLE)
# Note that, whilst using VERBATIM below makes things a lot less painful, we still have to double-escape things -
# once for CMake and once for bash. (Where something is single-escaped it probably means either that CMake needs the
# escape and the bash command does not, or vice versa.) Also, we have more invocations of sed that we strictly need
# because it makes things slightly easier to follow and debug.
string(
CONCAT changeLogProcessCommands
"cat ${filesToInstall_changeLogUncompressed} | "
# Skip over the introductory headings and paragraphs of CHANGES.markdown until we get to the first version line
"sed -n '/^## v/,$p' | "
# We want to change the release timestamp to maintainer + timestamp, but we don't want to create too long a line
# before we do the fold command below, so use "÷÷maintainer÷÷" as a placeholder for
# " -- ${CPACK_DEBIAN_PACKAGE_MAINTAINER} "
"sed -z 's/\\n### Release Timestamp\\n\\([^\\n]*\\)\\n/\\n÷÷maintainer÷÷\\1\\n/g' | "
# Join continued lines in bullet lists
"sed -z 's/\\n / /g' | "
# Change the version to package (version) etc. Stick a '÷' on the front of the line to protect it from
# modification below
"sed 's/^## v\\(.*\\)$/÷${PROJECT_NAME} (\\1-1) unstable\; urgency=low/' | "
# Change bullets to sub-bullets
"sed 's/^\\* / - /' | "
# Change headings to bullets
"sed 's/^### / * /' | "
# Change any lines that don't start with space OR a ÷ character to be bullets
"sed 's/^\\([^ ÷]\\)/ * \\1/' | "
# Split any long lines. Make the width less than 80 so we've got a margin go insert spaces at the start of
# bullet continuation lines.
"fold -s --width=72 | "
# With a lot of help from awk, reindent the lines that were split off from a long bullet line so that they align
# with that previous line.
"awk 'BEGIN { inBullet=0 } "
"{"
"if (!inBullet) {"
"inBullet=match($0, \"^( +)[^ ] \", spaces)\;"
"print\;"
"} else {"
"bulletContinues=match($0, \"^[^ ]\")\;"
"if (!bulletContinues) {"
"inBullet=match($0, \"^( +)[^ ] \", spaces)\;"
"print\;"
"} else {"
"print spaces[1] \" \" $0\;"
"}"
"}"
"}' | "
# Fix the "÷÷maintainer÷÷" placeholders
"sed 's/÷÷maintainer÷÷/ -- ${CPACK_DEBIAN_PACKAGE_MAINTAINER} /' | "
# Remove the protective '÷' from the start of any other lines
"sed 's/^÷//' | "
"gzip --best -n --to-stdout > ${filesToInstall_changeLogCompressed}"
)
add_custom_command(
TARGET ${fileName_executable} POST_BUILD
COMMAND bash -c ${changeLogProcessCommands}
VERBATIM
COMMENT "Processing ${filesToInstall_changeLogUncompressed} to ${filesToInstall_changeLogCompressed}"
MAIN_DEPENDENCY ${filesToInstall_changeLogUncompressed}
)
endif()
#=================================Installs=====================================
# Install executable.
install(TARGETS ${fileName_executable}
BUNDLE DESTINATION .
RUNTIME DESTINATION ${installSubDir_bin}
COMPONENT ${RUNTIME_INSTALL_COMPONENT})
if(WIN32)
#
# Shared libraries (DLLs) are a bit complicated on Windows. Because there is not really a standard package
# management system, we can't easily just say "we require libraries X, Y and Z". Instead, we need to ship said
# libraries in the same directory as our application's executable.
#
# (Of course, if you're just running on the same machine where you built the software, you could simply set the PATH
# variable to include all the directories where the DLLs are. But, in the general case, we're trying to create a
# package that does not require people to have pre-installed Qt, Xerces, Xalan, etc.)
#
# In theory, CMake can work out what DLLs we need and thereby generate a list of files that we need to ensure are
# copied as part of the install/packaging. In practice, everything seems to be a bit more complicated.
#
# See https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLLS
# (This replaces use of GET_RUNTIME_DEPENDENCIES which I just could not get to work correctly, despite following all
# sorts of helpful advice at
# https://stackoverflow.com/questions/62884439/how-to-use-cmake-file-get-runtime-dependencies-in-an-install-statement
# I always hit a "Failed to start objdump" error.)
#
# Note, however that this does not find all the DLLs we need. Perhaps this is related to the mention in the CMake
# doco that "Many Find Modules produce imported targets with the UNKNOWN type and therefore will be ignored."
#
add_custom_command(
TARGET ${fileName_executable} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:brewtarget> $<TARGET_FILE_DIR:brewtarget>
COMMAND_EXPAND_LISTS
)
#
# On MSYS2/MinGW, static libraries (including .dll.a files) live in /mingw64/lib (usually = C:/msys64/mingw64/lib/)
# and dynamically linked libraries (aka DLLs) live in /mingw64/bin (usually = C:/msys64/mingw64/bin/).
#
# First, we do a bit of a hack to get to the bin directory from the lib one, without hard-coding either (in case
# your 64-bit instance of MSYS2 is not installed at C:/msys64/). XercesC_LIBRARIES will be the .dll.a file for
# Xerces. Usually only one library for Xerces, but we take first in the list in case.
#
list(GET XercesC_LIBRARIES 0 xercesFirstLibrary)
get_filename_component(staticLibDir ${xercesFirstLibrary} DIRECTORY)
set(dynamicLibDir ${staticLibDir}/../bin)
message(STATUS "dynamicLibDir=" ${dynamicLibDir})
#
# Now, for each static library (/mingw64/lib/BlahBlah.dll.a) we know about, we get the corresponding dynamic one
# (/mingw64/bin/BlahBlah.dll) and copy it into the same directory as the .exe file.
#
# Just to make our lives fun, sometimes the static and dynamic versions of the library will have slightly different
# naming conventions. Eg, for Xalan, we have:
# /mingw64/bin/libxalan-c.dll
# /mingw64/lib/libxalan-c.dll.a
# But for Xerces, we have:
# /mingw64/lib/libxerces-c.dll.a
# /mingw64/bin/libxerces-c-3-2.dll
# Hence the extra '*' in the glob expression below.
#
# Finally, note that, to make debugging more interesting, the message command below outputs during "make" rather
# than during "make install".
#
foreach(staticLib IN LISTS XercesC_LIBRARIES XalanC_LIBRARIES)
get_filename_component(libBaseName ${staticLib} NAME_WE)
file(GLOB dynamicallyLinkedLibrary ${dynamicLibDir}/${libBaseName}*.dll)
message(STATUS "Install will copy DLL ${dynamicallyLinkedLibrary} to ${installSubDir_bin}")
install(FILES ${dynamicallyLinkedLibrary} DESTINATION ${installSubDir_bin})
endforeach()
#
# Now, because we're also not assuming that the person running the code will have MSYS2/MinGW installed, we need to
# include the DLLs that ship with them and get pulled in by GCC. I am hoping to find a less manual way of doing this
# but have not yet paydirt.
#
set(msys2Dlls
libbrotli
libbz2
libdouble-conversion
libfreetype
libglib
libgraphite
libharfbuzz
libiconv
libintl
libmd4c
libpcre
libpng16
libsqlite3 # You need this IN ADDITION to bin/sqldrivers/qsqlite.dll, which gets installed by windeployqt
libxalanMsg
libzstd
zlib1
)
foreach(dynamicLib IN LISTS msys2Dlls)
file(GLOB dynamicallyLinkedLibrary ${dynamicLibDir}/${dynamicLib}*.dll)
message(STATUS "Install will also copy DLL ${dynamicallyLinkedLibrary} to ${installSubDir_bin}")
install(FILES ${dynamicallyLinkedLibrary} DESTINATION ${installSubDir_bin})
endforeach()
# These are something to do with International Components for Unicode
message(STATUS "Install will copy ${IcuDlls} to ${installSubDir_bin}")
install(FILES ${IcuDlls} DESTINATION ${installSubDir_bin})
add_custom_command(
TARGET ${fileName_executable} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:brewtarget>
)
endif()
# Install the translations.
install(FILES ${QM_FILES}
DESTINATION "${installSubDir_data}/translations_qm"
COMPONENT ${DATA_INSTALL_COMPONENT} )
#=======================================================================================================================
#======================================= Install (both locally and for packages) =======================================
#=======================================================================================================================
# This is for "make install" (or "sudo make install") AND for "make package" and "make package_source".
#
# Note that, per https://cmake.org/cmake/help/latest/module/CPack.html, DESTINATION option of the install() command must
# be a relative path; otherwise installed files are ignored by CPack.
#
# When a relative path is given in the DESTINATION option of the install() command, it is interpreted relative to the
# value of the CMAKE_INSTALL_PREFIX variable.
#
# Install the data
install(FILES ${filesToInstall_data}
DESTINATION ${installSubDir_data}
COMPONENT ${DATA_INSTALL_COMPONENT})
# Install the documentation
install(FILES ${filesToInstall_docs}
DESTINATION ${installSubDir_doc}
COMPONENT ${DATA_INSTALL_COMPONENT})
# Install sounds
install(FILES ${filesToInstall_sounds}
DESTINATION "${installSubDir_data}/sounds"
COMPONENT ${DATA_INSTALL_COMPONENT})
if(UNIX AND NOT APPLE)
#----------- Linux -----------
# Install the icons
install(FILES ${filesToInstall_icons}
DESTINATION "${installSubDir_data}/icons/hicolor/scalable/apps/"
COMPONENT ${DATA_INSTALL_COMPONENT})
# Install the .desktop file
install(FILES ${filesToInstall_desktop}
DESTINATION "${installSubDir_data}/applications"
COMPONENT ${DATA_INSTALL_COMPONENT})
# Install friendly-format change log aka release notes
# Note that lintian does not like having a file called CHANGES.markdown in the doc directory, as it thinks it is a
# misnamed changelog.Debian.gz (even when changelog.Debian.gz is also present!) so you get a
# wrong-name-for-upstream-changelog warning.
# The simplest way round this is to rename CHANGES.markdown to RelaseNotes.markdown
install(FILES ${filesToInstall_changeLogUncompressed}
RENAME "RelaseNotes.markdown"
DESTINATION ${installSubDir_doc}
COMPONENT ${DATA_INSTALL_COMPONENT})
# Debian packages need to have the copyright file in a particular place (/usr/share/doc/PACKAGE/copyright)
# RPM packages don't like having duplicate files in the same directory (eg copyright and COPYRIGHT with same
# contents). So the simplest thing is to rename COPYRIGHT to copyright for both.
install(FILES "${repoDir}/COPYRIGHT"
RENAME "copyright"
DESTINATION ${installSubDir_doc}
COMPONENT ${DATA_INSTALL_COMPONENT})
# Each Debian package (which provides a /usr/share/doc/pkg directory) must install a Debian changelog file in
# /usr/share/doc/pkg/changelog.Debian.gz
install(FILES ${filesToInstall_changeLogCompressed}
RENAME "changelog.Debian.gz"
DESTINATION ${installSubDir_doc}
COMPONENT ${DATA_INSTALL_COMPONENT})
else()
#----------- Windows and Mac -----------
install(FILES "${repoDir}/COPYRIGHT"
DESTINATION ${installSubDir_doc}
COMPONENT ${DATA_INSTALL_COMPONENT})
endif()
if(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
DESTINATION bin
COMPONENT System)
endif()
#=======================================================================================================================
#====================================================== Packaging ======================================================
#=======================================================================================================================
# To make the packages, "make package". For source packages, "make package_source"
#
# We use CMake's CPack module (see https://cmake.org/cmake/help/latest/module/CPack.html and
# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Packaging%20With%20CPack.html) as our preferred way of
# creating install packages. You have slightly less control compared with doing things more manually,
# but it's a lot less hassle.
#
#
# Cross-platform packaging options
#
# Note that we don't bother setting the following
# - CPACK_PACKAGE_NAME as it defaults to the project name, which is what we want
# - CPACK_PACKAGE_DIRECTORY as it defaults to the build directory, which is what we want
# - CPACK_PACKAGE_VERSION_MAJOR as it defaults to the version given in the project command above, which is what we
# want
# - CPACK_PACKAGE_VERSION_MINOR for the same reason
# - CPACK_PACKAGE_VERSION_PATCH for the same reason
# - CPACK_SET_DESTDIR as it is only needed to is to "install software at non-default location"
# - CPACK_PACKAGING_INSTALL_PREFIX as the defaults from each generator should usually be fine
# - CPACK_PACKAGE_CONTACT as, AFAICT, it's only used to provide a default value for CPACK_DEBIAN_PACKAGE_MAINTAINER
#
set(CPACK_PACKAGE_VENDOR "The Brewtarget Team")
string(
CONCAT
CPACK_PACKAGE_DESCRIPTION
# Note that the lines here are not supposed to be more than 79 characters long, otherwise rpmlint will spew an error.
# (If they are more than 80 characters long, then lintian will also complain, though only with a warning.)
"Brewtarget is a calculator for brewing beer. It is a Qt-based program which\n"
"allows you to create recipes from a database of ingredients. It calculates\n"
"all the important parameters, helps you with mash temperatures, and just\n"
"makes the process of recipe formulation much easier."
)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GUI beer brewing software")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/Brewtarget/brewtarget")
#
message("CMAKE_PROJECT_NAME = ${CMAKE_PROJECT_NAME}")
message("PROJECT_VERSION = ${PROJECT_VERSION}")
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${PROJECT_VERSION}_${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "brewtarget-${PROJECT_VERSION}")
if(NOT WIN32)
# Setting this on Windows breaks the NSIS packager with some confusing message saying it can't find the file
set(CPACK_PACKAGE_ICON "${repoDir}/images/brewtarget.svg")
endif()
set(CPACK_PACKAGE_CHECKSUM "SHA256")
set(CPACK_RESOURCE_FILE_LICENSE "${repoDir}/COPYING.GPLv3")
set(CPACK_STRIP_FILES true)
set(CPACK_VERBATIM_VARIABLES true)
set(CPACK_PACKAGE_EXECUTABLES "${fileName_executable}" "Brewtarget")
#
# Variables for Source Package Generators
#
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${PROJECT_VERSION}_source")
set(CPACK_SOURCE_GENERATOR "TBZ2;")
set(CPACK_SOURCE_IGNORE_FILES "/.svn/"
"~$"
"/CMakeFiles/"
"/_CPack_Packages/"
"^brewtarget.*deb$"
"^brewtarget.*rpm$"
"^brewtarget.*tar.*$"
"CPack.*"
"Makefile"
"cmake_install.*"
"\\\\.o$"
"/brewtarget.dir/"
"moc_.*"
"qrc_brewtarget.*"
"ui_.*h"
"install_manifest.*"
"config\\\\.h")
if(UNIX AND NOT APPLE)
#================================================= Linux Packaging ==================================================
set(CPACK_GENERATOR "DEB;RPM;TBZ2;")
#-----------------DEB----------------
# See https://cmake.org/cmake/help/latest/cpack_gen/deb.html
# We don't set CPACK_DEBIAN_PACKAGE_NAME as it defaults to CMAKE_PROJECT_NAME, which is what we want
# (We don't want to put the version in the package name as it gets added to the file name automatically, so you'd
# end up with it twice. Plus you'd get a bad-package-name error from lintian.)
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
set(CPACK_DEBIAN_PACKAGE_VERSION "${PROJECT_VERSION}-1")
# See https://www.debian.org/doc/debian-policy/ch-binary.html#s-maintainer for more on the "maintainer", but
# essentially it's a required package property that needs to be either one person or a group mailing list. In the
# latter case, the individual maintainers need be listed in a separate property called "uploaders", though I can't
# see a way to set this via CPack.
#
# Also note, per https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-maintainer, that it's simplest
# to avoid having full stops in the maintainer's name.
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Philip Greggory Lee <rocketman768@gmail.com>")
# This should save us having to set CPACK_DEBIAN_PACKAGE_DEPENDS manually
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_SECTION "misc")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
# By default, CPack sets directory permissions with group_write enabled (775 in the old money). Lintian doesn't like
# this and wants only the owner to have write permission (755 in the old money). So we have to tell CPack here what
# permissions to use.
set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
#-----------------RPM-----------------
# See https://cmake.org/cmake/help/latest/cpack_gen/rpm.html
set(CPACK_RPM_FILE_NAME RPM-DEFAULT)
# RPM file name is supposed to be <NAME>-<VERSION>-<RELEASE>.<ARCH>.rpm. However, CPack doesn't, by default, stick
# RELEASE in there, so we add it to version. (The reason for having both version and release is that "version is
# controlled by the original author and release is controlled by whoever constructed the RPM". So we always set
# release to 1.)
set(CPACK_RPM_PACKAGE_VERSION "${PROJECT_VERSION}-1")
# CPACK_RPM_PACKAGE_DESCRIPTION defaults to CPACK_PACKAGE_DESCRIPTION_FILE rather than CPACK_PACKAGE_DESCRIPTION,
# so we have to set it explicitly here
set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION})
# This has to match one of the ValidLicenses values in packaging/rpmLintFilters.toml. (See comment in that file for
# more info.)
set(CPACK_RPM_PACKAGE_LICENSE "GPL-3.0-or-later")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Productivity")
# This should (I think) save us having to set CPACK_RPM_PACKAGE_REQUIRES manually
set(CPACK_RPM_PACKAGE_AUTOREQ yes)
# .:TBD:. Not sure what these are for...
set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#")
set(CPACK_RPM_USER_FILELIST "%ignore /usr"
"%ignore /usr/local"
"%ignore /usr/local/bin"
"%ignore /usr/local/share"
"%ignore /usr/local/share/applications")
elseif(WIN32)
#================================================ Windows Packaging =================================================
# http://www.thegigsite.com/cmake-2.6.0/CMakeCPackOptions.cmake
set(CPACK_GENERATOR "NSIS")
# The title displayed at the top of the installer.
set(CPACK_NSIS_PACKAGE_NAME "Brewtarget-${PROJECT_VERSION}")
# The display name string that appears in the Windows Apps & features in Control Panel
set(CPACK_NSIS_DISPLAY_NAME "Brewtarget-${PROJECT_VERSION}")
# ?
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "Brewtarget-${PROJECT_VERSION}")
# URL to a web site providing more information about your application
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/Brewtarget/brewtarget")
# The name of a *.ico file used as the main icon for the generated install program
set(CPACK_NSIS_MUI_ICON ${repoDir}/win/BrewtargetIcon_96.ico)
# Lists each of the executables and associated text label to be used to create Start Menu shortcuts.
# For example, setting this to the list ccmake;CMake would create a shortcut named "CMake" that would execute the
# installed executable ccmake.
set(CPACK_PACKAGE_EXECUTABLES "${fileName_executable};${capitalisedProjectName}")
# List of desktop links to create. Each desktop link requires a corresponding start menu shortcut as created by
# CPACK_PACKAGE_EXECUTABLES.
set(CPACK_CREATE_DESKTOP_LINKS ${fileName_executable})
# In theory CMake should work out for itself which tool to use for dependency resolution. In practice, on Windows,
# it seems there are issues. In an MSYS2/MinGW environment we want to use objdump rather than the Visual Studio
# dumpbin.
#
# .:TBD:. This is not sufficient to get objdump used...
set(CMAKE_OBJDUMP "objdump")
set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump)
set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND /usr/bin/objdump)
message(STATUS "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM is " ${CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM})
message(STATUS "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL is " ${CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL})
message(STATUS "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND is " ${CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND})
set(CPACK_NSIS_MODIFY_PATH ON)
# Extra start menu items.
#set(CPACK_NSIS_MENU_LINKS
# "bin/${fileName_executable}" "My Brewtarget"
# )
elseif(APPLE)
#================================================== Mac Packaging ===================================================
# It seems like there are two CPack generators that could potentially be used for Mac:
# - Bundle (https://cmake.org/cmake/help/latest/cpack_gen/bundle.html) which I think is used in conjunction with
# the DragNDrop generator
# - DragNDrop (https://cmake.org/cmake/help/latest/cpack_gen/dmg.html) which generates a DMG disk image from which
# the end user copies the application to her/his Applications folder
# - IFW (https://cmake.org/cmake/help/latest/cpack_gen/ifw.html) which generates a Qt installer
# Historically it looks like DMG images were what we released.
#
# See some info at https://doc.qt.io/qt-6/macos-deployment.html for "official" Qt way to do things, but this is using
# qmake. This page https://gitlab.kitware.com/cmake/community/-/wikis/doc/cpack/PackageGenerators is more helpful.
set(CPACK_GENERATOR "Bundle")
# Bundle name (displayed in the finder underneath the bundle icon).
set(CPACK_BUNDLE_NAME "${CMAKE_PROJECT_NAME}_${PROJECT_VERSION}")
# Bundle icon (displayed in the /Applications folder, on the dock, etc).
set(CPACK_BUNDLE_ICON "${filesToInstall_macIcons}")
# XML format Information Property List file containing application metadata
# Note that CFBundleVersion in this file should be the same as PROJECT_VERSION in this one
set(CPACK_BUNDLE_PLIST "${filesToInstall_macPropertyList}")
# Path to a startup script. This is a path to an executable or script that will be run whenever an end-user double-clicks the generated bundle in the OSX Finder. Optional.
#set(CPACK_BUNDLE_STARTUP_COMMAND "${fileName_executable}")
message(STATUS "Building Mac package")
#set(CPACK_GENERATOR "DragNDrop")
#set(CPACK_DMG_FORMAT "UDBZ")
endif()
# The documentation implies that all the CPACK_* variables need to be set before we include the CPack module, so that's
# what we do.
INCLUDE(CPack)
#=======================================================================================================================
#================================================= Custom Make Targets =================================================
#=======================================================================================================================
# These go at the end of the file so that they can use any of the variables created above
# `make install-data` or `make install-runtime`
add_custom_target(
install-data
COMMAND "${CMAKE_COMMAND}"
-DCOMPONENT=${DATA_INSTALL_COMPONENT}
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
)
add_custom_target(
install-runtime
DEPENDS ${fileName_executable}
COMMAND "${CMAKE_COMMAND}"
-DCOMPONENT=${RUNTIME_INSTALL_COMPONENT}
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
)
# Doxygen Custom Target
FIND_PROGRAM(DOXYGEN_CMD doxygen)
if(DOXYGEN_CMD)
set(DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile")
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in" ${DOXYFILE})
add_custom_target(source_doc
COMMAND ${DOXYGEN_CMD} ${DOXYFILE}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/doc"
)
endif()
# Some extra files for the "make clean" target
# Note that the ADDITIONAL_CLEAN_FILES property does NOT do any sort of glob pattern matching, so we have to use the
# file command to do that.
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
APPEND
PROPERTY ADDITIONAL_CLEAN_FILES ".*~$" # Kate backup files.
"CMakeLists.txt.user" # From QtCreator I think.
"CPackConfig.cmake"
"CPackSourceConfig.cmake")
file(GLOB packagesDebFiles "${CMAKE_CURRENT_BINARY_DIR}/*.deb")
file(GLOB packagesRpmFiles "${CMAKE_CURRENT_BINARY_DIR}/*.rpm")
file(GLOB packagesTarBz2Files "${CMAKE_CURRENT_BINARY_DIR}/*.tar.bz2")
file(GLOB packagesChecksumFiles "${CMAKE_CURRENT_BINARY_DIR}/*.*.sha256")
set_property(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
APPEND
PROPERTY ADDITIONAL_CLEAN_FILES "install_manifest.txt"
${packagesDebFiles}
${packagesRpmFiles}
${packagesTarBz2Files}
${packagesChecksumFiles})
# make addframeworks should copy the Qt libraries to the app.
if(APPLE)
set(QT_BINARY_DIR "${_qt5Core_install_prefix}/bin")
add_custom_target(
addframeworks ALL
COMMAND ${QT_BINARY_DIR}/macdeployqt "Brewtarget.app" -dmg
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${fileName_executable}
)
endif()
if(UNIX AND NOT APPLE)
add_custom_target(
package_lint
# Running lintian does a very strict check on the Debian package. You can find a list of all the error and
# warning codes at https://lintian.debian.org/tags.
#
# Some of the warnings are things that only matter for packages that actually ship with Debian itself - ie they
# won't stop the package working but are not strictly within the standards that the Debian project sets for the
# packages included in the distro.
#
# Still, we try to fix as many warnings as possible. As at 2022-08-11 we currently have one warning that we do
# not ship a man page. We should get to this at some point.
COMMAND lintian --no-tag-display-limit *.deb
# Running rpmlint is the equivalent exercise for RPMs. Most common error and warning codes are listed at
# https://fedoraproject.org/wiki/Common_Rpmlint_issues
COMMAND rpmlint --config ${repoDir}/packaging *.rpm
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running lintian and on deb package and rpmlint on rpm package. Warnings about man pages are expected!"
)
endif()