Building Web apps with Wt for Raspberry Pi

Introduction

What is Wt ?

In their introduction on the Wt main page they write:

“…Wt (pronounced as witty) is a C++ library for developing web applications…”

 

The idea of writing web apps in C++ sounds a bit weird at first, but I found Wt the ideal solution for me and the following project:

I wanted to use a Raspberry Pi as a mini server which periodically logs into a Fritzbox, reads out the phone caller list and displays this on a small LCD

Wt being the main service behind and an easy way to configure this and be able to query more detailed information.

Paths

Of course you can use any path for the work directory. And I could have used a $VAR_XX to specify the path. But – I used plain absolute paths in this documentation. I used /home/user as the work directory where I used subdirs like linaro, wt and so on.

So please replace this with your actual home or work path. I was very careful that the setup did not write outside this directory. You do not need to be root if you have enough rights inside this directory.

License

Wt is dual licensed, more information can be found on the download page

It can be either used under GPL or a commercial license (with fair pricing).

Build yourself, or ready-made ?

Sure, everything pre-built is nice. But see we-do-it-all-for-you scripts for potential problems.

In this post, I describe everything how I did it from scratch. If you are satiesfied with an “old-if-you-read-this version”,  you can skip some parts of the description.

I tell you which.

Install Qt creator and other tools

We assume Ubuntu 14.04 here as the host environment. There is no strict requirement for Qt creator, but see below.

sudo apt-get install g++

sudo apt-get install qtcreator

sudo apt-get install cmake

 

If you want to build everything youself, you can continue here. You can also download most of the needed stuff prebuilt from my site. In this case, go straight to “Build Template application (make)”

Downloading and building Wt

On the download page one can find different versions. I downloaded the tarball version 3.3.3

♦ Unpack:

tar xzf wt-3.3.3.tar.gz

The challenge for me was to cross compile this for the Raspberry Pi. On my post Cross compiling under Raspbian with Linaro toolchain I wrote about setting up a cross compile toolchain for the Raspberry Pi.

We assume here:

  • Target device is Raspberry Pi with Raspbian (2014-06-20-wheezy-raspbian)
  • Host environment is Ubuntu 14.04
  • We install everything under the home dir of user user (/home/user)
  • Cross compiler is installed and usable (/home/user/linaro/bin/arm-linux-gnueabihf-gcc)

 

Download boost

Boost is the universal C++ library and is used by Wt.

♦ Go to Sourecforge download page and download boost_1_55_0.tar.gz

Note: This is no longer the newest version. I assume newer versions will also do it, but I have not tested it.

♦ Unpack:

tar xzf boost_1_55_0.tar.gz

 

Download Qt

Qt should be well-known as the cross platform UI “and more” library.

Even if Wt is (from the programming model) a bit similar to Qt, it does not depend on it. One purpose of downloading and building Qt is to be able to use Qt creator as the IDE for the further development of Raspi apps.

Why build Qt from source, and not just use the Qt package from Ubuntu ?

Yes, this was my first idea. I thought I would be able to use Qt creator only as the IDE. But qmake needs a Qt installation built for the target platform to work properly. I searched the web a lot for this and even tried many suggestions. In the end I found out that the way described here will be the one to go. In addition, Qt can be used together with Qt, and I thought it would be of good value to be able to use some of the Qt non-GUI libs for my development.

♦ Go to Qt download page and download qt-everywhere-opensource-src-5.2.1.tar.gz

♦ Unpack:

tar xzf qt-everywhere-opensource-src-5.2.1.tar.gz

Download TinyMCE

♦ Go to TinyMCE download page and download TinyMCE 3.5.11

♦ Unpack:

unzip tinymce_3.5.11.zip

Download zlib

♦ Go to zlib page and download zlib source code, version 1.2.8, tar.gz format

♦ Unpack:

tar xzf zlib-1.2.8.tar.gz

Download bzip2

♦ Go to bzip download page and download 1.0.6 source tarball.

♦ Unpack:

tar xzf bzip2-1.0.6.tar.gz

Build zlib for ARM / Raspbian

cd zlib-1.2.8

mkdir build_rpi

cd build_rpi

