上面一篇博文中,博主嘗試建立一個很是簡單的helloworld包,過程詳見博文:http://my.oschina.net/hevakelcj/blog/410633git
本文將帶你們一塊兒深刻地學習一下OpenWrt包的 Makefile。咱們不只要知其然,還要知其因此然。shell
在上篇博文裏,包裏的 Makefile 內容以下:網絡
include $(TOPDIR)/rules.mk PKG_NAME:=helloworld PKG_RELEASE:=1 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) include $(INCLUDE_DIR)/package.mk define Package/helloworld SECTION:=utils CATEGORY:=Utilities TITLE:=Helloworld -- prints a snarky message endef define Package/helloworld/description It's my first package demo. endef define Build/Prepare echo "Here is Package/Prepare" mkdir -p $(PKG_BUILD_DIR) $(CP) ./src/* $(PKG_BUILD_DIR)/ endef define Package/helloworld/install echo "Here is Package/install" $(INSTALL_DIR) $(1)/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/ endef $(eval $(call BuildPackage,helloworld))
大概咱們能夠將簡代爲以下的結構:svn
include $(TOPDIR)/rules.mk # 這裏定義一系列的 PKG_XX include $(INCLUDE_DIR)/package.mk # 定義各類 Package, Build 宏 $(eval $(call BuildPackage,包名))
下面,咱們來一一拆解。工具
首先,include $(TOPDIR)/rules.mk,也就是將 SDK/rules.mk 文件中的內容導入進來。post
TOPDIR就是SDK的路徑。
學習
在 SDK/rules.mk 文件中,定義了許多變量。fetch
咱們能夠看出,在Makefile中,賦值是用 := ,而不是等號。
ui
好比上面的 BUILD_DIR, INCLUDE_DIR 等,都在這裏定義。還有:this
還有關於 TARGET_CC, TARGET_CXX 等很是有用的變量定義。
還有 TAR, FIND, INSTALL_BIN, INSTALL_DIR, INSTALL_DATA等等很是重要的變量定義。
官網上指定有以下變量須要設置:
PKG_NAME - The name of the package, as seen via menuconfig and ipkg
PKG_VERSION - The upstream version number that we're downloading
PKG_RELEASE - The version of this package Makefile
PKG_LICENSE - The license(s) the package is available under, SPDX form.
PKG_LICENSE_FILE- file containing the license text
PKG_BUILD_DIR - Where to compile the package
PKG_SOURCE - The filename of the original sources
PKG_SOURCE_URL- Where to download the sources from (directory)
PKG_MD5SUM - A checksum to validate the download
PKG_CAT - How to decompress the sources (zcat, bzcat, unzip)
PKG_BUILD_DEPENDS - Packages that need to be built before this package, but are not required at runtime. Uses the same syntax as DEPENDS below.
PKG_INSTALL - Setting it to "1" will call the package's original "make install" with prefix set to PKG_INSTALL_DIR
PKG_INSTALL_DIR - Where "make install" copies the compiled files
PKG_FIXUP - ???
PKG_SOURCE_PROTO - the protocol to use for fetching the sources (git, svn)
PKG_REV - the svn revision to use, must be specified if proto is "svn"
PKG_SOURCE_SUBDIR - must be specified if proto is "svn" or "git", e.g. "PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)"
PKG_SOURCE_VERSION - must be specified if proto is "git", the commit hash to check out
PKG_CONFIG_DEPENDS - specifies which config options depend on this package being selected
跟上面的 include $(TOPDIR)/rules.mk 是同樣的。就是把這個文件包含進來。
INCLUDE_DIR這個變量在 rules.mk 裏已經定義了:
那就是 SDK/include/package.mk 文件了,打開看看。
主要有如下幾個功能:
若是某個變量咱們沒有在上一部分裏定義,那裏在這個文件裏,它就會被指定爲默認值,好比:
上面的用 ?= 來表示給未定義的變量賦默認值。好比,若是沒有指定 PKG_MD5SUM,那麼就默認爲 unknow。
根據上部分用戶自定義的 PKG_XXXX 變量推導出更多相關的變量。
好比:
雖然我沒有看過相關的手冊,根據多年的從業經驗也能看出上面的意思來。
#若是定義了宏,就... ifdef 宏名 ... endif #若是宏相等 ifeq (宏1,宏2) ... endif strip $宏名 #將宏對應的值去除先後的空白字符 VAR += xxxx #在變量 VAR 後面追加 xxxx
我猜大概就是這樣,若是不對請指正。
再好比以下:
就這樣,它爲咱們提供了大量有價值的變量。
在 Makefile 中,宏的定義格式是:
define XXX/xxxx <宏的實體...> endef
package.mk會把大部分須要的宏都定義好。理想狀況下,用戶只須要定義好了 PKG_XXX 以後,不須要再自定義宏,默認的宏就能夠知足需求。
好比Build/Prepare/Default的定義:
Build/Prepare宏是在編譯前進行的,是準備工做。
能夠看出,它分了兩種狀況:
A,定義了 USE_GIT_TREE,則按git的方式定義。
B,定義了 USB_SOURCE_DIR,則按源碼在本地的方案定義。
最重要的一個宏是 BuildPackage。它會在 Makefile 的最後一行被引用。它的實現也就是在 package.mk 文件裏。以下爲其源碼:
define BuildPackage $(Build/IncludeOverlay) $(eval $(Package/Default)) #定義在package-defaults.mk文件裏 $(eval $(Package/$(1))) #調用用戶自定義的 Package/<包名> 宏 ifdef DESCRIPTION $$(error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description) endif #檢查有沒有定義 Package/<包名>/description宏,若是沒有定義,則以TITLE默認定義一個 ifndef Package/$(1)/description define Package/$(1)/description $(TITLE) endef endif BUILD_PACKAGES += $(1) $(STAMP_PREPARED): $$(if $(QUILT)$(DUMP),,$(call find_library_dependencies,$(DEPENDS))) #檢查 TITLE, CATEGORY, SECTION, VERSION 是否認義,若是沒有定義則報錯 $(foreach FIELD, TITLE CATEGORY SECTION VERSION, ifeq ($($(FIELD)),) $$(error Package/$(1) is missing the $(FIELD) field) endif ) #若是有定義DUMP,那就引入Dumpinfo/Package宏的內部。 #若是沒有,那麼就引用 Packaget/<包名>/targets裏面的每個target,若是沒有定義Packaget/<包名>/targets宏,那麼將PKG_TARGETS裏的每一個target取出來, #若是也沒有定義PKG_TARGETS,那就默認ipkg做爲target。將每個target,引用 BuildTarget/$(target)。 $(if $(DUMP), \ $(Dumpinfo/Package), \ $(foreach target, \ $(if $(Package/$(1)/targets),$(Package/$(1)/targets), \ $(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg) \ ), $(BuildTarget/$(target)) \ ) \ ) $(if $(PKG_HOST_ONLY)$(DUMP),,$(call Build/DefaultTargets,$(1))) endef
總結一下語法:
$() 表示要執行的一條語句
$(if 條件, 成立執行, 失敗執行) if條件分支
$(foreach 變量, 成員列表, 執行體) 成員遍歷語句
能夠看出,語句是能夠嵌套使用的。
$(N) 表示第N個參數
我定要爲咱們的package定義特定的宏:
Package/<包名> #包的參數
在 Package/<包名> 宏中定義與包相關的信息。
如Package/helloworld宏:
define Package/helloworld SECTION:=utils CATEGORY:=Utilities TITLE:=Helloworld -- prints a snarky message endef
除了上面所列的 SECTION,CATEGORY,TITLE變量外,還能夠定義:
SECTION - 包的種類
CATEGORY - 顯示在menuconfig的哪一個目錄下
TITLE - 簡單的介紹
DESCRIPTION - (deprecated) 對包詳細的介紹
URL - 源碼所在的網絡路徑
MAINTAINER - (required for new packages) 維護者是誰(出錯了聯繫誰)
DEPENDS - (optional) 須要依事的包,See below for the syntax.
USERID - (optional) a username:groupname pair to create at package installation time.
其它的宏能夠選擇性地定義,一般不必本身重寫。但有些狀況,package.mk中默認的宏不能知足咱們的需求。這時,咱們就須要本身重定義宏。
好比,咱們在爲helloworld寫Makefile時,咱們要求在編譯以前,將 SDK/package/helloworld/src/ 路徑下的文件複製到 PKG_BUILD_DIR 所指定的目錄下。
因而咱們從新定義Build/Prepare宏:
define Build/Prepare mkdir -p $(PKG_BUILD_DIR) $(CP) ./src/* $(PKG_BUILD_DIR)/ endef
如此以來,在咱們 make V=s 時,make工具會在編譯以前執行 Build/Prepare 宏裏的命令。
再好比,咱們要指定包的安裝方法:
define Package/helloworld/install $(INSTALL_DIR) $(1)/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/ endef
上面的這個宏就是指定了包的安裝過程。其中 INSTALL_DIR 定義在 rules.mk 文件裏。
INSTALL_DIR = install -d -m0755
INSTALL_BIN = install -m0755
$(1)爲第一個參數是./configure時的--prefix參數,一般爲""
展開以後就是:
define Package/helloworld/install install -d -m0755 /bin install -m0755 $(PKG_BUILD_DIR)/helloworld /bin/ endef
它的意思就一目瞭然了。
除了上面所列舉的這兩個宏外,在官網上也說明了其它可選的宏:
由該包安裝的配置文件的列表,一行一個文件。
對包描述的純文本
A set of commands to unpack and patch the sources. You may safely leave this undefined.
You can leave this undefined if the source doesn't use configure or has a normal config script, otherwise you can put your own commands here or use "$(call Build/Configure/Default,)" as above to pass in additional arguments for a standard configure script.
How to compile the source; in most cases you should leave this undefined, because then the default is used, which calls make. If you want to pass special arguments to make, use e.g. "$(call Build/Compile/Default,FOO=bar)
How to install the compiled source. The default is to call make install. Again, to pass special arguments or targets, use $(call Build/Install/Default,install install-foo) Note that you need put all the needed make arguments here. If you just need to add something to the "install" argument, don't forget the 'install' itself.
For things needed to compile packages against it (static libs, header files), but that are of no use on the target device.
A set of commands to copy files into the ipkg which is represented by the $(1) directory. As source you can use relative paths which will install from the unpacked and compiled source, or $(PKG_INSTALL_DIR) which is where the files in the Build/Install step above end up.
The actual text of the script which is to be executed before installation. Dont forget to include the #!/bin/sh. If you need to abort installation have the script return false.
The actual text of the script which is to be executed after installation. Dont forget to include the #!/bin/sh.
The actual text of the script which is to be executed before removal. Dont forget to include the #!/bin/sh. If you need to abort removal have the script return false.
The actual text of the script which is to be executed after removal. Dont forget to include the #!/bin/sh.
之因此有些宏是以"Package/"開頭,有的又以"Build/",是由於在一個Makefile裏生成多個包。OpenWrt默認認爲一個Makefile裏定義一個包,但咱們也能夠根據須要將其拆分紅多個。因此說,若是咱們只但願編譯一次,那麼只要有一系列的"Build/"的宏定義就能夠了。可是,咱們也能夠經過添加多個"Package/"宏定義,並調用 BuildPackage,來建立多個包。
在Makefile的最後一行是:
$(eval $(call BuildPackage,helloworld))
最重要的 BuildPackage定義在 package.mk 文件裏。見上面 BuildPackage 宏定義。