Cristian Adam

A Better QNX CMake Toolchain File

At the end of October 2018 on the Qt development mailing list it was announced that CMake was chosen as the build system (generator) for building Qt6. That also meant that The Qt Company will gradually stop investing in their in house Qbs build system.

I personally think is a good idea to have major C++ projects like Boost (July 2017 switch announcement! ), LLVM/Clang, and now Qt to use CMake as their build system (generator). We C++ developers should work together in having a common build system.

There was a bit of email traffic on this topic. There was some skepticism of CMake being able to support specialized operating systems like QNX, so I pointed to an October 2017 blog entry of Doug Schaefer named QNX CMake Toolchain File. There Doug Schaefer presents us with a minimal CMake Toolchain File.

Since I am lucky(sweat_smile) to have a QNX 7.0 license I tried to compile and run the recently released CMake 3.13.0 for the QNX 7.0 x86_64 target!

Basic CMake Compilation

The toolchain looks like this:

set(CMAKE_SYSTEM_NAME QNX)

set(arch gcc_ntox86_64)
set(ntoarch x86_64)
set(QNX_PROCESSOR x86_64)

set(CMAKE_C_COMPILER qcc)
set(CMAKE_C_COMPILER_TARGET ${arch})

set(CMAKE_CXX_COMPILER qcc -lang-c++)
set(CMAKE_CXX_COMPILER_TARGET ${arch})

set(CMAKE_ASM_COMPILER qcc -V${arch})
set(CMAKE_ASM_DEFINE_FLAG "-Wa,--defsym,")

set(CMAKE_RANLIB $ENV{QNX_HOST}/usr/bin/nto${ntoarch}-ranlib
    CACHE PATH "QNX ranlib Program" FORCE)
set(CMAKE_AR $ENV{QNX_HOST}/usr/bin/nto${ntoarch}-ar
    CACHE PATH "QNX qr Program" FORCE)

The build script looks like this:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir

cmake --build builddir --target install

Notice how I am using CMake 3.13.x’s -S and -B parameters! No more mkdir builddir && cd builddir commands anymore! Yeah! metal

The configuration step had some problems because it uses try_run and I was cross-compiling. Running the script a second time worked out fine.

The failed CMake configuration was due to:

CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
   KWSYS_LFS_WORKS (advanced)
   KWSYS_LFS_WORKS__TRYRUN_OUTPUT (advanced)

CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
   HAVE_POLL_FINE_EXITCODE (advanced)
   HAVE_POLL_FINE_EXITCODE__TRYRUN_OUTPUT (advanced)

 CMake Error at CMakeLists.txt:2 (project):
  The Ninja generator does not support Fortran using Ninja version

    1.8.2

  due to lack of required features.  Kitware has implemented the required
  features but as of this version of CMake they have not been integrated to
  upstream ninja.  Pending integration, Kitware maintains a branch at:

    https://github.com/Kitware/ninja/tree/features-for-fortran#readme

  with the required features.  One may build ninja from that branch to get
  support for Fortran.

CMake Error: CMAKE_Fortran_COMPILER not set, after EnableLanguage

The compilation fails at some point because libuv doesn’t have QNX support. The following patch gets things working!

diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt
index a503041be..364a1e75e 100644
--- a/Utilities/cmlibuv/CMakeLists.txt
+++ b/Utilities/cmlibuv/CMakeLists.txt
@@ -135,6 +135,23 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
     )
 endif()
 
+if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
+  list(APPEND uv_libraries
+    )
+  list(APPEND uv_headers
+    include/uv-posix.h
+    )
+  list(APPEND uv_defines
+    )
+  list(APPEND uv_sources
+    src/unix/bsd-ifaddrs.c
+    src/unix/no-fsevents.c
+    src/unix/no-proctitle.c
+    src/unix/posix-hrtime.c
+    src/unix/posix-poll.c
+    )
+endif()
+
 if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
   list(APPEND uv_libraries
     )
diff --git a/Utilities/cmlibuv/include/uv-unix.h b/Utilities/cmlibuv/include/uv-unix.h
index 455674d1a..10389f474 100644
--- a/Utilities/cmlibuv/include/uv-unix.h
+++ b/Utilities/cmlibuv/include/uv-unix.h
@@ -66,6 +66,8 @@
 # include "uv-bsd.h"
 #elif defined(__CYGWIN__) || defined(__MSYS__)
 # include "uv-posix.h"
+#elif defined(__QNXNTO__)
+# include "uv-posix.h"
 #endif
 
 #ifndef PTHREAD_BARRIER_SERIAL_THREAD

cmake, cpack, and ctest compiled and installed just fine! So… we’re done, right? smile