cmake -D CMAKE_C_COMPILER=/home/user/linaro/bin/arm-linux-gnueabihf-gcc -D CMAKE_INSTALL_PREFIX=/home/user/zlib_rpi ..

make

make install

Build boost for ARM / Raspbian

cd boost_1_55_0

./bootstrap.sh --prefix=/home/user/boost_rpi --without-libraries=python

♦ Edit project-config.jam, add two lines at the end:

using gcc : arm : /home/user/linaro/bin/arm-linux-gnueabihf-g++ ;
using zlib : 1.2.8 : /home/user/zlib_rpi/include /home/user/zlib_rpi/lib ;
./b2 install toolset=gcc-arm -s BZIP2_SOURCE=/home/user/bzip2-1.0.6

Build Wt for ARM / Raspbian

cd wt-3.3.3

mkdir build_rpi
cd build_rpi


cmake -D CMAKE_C_COMPILER=/home/user/linaro/bin/arm-linux-gnueabihf-gcc -D CMAKE_CXX_COMPILER=/home/user/linaro/bin/arm-linux-gnueabihf-g++ -D BOOST_ROOT:PATHNAME=/home/user/boost_rpi -D ZLIB_PREFIX=/home/user/zlib_rpi -D ZLIB_INCLUDE=/home/user/zlib_rpi/include -D CMAKE_LIBRARY_PATH=/home/user/linaro/arm-linux-gnueabihf/libc/usr/lib/arm-linux-gnueabihf  -D WT_CPP_11_MODE=-std=c++0x -D CMAKE_INSTALL_PREFIX=/home/user/wt_rpi -D CONFIGDIR=/home/user/wt_rpi/etc -D RUNDIR=/home/user/wt_rpi/run -D WEBUSER=user -D WEBGROUP=user ..


make
 
make install

Build Qt for ARM / Raspbian

cd qt-everywhere-opensource-src-5.2.1

./configure -v -extprefix /home/user/qt_rpi -opensource -confirm-license  -no-pkg-config -qt-zlib -qt-libjpeg -qt-freetype -qt-pcre -no-cups -no-glib -no-pch -no-nis  -nomake examples -nomake tests -no-gui  -no-widgets  -device linux-rasp-pi-g++ -device-option CROSS_COMPILE=/home/user/linaro/bin/arm-linux-gnueabihf- -skip qtlocation


make

make install

Create runtime environment for Raspbery Pi

Here we create a runtime environment for the execution of wt applications
on the Raspberry Pi

mkdir /home/user/Template_run
mkdir /home/user/Template_run/lib

cd /home/user/Template_run/lib

cp /home/user/boost_rpi/lib/libboost_*.so.1.55.0 .
cp -P /home/user/boost_rpi/lib/libboost_*.so .

cp -P /home/user/wt_rpi/lib/libwtdbo*.so* .
cp -P /home/user/wt_rpi/lib/libwt.so* .
cp -P /home/user/wt_rpi/lib/libwthttp.so* .

cp /home/user/qt_rpi/lib/libQt5Concurrent.so.5 .
cp /home/user/qt_rpi/lib/libQt5Core.so.5 .
cp /home/user/qt_rpi/lib/libQt5Network.so.5 .
cp /home/user/qt_rpi/lib/libQt5Script.so.5 .
cp /home/user/qt_rpi/lib/libQt5Xml.so.5 .


/home/user/linaro/bin/arm-linux-gnueabihf-strip -s lib*.so.3.3.3
/home/user/linaro/bin/arm-linux-gnueabihf-strip -s lib*.so.1.55.0


cd /home/user/Template_run

cp -R /home/user/wt_rpi/share/Wt/resources .

cp -R /home/user/tinymce/jscripts/tiny_mce resources

We should have this directory structure now:

Template_run/
  lib/
    libboost_*
    libQt*
    libwt*
  resources/
    ajax-loading.gif
    ...
    font-awesome/
    jPlayer/
    themes/
    tiny_mce/
      tiny_mce.js
      ...
    ...

♦ Pack the runtime (in /home/user/Template_run):

tar czf ../runtime_rpi.tar.gz *

Pack all created packages

For archiving purposes, we pack the created files:

tar czf boost_rpi.tar.gz boost_rpi

tar czf wt_rpi.tar.gz wt_rpi

