1. 程式人生 > >MT7688學習筆記(3)——定製OpenWrt系統及新增自開發軟體

MT7688學習筆記(3)——定製OpenWrt系統及新增自開發軟體

一、將檔案直接編譯進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 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 編譯應用程式

  1. 將應用程式的整個檔案傳到OpenWrt系統原始碼的openwrt/package目錄下,然後進入OpenWrt系統原始碼的頂層目錄,執行make menuconfig
  2. 在彈出的選單選項中,首先進入Utilities選項。 Utilities
  3. 接著就能看到我們自己寫的應用程式的選項如cpp,將該選項配置進系統,即選為*。 cpp
  4. 接下來,退出儲存,並重新編譯系統make V=99
  5. 編譯完成後,新生成的韌體裡面,就已經包含了我們的應用程式了。

2.5 編譯可執行檔案

  1. 將應用程式的整個檔案傳到OpenWrt系統原始碼的openwrt/package目錄下,然後進入OpenWrt系統原始碼的頂層目錄,執行make package/cpp/compile V=99
  2. 編譯會在/work/openwrt-hiwooya-stable-master/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/cpp目錄下進行。
  3. 最終在/work/openwrt-hiwooya-stable-master/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/cpp目錄下生成名為cpp的可執行檔案(根據Makefile來生成的)。
  4. 將可執行檔案編譯進OpenWrt韌體中,見上面 一、將檔案直接編譯進OpenWrt韌體中

• 由 Leung 寫於 2018 年 8 月 23 日