via Docker
Previously on this website:
But we haven't touched Mac yet! So, today we are compiling Qt6.8 on a Mac with M1 chip, macOS 15. We considered the excellent osxcross for cross-compilation, but are positive that Qt's WebEngine component (Chromium) would not be able to cross-compile - we need actual hardware.
You may wonder "why even compile at all" as Qt provides libraries/binaries for their releases. Well, the Qt installer does not offer a version of Qt that includes Vulkan via MoltenVK (so no QVulkanInstance
and QVulkanWindow
available). In addition, a custom Qt is advisable to ensure flags like -DQT_FEATURE_webengine_proprietary_codecs=OFF
are set before app distribution, or whatever else configuration options you'd like to provide.
We prepared a repository that will compile:
openssl3 lcms2 jpeg-turbo expat icu pcre2 graphite2 harfbuzz fontconfig freetype2 again cairo libb2 zstd tiff libwebp pcre2 fontconfig md4c libmng libevent double-conversion brotli libpng
The above dependencies are manually installed (see install_deps.sh) with the goal of
creating a folder ($prefix
) that only contains static libraries (.a
). We cannot use Homebrew,
as that will install dynamic libraries (.dylib
) which the Qt installation will give
precedence to when looking for libraries (it ignores CMAKE_FIND_LIBRARY_SUFFIXES
, not sure why).
In addition, we'll (unfortunately) "fix" pkgconfig files via patches on-the-fly, and add/modify CMake target files. This is to help Qt find the correct libraries during configuration stage.
We admit the repository is quite hacky (a collection of bash scripts) but nonetheless it should help, as compiling Qt on macOS with the aim to produce distributable libraries is non-trivial.
There are 3 steps:
This repository comes with the directory out/
filled with libraries that have
been previously compiled. Perhaps these libraries work for you, so that you may skip
step 1.
Clone this repo to start.
git clone <this_repo> ~/static
cd ~/static
For context, as of writing the MacOS SDK (over at /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
) was version 14.
We need some tooling to start.
brew install autoconf automake libtool python@3.12 virtualenv wget cmake node@22 ccache ninja
bash install_deps.sh
verify that this script successfully completes.
bash configure_qt.sh
configure_qt.sh
. For safety, the script will verify the path.configure_qt.sh
. For safety, the script will verify the path.rm -rf qt6/build
then run configure_qt.sh
again to start fresh.qt6/build/CMakeCache.txt
can be handy for a detailed description of what CMake was able to configureThe output is important, it should look like:
-- Configuring done (29.9s)
-- Generating done (4.7s)
-- Build files have been written to: /Users/foo/static/qt6/build
[*] detect which libraries are .dylib (and maybe need to be .a instead):
/Users/foo/VulkanSDK/1.3.290.0/macOS/lib/libvulkan.dylib
[*] detect any dylib's from brew (probably shouldnt link against homebrew stuff):
[*] also see 'summary.txt' for Qt configure output:
[*] to compile, 1) activate venv 'source venv/bin/activate' 2) 'cd qt6' 3) and:
[...]
Verify that:
$prefix
are in usesummary.txt
for Qt outputsource venv/bin/activate
cd qt6
cmake --build build --parallel 8 --target qtbase
cmake --build build --parallel 8 --target qtimageformats
cmake --build build --parallel 8 --target qtpositioning
cmake --build build --parallel 8 --target qtdeclarative
cmake --build build --parallel 1 --target qtwebengine
cmake --build build --parallel 8
note: webengine has parallel 1 to prevent a race condition (as well as requiring a lot of memory), the rest can be 8 threads.
When compiling is done, install Qt6 into $prefix/build
, for example: /Users/foo/static/build/
cmake --install build
Then finally use CMAKE_PREFIX_PATH
to compile your own Qt program against this custom Qt6.8:
cmake -DCMAKE_PREFIX_PATH="/Users/foo/static/build;/Users/foo/VulkanSDK/1.3.290.0/macOS/" -B build .
confirm these paths make sense.
git clone https://github.com/qt/qtbase.git --depth 1 --branch v6.8.0-beta4 /tmp/qtbase
cd /tmp/qtbase/examples/vulkan/hellovulkancubes
cmake -DCMAKE_PREFIX_PATH="/Users/foo/static/build;/Users/foo/VulkanSDK/1.3.290.0/macOS/" -B build .
make -Cbuild -j8
DYLD_PRINT_LIBRARIES=1 QT_VK_DEBUG=1 QT_LOGGING_RULES="qt.vulkan=true" QT_VULKAN_LIB=/Users/foo/VulkanSDK/1.3.290.0/macOS/lib/libMoltenVK.dylib ./build/hellovulkancubes.app/Contents/MacOS/hellovulkancubes
note QT_VULKAN_LIB
; ensure this path is correct.
Copy the following into on directory:
libMoltenVK.dylib
qt
Create qt.conf
with the contents:
[Paths]
Documentation = qt/doc
Headers = qt/include
Libraries = qt/lib
LibraryExecutables = qt/libexec
Binaries = qt/bin
Plugins = qt/plugins
QmlImports = qt/qml
Translations = qt/translations
Examples = qt/examples
Tests = qt/tests
The directory should look like this:
➜ $ ls -al
total 30672
drwxr-xr-x 6 kroket staff 192 8 Sep 01:37 .
drwxr-x---+ 58 kroket staff 1856 8 Sep 01:44 ..
-rwxr-xr-x 1 kroket staff 297472 8 Sep 01:30 hellovulkancubes
-rwxr-xr-x 1 kroket staff 15399712 8 Sep 01:23 libMoltenVK.dylib
drwxr-xr-x 13 kroket staff 416 8 Sep 01:24 qt
-rw-r--r-- 1 kroket staff 234 8 Sep 01:38 qt.conf
➜ $ ls qt/
bin include libexec mkspecs phrasebooks qml
doc lib metatypes modules plugins
Change rpath of your executable:
install_name_tool -add_rpath 'qt/lib' hellovulkancubes
note: you may need to remove any existing rpath with -delete_rpath <path>
Verify rpath is correct:
➜ $ otool -l hellovulkancubes
[...]
Load command 31
cmd LC_RPATH
cmdsize 24
path qt/lib (offset 12)
The executable is now portable (probably?! needs testing, DYLD_PRINT_LIBRARIES=1
looks good though):
QT_VULKAN_LIB=libMoltenVK.dylib ./hellovulkancubes
note: not sure how to get rid of QT_VULKAN_LIB
but you can set it via qputenv
before
constructing e.g QApplication
.
note #2: it is probably a better idea to create an actual "Framework" package. While the above will work, it may cause problems with notarization.
It is possible to cross-compile your Qt applications using osxcross using the above Qt installation, which is pretty cool. It boils down to:
libexec/
with their x86_64 counterparts (like moc
, rcc
, etc.). See also this forum postOSXCROSS_HOST, OSXCROSS_SDK, OSXCROSS_TARGET_DIR, OSXCROSS_TARGET
This has been a bit chaotic, but we hope it helps. Happy compiling.