#
# 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()
