一、將文件直接編譯進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)