CMake-GUI Building

What happens if we want to build cmake-gui? CMake is shipping cmake-gui for Windows/Mac/Linux as a GUI application statically linked to Qt.

So I went and compiled Qt 5.11.2 statically for QNX x86_64 with this script:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh
../qt-everywhere-src-5.11.2/configure \
  -xplatform qnx-x86-64-qcc \
  -release \
  -static \
  -no-fontconfig \
  -no-icu \
  --freetype=qt \
  -ccache \
  -nomake examples \
  -nomake tests \
  -skip qtwebengine \
  -prefix /opt/qt5 \
  -opensource

The magic part above is the -xplatform qnx-x86-64-qcc. I don’t build icu, fontconfig, because the QNX 7.0 VMware image doesn’t provide them, and I felt that it defeated my goal to deploy *.so files, hack LD_LIBRARY_PATH, and so on. I just wanted to run ./cmake-gui.

The toolchain does have libicu, which is quite a monster (31.4M!):

$ du -h /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicu*
0       /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicudata.so
0       /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicudata.so.58
26M     /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicudata.so.58.1
0       /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicui18n.so
0       /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicui18n.so.58
3.3M    /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicui18n.so.58.1
0       /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicuuc.so
0       /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicuuc.so.58
2.1M    /home/cadam/qnx700/target/qnx7/x86_64/usr/lib/libicuuc.so.58.1

My CMake build script would change to:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake

cmake --build builddir --target install

Unfortunately CMake configure step stops with the following error:


CMake Error in Source/QtDialog/CMakeLists.txt:
  No known features for CXX compiler

  "QCC"

  version 5.4.0.

As it turns out I hit QTBUG-54666: CMake fails to configure Android build!

The CMake package files that Qt provides require some C++ compiler features to be present.

So what did CMake detect using our QNX toolchain? Let’s just take a peak!

$ cat ./builddir/CMakeFiles/3.13.0/CMakeCXXCompiler.cmake | grep -i compile_features
set(CMAKE_CXX_COMPILE_FEATURES "")
set(CMAKE_CXX98_COMPILE_FEATURES "")
set(CMAKE_CXX11_COMPILE_FEATURES "")
set(CMAKE_CXX14_COMPILE_FEATURES "")
set(CMAKE_CXX17_COMPILE_FEATURES "")
set(CMAKE_CXX20_COMPILE_FEATURES "")

The above bug report has some workarounds for this problem, but what if we fixed this? QNX has QCC as a compiler wrapper around GCC, so what if I used GCC directly?

I came up with this small QNX toolchain:

set(CMAKE_SYSTEM_NAME QNX)

set(arch ntox86_64)
set(QNX_PROCESSOR x86_64)

set(CMAKE_C_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-gcc)
set(CMAKE_C_COMPILER_TARGET ${arch})

set(CMAKE_CXX_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-g++)
set(CMAKE_CXX_COMPILER_TARGET ${arch})

Now I was able to compile, but not to link. Oh no! scream

CMake-GUI Linking and Deployment

I had a look at what CMake was doing for Windows and came up with similar approach for QNX.

I needed to apply the following patch:

diff -Naur cmake-3.13.0-vanilla/Source/QtDialog/CMakeLists.txt cmake-3.13.0/Source/QtDialog/CMakeLists.txt
--- cmake-3.13.0-vanilla/Source/QtDialog/CMakeLists.txt    2018-11-20 15:49:09.000000000 +0100
+++ cmake-3.13.0/Source/QtDialog/CMakeLists.txt    2018-12-01 01:27:01.203767827 +0100
@@ -39,6 +39,12 @@
       PROPERTY COMPILE_DEFINITIONS USE_QWindowsIntegrationPlugin)
   endif()
 
+  if(CMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES)
+    list(APPEND CMake_QT_LIBRARIES ${CMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES})
+    set_property(SOURCE CMakeSetup.cxx
+      PROPERTY COMPILE_DEFINITIONS USE_QQnxIntegrationPlugin)
+  endif()
+
   # We need to install platform plugin and add qt.conf for Qt5 on Mac and Windows.
   # FIXME: This should be part of Qt5 CMake scripts, but unfortunately
   # Qt5 support is missing there.
diff -Naur cmake-3.13.0-vanilla/Source/QtDialog/CMakeSetup.cxx cmake-3.13.0/Source/QtDialog/CMakeSetup.cxx
--- cmake-3.13.0-vanilla/Source/QtDialog/CMakeSetup.cxx    2018-11-20 15:49:09.000000000 +0100
+++ cmake-3.13.0/Source/QtDialog/CMakeSetup.cxx    2018-12-01 14:28:09.902659579 +0100
@@ -49,6 +49,11 @@
 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
 #endif
 
