Write a makefile for using qt(4) on the raspberry pi

0
24

Introduction

I will describe how to write a makefile, that can compile a qt4 program on the raspberry pi (2).
I will not describe, how intall all needed components (simply because I don’t have records about that and explanations on the internet were applicable. Feel free to provide that process and I will update the article 🙂 )

The files I use for my sample are as follows:
main.cpp   // contains qt calls, but does not define a qt class
numberplacedoc.cpp // pure c++ file containing program logic
numberplacedoc.h
numberplaceview.cpp // implementation of new qt class (QtMainWindow inherit)
numberplaceview.h // definition of new qt class (QtMainWindow inherit)
ui_numberplaceview.h  // specifies the  layout of our window

Background

I need to show my programmed application to my professor. But I didn’t want to carry my laptop to the university all the time (it’s heavy and big). Instead I decided to bring my rapi(2) which is very small and light. My last work was this qt application described here (Sudoku game). Programming it was no problem but making it compile on the raspberry pi took a lot of time. A lot of tutorials out there seem to only be half-baked or/and my knowledge about makefiles was close to 0. So I decided to provide a fully functional makefile to work with.

Using the code

I created the sourcefiles with qt5, which makes it necessary to create a link inside the include/qt4 directory on the pi:

Sudo ln -s /usr/include/qt4/QtGui /usr/include/qt4/QtWidgets

Creating the makefile

Create a new file called „makefile“ inside the directory where all the sources are.

Setting up the basic variables of the makefile

 COMPILER=arm-linux-gnueabihf-g++ INCLUDEDIR = ./ INCLUDEDIR += /usr/include/qt4/ INCLUDEDIR += /usr/include/qt4/Qt INCLUDEDIR += /usr/include/qt4/QtGui INCLUDEDIR += /usr/include/qt4/QtCore LIBRARY += QtCore QtGui LIBRARYDIR = /usr/lib/arm-linux-gnueabihf LIBRARYDIR +=/usr/local/lib XLINK_LIBDIR = /lib/arm-linux-gnueabihf XLINK_LIBDIR += /usr/lib/arm-linux-gnueabihf INCDIR = $(patsubst %,-I%,$(INCLUDEDIR)) LIBDIR = $(patsubst %,-L%,$(LIBRARYDIR)) LIB = $(patsubst %,-l%,$(LIBRARY)) XLINKDIR = $(patsubst %,-Xlinker -rpath-link=%,$(XLINK_LIBDIR)) CPPSTD = -std=c++11 OPT = -O0 DEBUG = -g WARN= -Wall PTHREAD= -lpthread SPECIAL_LINK_OPTIONS = COMPILE_FLAGS = $(OPT) $(DEBUG) $(WARN) $(INCDIR) $(CPPSTD) COMPILE = $(COMPILER) $(COMPILE_FLAGS) -c LINK_FLAGS= $(LIBDIR) $(LIB) $(XLINKDIR) $(PTHREAD) $(SPECIAL_LINK_OPTIONS) LINK = $(COMPILER) $(LINK_FLAGS) EXECUTABLE = executable

Building everything by hand

First we will compile everything by hand to get a better feeling of what is happening. If you copy-paste this code, then make sure, to have TABS before the $(LINK) and $(COMPILE) statements. Spaces do not count.

Note for beginners: makefile targets

Think of a target like a „case“ inside a switch-case statement. The syntax is as followes:

 NPD=numberplacedoc NPV=numberplaceview OBJ_FILES = $(NPD).o $(NPV).o main.o $(NPV).moc.o all: $(OBJ_FILES) $(LINK) $(OBJ_FILES) -o $(EXECUTABLE) $(NPD).o: $(NPD).cpp $(NPD).h $(COMPILE) $(NPD).cpp main.o: main.cpp $(COMPILE) main.cpp $(NPV).o: $(NPV).cpp $(NPV).h $(COMPILE) $(NPV).cpp $(NPV).moc.o: $(NPV).moc.cpp $(COMPILE) $(NPV).moc.cpp $(NPV).moc.cpp: $(NPV).h moc-qt4 $(NPV).h -o $(NPV).moc.cpp 

To get a better view of the hierarchy, lets look at this picture:

Building

Now typing in

make

should build your executable ready to execute.

Errors

If you get „strange“ errors like

  • undefined reference to QApplication
  • virtual call inside the ctor and dtor

