65.9K
CodeProject 正在变化。 阅读更多。
Home

为在 Raspberry Pi 上使用 qt(4) 编写 makefile

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (7投票s)

2017 年 5 月 30 日

CPOL

5分钟阅读

viewsIcon

15206

downloadIcon

132

你想编译使用 qt 的 C++ 代码在树莓派上吗?以下是如何做到。

引言

我将描述如何编写一个 Makefile,可以在树莓派 (2) 上编译 qt4 程序。
我**不会**描述如何安装所有必需的组件(仅仅是因为我没有这方面的记录,并且网上的解释已经足够了。请随意提供这个过程,我将更新这篇文章 :))。

我使用的示例文件如下

  • main.cpp                      // 包含 qt 调用,但未定义 qt
  • numberplacedoc.cpp    // 纯 C++ 文件,包含程序逻辑
  • numberplacedoc.h
  • numberplaceview.cpp  // 新 qt 类的实现(继承 QtMainWindow
  • numberplaceview.h     // 新 qt 类的定义(继承 QtMainWindow
  • ui_numberplaceview.h // 指定窗口的布局

背景

我需要向我的教授展示我编写的应用程序。但我不希望一直带着我的笔记本电脑去大学(它又重又大)。因此,我决定带上我的 rapi(2),它非常小巧轻便。我最后的工作是这里描述的 qt 应用程序(数独游戏)。编写它不成问题,但让它在树莓派上编译却花费了很长时间。网上很多教程似乎都只是半成品,并且/或者我对 makefile 的了解接近于 0。所以我决定提供一个功能齐全的 makefile 来使用。

Using the Code

我使用 qt5 创建了源文件,这使得有必要在树莓派上的 include/qt4 目录中创建一个链接。

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

创建 Makefile

在包含所有源文件的目录中创建一个名为“makefile”的新文件。

设置 Makefile 的基本变量

# often called CXX instead of COMPILER
COMPILER=arm-linux-gnueabihf-g++

# defining include directories
INCLUDEDIR = ./
INCLUDEDIR += /usr/include/qt4/
INCLUDEDIR += /usr/include/qt4/Qt	
INCLUDEDIR += /usr/include/qt4/QtGui
INCLUDEDIR += /usr/include/qt4/QtCore

# tell c++ which libraries we want to use
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
#XLINK_LIBDIR += /usr/local/lib/

INCDIR = $(patsubst %,-I%,$(INCLUDEDIR))
LIBDIR = $(patsubst %,-L%,$(LIBRARYDIR))
LIB = $(patsubst %,-l%,$(LIBRARY))
XLINKDIR = $(patsubst %,-Xlinker -rpath-link=%,$(XLINK_LIBDIR))

# make sure we use c++11 standard!
CPPSTD = -std=c++11
OPT = -O0
DEBUG = -g
WARN= -Wall
PTHREAD= -lpthread

# further linker options you might want to add.
# if you make changes here, make sure to add them to 
#    SPECIAL_LINK_OPTIONS below.
#GPIO = -lwiringPi
#QTGUI = -lQtGui
#QTNETWORK = -lQtNetwork
#QTCORE = -lQtCore
#QT += core gui widget
#QT += opengl
SPECIAL_LINK_OPTIONS = #$(GPIO)

#set up the compile statement (flags often called CXXFLAGS)
COMPILE_FLAGS = $(OPT) $(DEBUG) $(WARN) $(INCDIR) $(CPPSTD)
COMPILE = $(COMPILER) $(COMPILE_FLAGS) -c

# set up link statement (flags often called LDLFLAGS)
LINK_FLAGS= $(LIBDIR) $(LIB) $(XLINKDIR) $(PTHREAD) $(SPECIAL_LINK_OPTIONS)
LINK = $(COMPILER) $(LINK_FLAGS)

EXECUTABLE = executable

手动构建一切

首先,我们将手动编译所有内容,以便更好地了解正在发生的事情。如果您复制粘贴此代码,请确保在 $(LINK)$(COMPILE) 语句前有 TAB 键。空格无效。

新手提示:makefile 目标

将目标想象成 switch-case 语句中的“case”。语法如下

#########################################
####### START OF COMPILE STATEMENTS ########

#make shorter names to work with
NPD=numberplacedoc
NPV=numberplaceview

OBJ_FILES = $(NPD).o $(NPV).o main.o $(NPV).moc.o

# put everything together
all: $(OBJ_FILES)
$(LINK) $(OBJ_FILES) -o $(EXECUTABLE)

# usual compile of pure c++ file
$(NPD).o: $(NPD).cpp $(NPD).h
$(COMPILE) $(NPD).cpp 

# usual compile of code using qt (calling qt functions)
main.o: main.cpp
$(COMPILE) main.cpp 

# usual compile of code implementing a qt class
$(NPV).o: $(NPV).cpp $(NPV).h
$(COMPILE) $(NPV).cpp 

# usual compile of generated cpp file
$(NPV).moc.o: $(NPV).moc.cpp
$(COMPILE)  $(NPV).moc.cpp 