+#if defined(USE_QQnxIntegrationPlugin)
+Q_IMPORT_PLUGIN(QQnxIntegrationPlugin);
+#endif
+
+
 int main(int argc, char** argv)
 {
   cmsys::Encoding::CommandLineArguments encoding_args =

Now the build script looks like this:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

# CMake ; separated list
LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a;"
LIBS+="`pwd`/qt5/lib/libQt5EglSupport.a;"
LIBS+="`pwd`/qt5/lib/libQt5EventDispatcherSupport.a;"
LIBS+="`pwd`/qt5/lib/libQt5FontDatabaseSupport.a;"
LIBS+="`pwd`/qt5/lib/libQt5Core.a;"
LIBS+="`pwd`/qt5/lib/libQt5Gui.a;"
LIBS+="`pwd`/qt5/lib/libqtharfbuzz.a;"
LIBS+="`pwd`/qt5/lib/libqtpcre2.a;"
LIBS+="`pwd`/qt5/lib/libqtfreetype.a;"
LIBS+="-lpng16;-lz;-lslog2;"
LIBS+="-lscreen;-lpps;-lEGL;-lGLESv2"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake
    
cmake --build builddir --target install

That looks pretty scary! That’s because Qt’s CMake files do not track dependencies when built in static mode.

This is being tracked and hopefully soon fixed, as seen here: QTBUG-38913: Can’t link against static Qt5 (missing usage requirements for static libs wrt harfbuzz/glib/others).

Until Qt fixes their CMake files, we could just do the following:

diff -Naur qt5-vanilla/lib/cmake/Qt5Core/Qt5CoreConfig.cmake qt5/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
--- qt5-vanilla/lib/cmake/Qt5Core/Qt5CoreConfig.cmake   2018-12-01 12:37:49.000000000 +0100
+++ qt5/lib/cmake/Qt5Core/Qt5CoreConfig.cmake   2018-12-01 22:11:51.552732104 +0100
@@ -111,8 +111,16 @@
     list(REMOVE_DUPLICATES Qt5Core_COMPILE_DEFINITIONS)
     list(REMOVE_DUPLICATES Qt5Core_EXECUTABLE_COMPILE_FLAGS)
 
-    set(_Qt5Core_LIB_DEPENDENCIES "")
-
+    set(_Qt5Core_LIB_DEPENDENCIES
+        ${_qt5Core_install_prefix}/lib/libQt5EglSupport.a
+        ${_qt5Core_install_prefix}/lib/libQt5EventDispatcherSupport.a
+        ${_qt5Core_install_prefix}/lib/libQt5FontDatabaseSupport.a
+        ${_qt5Core_install_prefix}/lib/libqtharfbuzz.a
+        ${_qt5Core_install_prefix}/lib/libqtpcre2.a
+        ${_qt5Core_install_prefix}/lib/libqtfreetype.a
+        -lpng16 -lz -lslog2
+        -lscreen -lpps -lEGL -lGLESv2
+    )
 
     add_library(Qt5::Core STATIC IMPORTED)
     set_property(TARGET Qt5::Core PROPERTY IMPORTED_LINK_INTERFACE_LANGUAGES CXX)

Now the build script looks like this:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake
    
cmake --build builddir --target install

That’s more like it!

cmake-gui builds and links fine now. In order to run it on the VM, I need to have sftp / ssh access. This is done by running vi /etc/ssh/sshd_config and change # PermitRootLogin no to PermitRootLogin yes.

After deployment and running /etc/graphics-startup.sh I was able to run /root/installdir/bin/cmake-gui, but then got these nice warnings:

QFontDatabase: Cannot find font directory /opt/qt5/lib/fonts.
Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.

This can be fixed in two ways, either set QT_QPA_FONTDIR environment variable to /usr/share/fonts, or create a symlink like:

# mkdir -p /opt/qt5/lib
# ln -s /usr/share/fonts /opt/qt5/lib/fonts

And now I can present this beautiful screenshot:

Let me see you stripped!

CMake when it uses GCC/Clang it builds binaries unstripped, with debug information. Let’s see how big the above resulted binaries are:

$ du -ah installdir/bin/
8.0M    installdir/bin/cmake
26M     installdir/bin/cmake-gui
8.2M    installdir/bin/cpack
9.2M    installdir/bin/ctest
52M     installdir/bin/

CMake does have in the CMakeCache.txt an entry called CMAKE_STRIP, which is the case of the original QNX toolchain is set to /usr/bin/strip, because CMake’s share/cmake-3.13/Modules/CMakeFindBinUtils.cmake has a bug for QNX, it can’t determine the ${_CMAKE_TOOLCHAIN_PREFIX} variable!

This is the reason why the original QNX toolchain had entries to ar and ranlib utilities.

My toolchain simply works, because the GNU GCC detection mechanism of ${_CMAKE_TOOLCHAIN_PREFIX} still applies!

But how can we use CMAKE_STRIP? Well, CMake has an undocumented target named install/strip!

The build script looks like this now:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake
    
cmake --build builddir --target install/strip

How big are the binaries now?

$ du -ah installdir/bin
6.7M    installdir/bin/cmake
23M     installdir/bin/cmake-gui
7.0M    installdir/bin/cpack
7.9M    installdir/bin/ctest
44M     installdir/bin

That’s like ~15% binary size decrease!

Interprocedural optimization (IPO)

CMake starting with version 3.9 has support for Interprocedural optimization (IPO) for GCC and Clang compilers.

If we have a look at share/cmake-3.13/Modules/Compiler/QCC.cmake we could find:

  set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE NO)
  set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER NO)

