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
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
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
$(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:
Now typing in
should build your executable ready to execute.
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.
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:
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
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:
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
moc.cpp, it will get cooked here.
.o-files can only be ordered within the
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)
- The tutorial I was using:
- Explanations about “crazy” commands withing a makefile (like $<)
- A video of my raspi case (whoever is interested, how “small” and “light” it is)