diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c85a75cff..415913708 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -154,36 +154,33 @@ jobs: Expand-Archive -Path C:/_/build/nsis/Nsislog.zip -DestinationPath C:/_/build/nsis/Nsislog Tree /f C:/_/build - # Somehow, running the configure script sets up CMake to use Ninja rather than Makefiles, which is then in - # conflict with our other assumptions about CMake. Error message says remove CMakeCache.txt before running CMake, - # so we do that as a short-term fix (on the assumption that, longer term, we'll be moving to Meson). + # The configure script does default set-up for CMake that is enough for us here - name: CMake Config shell: msys2 {0} run: | cd /C/_ ./configure - cd build - rm CMakeCache.txt - cmake .. -DCMAKE_RC_COMPILER:FILEPATH=windres.exe -G "MinGW Makefiles" + + - name: Build (with Meson) + shell: msys2 {0} + run: | + cd /C/_/mbuild + pwd + meson compile # The pwd and find ../third-party commands below are just diagnostics, but it's generally useful to have too # much rather than not enough diagnostic info on these GitHub action builds + # + # This is the same reason we specify the --verbose option on CMake - name: Build (with CMake) shell: msys2 {0} run: | cd /C/_/build pwd tree -sh - cmake --build . + cmake --build . --verbose ls - - name: Build (with Meson) - shell: msys2 {0} - run: | - cd /C/_/mbuild - pwd - meson compile - # The 'export QT_DEBUG_PLUGINS=1' give us diagnostics in the event that there are problems initialising QT # The 'export QT_QPA_PLATFORM=offscreen' stops Qt's xcb sub-module trying to connect to a non-existent display # (which would cause the test runner to abort before running any tests). diff --git a/CMakeLists.txt b/CMakeLists.txt index 87342ff58..fc71a8bb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,9 +70,14 @@ # cmake -DDO_RELEASE_BUILD=OFF .. # -# Uncomment the next line for slightly verbose build output +# Uncomment the next "set" line for slightly verbose build output # Alternatively, for very verbose output, invoke make as follows: -# $ make VERBOSE=1 +# +# make VERBOSE=1 +# +# On Windows, you need: +# +# cmake --build . --verbose # #set(CMAKE_VERBOSE_MAKEFILE ON) @@ -266,9 +271,17 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) # However, this is not sufficient. So, for the moment, we suppress the rpmlint error (see # packaging/linux/rpmLintFilters.toml). # - set(CMAKE_CXX_FLAGS_RELEASE "-Wall -ansi -pedantic -Wno-long-long -O2 -z noexecstack") + # Additionally, NOTE that "-z options are just not supported for Windows versions of ld" (as mentioned at + # https://stackoverflow.com/questions/55418931/ld-exe-unrecognized-option-z). So we have to exclude that option + # on Windows. + # + if(NOT WIN32) + set(CMAKE_CXX_FLAGS_RELEASE "-Wall -ansi -pedantic -Wno-long-long -O2 -z noexecstack") + else() + set(CMAKE_CXX_FLAGS_RELEASE "-Wall -ansi -pedantic -Wno-long-long -O2") + endif() # - # -g3 should give even more debugging information thatn -g (which is equivalent to -g2) + # -g3 should give even more debugging information than -g (which is equivalent to -g2) # -no-pie -fno-pie -rdynamic are needed for Boost stacktrace to work properly - at least according to comments # at https://stackoverflow.com/questions/52583544/boost-stack-trace-not-showing-function-names-and-line-numbers # @@ -295,6 +308,20 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-fconcepts) endif() +# +# On Ubuntu 22.04, the packages for Qt6 differ from those for Qt5 in that they require you to build with the PIC option, +# otherwise we'll get the error: "You must build your code with position independent code if Qt was built with +# -reduce-relocations. " "Compile your code with -fPIC (and not with -fPIE)." +# +# On certain instances of Windows, we'll get "relocation truncated to fit" linker errors if we don't build with position +# independent code (see +# https://stackoverflow.com/questions/10486116/what-does-this-gcc-error-relocation-truncated-to-fit-mean for more +# explanation). +# +if((UNIX AND NOT APPLE) OR WIN32) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif() + # 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 @@ -401,23 +428,25 @@ message(STATUS "Doing ${CMAKE_BUILD_TYPE} build (DO_RELEASE_BUILD = ${DO_RELEASE # if(UNIX AND NOT APPLE) execute_process(COMMAND lsb_release -rs OUTPUT_VARIABLE RELEASE_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) - # This is the latest version of Qt5 available on Ubuntu 22.04 - set(QT5_MIN_VERSION 5.15.3) + # As of 2024-09-30: + # - Qt 6.2.4 is the maximum available in Ubuntu 22.04 (Jammy). + # - Qt 6.4.2 is the maximum available in Ubuntu 24.04 (Noble). + set(QT_MIN_VERSION 6.2.4) else() - # Windows and Mac have a slightly newer version of Qt 5.15 LTS - set(QT5_MIN_VERSION 5.15.13) + # Windows and Mac may have newer versions, but we keep everything the same for now + set(QT_MIN_VERSION 6.2.4) endif() if(APPLE) # - # The Qt5 documentation to using CMake at https://doc.qt.io/qt-5/cmake-get-started.html says we need to set + # The Qt6 documentation to using CMake at https://doc.qt.io/qt-5/cmake-get-started.html says we need to set # CMAKE_PREFIX_PATH environment variable to the Qt 5 installation prefix. In theory, setting the corresponding CMake # variable should do the same job (per https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html). # # It seems this is only needed on MacOS. According to ./third-party/valijson/README.md we are not the only ones to # experience this! # - execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE CMAKE_PREFIX_PATH) + execute_process(COMMAND brew --prefix qt6 OUTPUT_VARIABLE CMAKE_PREFIX_PATH) message(STATUS "CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}") endif() @@ -451,8 +480,8 @@ set_property(GLOBAL PROPERTY AUTOGEN_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/autog 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 +# Although it's possible to do individual find_package commands for each bit of Qt we want to use (Qt6Core, Qt6Widgets, +# Qt6Sql, etc), the newer, and more compact, way of doing things (per # https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html and https://doc.qt.io/qt-5/cmake-get-started.html) is to do # one find for Qt as a whole and to list the components that we need. Depending on what versions of CMake and Qt you # have installed, the way to find out what components exist varies a bit, but there is a relatively recent list at @@ -476,16 +505,16 @@ set(qtToolsComponents LinguistTools ) list(APPEND qtAllComponents ${qtCommonComponents} ${qtToolsComponents} ${qtTestComponents}) -find_package(Qt5 ${QT5_MIN_VERSION} COMPONENTS ${qtAllComponents} REQUIRED) +find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS ${qtAllComponents} REQUIRED) # Each package has its own include directories that we need to make sure the compiler knows about foreach(qtComponent IN LISTS qtAllComponents) # Sometimes it's useful that part of a variable name can be specified by expanding another variable! - include_directories(${Qt5${qtComponent}_INCLUDE_DIRS}) + include_directories(${Qt6${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) +if(Qt6_POSITION_INDEPENDENT_CODE) # This will initialize the POSITION_INDEPENDENT_CODE property on all the targets set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() @@ -496,28 +525,28 @@ if(WIN32) #================================================= Windows Qt Stuff ================================================= #==================================================================================================================== # .:TBD:. Not sure whether/why we need these additional Qt components on Windows - #find_package(Qt5MultimediaWidgets REQUIRED) - #find_package(Qt5OpenGL REQUIRED) - -# get_target_property(QtMultimediaWidgets_location Qt5::MultimediaWidgets LOCATION_${CMAKE_BUILD_TYPE}) -# get_target_property(QtOpenGL_location Qt5::OpenGL LOCATION_${CMAKE_BUILD_TYPE}) - 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(QtNetwork_location Qt5::Network 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}) + #find_package(Qt6MultimediaWidgets REQUIRED) + #find_package(Qt6OpenGL REQUIRED) + +# get_target_property(QtMultimediaWidgets_location Qt6::MultimediaWidgets LOCATION_${CMAKE_BUILD_TYPE}) +# get_target_property(QtOpenGL_location Qt6::OpenGL LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtCore_location Qt6::Core LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtGui_location Qt6::Gui LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtMultimedia_location Qt6::Multimedia LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtNetwork_location Qt6::Network LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtPrintSupport_location Qt6::PrintSupport LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQgif_location Qt6::QGifPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQico_location Qt6::QICOPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQjpeg_location Qt6::QJpegPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQsvgIcon_location Qt6::QSvgIconPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQsvg_location Qt6::QSvgPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQtiff_location Qt6::QTiffPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtQWindows_location Qt6::QWindowsIntegrationPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtSqliteDriver_location Qt6::QSQLiteDriverPlugin LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtSql_location Qt6::Sql LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtSvg_location Qt6::Svg LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtWidgets_location Qt6::Widgets LOCATION_${CMAKE_BUILD_TYPE}) + get_target_property(QtXml_location Qt6::Xml LOCATION_${CMAKE_BUILD_TYPE}) # .:TBD:. Not clear whether/where these xxx_DLLs variables get used set(Qt_DLLs ${QtCore_location} @@ -547,25 +576,31 @@ if(WIN32) 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}") + get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) + get_filename_component(QT_BIN_DIR "${_qmake_executable}" DIRECTORY) + message("QT_BIN_DIR = ${QT_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}) - # Per https://cmake.org/cmake/help/latest/command/add_executable.html, "IMPORTED executables are useful for - # convenient reference from commands like add_custom_command()". - add_executable(Qt5::windeployqt IMPORTED) - set_target_properties(Qt5::windeployqt PROPERTIES IMPORTED_LOCATION ${WINDEPLOYQT_EXECUTABLE}) + # On some systems at least, looks like Qt6::windeployqt is already available in CMake (when Qt5::windeployqt) was + # not. If it is, then we can't try to set it up again as we'll get an error ("add_executable cannot create imported + # target "Qt6::windeployqt" because another target with the same name already exists"). + # + if (NOT TARGET Qt6::windeployqt) + find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}") + if(EXISTS ${WINDEPLOYQT_EXECUTABLE}) + # Per https://cmake.org/cmake/help/latest/command/add_executable.html, "IMPORTED executables are useful for + # convenient reference from commands like add_custom_command()". + add_executable(Qt6::windeployqt IMPORTED) + set_target_properties(Qt6::windeployqt PROPERTIES IMPORTED_LOCATION ${WINDEPLOYQT_EXECUTABLE}) + endif() endif() # International Components for Unicode - file(GLOB IcuDlls "${QT5_BIN_DIR}/libicu*.dll") + file(GLOB IcuDlls "${QT_BIN_DIR}/libicu*.dll") elseif(APPLE) #==================================================================================================================== #=================================================== Mac Qt Stuff =================================================== @@ -579,18 +614,18 @@ elseif(APPLE) # process of creating a deployable [folder / applicaiton bundle] that contains [the necessary Qt dependencies]" - ie # so that the end user does not have to install Qt to run our software). They have completely different # implementations and command line options, so it would be unhelpful to try to treat them identically. - find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT5_BIN_DIR}") + find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}") if(EXISTS ${MACDEPLOYQT_EXECUTABLE}) # Per https://cmake.org/cmake/help/latest/command/add_executable.html, "IMPORTED executables are useful for # convenient reference from commands like add_custom_command()". - add_executable(Qt5::macdeployqt IMPORTED) - set_target_properties(Qt5::macdeployqt PROPERTIES IMPORTED_LOCATION ${MACDEPLOYQT_EXECUTABLE}) + add_executable(Qt6::macdeployqt IMPORTED) + set_target_properties(Qt6::macdeployqt PROPERTIES IMPORTED_LOCATION ${MACDEPLOYQT_EXECUTABLE}) endif() endif() # Uncomment the following line to show what version of Qt is actually being used -message(STATUS "Using Qt version " ${Qt5Core_VERSION}) +message(STATUS "Using Qt version " ${Qt6Core_VERSION}) #===================================================== Find Boost ====================================================== # Boost is a collection of separate libraries, some, but not all, of which are header-only. We only specify the Boost @@ -659,7 +694,14 @@ message(STATUS "Using Qt version " ${Qt5Core_VERSION}) # libraries. Boost JSON needs a more recent version than 1.71. # set(Boost_USE_STATIC_LIBS ON) -find_package(Boost 1.79.0 REQUIRED) +if(WIN32) + find_package(Boost 1.79.0 REQUIRED) +elseif(APPLE) + find_package(Boost 1.79.0 REQUIRED) +else() + # Note that header-only libraries don't have a component + find_package(Boost 1.79.0 REQUIRED COMPONENTS stacktrace_backtrace) +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}") @@ -919,32 +961,17 @@ include(src/CMakeLists.txt) # - 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 +# Getting both these things done in Qt5 was a bit complicated as qt_add_translation() _only_ does the latter. But, +# with Qt6, we now have qt_add_lupdate that does the former. NOTE that we will want to tweak the syntax of +# qt_add_lupdate once we are on Qt 6.7 -- per https://doc.qt.io/qt-6/qtlinguist-cmake-qt-add-lupdate.html -- because a +# change is introduced with that release and the syntax used here then becomes deprecated. # -qt5_add_translation(QM_FILES ${translationSourceFiles}) +qt_add_translation(QM_FILES ${translationSourceFiles}) # Add a target for the QM_FILES so that we can add the translations as a dependency for the executable later. add_custom_target(translationsTarget DEPENDS ${QM_FILES}) +qt_add_lupdate(translationsTarget TS_FILES ${repoDir}/src -ts ${translationSourceFiles} SOURCES ${filesToCompile_cpp} ${filesToCompile_ui}) -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 "") @@ -1123,7 +1150,7 @@ if(APPLE) "-framework ApplicationServices") endif() foreach(qtComponent IN LISTS qtCommonComponents) - list(APPEND appAndTestCommonLibraries "Qt5::${qtComponent}") + list(APPEND appAndTestCommonLibraries "Qt6::${qtComponent}") endforeach() message("appAndTestCommonLibraries: ${appAndTestCommonLibraries}") target_link_libraries(${fileName_executable} ${appAndTestCommonLibraries}) @@ -1138,8 +1165,8 @@ add_executable(${fileName_unitTestRunner} $) #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) +# Test app needs all the same libraries as the main app, plus Qt6::Test +target_link_libraries(${fileName_unitTestRunner} ${appAndTestCommonLibraries} Qt6::Test) message("Unit Test Runner: ./${fileName_unitTestRunner}") diff --git a/configure b/configure index ea358e56a..4ec820f29 100755 --- a/configure +++ b/configure @@ -1,6 +1,32 @@ #!/bin/bash +#---------------------------------------------------------------------------------------------------------------------- +# configure is part of Brewtarget, and is copyright the following authors 2009-2024: +# • Matt Young +# • Philip Greggory Lee +# +# 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 +# . +#---------------------------------------------------------------------------------------------------------------------- -# stop when sth failed +# +# This script can be used to help set up the build directory for doing CMake builds. +# +# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ +# NB: Meson and the `bt` build tool Python script are now the primary way of building and packaging the software. You +# can also still CMake to compile the product and install it locally, but we no longer support using CMake to do +# packaging. +# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ +# + +# Stop when something failed set -e PREFIX="" @@ -63,6 +89,15 @@ fi echo "CMAKEOPTIONS: $CMAKEOPTIONS" +# When a git repository is cloned, the submodules don't get cloned until you specifically ask for it +if [ ! -d third-party ] +then + echo "Pulling in submodules" + mkdir -p third-party + git submodule init + git submodule update +fi + # Create dir only if needed mkdir -p build @@ -71,7 +106,4 @@ cd build/ cmake $CMAKEOPTIONS ../ # Tell the user what to do (if everything went well...) -echo "" -echo "" -echo -e "\tNow, cd to build/ and run \"make\"" -echo "" +echo -e "\n\n\tNow, cd to build/ and run \"make\" (or \"cmake --build .\" on Windows)\n" diff --git a/doc/manpage.1.md.in b/doc/manpage.1.md.in index cc35a9ab6..6ad4f355d 100644 --- a/doc/manpage.1.md.in +++ b/doc/manpage.1.md.in @@ -15,7 +15,7 @@ all the important parameters, helps you with mash temperatures, and just makes the process of recipe formulation much easier. # COPYRIGHT -Copyright © various authors 2009-2023 -- see accompanying documentation +Copyright © various authors 2009-2024 -- see accompanying documentation @CONFIG_APPLICATION_NAME_UC@ 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 diff --git a/meson.build b/meson.build index d718797c0..cad2fd523 100644 --- a/meson.build +++ b/meson.build @@ -141,8 +141,8 @@ # - Meson 0.59.0 or newer to use qt.compile_resources, qt.compile_ui and qt.compile_moc # - Meson 0.60.0 or newer to use + to append items to lists (aka 'list.' feature -- at least that's what the # warning message says if you've specified a lower minimum version of Meson) -# We would/will need: # - Meson 0.62.0 or newer for dep 'dl' custom lookup, but current version of Meson on Ubuntu 22.04 LTS is only 0.61.2 +# - Meson 0.63.0 or newer to correctly locate versions of Qt >= 6.1 -- see https://mesonbuild.com/Qt6-module.html # # NB: Per https://mesonbuild.com/Reference-manual_functions.html#project the default_options settings "are only used # when running Meson for the first time"! So if you change any of the default_options settings you *MUST* delete @@ -275,8 +275,8 @@ testRunnerTargetName = mainExecutableTargetName + '_tests' #======================================================================================================================= #==================================================== Meson modules ==================================================== #======================================================================================================================= -# Import the Qt tools. See https://mesonbuild.com/Qt5-module.html -qt = import('qt5') +# Import the Qt tools. See https://mesonbuild.com/Qt6-module.html +qt = import('qt6') # File System module - see https://mesonbuild.com/Fs-module.html fs = import('fs') @@ -325,16 +325,14 @@ sharedLibraryPaths = [] # 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. # -# 2024-05-19: Removed support for Ubuntu 20.04 because the latest version of Qt available in its packages is 5.12.8, -# which is no longer supported (and, amongst other things, has an old version of SQLite that does not -# support "DROP COLUMN"). Qt 5.15 is the long-term support version of Qt5. +# As of 2024-09-30: +# - Qt 6.2.4 is the maximum available in Ubuntu 22.04 (Jammy). +# - Qt 6.4.2 is the maximum available in Ubuntu 24.04 (Noble). # -# For the moment, max version we can have here is 5.15.3, because that's what Ubuntu 22.04 topped out at. But note -# that Meson will be happy with newer versions (5.15.13 on Windows & Mac). - +minVersionOfQt = '>=6.2.4' # Tell meson which Qt modules we need -qtCommonDependencies = dependency('qt5', - version : '>=5.15.3', +qtCommonDependencies = dependency('qt6', + version : minVersionOfQt, modules : ['Core', 'Gui', # Needed for QColor on Mac? 'Multimedia', @@ -348,9 +346,9 @@ qtCommonDependencies = dependency('qt5', static : true) # The Qt Gui module is only needed for the main program. (We don't want the tests to try to load it or it could barf # in a GitHub action that does not have a display running.) -qtMainExeDependencies = dependency('qt5', version : '>=5.15.3', modules: ['Gui']) +qtMainExeDependencies = dependency('qt6', version : minVersionOfQt, modules: ['Gui']) # The Qt Test module is only needed for the unit tests -qtTestRunnerDependencies = dependency('qt5', version : '>=5.15.3', modules: ['Test']) +qtTestRunnerDependencies = dependency('qt6', version : minVersionOfQt, modules: ['Test']) #===================================================== Find Boost ====================================================== # Boost is a collection of separate libraries, some, but not all, of which are header-only. We only specify the Boost @@ -1232,7 +1230,21 @@ generatedFromMocForUnitTests = qt.compile_moc(headers : unitTestMocHeaders, # manually from the command line with lrelease # Calling qt.compile_translations will do only the latter, so we need to do the former directly # +# +# NB: In Qt 5, the `lupdate` command line tool is just invoked as `lupdate` on all three of our build platforms. In +# Qt 6, it's a bit different. We still call `lupdate` on Mac, but, on Windows we need to call `lupdate-qt6` (see file +# list at https://packages.msys2.org/packages/mingw-w64-x86_64-qt6-tools). This is presumably to allow Qt 5 and Qt 6 to +# coexist, but we have to take account of it here. +# +lupdate_name = 'lupdate' +if host_machine.system() == 'windows' + lupdate_name = 'lupdate-qt6' +elif host_machine.system() == 'linux' + lupdate_name = 'lupdate' +endif +lupdate_executable = find_program(lupdate_name, required : true) +# # Call lupdate to ensure the .ts files are synced with the source code. We need: # lupdate meson.project_source_root()/src meson.project_source_root()/ui -ts [list of .ts files] # This tells lupdate to recursively scan the src/ and ui/ subdirectories and update the specified ts files @@ -1240,9 +1252,13 @@ generatedFromMocForUnitTests = qt.compile_moc(headers : unitTestMocHeaders, # # We make a point here of displaying the output of run_command because we want to show message emitted by lupdate about # what it did. -message('Running lupdate on the following ts files:', run_command('ls', translationSourceFiles, check: true).stdout()) +# +message( + 'Running lupdate (', lupdate_executable.full_path(), ') on the following ts files:', + run_command('ls', translationSourceFiles, check: true).stdout() +) message( - run_command('lupdate', + run_command(lupdate_executable, meson.project_source_root() + '/src', meson.project_source_root() + '/ui', '-ts', diff --git a/packaging/linux/control.in b/packaging/linux/control.in index a53fe7275..1b7103172 100644 --- a/packaging/linux/control.in +++ b/packaging/linux/control.in @@ -75,27 +75,26 @@ Architecture: amd64 # Note that you can see the version of a package libfoobar by running the following command from the shell: # apt-cache show foobar | grep Version # -# Note too that we need either libqt5gui5 or libqt5gui5-gles, not both (which is not possible as they conflict). -# # Normally, this field is (surprisingly) not allowed to be "folded" (ie split across multiple lines). However, we do # our own folding in the bt build script, so the backslash line continuations are OK here! # -# I _think_ libqt5core5a has been replaced by libqt5core5t64 +# From trial-and-error, we discover which libraries have a "t64" version which is used instead of the "base" one (eg +# libqt6network6t64 instead of libqt6network6). This is to do with upgrades to 64-bit time (to fix the "year 2038 +# problem") -- per https://wiki.debian.org/ReleaseGoals/64bit-time. # Depends: \ - libc6 (>= 2.34 ), \ - libgcc-s1 (>= 3.3 ), \ - libqt5core5t64 (>= 5.15.3), \ - libqt5gui5 (>= 5.15.3) | \ - libqt5gui5-gles (>= 5.15.3), \ - libqt5multimedia5 (>= 5.15.3), \ - libqt5network5 (>= 5.15.3), \ - libqt5printsupport5 (>= 5.15.3), \ - libqt5sql5 (>= 5.15.3), \ - libqt5widgets5 (>= 5.15.3), \ - libstdc++6 (>= 11 ), \ - libxalan-c112 (>= 1.12 ), \ - libxerces-c3.2 (>= 3.2 ) + libc6 (>= 2.35 ), \ + libgcc-s1 (>= 12.3 ), \ + libqt6core6t64 (>= 6.2.4), \ + libqt6gui6t64 (>= 6.2.4) | \ + libqt6multimedia6 (>= 6.2.4), \ + libqt6network6t64 (>= 6.2.4), \ + libqt6printsupport6t64 (>= 6.2.4), \ + libqt6sql6t64 (>= 6.2.4), \ + libqt6widgets6t64 (>= 6.2.4), \ + libstdc++6 (>= 12.3 ), \ + libxalan-c112t64 (>= 1.12 ), \ + libxerces-c3.2t64 (>= 3.2.3) # # Installed-Size (Optional) : an estimate of the total amount of disk space required to install the named package # The disk space is given as the integer value of the estimated installed size in bytes, divided by 1024 and rounded diff --git a/packaging/linux/rpm.spec.in b/packaging/linux/rpm.spec.in index 2b41d144a..d9d98ef02 100644 --- a/packaging/linux/rpm.spec.in +++ b/packaging/linux/rpm.spec.in @@ -62,13 +62,13 @@ BuildArch : x86_64 # Requires : \ libgcc_s1 >= 3.3 , \ - libqt5core5 >= 5.9.5, \ - libqt5gui5 >= 5.9.5, \ - libqt5multimedia5 >= 5.9.5, \ - libqt5network5 >= 5.9.5, \ - libqt5printsupport5 >= 5.9.5, \ - libqt5sql5 >= 5.9.5, \ - libqt5widgets5 >= 5.9.5, \ + libqt6core6 >= 6.2.4, \ + libqt6gui6 >= 6.2.4, \ + libqt6multimedia6 >= 6.2.4, \ + libqt6network6 >= 6.2.4, \ + libqt6printsupport6 >= 6.2.4, \ + libqt6sql6 >= 6.2.4, \ + libqt6widgets6 >= 6.2.4, \ libstdc++6 >= 11 , \ libxalan-c112 >= 1.12 , \ libxerces-c3_2 >= 3.2 diff --git a/scripts/buildTool.py b/scripts/buildTool.py index e4c9dcdd4..0fe8eb8b3 100755 --- a/scripts/buildTool.py +++ b/scripts/buildTool.py @@ -415,6 +415,12 @@ def installDependencies(): # - The rpm and rpmlint packages are for creating RPM packages # - We need python-dev to build parts of Boost -- though it may be we could do without this as we only use a # few parts of Boost and most Boost libraries are header-only, so do not require compilation. + # - To keep us on our toes, some of the package name formats change between Qt5 and Qt6. Eg qtmultimedia5-dev + # becomes qt6-multimedia-dev. Also libqt5multimedia5-plugins has no direct successor in Qt6. + # + # I have struggled to find how to install a Qt6 version of lupdate. Compilation on Ubuntu 24.04 seems to work + # fine with the 5.15.13 version of lupdate, so we'll make sure that's installed. Various other comments below + # about lupdate are (so far unsuccessful) attempts to get a Qt6 version of lupdate installed. # log.info('Ensuring libraries and frameworks are installed') btUtils.abortOnRunFail(subprocess.run(['sudo', 'apt-get', 'update'])) @@ -425,10 +431,9 @@ def installDependencies(): 'coreutils', 'debhelper', 'git', - 'libqt5multimedia5-plugins', - 'libqt5sql5-psql', - 'libqt5sql5-sqlite', - 'libqt5svg5-dev', + 'libqt6sql6-psql', + 'libqt6sql6-sqlite', + 'libqt6svg6-dev', 'libxalan-c-dev', 'libxerces-c-dev', 'lintian', @@ -437,10 +442,13 @@ def installDependencies(): 'pandoc', 'python3', 'python3-dev', + 'qmake6', # Possibly needed for Qt6 lupdate 'qtbase5-dev', - 'qtmultimedia5-dev', - 'qttools5-dev', - 'qttools5-dev-tools', + 'qt6-l10n-tools', # Needed for Qt6 lupdate? + 'qt6-multimedia-dev', + 'qt6-tools-dev', + 'qttools5-dev-tools', # For Qt5 version of lupdate, per comment above + 'qt6-tools-dev-tools', 'rpm', 'rpmlint'] ) @@ -660,37 +668,47 @@ def installDependencies(): ) ) -### # -### # Commented this out as, as of 2024, we don't support Ubuntu 20.04 any more. -### # -### # Ubuntu 20.04 packages only have Meson 0.53.2, and we need 0.60.0 or later. In this case it means we have to -### # install Meson via pip, which is not ideal on Linux. -### # -### # Specifically, as explained at https://mesonbuild.com/Getting-meson.html#installing-meson-with-pip, although -### # using the pip3 install gets a newer version, we have to do the pip install as root (which is normally not -### # recommended). If we don't do this, then running `meson install` (or even `sudo meson install`) will barf on -### # Linux (because we need to be able to install files into system directories). -### # -### # So, where a sufficiently recent version of Meson is available in the distro packages (eg -### # `sudo apt install meson` on Ubuntu etc) it is much better to install this. Installing via pip is a last -### # resort. -### # -### # The distro ID we get from 'lsb_release -is' will be 'Ubuntu' for all the variants of Ubuntu (eg including -### # Kubuntu). Not sure what happens on derivatives such as Linux Mint though. -### # -### distroName = str( -### btUtils.abortOnRunFail(subprocess.run(['lsb_release', '-is'], encoding = "utf-8", capture_output = True)).stdout -### ).rstrip() -### log.debug('Linux distro: ' + distroName) -### if ('Ubuntu' == distroName): -### ubuntuRelease = str( -### btUtils.abortOnRunFail(subprocess.run(['lsb_release', '-rs'], encoding = "utf-8", capture_output = True)).stdout -### ).rstrip() -### log.debug('Ubuntu release: ' + ubuntuRelease) -### if (Decimal(ubuntuRelease) < Decimal('22.04')): -### log.info('Installing newer version of Meson the hard way') -### btUtils.abortOnRunFail(subprocess.run(['sudo', 'apt', 'remove', '-y', 'meson'])) -### btUtils.abortOnRunFail(subprocess.run(['sudo', 'pip3', 'install', 'meson'])) + # + # Although Ubuntu 24.04 gives us Meson 1.3.2, Ubuntu 22.04 packages only have Meson 0.61.2. We need Meson + # 0.63.0 or later. In this case it means we have to install Meson via pip, which is not ideal on Linux. + # + # Specifically, as explained at https://mesonbuild.com/Getting-meson.html#installing-meson-with-pip, although + # using the pip3 install gets a newer version, we have to do the pip install as root (which is normally not + # recommended). If we don't do this, then running `meson install` (or even `sudo meson install`) will barf on + # Linux (because we need to be able to install files into system directories). + # + # So, where a sufficiently recent version of Meson is available in the distro packages (eg + # `sudo apt install meson` on Ubuntu etc) it is much better to install this. Installing via pip is a last + # resort. + # + # The distro ID we get from 'lsb_release -is' will be 'Ubuntu' for all the variants of Ubuntu (eg including + # Kubuntu). Not sure what happens on derivatives such as Linux Mint though. + # + # ANOTHER problem on Ubuntu 22.04 is that lupdate doesn't work with Qt6, because it runs qtchooser which does + # not work with Qt6 on Ubuntu 22.04 because of the following "won't fix" + # bug: https://bugs.launchpad.net/ubuntu/+source/qtchooser/+bug/1964763. The workaround suggested at + # https://askubuntu.com/questions/1460242/ubuntu-22-04-with-qt6-qmake-could-not-find-a-qt-installation-of is + # to run `sudo qtchooser -install qt6 $(which qmake6)`, so that's what we do here after sorting out the Meson + # install. + # + distroName = str( + btUtils.abortOnRunFail(subprocess.run(['lsb_release', '-is'], encoding = "utf-8", capture_output = True)).stdout + ).rstrip() + log.debug('Linux distro: ' + distroName) + if ('Ubuntu' == distroName): + ubuntuRelease = str( + btUtils.abortOnRunFail(subprocess.run(['lsb_release', '-rs'], encoding = "utf-8", capture_output = True)).stdout + ).rstrip() + log.debug('Ubuntu release: ' + ubuntuRelease) + if (Decimal(ubuntuRelease) < Decimal('24.04')): + log.info('Installing newer version of Meson the hard way') + btUtils.abortOnRunFail(subprocess.run(['sudo', 'apt', 'remove', '-y', 'meson'])) + btUtils.abortOnRunFail(subprocess.run(['sudo', 'pip3', 'install', 'meson'])) + # + # Now fix lupdate + # + fullPath_qmake6 = shutil.which('qmake6') + btUtils.abortOnRunFail(subprocess.run(['sudo', 'qtchooser', '-install', 'qt6', fullPath_qmake6])) #----------------------------------------------------------------------------------------------------------------- #--------------------------------------------- Windows Dependencies ---------------------------------------------- @@ -770,6 +788,22 @@ def installDependencies(): # sites, so we now specify it routinely. # # As noted above, we no longer support 32-bit ('i686') builds and now only support 64-bit ('x86_64') ones. + # NOTE that, as explained at + # https://forum.qt.io/topic/140029/i-ve-downloaded-the-qt6-version-and-mingw-for-gcc-11-version/7, we will + # still see mention of "mingw32" in bits of the toolchain on 64-bit builds, but the "32" in the name is there + # for historical reasons and does not mean it's not a fully 64-bit build! + # + # Compiling the list of required packages here involves a bit of trial-and-error. A good starting point for + # what we probably need is found by searching for qt6 in the list at https://packages.msys2.org/base. However, + # it can still be challenging to work out which package provided the missing binary or library that is + # preventing your build from working. + # + # Eg, when you install mingw-w64-x86_64-qt6-static, you get a message saying mingw-w64-x86_64-clang-libs is an + # "optional dependency" required for lupdate and qdoc. Since we need lupdate, we therefore need to install + # clang-libs, even though our own compilation is done with GCC. (In fact, per comments in meson.build, lupdate + # also gets a name change to lupdate-qt6, but we don't have to worry about that here!) + # + # So, it may be that the list below is not minimal, but it should be sufficient! # # 2024-07-29: TBD: Not totally sure we need angleproject. It wasn't previously a requirement, but, as of # recently, windeployqt complains if it can't find it. The alternative would be to pass @@ -786,14 +820,18 @@ def installDependencies(): 'git', 'mingw-w64-' + arch + '-boost', 'mingw-w64-' + arch + '-cmake', + 'mingw-w64-' + arch + '-clang-libs', # Needed for lupdate 'mingw-w64-' + arch + '-libbacktrace', 'mingw-w64-' + arch + '-meson', 'mingw-w64-' + arch + '-nsis', 'mingw-w64-' + arch + '-freetype', 'mingw-w64-' + arch + '-harfbuzz', - 'mingw-w64-' + arch + '-qt5-base', - 'mingw-w64-' + arch + '-qt5-static', - 'mingw-w64-' + arch + '-qt5', + 'mingw-w64-' + arch + '-qt6-base', + 'mingw-w64-' + arch + '-qt6-declarative', # Also needed for lupdate? + 'mingw-w64-' + arch + '-qt6-static', + 'mingw-w64-' + arch + '-qt6-tools', + 'mingw-w64-' + arch + '-qt6-translations', + 'mingw-w64-' + arch + '-qt6', 'mingw-w64-' + arch + '-toolchain', 'mingw-w64-' + arch + '-xalan-c', 'mingw-w64-' + arch + '-xerces-c', @@ -872,8 +910,6 @@ def installDependencies(): # too (as the former depends on the latter). However, I think it's clearer to explicitly list all the direct # dependencies (eg we do make calls directly into Xerces). # - # For the moment, we install Qt 5 (= 5.15.13), as there are code changes required to use Qt 6 - # # .:TBD:. Installing Boost here doesn't seem to give us libboost_stacktrace_backtrace # Also, trying to use the "--cc=clang" option to install boost gives an error ("Error: boost: no bottle # available!") For the moment, we're just using Boost header files on Mac though, so this should be @@ -894,7 +930,7 @@ def installDependencies(): 'ninja', 'pandoc', 'tree', - 'qt@5', + 'qt@6', # 'xalan-c', # 'xerces-c' ] @@ -921,44 +957,47 @@ def installDependencies(): log.debug('Installing ' + packageToInstall + ' via Homebrew') btUtils.abortOnRunFail(subprocess.run(['brew', 'install', packageToInstall])) # - # By default, even once Qt5 is installed, Meson will not find it + # By default, even once Qt is installed, Meson will not find it # # See https://stackoverflow.com/questions/29431882/get-qt5-up-and-running-on-a-new-mac for suggestion to do - # the following to run `brew link qt5 --force` to "symlink the various Qt5 binaries and libraries into your + # the following to run `brew link qt5 --force` to "symlink the various Qt binaries and libraries into your # /usr/local/bin and /usr/local/lib directories". # - btUtils.abortOnRunFail(subprocess.run(['brew', 'link', '--force', 'qt5'])) - - # - # Additionally, per lengthy discussion at https://github.com/Homebrew/legacy-homebrew/issues/29938, it seems - # we might also need either: - # ln -s /usr/local/Cellar/qt5/5.15.7/mkspecs /usr/local/mkspecs - # ln -s /usr/local/Cellar/qt5/5.15.7/plugins /usr/local/plugins - # or: - # export PATH=/usr/local/opt/qt5/bin:$PATH - # The former gives permission errors, so we do the latter in mac.yml - # - # But the brew command to install Qt also tells us to do the following: - # - # echo 'export PATH="/usr/local/opt/qt@5/bin:$PATH"' >> ~/.bash_profile - # export LDFLAGS="-L/usr/local/opt/qt@5/lib" - # export CPPFLAGS="-I/usr/local/opt/qt@5/include" - # export PKG_CONFIG_PATH="/usr/local/opt/qt@5/lib/pkgconfig" - # - # Note however that, in a GitHub runner, the first of these will give "[Errno 13] Permission denied". + btUtils.abortOnRunFail(subprocess.run(['brew', 'link', '--force', 'qt6'])) + + # + # Further notes from when we did this for Qt5: + # ┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ + # │ Additionally, per lengthy discussion at https://github.com/Homebrew/legacy-homebrew/issues/29938, it │ + # │ seems we might also need either: │ + # │ ln -s /usr/local/Cellar/qt5/5.15.7/mkspecs /usr/local/mkspecs │ + # │ ln -s /usr/local/Cellar/qt5/5.15.7/plugins /usr/local/plugins │ + # │ or: │ + # │ export PATH=/usr/local/opt/qt5/bin:$PATH │ + # │ The former gives permission errors, so we do the latter in mac.yml │ + # │ │ + # │ But the brew command to install Qt also tells us to do the following: │ + # │ │ + # │ echo 'export PATH="/usr/local/opt/qt@5/bin:$PATH"' >> ~/.bash_profile │ + # │ export LDFLAGS="-L/usr/local/opt/qt@5/lib" │ + # │ export CPPFLAGS="-I/usr/local/opt/qt@5/include" │ + # │ export PKG_CONFIG_PATH="/usr/local/opt/qt@5/lib/pkgconfig" │ + # │ │ + # │ Note however that, in a GitHub runner, the first of these will give "[Errno 13] Permission denied". │ + # └──────────────────────────────────────────────────────────────────────────────────────────────────────┘ # try: # See # https://stackoverflow.com/questions/1466000/difference-between-modes-a-a-w-w-and-r-in-built-in-open-function # for a good summary (clearer than the Python official docs) of the mode flag on open. with open("~/.bash_profile", "a+") as bashProfile: - bashProfile.write('export PATH="/usr/local/opt/qt@5/bin:$PATH"') + bashProfile.write('export PATH="/usr/local/opt/qt@6/bin:$PATH"') except IOError as ioe: # This is not fatal, so we just note the error and continue log.warning("Unable to write to .bash_profile: " + ioe.strerror) - os.environ['LDFLAGS'] = '-L/usr/local/opt/qt@5/lib' - os.environ['CPPFLAGS'] = '-I/usr/local/opt/qt@5/include' - os.environ['PKG_CONFIG_PATH'] = '/usr/local/opt/qt@5/lib/pkgconfig' + os.environ['LDFLAGS'] = '-L/usr/local/opt/qt@6/lib' + os.environ['CPPFLAGS'] = '-I/usr/local/opt/qt@6/include' + os.environ['PKG_CONFIG_PATH'] = '/usr/local/opt/qt@6/lib/pkgconfig' # # See comment about CMAKE_PREFIX_PATH in CMakeLists.txt. I think this is rather too soon to try to do this, @@ -967,7 +1006,7 @@ def installDependencies(): # Typically, this is going to set CMAKE_PREFIX_PATH to /usr/local/opt/qt@5 # qtPrefixPath = btUtils.abortOnRunFail( - subprocess.run(['brew', '--prefix', 'qt@5'], capture_output=True) + subprocess.run(['brew', '--prefix', 'qt@6'], capture_output=True) ).stdout.decode('UTF-8').rstrip() log.debug('Qt Prefix Path: ' + qtPrefixPath) os.environ['CMAKE_PREFIX_PATH'] = qtPrefixPath; @@ -1884,11 +1923,14 @@ def doPackage(): # folder tree "containing the Qt-related dependencies (libraries, QML imports, plugins, and translations) # required to run the application from that folder". # + # In the MSYS2 packaging of Qt6 at least, per https://packages.msys2.org/packages/mingw-w64-x86_64-qt6-base, + # windeployqt is renamed to windeployqt6. + # log.debug('Running windeployqt') previousWorkingDirectory = pathlib.Path.cwd().as_posix() os.chdir(dir_packages_win_bin) btUtils.abortOnRunFail( - subprocess.run(['windeployqt', + subprocess.run(['windeployqt6', '--verbose', '2', # 2 is the maximum projectName + '.exe'], capture_output=False) @@ -2213,7 +2255,8 @@ def doPackage(): ).stdout.decode('UTF-8') log.debug('Output of `otool -L' + capitalisedProjectName + '`: ' + otoolOutputExe) # - # The output from otool at this stage will be along the following lines: + # The output from otool at this stage will be along the following lines (though what's below is from when we + # were using Qt5): # # [capitalisedProjectName]: # /usr/local/opt/qt@5/lib/QtCore.framework/Versions/5/QtCore (compatibility version 5.15.0, current version 5.15.8) diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index 5f97ddd3c..7e888eced 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -25,6 +25,7 @@ #include "Localization.h" #include "measurement/Measurement.h" +#include "measurement/UnitSystem.h" #include "model/BrewNote.h" #include "PersistentSettings.h" diff --git a/src/Localization.cpp b/src/Localization.cpp index 806819a78..92a97299c 100644 --- a/src/Localization.cpp +++ b/src/Localization.cpp @@ -188,7 +188,7 @@ double Localization::toDouble(QString text, bool* ok) { double Localization::toDouble(NamedEntity const & element, BtStringConst const & propertyName, char const * const caller) { - if (element.property(*propertyName).canConvert(QVariant::String) ) { + if (element.property(*propertyName).canConvert()) { // Get the amount QString value = element.property(*propertyName).toString(); bool ok = false; @@ -222,7 +222,11 @@ void Localization::loadTranslations() { } // Load translators. - defaultTrans.load("qt_" + Localization::getLocale().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + bool succeeded = defaultTrans.load("qt_" + Localization::getLocale().name(), + QLibraryInfo::path(QLibraryInfo::TranslationsPath)); + if (!succeeded) { + qWarning() << Q_FUNC_INFO << "Error loading translations for" << Localization::getLocale().name(); + } if (getCurrentLanguage().isEmpty()) { setLanguage(getSystemLanguage()); } diff --git a/src/Logging.h b/src/Logging.h index 68a2ef033..2c62036fd 100644 --- a/src/Logging.h +++ b/src/Logging.h @@ -135,7 +135,7 @@ namespace Logging { extern QDir getDirectory(); /** - * \brief Initialize logging to utilize the built in logging functionality in QT5 + * \brief Initialize logging to utilize the built in logging functionality in Qt. * This has to be called before any logging is done, but after PersistentSettings::initialise() is called. * \return */ diff --git a/src/RadarChart.cpp b/src/RadarChart.cpp index cc5591b38..416f21380 100644 --- a/src/RadarChart.cpp +++ b/src/RadarChart.cpp @@ -17,25 +17,7 @@ #include #include - -// -// C++20 introduces std::numbers::pi, which has better cross-platform support than the M_PI constant in cmath. However, -// older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) do not ship with the new header. -// -#if defined(__linux__ ) && defined(__GNUC__) && (__GNUC__ < 10) - -// I hope this is allowed. We'll be able to get rid of it once we stop needing to support old versions of GCC -namespace std { - namespace numbers { - constexpr double pi = M_PI; - } -} - -#else - -#include // For std::numbers::pi - -#endif +#include #include #include @@ -97,7 +79,7 @@ class RadarChart::impl { */ void updateMaxAxisValue() { double maxInAllSeries = 0.0; - for (auto currSeries : qAsConst(this->allSeries)) { + for (auto const & currSeries : std::as_const(this->allSeries)) { // It's a coding error if we have a series with a null pointer. (If the object has gone away, we should remove // the series.) Q_ASSERT(currSeries.object); @@ -316,7 +298,7 @@ void RadarChart::paintEvent(QPaintEvent *event) { // Now plot the actual data // QPen seriesPen{allSeriesPen}; - for (auto currSeries : qAsConst(this->pimpl->allSeries)) { + for (auto const & currSeries : std::as_const(this->pimpl->allSeries)) { seriesPen.setColor(currSeries.color); painter.setPen(seriesPen); QVector seriesPoints(this->pimpl->variableNames.size()); diff --git a/src/RecipeFormatter.cpp b/src/RecipeFormatter.cpp index 99e410def..86fd09583 100644 --- a/src/RecipeFormatter.cpp +++ b/src/RecipeFormatter.cpp @@ -1156,7 +1156,7 @@ QString RecipeFormatter::getBBCodeFormat() { } QString tmp = ""; - QRegularExpression const regexp("(^[^\n]*\n)(.*$)"); // Regexp to match the first line of tables + QRegularExpression const regexp{"(^[^\n]*\n)(.*$)"}; // Regexp to match the first line of tables auto style = this->pimpl->rec->style(); diff --git a/src/database/Database.cpp b/src/database/Database.cpp index 5c84b472e..35448ad95 100644 --- a/src/database/Database.cpp +++ b/src/database/Database.cpp @@ -1116,29 +1116,30 @@ void Database::setForeignKeysEnabled(bool enabled, QSqlDatabase connection, Data type = this->dbType(); } + QString queryString{""}; switch (type) { case Database::DbType::SQLITE: - if (enabled) { - connection.exec("PRAGMA foreign_keys=on"); - } else { - connection.exec("PRAGMA foreign_keys=off"); - } + queryString = QString{"PRAGMA foreign_keys=%1"}.arg(enabled ? "on": "off"); break; case Database::DbType::PGSQL: // This is a bit of a hack, and needs you to be connected as super user, but seems more robust than // "SET CONSTRAINTS ALL DEFERRED" which requires foreign keys to have been set up in a particular way in the // first place (see https://www.postgresql.org/docs/13/sql-set-constraints.html). - if (enabled) { - connection.exec("SET session_replication_role TO 'origin'"); - } else { - connection.exec("SET session_replication_role TO 'replica'"); - } + queryString = QString{"SET session_replication_role TO '%1'"}.arg(enabled ? "origin": "replica"); break; default: // It's a coding error (somewhere) if we get here! Q_ASSERT(false); } + BtSqlQuery sqlQuery{connection}; + sqlQuery.prepare(queryString); + if (!sqlQuery.exec()) { + qCritical() << + Q_FUNC_INFO << "Error executing database query " << queryString << ": " << sqlQuery.lastError().text(); + return; + } + return; } diff --git a/src/database/DatabaseSchemaHelper.cpp b/src/database/DatabaseSchemaHelper.cpp index a039b1488..cc38260b4 100644 --- a/src/database/DatabaseSchemaHelper.cpp +++ b/src/database/DatabaseSchemaHelper.cpp @@ -2335,19 +2335,19 @@ int DatabaseSchemaHelper::schemaVersion(QSqlDatabase & db) { // Initially, versioning was done with strings, so we need to convert // the old version strings to integer versions - if ( ver.convert(QVariant::Int) ) { + if (ver.canConvert()) { return ver.toInt(); } - if (stringVer == "2.0.0" ) { + if (stringVer == "2.0.0") { return 1; } - if (stringVer == "2.0.2" ) { + if (stringVer == "2.0.2") { return 2; } - if (stringVer == "2.1.0" ) { + if (stringVer == "2.1.0") { return 3; } diff --git a/src/database/ObjectStore.cpp b/src/database/ObjectStore.cpp index 410ae2585..6aa812c6e 100644 --- a/src/database/ObjectStore.cpp +++ b/src/database/ObjectStore.cpp @@ -1088,7 +1088,7 @@ class ObjectStore::impl { Q_ASSERT(ObjectStore::FieldType::Int == fieldDefn->fieldType); if (propertyBindValue.toInt() <= 0) { qDebug() << Q_FUNC_INFO << "Treating" << propertyBindValue << "foreign key value as NULL"; - propertyBindValue = QVariant(QVariant::Int); + propertyBindValue = QVariant{QMetaType{QMetaType::Int}}; } } sqlQuery.bindValue(QString{":%1"}.arg(*columnToUpdateInDb), propertyBindValue); @@ -1249,7 +1249,7 @@ class ObjectStore::impl { // Q_ASSERT(sqlQuery.driver()->hasFeature(QSqlDriver::LastInsertId)); QVariant rawPrimaryKey = sqlQuery.lastInsertId(); - Q_ASSERT(rawPrimaryKey.canConvert(QMetaType::Int)); + Q_ASSERT(rawPrimaryKey.canConvert()); primaryKeyInDb = rawPrimaryKey.toInt(); // diff --git a/src/main.cpp b/src/main.cpp index ebba91965..e1f3844f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -100,7 +100,14 @@ int main(int argc, char **argv) { // own logging. //qputenv("QT_DEBUG_PLUGINS", QByteArray("1")); - QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + // + // In Qt5, we used to make the following call here: + // + // QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + // + // However, in Qt6, this serves no purpose as "‘Qt::AA_EnableHighDpiScaling’ is deprecated: High-DPI scaling is + // always enabled. This attribute no longer has any effect." + // // // Various bits of Qt initialisation need to be done straight away for other Qt functionality to work correctly diff --git a/src/model/MashStep.cpp b/src/model/MashStep.cpp index 862f4283e..d03ce7340 100644 --- a/src/model/MashStep.cpp +++ b/src/model/MashStep.cpp @@ -124,7 +124,7 @@ MashStep::MashStep(MashStep const & other) : Step {other}, StepBase{}, + MashStepOptions>{other}, m_type {other.m_type }, m_amount_l {other.m_amount_l }, m_infuseTemp_c {other.m_infuseTemp_c }, diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index b8b4269b7..3a34ffa3a 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -271,23 +271,22 @@ class Recipe::impl { * \brief Make copies of the Instructions from one Recipe and add them to another - typically * because we are copying the Recipe. */ - template void copyList(Recipe & us, Recipe const & other) { + void copyInstructions(Recipe & us, Recipe const & other) { qDebug() << Q_FUNC_INFO; - for (int otherIngId : other.pimpl->accessIds()) { + for (int otherInstructionId : other.pimpl->instructionIds) { // Make and store a copy of the current Hop/Fermentable/etc object we're looking at in the other Recipe - auto otherIngredient = ObjectStoreWrapper::getById(otherIngId); - auto ourIngredient = copyIfNeeded(*otherIngredient); + auto otherInstruction = ObjectStoreWrapper::getById(otherInstructionId); + auto ourInstruction = copyIfNeeded(*otherInstruction); // Store the ID of the copy in our recipe - this->accessIds().append(ourIngredient->key()); + this->instructionIds.append(ourInstruction->key()); qDebug() << - Q_FUNC_INFO << "After adding" << ourIngredient->metaObject()->className() << "#" << ourIngredient->key() << - ", Recipe" << us.name() << "has" << this->accessIds().size() << "of" << - NE::staticMetaObject.className(); + Q_FUNC_INFO << "After adding Instruction #" << ourInstruction->key() << + ", Recipe" << us.name() << "has" << this->instructionIds.size() << "Instructions"; // Connect signals so that we are notified when there are changes to the Hop/Fermentable/etc we just added to // our recipe. - connect(ourIngredient.get(), &NamedEntity::changed, &us, &Recipe::acceptChangeToContainedObject); + connect(ourInstruction.get(), &NamedEntity::changed, &us, &Recipe::acceptChangeToContainedObject); } return; } @@ -1839,12 +1838,12 @@ Recipe::Recipe(Recipe const & other) : this->pimpl->copyAdditions(other); this->pimpl->copyAdditions(other); this->pimpl->copyAdditions(other); - this->pimpl->copyList(*this, other); + this->pimpl->copyInstructions(*this, other); // // You might think that Style, Mash and Equipment could safely be shared between Recipes. However, AFAICT, none of // them is. Presumably this is because users expect to be able to edit them in one Recipe without changing the - // settings for any other Recipe. + // settings for any other Recipe. TODO: We should change this so we can retire copyIfNeeded. // // We also need to be careful here as one or more of these may not be set to a valid value. // diff --git a/src/model/RecipeAdjustmentSalt.h b/src/model/RecipeAdjustmentSalt.h index de95ba6b7..4b8bfe6c8 100755 --- a/src/model/RecipeAdjustmentSalt.h +++ b/src/model/RecipeAdjustmentSalt.h @@ -125,7 +125,7 @@ class RecipeAdjustmentSalt : public IngredientInRecipe, }; Q_DECLARE_METATYPE(Salt) -Q_DECLARE_METATYPE(Salt *) +//Q_DECLARE_METATYPE(Salt *) BT_DECLARE_METATYPES(RecipeAdjustmentSalt) #endif diff --git a/src/model/Yeast.cpp b/src/model/Yeast.cpp index f2119414e..f23bb9c82 100644 --- a/src/model/Yeast.cpp +++ b/src/model/Yeast.cpp @@ -235,7 +235,6 @@ Yeast::Yeast(NamedParameterBundle const & namedParameterBundle) : // If we're being constructed from a BeerXML file, then we might only have typical attenuation rather than min and // max. Best we can do in that scenario is set min and max to the supplied value. if (namedParameterBundle.contains(PropertyNames::Yeast::attenuationTypical_pct)) { - double attenuationTypical_pct{namedParameterBundle.val(PropertyNames::Yeast::attenuationTypical_pct)}; if (!this->m_attenuationMin_pct) { ASSIGN_REGULAR_FROM_NPB(m_attenuationMin_pct, namedParameterBundle, PropertyNames::Yeast::attenuationTypical_pct); } if (!this->m_attenuationMax_pct) { ASSIGN_REGULAR_FROM_NPB(m_attenuationMax_pct, namedParameterBundle, PropertyNames::Yeast::attenuationTypical_pct); } } diff --git a/src/serialization/json/BeerJson.cpp b/src/serialization/json/BeerJson.cpp index 2fd514b4b..0b35685b9 100755 --- a/src/serialization/json/BeerJson.cpp +++ b/src/serialization/json/BeerJson.cpp @@ -30,8 +30,6 @@ #include #include -#include -#include #include "database/ObjectStoreWrapper.h" #include "model/Boil.h" diff --git a/src/serialization/json/BeerJson.h b/src/serialization/json/BeerJson.h index 964dea9bf..28429392b 100755 --- a/src/serialization/json/BeerJson.h +++ b/src/serialization/json/BeerJson.h @@ -19,11 +19,10 @@ #include // For PImpl +#include #include - -class QFile; -class QString; -class QTextStream; +#include +#include namespace BeerJson { /*! diff --git a/src/serialization/json/JsonSchema.cpp b/src/serialization/json/JsonSchema.cpp index 1d3a2bc62..a9f978390 100755 --- a/src/serialization/json/JsonSchema.cpp +++ b/src/serialization/json/JsonSchema.cpp @@ -188,13 +188,6 @@ class JsonSchema::impl { * \return Pointer to a Boost.JSON value which is the root of the document tree */ boost::json::value const * getReferencedDocument(std::string const & uri) { - // We assert that JsonSchema::fetchReferencedDocument is not calling us on a JsonSchema whose pimpl member - // variable has not yet been set. CLang thinks this assert is unnecessary ("warning: 'this' pointer cannot be - // null in well-defined C++ code; comparison may be assumed to always evaluate to true - // [-Wtautological-undefined-compare"]) so we disable the assert on that compiler (which is currently only MacOS). -#ifndef __clang__ - Q_ASSERT(this != nullptr); -#endif qDebug() << Q_FUNC_INFO << "Request for" << uri.c_str(); QString schemaFilePath = QString("%1/%2").arg(this->baseDir, uri.c_str()); if (!this->schemaFileCache.contains(schemaFilePath)) { @@ -309,5 +302,9 @@ bool JsonSchema::validate(boost::json::value const & document, QTextStream & use boost::json::value const * JsonSchema::fetchReferencedDocument(std::string const & uri) { // It's a coding error if we asked Valijson to load a schema without setting currentJsonSchema Q_ASSERT(currentJsonSchema); + + // It's also a coding error if the pimpl member variable of currentJsonSchema has not yet been set. + Q_ASSERT(currentJsonSchema->pimpl); + return currentJsonSchema->pimpl->getReferencedDocument(uri); } diff --git a/src/serialization/xml/BeerXml.cpp b/src/serialization/xml/BeerXml.cpp index 4493d244a..f9741b29b 100755 --- a/src/serialization/xml/BeerXml.cpp +++ b/src/serialization/xml/BeerXml.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include "config.h" // For CONFIG_VERSION_STRING @@ -1236,8 +1236,8 @@ BeerXML::~BeerXML() = default; void BeerXML::createXmlFile(QFile & outFile) const { QTextStream out(&outFile); - // BeerXML specifies the ISO-8859-1 encoding - out.setCodec(QTextCodec::codecForMib(CharacterSets::ISO_8859_1_1987)); + // BeerXML specifies the ISO-8859-1 (Latin 1) encoding + out.setEncoding(QStringConverter::Latin1); out << "\n" @@ -1259,9 +1259,7 @@ template void BeerXML::toXml(QList const & nes, QFile & ou // ... tags and so on. QTextStream out(&outFile); // BeerXML specifies the ISO-8859-1 encoding - // .:TODO:. In Qt6, QTextCodec and QTextStream::setCodec have been removed and are replaced by QStringConverter - // (which is new in Qt6). - out.setCodec(QTextCodec::codecForMib(CharacterSets::ISO_8859_1_1987)); + out.setEncoding(QStringConverter::Latin1); out << "<" << BEER_XML_RECORD_DEFN.m_recordName << "S>\n"; for (NE const * ne : nes) { std::unique_ptr xmlRecord{ diff --git a/src/tableModels/BtTableModel.cpp b/src/tableModels/BtTableModel.cpp index e0c5a0596..0add739a4 100644 --- a/src/tableModels/BtTableModel.cpp +++ b/src/tableModels/BtTableModel.cpp @@ -165,7 +165,7 @@ void BtTableModel::contextMenu(QPoint const & point) { // User will either have selected a SystemOfMeasurement or a UnitSystem::RelativeScale. We can know which based // on whether it's the menu or the sub-menu that it came from. - bool isTopMenu{invoked->parentWidget() == menu.get()}; + bool isTopMenu{invoked->parent() == menu.get()}; if (isTopMenu) { // It's the menu, so SystemOfMeasurement std::optional const whatSelected = diff --git a/src/tableModels/WaterTableModel.cpp b/src/tableModels/WaterTableModel.cpp index 485990e5e..817bd7dbb 100644 --- a/src/tableModels/WaterTableModel.cpp +++ b/src/tableModels/WaterTableModel.cpp @@ -262,7 +262,7 @@ bool WaterTableModel::setData(QModelIndex const & index, QVariant const & value, return false; } - bool retval = value.canConvert(QVariant::String); + bool retval = value.canConvert(); if (!retval) { return retval; } diff --git a/src/trees/TreeModel.cpp b/src/trees/TreeModel.cpp index 842fe68d4..c113cad9d 100644 --- a/src/trees/TreeModel.cpp +++ b/src/trees/TreeModel.cpp @@ -579,7 +579,7 @@ template Misc * TreeModel::getItem(QModelIndex const & index template Yeast * TreeModel::getItem(QModelIndex const & index) const; template BrewNote * TreeModel::getItem(QModelIndex const & index) const; template Style * TreeModel::getItem