The original QNX toolchain is not use for us. But my toolchain is GCC based, which should just work.

CMake 3.13.0 source code doesn’t have support for building with IPO, but applying the following patch enables it:

diff -Naur cmake-3.13.0-vanilla/CMakeLists.txt cmake-3.13.0/CMakeLists.txt
--- cmake-3.13.0-vanilla/CMakeLists.txt    2018-11-20 15:49:09.000000000 +0100
+++ cmake-3.13.0/CMakeLists.txt    2018-11-24 22:53:52.323711946 +0100
@@ -117,6 +117,19 @@
   "Build CMake Developer Reference" OFF)
 mark_as_advanced(CMake_BUILD_DEVELOPER_REFERENCE)
 
+# option to build using interprocedural optimizations (IPO/LTO)
+if (NOT CMAKE_VERSION VERSION_LESS 3.12.2)
+  option(CMake_BUILD_LTO "Compile CMake with link-time optimization if supported" OFF)
+  if(CMake_BUILD_LTO)
+    cmake_policy(SET CMP0069 NEW)
+    include(CheckIPOSupported)
+    check_ipo_supported(RESULT HAVE_IPO)
+    if(HAVE_IPO)
+      set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+    endif()
+  endif()
+endif()
+
 #-----------------------------------------------------------------------
 # a macro to deal with system libraries, implemented as a macro
 # simply to improve readability of the main script
diff -Naur cmake-3.13.0-vanilla/Source/kwsys/CMakeLists.txt cmake-3.13.0/Source/kwsys/CMakeLists.txt
--- cmake-3.13.0-vanilla/Source/kwsys/CMakeLists.txt    2018-11-20 15:49:09.000000000 +0100
+++ cmake-3.13.0/Source/kwsys/CMakeLists.txt    2018-11-24 22:53:26.555712768 +0100
@@ -90,6 +90,7 @@
     CMP0048 # CMake 3.0, Let the project command manage version variables.
     CMP0056 # CMake 3.2, Honor link flags in try_compile() source-file signature.
     CMP0063 # CMake 3.3, Honor visibility properties for all target types.
+    CMP0069 # CMake 3.9, INTERPROCEDURAL_OPTIMIZATION is enforced when enabled.
     )
   IF(POLICY ${p})
     CMAKE_POLICY(SET ${p} NEW)
diff -Naur cmake-3.13.0-vanilla/Utilities/cmcurl/CMakeLists.txt cmake-3.13.0/Utilities/cmcurl/CMakeLists.txt
--- cmake-3.13.0-vanilla/Utilities/cmcurl/CMakeLists.txt    2018-11-20 15:49:11.000000000 +0100
+++ cmake-3.13.0/Utilities/cmcurl/CMakeLists.txt    2018-11-24 22:57:54.247704229 +0100
@@ -132,6 +132,8 @@
 
 project(CURL C)
 
+cmake_policy(SET CMP0069 NEW)
+
 if(0) # This code not needed for building within CMake.
 message(WARNING "the curl cmake build system is poorly maintained. Be aware")
 endif()

Now the build script looks like this:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake \
    -DCMake_BUILD_LTO=ON
    
cmake --build builddir --target install/strip

The binary sizes are:

$ du -ah installdir/bin/
5.5M    installdir/bin/cmake
21M     installdir/bin/cmake-gui
5.3M    installdir/bin/cpack
6.2M    installdir/bin/ctest
37M     installdir/bin/

That’s like ~16% binary size decrease!

