Codebase list brewtarget / run/5d0a0df0-ec49-4fd4-994c-f781f6abc22a/main CMakeLists.txt
run/5d0a0df0-ec49-4fd4-994c-f781f6abc22a/main

Tree @run/5d0a0df0-ec49-4fd4-994c-f781f6abc22a/main (Download .tar.gz)

CMakeLists.txt @run/5d0a0df0-ec49-4fd4-994c-f781f6abc22a/mainraw · history · blame

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
#
# 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()