CMake Lists
CMake Lists
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
# CMakeLists.txt is part of Brewtarget, and is Copyright the following authors
2009-2024
# - Chris Pavetto <[email protected]>
# - Dan Cavanagh <[email protected]>
# - Daniel Moreno <[email protected]>
# - Daniel Pettersson <[email protected]>
# - Kregg Kemper <[email protected]>
# - Matt Young <[email protected]>
# - Maxime Lavigne (malavv) <[email protected]>
# - Mik Firestone <[email protected]>
# - Philip Greggory Lee <[email protected]>
# - Robby Workman <[email protected]>
# - Théophane Martin <[email protected]>
#
# 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/>.
#╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
# NB: Meson and the `bt` build tool Python script are now the primary way of
building and packaging the software. You
# can also still CMake to compile the product and install it locally, but we no
longer support using CMake to do
# packaging. (Over time the intention is to remove packaging-specific code
from this script, not least as it does
# not work properly on Mac and Windows.)
# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
#
# 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
#
# 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
#
# Custom make targets:
# * make source_doc - Makes Doxygen HTML documentation of the source in
doc/html
# * make install-data
# * make install-runtime
#
# 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 "set" line for slightly verbose build output
# Alternatively, for very verbose output, invoke make as follows:
#
# make VERBOSE=1
#
# On Windows, you need:
#
# cmake --build . --verbose
#
#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)
# Compatibility settings used with modern CMAKE (>= 3.29) used to pull
BoostConfig.cmake from upstream
# -> FindBoost cmake module was removed in recent versions.
# See https://cmake.org/cmake/help/latest/module/FindBoost.html
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_GREATER_EQUAL 3.29)
cmake_policy(SET CMP0167 NEW)
endif()
#==================================================================================
=====================================
#================================================ Other preliminaries
=================================================
#==================================================================================
=====================================
# Set the target binary architecture for targets on macOS
if(APPLE AND NOT CMAKE_OSX_ARCHITECTURES)
# Per https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html,
"the value of this variable should
# be set prior to the first project() or enable_language() command invocation
because it may influence configuration
# of the toolchain and flags".
set(CMAKE_OSX_ARCHITECTURES x86_64, arm64) # Build both x86_64 and arm64 for
Apple silicon.
#set(CMAKE_OSX_ARCHITECTURES i386 x86_64) # Build intel binary.
#set(CMAKE_OSX_ARCHITECTURES ppc i386 ppc64 x86_64) # Build universal binary.
endif()
#==================================================================================
=====================================
#============================================== 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 4.0.9 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)
# We use this in the program, to tell users where to get support.
set(CONFIG_GITHUB_URL "https://github.com/${capitalisedProjectName}/$
{projectName}")
set(CONFIG_WEBSITE_URL "https://www.brewtarget.beer/")
set(CONFIG_ORGANIZATION_DOMAIN "brewken.com")
#==================================================================================
=====================================
#======================================================= 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
=====================================================
#==================================================================================
=====================================
# 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")
set(installSubDir_data "share/${CMAKE_PROJECT_NAME}")
set(installSubDir_doc "share/doc/${CMAKE_PROJECT_NAME}")
set(installSubDir_bin "bin")
# According to https://specifications.freedesktop.org/menu-spec/menu-spec-
1.0.html#paths, .desktop files need to live
# in one of the $XDG_DATA_DIRS/applications/. (Note that $XDG_DATA_DIRS is a
colon-separated list of directories, typically
# defaulting to /usr/local/share/:/usr/share/. but on another system it might be
# /usr/share/plasma:/usr/local/share:/usr/share:/var/lib/snapd/desktop:/var/
lib/snapd/desktop). When combined with
# CMAKE_INSTALL_PREFIX, "share/applications" should end up being one of these.
set(installSubDir_applications "share/applications")
# It's a similar but slightly more complicated story for where to put icons.
(See
# https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-
latest.html#directory_layout for all the
# details.)
set(installSubDir_icons "share/icons")
elseif(WIN32)
#============================================ Windows Install Directories
===========================================
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()
if (APPLE)
# On Mac we ask CMake to try to find static libraries when available -- because
it's so painful shipping dynamic
# libraries in a Bundle.
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
#==================================================================================
=====================================
#=============================================== Installation Components
===============================================
#==================================================================================
=====================================
# For architecture-independent data
set(DATA_INSTALL_COMPONENT "Data")
# For architecture-dependent binaries
set(RUNTIME_INSTALL_COMPONENT "Runtime")
#==================================================================================
=====================================
#============================================== 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++23 for std::ranges::zip_view, C++20 for std::map::contains() and
concepts, C++17 or later for nested
# namespaces and structured bindings, and C++11 or later for lambdas.
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include_directories(${repoDir}/third-party/valijson/include/)
include_directories(${repoDir}/src)
include_directories("${CMAKE_BINARY_DIR}/src") # In case of out-of-source build.
# 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/linux/rpmLintFilters.toml).
#
# Additionally, NOTE that "-z options are just not supported for Windows
versions of ld" (as mentioned at
# https://stackoverflow.com/questions/55418931/ld-exe-unrecognized-option-z).
So we have to exclude that option
# on Windows.
#
if(NOT WIN32)
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -ansi -pedantic -Wno-long-long -O2 -z
noexecstack")
else()
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -ansi -pedantic -Wno-long-long -O2")
endif()
#
# -g3 should give even more debugging information than -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()
#
# On Ubuntu 22.04, the packages for Qt6 differ from those for Qt5 in that they
require you to build with the PIC option,
# otherwise we'll get the error: "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)."
#
# On certain instances of Windows, we'll get "relocation truncated to fit" linker
errors if we don't build with position
# independent code (see
# https://stackoverflow.com/questions/10486116/what-does-this-gcc-error-relocation-
truncated-to-fit-mean for more
# explanation).
#
if((UNIX AND NOT APPLE) OR WIN32)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
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.
#
# This is needed for Boost stacktrace on Mac
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE")
endif()
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)
#==================================================================================
=====================================
#=================================================== 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.
#
if(UNIX AND NOT APPLE)
execute_process(COMMAND lsb_release -rs OUTPUT_VARIABLE RELEASE_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
# As of 2024-09-30:
# - Qt 6.2.4 is the maximum available in Ubuntu 22.04 (Jammy).
# - Qt 6.4.2 is the maximum available in Ubuntu 24.04 (Noble).
set(QT_MIN_VERSION 6.2.4)
else()
# Windows and Mac may have newer versions, but we keep everything the same for
now
set(QT_MIN_VERSION 6.2.4)
endif()
if(APPLE)
#
# The Qt6 documentation to using CMake at https://doc.qt.io/qt-5/cmake-get-
started.html says we need to set
# CMAKE_PREFIX_PATH environment variable to the Qt 5 installation prefix. In
theory, setting the corresponding CMake
# variable should do the same job (per
https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html).
#
# It seems this is only needed on MacOS. According to
./third-party/valijson/README.md we are not the only ones to
# experience this!
#
execute_process(COMMAND brew --prefix qt6 OUTPUT_VARIABLE CMAKE_PREFIX_PATH)
message(STATUS "CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}")
endif()
# 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)
#
# Although it's possible to do individual find_package commands for each bit of Qt
we want to use (Qt6Core, Qt6Widgets,
# Qt6Sql, 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(Qt6 ${QT_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(${Qt6${qtComponent}_INCLUDE_DIRS})
endforeach()
#==================================================================================
==================================
#================================================= Windows Qt Stuff
=================================================
#==================================================================================
==================================
# .:TBD:. Not sure whether/why we need these additional Qt components on
Windows
#find_package(Qt6MultimediaWidgets REQUIRED)
#find_package(Qt6OpenGL REQUIRED)
# get_target_property(QtMultimediaWidgets_location Qt6::MultimediaWidgets
LOCATION_${CMAKE_BUILD_TYPE})
# get_target_property(QtOpenGL_location Qt6::OpenGL
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtCore_location Qt6::Core
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtGui_location Qt6::Gui
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtMultimedia_location Qt6::Multimedia
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtNetwork_location Qt6::Network
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtPrintSupport_location Qt6::PrintSupport
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQgif_location Qt6::QGifPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQico_location Qt6::QICOPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQjpeg_location Qt6::QJpegPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQsvgIcon_location Qt6::QSvgIconPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQsvg_location Qt6::QSvgPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQtiff_location Qt6::QTiffPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtQWindows_location Qt6::QWindowsIntegrationPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtSqliteDriver_location Qt6::QSQLiteDriverPlugin
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtSql_location Qt6::Sql
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtSvg_location Qt6::Svg
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtWidgets_location Qt6::Widgets
LOCATION_${CMAKE_BUILD_TYPE})
get_target_property(QtXml_location Qt6::Xml
LOCATION_${CMAKE_BUILD_TYPE})
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})
### #
### # 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".
### #
### # On some systems at least, looks like Qt6::windeployqt is already available
in CMake (when Qt5::windeployqt) was
### # not. If it is, then we can't try to set it up again as we'll get an error
("add_executable cannot create imported
### # target "Qt6::windeployqt" because another target with the same name already
exists").
### #
### if (NOT TARGET Qt6::windeployqt)
### find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}")
### if(EXISTS ${WINDEPLOYQT_EXECUTABLE})
### # Per https://cmake.org/cmake/help/latest/command/add_executable.html,
"IMPORTED executables are useful for
### # convenient reference from commands like add_custom_command()".
### add_executable(Qt6::windeployqt IMPORTED)
### set_target_properties(Qt6::windeployqt PROPERTIES IMPORTED_LOCATION $
{WINDEPLOYQT_EXECUTABLE})
### endif()
### endif()
###elseif(APPLE)
###
#==================================================================================
==================================
### #=================================================== Mac Qt Stuff
===================================================
###
#==================================================================================
==================================
###
### # The macdeployqt executable shipped with Qt does for Mac what windeployqt
does for Windows -- see
### # https://doc.qt.io/qt-6/macos-deployment.html#the-mac-deployment-tool
### #
### # At first glance, you might thanks that, with a few name changes, we might
share all the CMake code for macdeployqt
### # and windeployqt. However, as you will see below, the two programs share
_only_ a top-level goal ("automate the
### # process of creating a deployable [folder / applicaiton bundle] that
contains [the necessary Qt dependencies]" - ie
### # so that the end user does not have to install Qt to run our software).
They have completely different
### # implementations and command line options, so it would be unhelpful to try
to treat them identically.
### find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}")
### if(EXISTS ${MACDEPLOYQT_EXECUTABLE})
### # Per https://cmake.org/cmake/help/latest/command/add_executable.html,
"IMPORTED executables are useful for
### # convenient reference from commands like add_custom_command()".
### add_executable(Qt6::macdeployqt IMPORTED)
### set_target_properties(Qt6::macdeployqt PROPERTIES IMPORTED_LOCATION $
{MACDEPLOYQT_EXECUTABLE})
### endif()
endif()
# Uncomment the following line to show what version of Qt is actually being used
message(STATUS "Using Qt version " ${Qt6Core_VERSION})
#
# Extra requirements for Boost Stacktrace
#
# Per
https://www.boost.org/doc/libs/1_76_0/doc/html/stacktrace/configuration_and_build.h
tml, 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}")
if(APPLE)
# TBD: Is this also needed when static linking Xerces on MacOS?
find_package(CURL REQUIRED)
endif()
#=======================================================Valijson===================
=====================================
# Since Valijson is also hosted at github
(https://github.com/tristanpenman/valijson) we just add it as a submodule
# (via git clone https://github.com/tristanpenman/valijson third-party/valijson).
Then the following also come from
# https://github.com/tristanpenman/valijson#readme
#
# Valijson is a header-only library, so we don't need to build or link to any
libraries etc (unless you want to build
# its test suite - in which case, uncomment the add_subdirectory line and add
ValiJSON::valijson to both lists of
# target_link_libraries in src/CMakeLists.txt).
#
# Previously we were using CMake ExternalProject_Add to pull Valijson in, but this
is unnecessary (because we don't need
# to build Valijson) and somewhat harder to debug when it goes wrong. So, instead,
per the readme at
# https://github.com/tristanpenman/valijson, we just added ValiJSON as a git
submodule from the command line.
#
message(STATUS "Valijson source at
${CMAKE_CURRENT_SOURCE_DIR}/third-party/valijson")
# Per comment above, leave the next line commented out unless you want to build the
ValiJSON test suite
#add_subdirectory(third-party/valijson)
add_compile_definitions(VALIJSON_USE_EXCEPTIONS)
include_directories(${ROOTDIR}/third-party/valijson/include)
execute_process(COMMAND git config --global --add safe.directory
third-party/valijson
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
execute_process(COMMAND git submodule update --init --recursive
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
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}/resources.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.md)
# 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_da.ts # Danish
${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 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(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
#
# Getting both these things done in Qt5 was a bit complicated as
qt_add_translation() _only_ does the latter. But,
# with Qt6, we now have qt_add_lupdate that does the former. NOTE that we will
want to tweak the syntax of
# qt_add_lupdate once we are on Qt 6.7 -- per https://doc.qt.io/qt-6/qtlinguist-
cmake-qt-add-lupdate.html -- because a
# change is introduced with that release and the syntax used here then becomes
deprecated.
#
qt_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})
qt_add_lupdate(translationsTarget TS_FILES ${repoDir}/src -ts $
{translationSourceFiles} SOURCES ${filesToCompile_cpp} ${filesToCompile_ui})
set(desktopIcon "")
# 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})
if(APPLE)
#
# We have to tell CMake what things other than the executable etc to include in
the Mac Applicaiton Bundle
#
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")
#==================================================================================
==================================
#================================================ Mac Bundle Settings
===============================================
#==================================================================================
==================================
# See https://cmake.org/cmake/help/latest/prop_tgt/MACOSX_BUNDLE_INFO_PLIST.html
for the CMake doco and
#
https://developer.apple.com/documentation/bundleresources/information_property_list
/bundle_configuration for the
# Apple documentation
#
#
# This sets the bundle property key CFBundleShortVersionString, which is "a
user-visible string for the version of
# the bundle. The required format is three period-separated integers, such as
10.14.1. The string can only contain
# numeric characters (0-9) and periods."
#
# One might think that CFBundleShortVersionString and CFBundleVersion are so
similar as not to merit being separate
# keys, but it is not for us to question whether the left hand of Apple knows
what the right hand is doing.
#
# Confusingly there is also a CMake target property called
MACOSX_BUNDLE_LONG_VERSION_STRING which claims to set a
# CFBundleLongVersionString bundle property key. However, it seems this bundle
property key is long obsolete.
#
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
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 - suppresses the output command window.
# -Wl,-enable-stdcall-fixup - If the link finds a symbol that it cannot
resolve, it will attempt to do “fuzzy
# linking” by looking for another defined symbol
that differs only in the format of
# the symbol name (cdecl vs stdcall) and will
resolve that symbol by linking to the
# match (and also print a warning).
# -Wl,-enable-auto-import - Do sophisticated linking of _symbol to
__imp__symbol for DATA imports from DLLs, and
# create the necessary thunking symbols when
building the import libraries with those
# DATA exports.
# -Wl,-enable-runtime-pseudo-reloc - Fixes some of the problems that can
occur with -enable-auto-import
# -mthreads - specifies that MinGW-specific thread support is to be used
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"
)
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}
${OPENSSL_LIBRARIES}
)
if(APPLE)
# Static linking Xerces and Xalan on MacOS means we have to explicitly say what
libraries and frameworks they in turn
# depend on. It would be neat to find some automated tool that does this for
us.
list(APPEND appAndTestCommonLibraries CURL::libcurl
"-framework CoreFoundation"
"-framework CoreServices"
"-framework Carbon"
"-framework Foundation"
"-framework Cocoa"
"-framework ApplicationServices")
endif()
foreach(qtComponent IN LISTS qtCommonComponents)
list(APPEND appAndTestCommonLibraries "Qt6::${qtComponent}")
endforeach()
message("appAndTestCommonLibraries: ${appAndTestCommonLibraries}")
target_link_libraries(${fileName_executable} ${appAndTestCommonLibraries})
#=================================Tests========================================
# We used to 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). HOWEVER, we've never done that on the Meson
build and it's a pain in the neck to
# do things differently on the CMake build: when we're running the unit tests
because of the way we look for the data
# directory (see initResourceDir() function in Application.cpp).
add_executable(${fileName_unitTestRunner}
${repoDir}/src/unitTests/Testing.cpp
$<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 Qt6::Test
target_link_libraries(${fileName_unitTestRunner} ${appAndTestCommonLibraries}
Qt6::Test)
#=================================Installs=====================================
# Install executable.
install(TARGETS ${fileName_executable}
BUNDLE DESTINATION .
RUNTIME DESTINATION ${installSubDir_bin}
COMPONENT ${RUNTIME_INSTALL_COMPONENT})
#==================================================================================
=====================================
#================================================== Install (locally)
==================================================
#==================================================================================
=====================================
# This is for "make install" (or "sudo make install")
#
# 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 sounds
install(FILES ${filesToInstall_sounds}
DESTINATION "${installSubDir_data}/sounds"
COMPONENT ${DATA_INSTALL_COMPONENT})
if(UNIX AND NOT APPLE)
#----------- Linux -----------
# Install the icons
# Per https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-
latest.html#install_icons, "installing a
# svg icon in $prefix/share/icons/hicolor/scalable/apps means most desktops will
have one icon that works for all
# sizes".
install(FILES ${filesToInstall_icons}
DESTINATION "${installSubDir_icons}/hicolor/scalable/apps/"
COMPONENT ${DATA_INSTALL_COMPONENT})
if(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
DESTINATION bin
COMPONENT System)
endif()
#==================================================================================
=====================================
#=============================================== Post-compilation message
==============================================
#==================================================================================
=====================================
#
# After running `make`, you usually want to run `make install`. This is an easy
step to miss for someone compiling from
# source for the first time, so it's common practice to give a reminder about it.
This trick of making a special cmake
# target comes from https://stackoverflow.com/questions/25240105/how-to-print-
messages-after-make-done-with-cmake. It
# does mean the message also gets printed during `sudo make install` which is, at
best, unnecessary. But I think, on
# balance, it's better than not having a message.
#
# Putting the message in a different color is usually helpful because it makes it
stand out from all the other CMake
# output. Hard-coding the color here is a small risk in that we don't know how
well it will show up on whatever color
# scheme the terminal is using. OTOH, AIUI the color of CMake's own messages is
hard-coded, so, if we use one of the
# same colors, we're not making things any worse.
# (BTW, I cannot find reference to cmake_echo_color in the CMake documentation, but
it seems to work!)
#
add_custom_target(
FinalMessage ALL
${CMAKE_COMMAND} -E cmake_echo_color --bold --green "⭐⭐⭐ Finished compiling $
{capitalisedProjectName}. Please run sudo make install to install locally. ⭐⭐⭐"
COMMENT "Final Message"
)
add_dependencies(FinalMessage ${fileName_executable})
#==================================================================================
=====================================
#================================================= Custom Make Targets
=================================================
#==================================================================================
=====================================
# These go at the end of the file so that they can use any of the variables created
above