tar czf qt_rpi.tar.gz qt_rpi

tar czf zlib_rpi.tar.gz zlib_rpi

This can be helpful if we rebuild our whole system and don’t want to create
everything from scratch.

Build Template application (make)

Ok, you are here either from the step by step instructions and you built everything yourself, or you just want to dowload the pre-built libraries.

In the former case, go straight to “Download this sample:”

In the latter case, download these files:

boost_rpi.tar.gz

MD5: 1fb6bfb00b5e0c169302536e8f969917
SHA-1: 3ca61e13b7ae5ded8b649108a72f59bd93d84179

 

qt_rpi.tar.gz

MD5: 5160d40552fbf77b8fb2e20ea7281dd1
SHA-1: db5a26d5d24f1e494aac623d54b29082491f3674

 

zlib_rpi.tar.gz

MD5: be32fce064f6b5689e0fafccc26ceb5a
SHA-1: da9566db409c40330ecb0fc9fd1ba2b5f8b64a22

 

wt_rpi.tar.gz

MD5: b913c9efeeac4d85b6a3e22222f82003
SHA-1: 144d51ee122d03bddb3d5ebbdb15a2ccf79ef8f6

 

runtime_rpi.tar.gz

MD5: 1db8d928de8e99a4f4cbab82d8126466
SHA-1: 8ab7f17375f8478c28590d8ddbb5da36fb22b230

Unpack all files (in the work directory we assume /home/user here):

tar xzf xxxxxx.tar.gz

Build template app

Now,  everything is on the right place. Now we can build our first sample web application.

Download this sample:

Template.tar.gz

MD5: 016318e9823fb84b8838ac80ea48bb17
SHA-1: a687cbb9daecc2ccabeb856af57a28f33188b49c

Unpack:

tar xzf Template.tar.gz

♦ Edit Makefile to match your paths. Ok, I could have used some better way of writing this Makefile and didn’t use hardcoded absolute paths … but it is how it is.

make

Everything should build. You end with an ARM executable named “Template”

Install runtime on the Raspbery Pi

♦ Copy the previously built runtime to the raspi.

scp runtime_rpi.tar.gz root@name-or-ip-of-raspi:/tmp

♦ Login (as root in our example) into the raspi

cd /opt
mkdir wt
cd wt
mkdir Template
cd Template
tar xzf /tmp/runtime_rpi.tar.gz

(this tar extraction creates the two dirs (lib and resources) directly in the current directory !)

♦ Copy the previously built Template executable to the raspi.

scp Template root@name-or-ip-of-raspi:/opt/wt/Template

♦ Start the Template on the raspi (in /opt/wt/Template):

./Template --docroot . --http-address 0.0.0.0 

♦ Start a web browser and navigate to the name / ip of the raspi

Using Qt Creator

♦ Tools →  Options → Devices

♦ Add…

♦ Generic Linux Device → Start Wizard

The name to identify this configuration: Raspi

The device’s host name or IP address: name-or-ip-of-raspi

The user name to log into the device: root

The authentication type: Password

The user’password: root-password

♦ Next → Finish

♦ Apply

♦ Tools →  Options → Build & Run → Compilers

♦ Add → GCC

Name: GCC_RPI
Compiler path: /home/user/linaro/bin/arm-linux-gnueabihf-g++

♦ Apply

♦ Tools →  Options → Build & Run → Qt Versions

♦ Add…

open /home/user/qt_rpi/bin/qmake

♦ Tools →  Options → Build & Run → Kits

♦ Add

Name: RPI

Device type: Generic Linux Device

Device: Raspi

Compiler: GCC_RPI

Debugger: None

Qt version: Qt 5.21 (qt_rpi)

I have created a custom project template.

Download it from here:

custom_wizard.tar.gz

MD5: 39142ffaa733717f209f107cd664ca41
SHA-1: ad0c992e29c80257419b7c5e7d5b7e3238db8275

 

In home directory:

tar xzf custom_wizard.tar.gz

This will extract these files:

/home/user/.config/QtProject/qtcreator/templates/wizards/rpiwt/
  main.cpp
  main.h
  project.pro
  rpi.png
  wizard.xml

