sábado, 17 de noviembre de 2018

Using libgps instead of libQgpsmm within a Qt application

I was in need of creating a Qt application using current Debian stable (Stretch) and gpsd. I could have used libQgpsmm which creates a QTcpSocket for stablishing the connection to the gpsd daemon. But then I hit an issue: libQgpsmm was switched to Qt 5 after the Strech release, namely in gpsd 3.17-4. And I'm using Qt 5.

So the next thing to do is to use libgps itself, which is written in C. In this case one needs to call gps_open() to open a connection, gps_stream() to ask for the needed stream... and use gps_waiting() to poll the socket for data.

gps_waiting() checks for data for a maximum of time specified in it's parameters. That means I would need to create a QTimer and poll it to get the data. Poll it fast enough for the application to be responsive, but not too excessively to avoid useless CPU cycles.

I did not like this idea, so I started digging gpsd's code until I found that it exposes the socket it uses in it's base struct, struct gps_data_t's gps_fd. So the next step was to set up a QSocketNotifier around it, and use it's activated() signal.

So (very) basically:

// Class private:
struct gps_data_t mGpsData;
QSocketNotifier * mNotifier;

// In the implementation:
result = gps_open("localhost", DEFAULT_GPSD_PORT, &mGpsData);
// [...check result status...]

result = gps_stream(&mGPSData,WATCH_ENABLE|WATCH_JSON, NULL);
// [...check result status...]

//  Set up the QSocketNotifier instance.
mNotifier = new QSocketNotifier(mGpsData.gps_fd, QSocketNotifier::Read, this); 

connect(mNotifier, &QSocketNotifier::activated, this, &MyGps::readData);

And of course, calling gps_read(&mGpsData) in MyGps::readData(). With this every time there is activity on the socket readData() will be called, an no need to set up a timer anymore.

viernes, 2 de noviembre de 2018

Cross compiling CMake-based projects using Ubuntu/Debian's multi arch

As you probably already know Ubuntu (and then Debian) added Multi-Arch support quite some time ago. This means that you can install library packages from multiple architectures on the same machine.

Thanks to the work of many people, in which I would like to specially mention Helmut Grohne, we are now able to cross compile Debian packages using standard sbuild chroots. He even was kind enough to provide me with some numbers:

We have 28790 source packages in Debian unstable.
Of those, 13358 (46.3%) build architecture-dependent binary packages.
Of those, 7301 (54.6%) have satisfiable cross Build-Depends.
Of those, 3696 (50.6% of buildable, 27.6% of sources) were attempted.
Of those, 2695 (72.9% of built, 36.9% of buildable, 20.1% of sources) were successful.
633 bugs affecting 772 packages (7.23% of 10663 unsuccessful) are reported.

Now I asked myself if I could use this to cross compile the code I'm working on without the need of doing a full Debian package build.

My projects uses CMake, so we can cross compile by providing a suitable CMAKE_TOOLCHAIN_FILE.

And so the first question is:

How do we create the necessary file using what Multi-Arch brings to our table?


I asked Helmut and he did not only provide me with lots of tips, he also provided me with the following script, which I modified a little:

Now we can run the script providing it with the desired host arch and voilá, we have our toolchain file.

#!/bin/sh

#set -x

ARCH=$1

DEB_HOST_GNU_TYPE=$(dpkg-architecture -f "-a$1" -qDEB_HOST_GNU_TYPE)
DEB_HOST_GNU_CPU=$(dpkg-architecture -f "-a$1" -qDEB_HOST_GNU_CPU)
case "$(dpkg-architecture -f "-a$1" -qDEB_HOST_ARCH_OS)" in
        linux) system_name=Linux; ;;
        kfreebsd) system_name=kFreeBSD; ;;
        hurd) system_name=GNU; ;;
        *) exit 1; ;;
esac

cat <> cmake_toolchain_$1.cmake
# Use it while calling CMake:
#   mkdir build; cd build
#   cmake -DCMAKE_TOOLCHAIN_FILE="../cmake_toolchain_.cmake" ../
set(CMAKE_SYSTEM_NAME "$system_name")
set(CMAKE_SYSTEM_PROCESSOR "$DEB_HOST_GNU_CPU")
set(CMAKE_C_COMPILER "$DEB_HOST_GNU_TYPE-gcc")
set(CMAKE_CXX_COMPILER "$DEB_HOST_GNU_TYPE-g++")
set(PKG_CONFIG_EXECUTABLE "$DEB_HOST_GNU_TYPE-pkg-config")
set(PKGCONFIG_EXECUTABLE "$DEB_HOST_GNU_TYPE-pkg-config")
EOF


Can we improve this?

Helmut mentioned that meson provides debcrossgen, a script that automates this step. Meson is written in python, so it only needs to know the host architecture to create the necessary definitions.

CMake is not interpreted, but maybe it has a way to know the host arch in advance. If this is true maybe a helper could be added to help in the process. Ideas (or even better, patches/code!) welcomed.