@Axel-Spoerl Thanks for your feedback!
I took your advice given in the bug report about installing multiple versions of ICU in parallel (much easier than building Qt from source). It was quite tricky getting it to work because of the "renaming" feature that ICU uses. Requires some special "if...else" logic in the qmake project file, but it looks OK now.
I had installed the qt5-dev libraries from the Ubuntu 24.04.3 repositories which are at Qt version 5.15.13. Also, I installed the ICU dev packages from the Ubuntu repos which are compatible with their Qt 5 libraries. I have Qt 6 libraries installed under a folder /home/Qt/...which Qt Creator made, and these are not in the $PATH. So the trick was to find out which versions of ICU are needed by each QtCore .so library.
Qt 6.5.3 up to and including Qt 6.6 use ICU 56, and versions 6.7 and above use ICU 73. So I built ICU from source, being sure to download the appropriate ICU release packages, and installed those to appropriate subfolders under my /opt/ directory:
/opt/local/icu56/...
/opt/local/icu73/...
In addition to that, the INCLUDEPATH variable needs to be set in the qmake project file so that the headers in /opt/... are found BEFORE the headers in usr/include/unicode. This is what that part of my .pro qmake project file looks like now:
if(equals(QT_MAJOR_VERSION,6)) {
if(lessThan(QT_MINOR_VERSION,7)) {
ICU_VERSION_SUFFIX = 56
} else {
ICU_VERSION_SUFFIX = 73
}
ICU_INCLUDES="/opt/local/icu$${ICU_VERSION_SUFFIX}/include"
ICU_LIBS_DIR="/opt/local/icu$${ICU_VERSION_SUFFIX}/lib"
#####################################################
# This "#define ICU_VER=..." macro enables the header
# "icu_includes.hpp" to find the correct ICU headers:
#####################################################
ICU_VERSION = ICU_VER=$${ICU_VERSION_SUFFIX}
DEFINES += "$${ICU_VERSION}"
} else {
# Qt 5 will use the system ICU libraries, which are ICU version 74.2:
ICU_INCLUDES="/usr/include"
ICU_LIBS_DIR="/usr/lib/x86_64-linux-gnu"
}
LIBS += -L\""$${ICU_LIBS_DIR}"\" -licudata -licui18n -licuio -licutest -licutu -licuuc
INCLUDEPATH += \
$${ICU_INCLUDES} \
include \
# etc.
Then I have one header icu_includes.hpp which looks like this:
#ifndef ICU_INCLUDES_HPP
#define ICU_INCLUDES_HPP
#ifndef ICU_VER
/* ICU_VER is not defined for Qt 5, only for different flavors of Qt 6: */
#include <unicode/utypes.h>
#include <unicode/uclean.h>
#include <unicode/ucsdet.h>
#elif ICU_VER==73
#include "/opt/local/icu73/include/unicode/utypes.h"
#include "/opt/local/icu73/include/unicode/uclean.h"
#include "/opt/local/icu73/include/unicode/ucsdet.h"
#elif ICU_VER==56
#include "/opt/local/icu56/include/unicode/utypes.h"
#include "/opt/local/icu56/include/unicode/uclean.h"
#include "/opt/local/icu56/include/unicode/ucsdet.h"
#endif
#endif // ICU_INCLUDES_HPP
All other code which calls ICU functions directly (only one .cpp file, actually) includes this header. Since project include paths take preference over system paths, the "renaming" of functions like u_init() still works correctly. That means that u_init(), for example, resolves to u_init_56(), or u_init_73(), or even u_init_74()depending on the Qt version.
Now it has occurred to me that I could auto-generate the header using a file icu_includes.hpp.in and using variable substitutions from the project file (my next "TO DO"...)
So, one interesting fact is that my project, when built with Qt 5, uses a higher ICU library version than when built with Qt 6! (but only because that is what the Ubuntu 24.04 system installs).
I'm sure this scenario can be improved upon, so your suggestions are most welcome! :)