/home/user/.config/QtProject/qtcreator/templates/wizards/rpiwtqt/
  main.cpp
  main.h
  project.pro
  rpi.png
  wizard.xml
  QtObjectSend.cpp
  QtObjectSend.h
  QtObjectReceive.cpp
  QtObjectReceive.h
  DispatchThread.cpp
  DispatchThread.h
  WQApplication.cpp
  WQApplication.h

♦ Now, in Qt Creator:

♦ New Project → Custom Projects → RaspiWt → Choose…

Name: -enter name for sample project-

In the “Kit Selection”:

♦ uncheck Desktop
♦ check RPI

♦ Next

♦ The checkbox “Add optional boost libs” adds some additional boost libaries not needed for Wt itself, but which may be used by your program

♦ In “lib + include path for boost and Wt” enter the path under which boost_rpi und wt_rpi are located

♦ In “run path on Raspberry Pi” enter the path where the wt runtime project will be located

♦ Next → Finish

♦ Projects → RPI → Run

♦ Under Run → Arguments enter:

--docroot /opt/wt/ --http-address 0.0.0.0

♦ Replace the path for docroot with your path on the Raspi where the executable will be deployed.

♦ Make sure that “lib” and “resources” subdirectories are located under this directory

♦ Build

♦ Press the green right arrow.

The built executable will be copied (with scp) to the Raspberry and the web application will be started directly on the Raspi.

In the “Application Output” pane the output will be displayed.

This should look similar to:

stdin: is not a tty
[2014-Jun-01 13:08:28.588074] 2374 - [info] "WServer/wthttp: initializing built-in wthttpd"
[2014-Jun-01 13:08:28.598015] 2374 - [info] "wthttp: started server: http://0.0.0.0:80"

♦ To stop the running application, press the red square button under the “Application Output” pane

Further information

Cross compiler toolchain

See my post Cross compiling under Raspbian with Linaro toolchain.

boost

This was rather easy. We have to set the installation directory with
--prefix=/home/user/boost_rpi and name the cross compiler to use.

Wt

This needs some further explanations:

-D CMAKE_LIBRARY_PATH=/home/user/linaro/arm-linux-gnueabihf/libc/usr/lib/arm-linux-gnueabihf

If this is omitted, we get some linker errors complaining about missing references:

[ 94%] Built target wtdbosqlite3
Linking CXX executable test.sqlite3
../src/Wt/Dbo/backend/libwtdbosqlite3.so.3.3.2: undefined reference to `dlopen'
../src/Wt/Dbo/backend/libwtdbosqlite3.so.3.3.2: undefined reference to `dlclose'
../src/Wt/Dbo/backend/libwtdbosqlite3.so.3.3.2: undefined reference to `dlerror'
../src/Wt/Dbo/backend/libwtdbosqlite3.so.3.3.2: undefined reference to `dlsym'

The cause is in src/Wt/Dbo/backend/CMakeLists.txt:

  IF(NOT WIN32)
    FIND_LIBRARY(DL_LIB
      NAMES
      dl
      )
    IF(DL_LIB)
      TARGET_LINK_LIBRARIES(wtdbosqlite3 ${DL_LIB})
    ENDIF(DL_LIB)
  ENDIF(NOT WIN32)

The problem is here that FIND_LIBRARY does not find our ARM libaries. So we help with giving the complete path. Luckily, setting CMAKE_LIBRARY_PATH to this special subdirectory does not have any side effects.

-D CONFIGDIR=/home/user/wt_rpi/etc 

Path for configuration files (default is /etc/wt/)
During build, the wt directory will be created. To prevent this, we set the path relative to our home directory.

-D RUNDIR=/home/user/wt_rpi/run 

Default path for wt session management (only used by FCGI connector; not relative to CMAKE_INSTALL_PREFIX). Similar to CONFIGDIR

-D WEBUSER=user -D WEBGROUP=user ..

During build, the run dir will be chown-ed to apache:apache if not explicitly set. So we just set our own user / group here.

Qt

Qt Creator is very good in cross building. We could use cmake, but using QMake has some advantages. We need a qmake command which is a i386 executable, but especially prepared for the target architecture.

In my tests, Qt creator 2.5 (e.g. from Debian 7) did not work. I had to use 3.x