一、将文件直接编译进OpenWrt固件中
在源码目录下创建“files”目录,这个目录可以看成是根目录的映射,只要将要打包到固件的文件按照根目录的目录结构存放文件即可。
例如:
1.修改network配置文件源码固件
Ubuntu中openwrt-hiwooya-stable/files/etc/config/network <-----对应-----> 嵌入式系统中/etc/config/network
2.添加可执行文件setwifi
Ubuntu中openwrt-hiwooya-stable/files/usr/sbin/setwifi <-----对应-----> 嵌入式系统/usr/sbin/setwifi
二、添加软件包ipk的方法
2.1 简介
OpenWrt 是一个比较完善的嵌入式Linux开发平台,在无线路由器应用上已有4000多个软件包。用户可以按照OpenWrt的约定增加开源软件或自行开发的软件。加入软件包需要在package目录下创建一个目录,以包含该软件包的各种信息和与OpenWrt建立联系的文件。然后创建一个Makefile与OpenWrt建立联系,Makefile需要遵循OpenWrt的约定。另外可以创建一个patchs目录保存patch文件,对下载的源代码进行适量修改。
2.2 Makefile语法
2.2.1 引入文件
OpenWrt使用三个Makefile的子文件,分别为:
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
由这些Makefile子文件确定软件包加入OpenWrt的方式和方法。$(TOPDIR)/rules.mk一般在Makefile的开头,$(INCLUDE_DIR)/kernel.mk文件对于软件包为内核时是不可缺少的,$(INCLUDE_DIR)/package.mk一般在软件包的基本信息完成后再引入。
2.2.2 编写软件包的基本信息
软件包的信息均以PKG_开头,其意思和作用如下:
- PKG_NAME 表示软件包名称,将在menuconfig和ipkg可以看到。
- PKG_VERSION 表示软件包版本号。
- PKG_RELEASE 表示Makefile的版本号。
- PKG_SOURCE 表示源代码的文件名。
- PKG_SOURCE_URL 表示源代码的下载网站位置。@SF表示在sourceforge网站,@GNU表示在GNU网站,还有@GONE、@KERNEL。
- PKG_MD5SUM 表示源代码文件的校验码。用于核对软件包是否下载正确。
- PKG_CAT 表示源代码文件的解压方法。包括zcat,bzcat,unzip等。
- PKG_BUILD_DIR 表示软件包编译目录。它的父目录为$(BUILD_DIR)。如果不指定,默认为$(BUILD_DIR)/$(PKG_NAME)/$(PKG_VERSION)。
2.2.3 编译包定义
应用程序和内核驱动模块的定义不一样。应用程序软件包使用Package,内核驱动模块使用KernelPackage。
2.2.3.1 应用程序编译包定义
应用程序的编译包以Package/开头。然后接着软件名,在Package定义中的软件名可以与软件包名不一样,而且可以多个定义。下面使用$(PKG_NAME)只是做一个标志,并非真正使用$(PKG_NAME),如Package/$(PKG_NAME)。
- SECTION 表示包的类型,预留。
- CATRGORY 表示分类,在make menuconfig的菜单下将可以找到。
- TITLE 用于软件包的简短描述。
- DESCRIPTION 用于软件包的详细描述,已放弃使用。如果使用DESCRIPTION将会提示“error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description”。
- URL 表示软件包的下载位置。
- MAINTAIER 表示维护者,选项。
- DEPENDS 表示与其他软件的依赖。即编译或安装其他软件时需要说明。如果存在多个依赖,则每个依赖需要用空格分开。依赖前使用+号表示默认为显示,即对象没有选中时也会显示,使用@则默认为不显示,即当依赖对象选中后才显示。
在用户空间的应用程序软件包中没有内核驱动模块的AUTOLOAD参数。如果应用软件需要在boot时自动运行,则需要在/etc/init.d中增加相应的脚本文件。脚本文件需要START参数,说明在boot时的优先级,如果在boot过程启动后再关闭,则需要进一步设置STOP参数。如果STOP参数存在,其值必须大于START。脚本文件需要start()和stop()两个函数,start()是执行程序,stop()是关闭程序。关闭程序一般需要执行killall命令。由/etc/rc.d/S10boot知道,装载内核驱动模块的优先级为10,需要使用自己设计的内核驱动模块的程序其START的值必须大于10。同样由/etc/rc.d/S40network知道,使用网络通信的程序其START的值必须大于40。
- Package/$(PKG_NAME)/conffiles 本包安装的配置文件,一行一个。如果文件结尾使用/,则表示为目录。用于备份配置文件说明,在sysupgrade命令执行时将会用到。
- Package/$(PKG_NAME)/description 软件包的详细描述,取代前面提到的DESCRIPTION详细描述。
- Build/Prepare 编译准备方法,对于网上下载的软件包不需要再描述。对于非网上下载或自行开发的软件包必须说明编译准备方法。一般的准备方法为:
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
按OpenWrt的习惯,一般把自己设计的程序全部在src目录下。
-
Build/Compile 编译方法,没有特别说明的可以不予以定义。如果不定义将默认使用编译方法Build/Compile/Default。
自行开发的软件包可以考虑使用下面的定义。
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) CFLAGS="$(TARGET_CFLAGS) -I $(LINUX_DIR)/include"
endef
- Package/$(PKG_NAME)/install 软件包的安装方法,包括一系列拷贝编译好的文件到指定位置。调用时会带一个参数,就是嵌入式系统的镜像文件系统目录,因此$(1)表示嵌入式系统的镜像目录。一般可以采用下面的方法:
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_DIR) $(PKG_BUILD_DIR)/ $(PKG_NAME) $(1)/usr/bin/
endef
INSTALL_DIR、INSTALL_BIN在$(TOPDIR)/rules.mk文件定义,所以本Makefile必须引入$(TOPDIR)/rules.mk文件。
INSTALL_DIR:=install -d -m0755 意思是创建所属用户可读写和执行,其他用户可读可执行的目录。
INSTALL_BIN:=install -m0755 意思是编译好的文件存放到镜像文件目录。
如果用户空间的应用软件在boot时要自动运行,则需要在安装方法说明中增加自动运行的脚本文件安装和配置文件安装方法。
例如:
define Package/mountd/install
$(INSTALL_DIR) $(1)/sbin/ $(1)/etc/config/ $(1)/etc/init.d/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/mountd $(1)/sbin/
$(INSTALL_DATA) ./files/mountd.config $(1)/etc/config/mountd
$(INSTALL_BIN) ./files/mountd.init $(1)/etc/init.d/mountd
endef
安装文件放在files子目录下,不要与源代码文件目录src混在一起,以提高可读性。使用清晰的文件扩展名,更方便安装识别文件。
-
Package/$(PKG_NAME)/preinst 软件包安装前处理方法,使用脚本语言,因此定义的第一行需要下面的格式
#!/bin/sh
调用时带入的参数为嵌入式系统的镜像目录。 - Package/$(PKG_NAME)/postinst 软件包安装后处理方法,使用脚本语言。
- Package/$(PKG_NAME)/prerm 软件包删除前处理方法,使用脚本语言。
- Package/$(PKG_NAME)/postrm 软件包删除后处理方法,使用脚本语言。
2.2.3.2 内核驱动模块包定义
Linux分为内核空间和用户空间。开发者开发的内核部分可以直接加入Linux的Kernel程序,也可以生成内核模块以便需要时装入内核。OpenWrt一般希望开发者生成内核模块,在Linux启动后自动装载或手工使用insmod命令装载。内核模块使用KernelPackage开头,其他与一般应用软件包基本相同。
在内核驱动模块定义中增加了:
- SUBMENU 表示子菜单位置,在$(INCLUDE)/kernel.mk对内核模块定义了CATEGORY为kernel modules,所以内核模块在menuconfig中的主菜单为kernel modules,然后有下一级子菜单$(SUBMENU)。在子菜单下可以看到以kmod-$(PKG_NAME)项目。
- DEFAULT 表示直接编入内核或产生内核模块,y表示直接编入内核,m表示产生内核模块。
-
AUTOLOAD 表示自动装入内核,一般表示方法为:
AUTOLOAD:=$(call AutoLoad, $(PRIORITY),$(AUTOLOAD_MODS))
AutoLoad的第一个参数$(PRIORITY)为优先级,01为最优先,99为最后装载。有关自动装载可以在/etc/modules.d目录下看到,第二个参数$(AUTOLOAD_MODS)模块名,每个模块名以空格符分隔。即可同时装载多个内核模块。
在开发过程最好不要使用自动装载,经过严格调试后再使用,可以减轻调试的工作量。
2.2.4 使用定义
完成前面定义后,必须使用eval函数实现各种定义。其格式为:
对于一般应用软件包
$(eval $(call Package, $(PKG_NAME)))
或对于内核驱动模块
$(eval $(call KernelPackage, $(PKG_NAME)))
如果一个软件包有多个程序,例如:一个应用程序有自己的内核驱动模块,上面使用PKG_NAME需要灵活变通。eval函数可以设计多个。也可以当成多个软件包处理。
2.3 写Makefile文件
在openwrt-hiwooya-stable-master/package/cpp(你的程序文件夹名)/src下创建一个Makefile,用于编译该应用程序。
# build helloworld executable when user executes "make"
LDFLAGS += -L$(TOPDIR)/staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/lib/mysql
LDFLAGS += -L$(TOPDIR)/staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/lib/libevent
LDFLAGS += -L$(TOPDIR)/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/lib/ -lstdc++ -Wno-explicit
LDFLAGS += -L$(TOPDIR)/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/libjsoncpp/
LIBS += -lmysqlclient -lpthread -lrt -lcurl -ljsoncpp -levent
CXXFLAGS += -I$(TOPDIR)/staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/include/mysql
CXXFLAGS += -I$(TOPDIR)/staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/include/libevent
CXXFLAGS += -I$(TOPDIR)/staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/include
#CXXFLAGS += -I$(TOPDIR)/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/json-c-0.12
CXXFLAGS += -I$(TOPDIR)/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/libjsoncpp/include/
CXXFLAGS += -std=c++11
OBJ=cpp.o common.o Serial.o protocol.o CHttpClient.o \
CSocket.o CUDPSocket.o DALReceive.o SocketSend.o DALSend.o Central.o CMsg.o \
DALBase.o CustomMessage.o HCIModule.o HCIMatch.o \
SQLBase.o HCIServiceObject.o SQLServiceObject.o HCIEvent.o SQLEvent.o \
HCIGateway.o SQLGateway.o Gateway.o \
HCIIfttt.o SQLIfttt.o HCIAction.o SQLAction.o HCIDebug.o ServiceObject.o CustomCfg.o \
Load.o Event.o HandleThread.o HCIIftttLog.o SQLIftttLog.o Action.o \
Ifttt.o IftttLog.o Timer.o JsonVector.o HttpServer.o WebServer.o TimerThread.o Buffer.o
cpp: $(OBJ)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(LIBS) $(OBJ) -o $@
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(LIBS) -c $< -w
# remove object files and executable when user executes "make clean"
clean:
rm *.o cpp
在openwrt-hiwooya-stable-master/package/cpp(你的程序文件夹名)下创建一个Makefile,用于将该应用程序编译进OpenWrt系统。
##############################################
# OpenWrt Makefile for cpp program
#
#
# Most of the variables used here are defined in
# the include directives below. We just need to
# specify a basic description of the package,
# where to build our program, where to find
# the source files, and where to install the
# compiled program on the router.
#
# Be very careful of spacing in this file.
# Indents should be tabs, not spaces, and
# there should be no trailing whitespace in
# lines that are not commented.
#
##############################################
include $(TOPDIR)/rules.mk // 必须引入$(TOPDIR)/rules.mk文件
# Name and release number of this package
PKG_NAME:=cpp // 软件包名称
PKG_RELEASE:=1 // Makefile的版本号
# This specifies the directory where we're going to build the program.
# The root build directory, $(BUILD_DIR), is by default the build_mipsel
# directory in your OpenWrt SDK directory
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) // 软件包编译目录
include $(INCLUDE_DIR)/package.mk // 引入$(INCLUDE_DIR)/package.mk文件
# Specify package information for this program.
# The variables defined here should be self explanatory.
# If you are running Kamikaze, delete the DESCRIPTION
# variable below and uncomment the Kamikaze define
# directive for the description below
define Package/cpp
SECTION:=utils // 包的类型
CATEGORY:=Utilities // 包的分类,在make menuconfig的菜单下将可以找到。
// 与其他软件的依赖
DEPENDS:=+libmysqlclient +libpthread +libreadline +libstdcpp +librt +libcurl +libjsoncpp +libevent2
TITLE:=alm-tn01 // 软件包的简短描述
endef
# Uncomment portion below for Kamikaze and delete DESCRIPTION variable above
define Package/cpp/description // 软件包的详细描述
If you can't figure out what this program does, you're probably
brain-dead and need immediate medical attention.
endef
# Specify what needs to be done to prepare for building the package.
# In our case, we need to copy the source files to the build directory.
# This is NOT the default. The default uses the PKG_SOURCE_URL and the
# PKG_SOURCE which is not defined here to download the source from the web.
# In order to just build a simple program that we have just written, it is
# much easier to do it this way.
define Build/Prepare // 自行开发软件包的编译准备方法
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/ * $(PKG_BUILD_DIR)/
endef
# We do not need to define Build/Configure or Build/Compile directives
# The defaults are appropriate for compiling a simple program such as this one
# Specify where and how to install the program. Since we only have one file,
# the cpp executable, install it by copying it to the /bin directory on
# the router. The $(1) variable represents the root directory on the router running
# OpenWrt. The $(INSTALL_DIR) variable contains a command to prepare the install
# directory if it does not already exist. Likewise $(INSTALL_BIN) contains the
# command to copy the binary file from its current location (in our case the build
# directory) to the install directory.
define Package/cpp/install // 软件包的安装方法
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/cpp $(1)/bin/
$(INSTALL_DIR) $(1)/etc/init.d // 自动运行的脚本文件安装和配置文件安装方法
$(INSTALL_BIN) ./files/cpp.init $(1)/etc/init.d/cpp
$(INSTALL_DIR) $(1)/etc/app
endef
# This line executes the necessary commands to compile our program.
# The above define directives specify all the information needed, but this
# line calls BuildPackage which in turn actually uses this information to
# build a package.
$(eval $(call BuildPackage,cpp)) // 使用定义
2.4 编译应用程序
- 将应用程序的整个文件传到OpenWrt系统源码的openwrt/package目录下,然后进入OpenWrt系统源码的顶层目录,执行
make menuconfig
。 -
在弹出的菜单选项中,首先进入Utilities选项。
-
接着就能看到我们自己写的应用程序的选项如cpp,将该选项配置进系统,即选为*。
- 接下来,退出保存,并重新编译系统
make V=99
。 - 编译完成后,新生成的固件里面,就已经包含了我们的应用程序了。
2.5 编译可执行文件
- 将应用程序的整个文件传到OpenWrt系统源码的openwrt/package目录下,然后进入OpenWrt系统源码的顶层目录,执行
make package/cpp/compile V=99
。 - 编译会在/work/openwrt-hiwooya-stable-master/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/cpp目录下进行。
- 最终在/work/openwrt-hiwooya-stable-master/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/cpp目录下生成名为cpp的可执行文件(根据Makefile来生成的)。
- 将可执行文件编译进OpenWrt固件中,见上面 一、将文件直接编译进OpenWrt固件中。
• 由 Leung 写于 2018 年 8 月 23 日
• 参考:F403科技创意室(www.f403tech.com)