Or other strange c++ errors that somehow refer to qt, make sure

  • numberplaceview.moc.cpp is correctly compiled
  • numberplaceview.moc.o does exist
  • numberplaceview.moc.o and is inside the linking list

Please provide feedback here for ANY error you encounter, so we can list and solve them here.

Explanation

Basically everything is as if you have a pure c++ program. Only, we create one spezial new cpp file, that is created by the qt4 compiler, that needs to go into the linklist. This is the whole magic of the building process.

Important note: if this file is not in the list, we will get some strange Errors.

Also note, that we didn’t care about ui_numberplaceview.h in our building process

Generalize (practicalize) the building command

I hope you now have an idea of what is going on throughout the building process.

Now, nobody wants to list their files like this all the time they start a new project. For that purpose, we’ll generalize the building command so the makefile grabs all necessary files by itself (through patterns inside the filenames).

Note: The provided makefile are searching for sources only in the current directory.

First, we change the path of our result, to make it better customizable:

OUTPUT_PATH = ./ EXECUTABLE := $(OUTPUT_PATH)executable

We want an intermediate directory to not pollute our folder with object files:

TMP_DIR=tmp/

We need to define files, that qt needs to generate. For better understanding purposes, I call only one command per line.

I use the pattern *view.h or qt*.h to determine files, that need to be generated. That mean, if you want to use this automation, your files, that need to be qt-compiled should have this pre- or suffix. Feel free to change the pattern or add files manually.

 H_FILES_FOR_QT := $(wildcard *view.h) H_FILES_FOR_QT += $(wildcard qt*.h) H_FILES_FOR_QT := $(filter-out $(wildcard ui_*), $(H_FILES_FOR_QT))

Now we’re ready to tell, which files we actually need:

 CPP_FILES_TO_GEN := $(H_FILES_FOR_QT:.h=.moc.cpp)

Make removes „intermediate“ files automatically. But I want to keep them, so we can partial rebuild better, so:

 .SECONDARY: $(CPP_FILES_TO_GEN)

Put all lists together and create a big list of all cpp-files, that want to be compiled:

 CPP_FILES := $(wildcard *.cpp) $(CPP_FILES_TO_GEN)

Same goes for object files. But we want them inside our tmp-dir. So add that to their path:

 OBJECT_FILES := $(addprefix $(TMP_DIR),$(CPP_FILES:.cpp=.o)) 

Note for beginners: @-sign

I use the „@“-sign to suppress the command itself, so we get a much more pretty output.

We want to make sure our TMP_DIR does exists before object files get created there.

 all: $(TMP_DIR) linkAll @echo =============================== @echo ==== build successfull ======== @echo =============================== $(TMP_DIR): @echo create $(TMP_DIR) for intermediate files @mkdir $(TMP_DIR)

For linking actually we need the TMP_DIR, but it is not a prerequisite here. The reason fort hat is, that

linkAll: $(OBJECT_FILES) @echo @echo =============================== @echo === compiling finished ======== @echo =============================== @echo @echo Linking... @$(LINK) $? -o $(EXECUTABLE)

This is the heart of the compiling process. If a prerequesite „orders“ an .o-file or moc.cpp, it will get cooked here. .o-files can only be ordered within the TMP_DIR. moc.cpp-files are ordered where all sources are.

 $(TMP_DIR)%.o: %.cpp @echo compiling: $< to $@ @$(COMPILE) $< -o $@ %.moc.cpp: %.h @echo creating moc: $@ @moc-qt4 $< -o $@

Defining which files should be cleaned up in case of a new build

 clean: @echo remove intermediate files in: $(TMP_DIR) @rm -f $(TMP_DIR)* @echo remove midl-compiler files from qt @rm -f $(CPP_FILES_TO_GEN) @echo remove $(EXECUTABLE) @rm -f $(EXECUTABLE)

Mentionable sources

  • The tutorial I was using:
    • http://hertaville.com/cross-compiling-qt4-app.html
  • Explanations about “crazy” commands withing a makefile (like $<)
    • http://stackoverflow.com/questions/3220277/what-do-the-makefile-symbols-and-mean
  • A video of my raspi case (whoever is interested, how “small” and “light” it is)
    • https://www.youtube.com/watch?v=FptZs2IyNOo

LEAVE A REPLY