# this is the heart: create a new cpp file through the qt4 middle compiler
$(NPV).moc.cpp: $(NPV).h
moc-qt4 $(NPV).h  -o $(NPV).moc.cpp

######## END OF COMPILE STATEMENTS #########
#########################################

为了更好地了解层次结构,让我们看看这张图

构建

现在输入

make

应该会构建好您的可执行文件,随时可以运行。

错误

如果您收到“奇怪”的错误,例如

  • 未定义的引用 QApplication
  • ctordtor 中进行虚调用

或者其他任何与 qt 相关的奇怪 C++ 错误,请确保

  • numberplaceview.moc.cpp 已正确编译
  • numberplaceview.moc.o 存在
  • numberplaceview.moc.o 并且已包含在链接列表中

如果您遇到**任何**错误,请在此处提供反馈,以便我们在此列出并解决它们。

解释

基本上,一切就像您有一个纯 C++ 程序一样。只是,我们创建了一个特殊的新的 cpp 文件,该文件由 qt4 编译器创建,并且需要放入链接列表。这就是构建过程的全部魔力。

重要提示:如果此文件不在列表中,我们将遇到一些奇怪的错误

另请注意,在我们的构建过程中,我们没有关心 ui_numberplaceview.h

泛化(实用化)构建命令

我希望您现在已经对构建过程中发生的事情有了一些了解。

现在,没有人愿意在每次开始新项目时都一直像这样列出他们的文件。为此,我们将泛化构建命令,以便 makefile 能自动获取所有必要的文件(通过文件名中的模式)。

注意:提供的 makefile 只在当前目录中搜索源文件。

首先,我们更改结果的路径,使其更易于自定义

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

我们想要一个中间目录,以免将对象文件弄乱我们的文件夹

TMP_DIR=tmp/

我们需要定义 qt 需要生成的文件。为了更好地理解,我每行只调用一个命令。

我使用模式 *view.hqt*.h 来确定需要生成的文件。这意味着,如果您想使用此自动化,您的需要 qt 编译的文件应该具有此前缀或后缀。您可以随时更改模式或手动添加文件。

# find out which files we need to midl-compile with qt
#  Feel free to add specific files, that don't follow the pattern
H_FILES_FOR_QT := $(wildcard *view.h)
H_FILES_FOR_QT += $(wildcard qt*.h)

# remove all gui files generated by qt (they don't need to be midl-compiled)
H_FILES_FOR_QT := $(filter-out $(wildcard ui_*), $(H_FILES_FOR_QT))

现在我们准备好指示我们实际需要哪些文件了

# finally: defining all files we need to generate
CPP_FILES_TO_GEN := $(H_FILES_FOR_QT:.h=.moc.cpp)

Make 会自动删除“中间”文件。但我希望保留它们,以便我们可以更好地进行部分重建,因此

# prevent .moc-files being removed by make automatically
.SECONDARY: $(CPP_FILES_TO_GEN)

将所有列表放在一起,创建一个包含所有要编译的 cpp 文件的列表

# recognizing everything, that needs to be compiled
# ---------------------------------------------------
CPP_FILES := $(wildcard *.cpp) $(CPP_FILES_TO_GEN)

对象文件也是如此。但我们希望它们在我们的 tmp 目录中。所以将它们添加到它们的路径中

# defining all object files needed (inside intermediate dir)
# -------------------------------------------------------------
OBJECT_FILES := $(addprefix $(TMP_DIR),$(CPP_FILES:.cpp=.o)) 

新手提示:@-符号

我使用“@”符号来抑制命令本身,这样我们就能获得更漂亮的输出。

我们希望确保我们的 TMP_DIR 在对象文件创建在那里之前存在。

# defining the linking process
# ---------------------------------

all: $(TMP_DIR) linkAll
	@echo ===============================
	@echo ==== build successful ========
	@echo ===============================

# make sure we have an intermediate directory
$(TMP_DIR): 
	@echo create $(TMP_DIR) for intermediate files
	@mkdir $(TMP_DIR)

实际上,对于链接,我们需要 TMP_DIR,但它在这里不是一个先决条件。原因是,

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

这是编译过程的核心。如果一个先决条件“命令”了一个 .o 文件或 moc.cpp,它就会在这里被处理。.o 文件只能在 TMP_DIR 内被命令。moc.cpp 文件在所有源文件所在的位置被命令。

# for any object file that is needed (specified above) build it from the cpp
# making output more pretty by using @
$(TMP_DIR)%.o: %.cpp
	@echo compiling: $< to $@
	@$(COMPILE) $< -o $@

# for any needed .moc.cpp file: build it with moc-qt4 from the header
%.moc.cpp: %.h
	@echo creating moc: $@
	@moc-qt4 $< -o $@ 

定义在进行新构建时应该清理哪些文件

# defining cleanup process
# ------------------------------------

# if "clean" exists as a file, this code will break, because
#  target always specify files. So don't create a file called "clean" :)
# call: "make clean" for using this:
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)

提及的来源

历史

  • 2017 年 5 月 30 日:初始版本
© . All rights reserved.