Fancy Debug Build Flags

In the article Improving C++ Builds with Split DWARF we learn about -gsplit-dwarf compilation flag which speeds up compilation times in Debug mode.

Unfortunately QCC compiler wrapper doesn’t forward this flag to GCC disappointed, fortunately my toolchain file makes this possible, since we’re using directly the GCC compiler!

Let’s build a normal Debug build with this script:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake
    
cmake --build builddir --target install

The builddir and installdir sizes were:

$ du -sh builddir
2.6G    builddir
$ du -sh installdir
686M    installdir

Now let’s enable all the fancy debug build flags:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

export CFLAGS="-gsplit-dwarf -fuse-ld=gold"
export CXXFLAGS=$CFLAGS
export LDFLAGS=-Wl,--gdb-index

LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake
    
cmake --build builddir --target install

I searched after the presence of dwo files. This works starting with QNX 7.0!

The builddir and installdir sizes have become:

$ find builddir -type f -name "*.dwo" | wc
    826     826   62932
$ du -sh builddir
1.3G    builddir
$ du -sh installdir
187M    installdir

That’s like 50% size reduction for builddir, and 72% size reduction for installdir!

Need for speed

Remember how QCC is a wrapper for GCC, when coupled with ccache you should make sure that you have direct hits and not preprocessed ones!

This can lead to 30% speed degradation, depending on your QNX toolchain and ccache usage.

This version of the build script enables ccache, and since we use GCC directly we should mostly get direct hits:

#!/bin/bash
source $HOME/qnx700/qnxsdp-env.sh

LIBS="`pwd`/qt5/plugins/platforms/libqqnx.a"

cmake -B builddir -S cmake-3.13.0 -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=`pwd`/qnx7_gcc.cmake \
    -DCMAKE_INSTALL_PREFIX=`pwd`/installdir \
    -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
    -DCMAKE_C_COMPILER_LAUNCHER=ccache \
    -DBUILD_QtDialog=ON \
    -DCMake_QT_STATIC_QQnxIntegrationPlugin_LIBRARIES="$LIBS" \
    -DCMAKE_PREFIX_PATH=`pwd`/qt5/lib/cmake
    
cmake --build builddir --target install/strip

The statistics were:

$ ccache -s
cache directory                     /home/cadam/.ccache
primary config                      /home/cadam/.ccache/ccache.conf
secondary config      (readonly)    /etc/ccache.conf
stats zero time                     Sun Dec  2 00:05:30 2018
cache hit (direct)                   820
cache hit (preprocessed)               4
cache miss                             0
cache hit rate                    100.00 %
cleanups performed                     0
files in cache                     21883
cache size                         671.5 MB
max cache size                      50.0 GB

I don’t know why I had 4 preprocessed cache hits smile

Qt Creator Integration

Qt Creator has had some problems with the QNX / CMake integration. For example these bugs:

The good news is that with the GCC toolchain file these bugs are no longer reproduceable! metal

Devil is in the details

If we have a closer look at what CMake compiler detection cmake file (builddir/CMakeFiles/3.13.0/CMakeCXXCompiler.cmake) contains for the CMAKE_CXX_IMPLICIT_LINK_LIBRARIES, we can see that there is a difference between the original QNX toolchain file and my own. It’s mainly about libgcc.a.

Luckily CMake can be configured to adjust to this, and my toolchain file is a bit more complicated smile

set(CMAKE_SYSTEM_NAME QNX)

set(arch ntox86_64)
set(QNX_PROCESSOR x86_64)

set(CMAKE_C_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-gcc)
set(CMAKE_C_COMPILER_TARGET ${arch})

set(CMAKE_CXX_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-g++)
set(CMAKE_CXX_COMPILER_TARGET ${arch})

file(GLOB_RECURSE libgcc_a 
  "$ENV{QNX_HOST}/usr/lib/gcc/${QNX_PROCESSOR}*/*/pic/libgcc.a")

set(CMAKE_C_STANDARD_LIBRARIES_INIT
  "${libgcc_a} -lc -Bstatic -lcS ${libgcc_a}")
set(CMAKE_CXX_STANDARD_LIBRARIES_INIT
  "-lc++ -lm ${CMAKE_C_STANDARD_LIBRARIES_INIT}")

set(CMAKE_EXE_LINKER_FLAGS_INIT "-nodefaultlibs")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-nodefaultlibs")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-nodefaultlibs")

If you want an ARM 64 version, just change these two lines:

set(arch ntoaarch64)
set(QNX_PROCESSOR aarch64)

I hope you have enjoyed this C++ compilation ride in the world of the exotic operating system that is QNX!

Comments