OpenWRT开发之——BuildPackage剖析

前言

在之前的博文里详细地讲述了如何在OpenWrt下建立一个软件包(package),如:[OpenWrt对C++11的支持],[OpenWrt创建软件包]。

但是有个问题博主始终没有弄明白。为什么我们 make 一下,管理器就为我们从网上仓库下载软件源码,并编译打包。这个过程是怎么回事儿?还有,为什么我们在 package/<包名>/ 下的Makefile文件下的最后一行是:

$(eval $(call BuildPackage,xxx))

这句话的语意是什么?BuildPackage是什么?

带着这个问题,我们今天来研究一下 BuildPackage 以及展开后的Makefile全貌。

如果对Makefile的格式不太熟悉的同学,请访问我之前的博文:[Makefile学习笔记],了解Makefile的基本语法。

正文

我们还是来看看一个完整的Makefile内貌,如cpp11-demo的Makefile:

include $(TOPDIR)/rules.mk

PKG_NAME:=cpp11-demo
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)

include $(INCLUDE_DIR)/package.mk

define Package/cpp11-demo
    SECTION:=utils
    CATEGORY:=Utilites
    TITLE:=$(PKG_NAME)
    DEPENDS:=+libstdcpp
endef

define Build/Prepare
    $(MKDIR) -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)
endef

define Package/cpp11-demo/install
    $(INSTALL_DIR) $(1)/usr/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(PKG_NAME) $(1)/usr/bin
endef

$(eval $(call BuildPackage,cpp11-demo))

知道Makefile基本语法的同学们都知道 define xxxx ... endef 其实就是给 xxxx 变量赋值,只不过是多行文本而已。

依此,可以将上面的同意为:

include $(TOPDIR)/rules.mk

PKG_NAME:=cpp11-demo
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)

include $(INCLUDE_DIR)/package.mk

Package/cpp11-demo=XXXXXX
Build/Prepare=YYYYYY
Package/cpp11-demo/install=XXXXX

$(eval $(call BuildPackage,cpp11-demo))

从上面,没有看到一个目标的定义,如下面这样:

ALL:$(target)

objects:=main.o

$(target):$(objects)
    $(CC) $(CFLAGS) -o [email protected] $^

.PHONY clean
    -$(RM) $(objects)

那,这些目标在哪里?

玄机就在最后一行的BuildPackage,它根据上面定义的变量生成了所有上面的一切。

$(eval <text>),是将<text>作为Makefile文件中的一部分。

$(eval $(call BuildPackage,cpp11-demo)),是引用BuildPackage变量中的内容,并将内部中的$(1)替换为cpp11-demo,然后将处理后的内部作为Makefile的一部分。

1. 研究BuildPackage

BuildPackage 变量定义在 include/package.mk 文件中。

define BuildPackage
  $(Build/IncludeOverlay)
  $(eval $(Package/Default))
  $(eval $(Package/$(1)))

ifdef DESCRIPTION
$$(error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description)
endif

ifndef Package/$(1)/description
define Package/$(1)/description
    $(TITLE)
endef
endif

  BUILD_PACKAGES += $(1)
  $(STAMP_PREPARED): $$(if $(QUILT)$(DUMP),,$(call find_library_dependencies,$(DEPENDS)))

  $(foreach FIELD, TITLE CATEGORY SECTION VERSION,
    ifeq ($($(FIELD)),)
      $$(error Package/$(1) is missing the $(FIELD) field)
    endif
  )

  $(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


tip1

BuidPackage的第一行就是:$(Build/IncludeOverlay),其定义:

define Build/IncludeOverlay
  $(eval -include $(wildcard $(TOPDIR)/overlay/*/$(PKG_NAME).mk))
  define Build/IncludeOverlay
  endef
endef

tip2

其中第9~13行:

ifndef Package/$(1)/description
define Package/$(1)/description
    $(TITLE)
endef
endif

如果没有定义Package/$(1)/description,那么就以$(TITLE)来定义一个默认的。这里$(1)为 "cpp11-demo"。

tip3

  BUILD_PACKAGES += $(1)

将cpp11-demo追加到变量 BUILD_PACKAGES 后面,可能是将来编译的时候需要。

tip4

  $(STAMP_PREPARED): $$(if $(QUILT)$(DUMP),,$(call find_library_dependencies,$(DEPENDS)))

如果 $(QUILT)$(DUMP) 都没有定义,那么以$(DEPENDS)为参引用 find_library_dependencies 变量。

其中 find_library_dependencies 定义如下:

find_library_dependencies = $(wildcard $(patsubst %,$(STAGING_DIR)/pkginfo/%.version,     $(filter-out $(BUILD_PACKAGES),$(foreach dep,         $(filter-out @%, $(patsubst +%,%,$(1))),         $(if $(findstring :,$(dep)),             $(word 2,$(subst :,$(space),$(dep))),             $(dep)         )     ))))

博主暂不解释,有点复杂,略过。其功能应该是找出依赖的库。

生成:

XXXX/.prepared : aa bb cc dd

tip5

  $(foreach FIELD, TITLE CATEGORY SECTION VERSION,
    ifeq ($($(FIELD)),)
      $$(error Package/$(1) is missing the $(FIELD) field)
    endif
  )

这几句是判断 FIELD, TITLE, CATEGORY, SECTION, VERSION 这几个变量有没有定义,如果没有定义那就就报错。

tip6

  $(if $(DUMP),     $(Dumpinfo/Package),     $(foreach target,       $(if $(Package/$(1)/targets),$(Package/$(1)/targets),         $(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg)       ), $(BuildTarget/$(target))     )   )

虽然写得有点复杂,但意思是:

将 Package/$(1)/targets 或 PKG_TARGETS 中的每一项或 ipk 作为$(target),引用 $(BuildTarget/$(target))。其中:

BuildTarget/bin 定义在 include/package-bin.mk

BuildTarget/ipk 定义在 include /package-ipk.mk

具体这两个变量的内容博主暂时不去研究。其内容莫非是描述如何生成bin与ipk目标。这里留个引子,下次再研究。

如果没有定义 Package/$(1)/targets 与 PKG_TARGETS,那么上次就默认将ipk作为target,引用 BuildTarget/ipk 变量了。

tip7

  $(if $(PKG_HOST_ONLY)$(DUMP),,$(call Build/DefaultTargets,$(1)))

如果没有定义 PKG_HOST_ONLY 与 DUMP,那么这句可以简化为:

$(call Build/DefaultTargets,$(1))

即默认的目标构建。

2. 研究Build/DefaultTargets

如下为 Build/DefaultTargets 的定义:

define Build/DefaultTargets
  $(if $(QUILT),$(Build/Quilt))
  $(if $(USE_SOURCE_DIR)$(USE_GIT_TREE),,$(if $(strip $(PKG_SOURCE_URL)),$(call Download,default)))
  $(call Build/Autoclean)

  download:
    $(foreach hook,$(Hooks/Download),
        $(call $(hook))$(sep)
    )

  $(STAMP_PREPARED) : export PATH=$$(TARGET_PATH_PKG)
  $(STAMP_PREPARED):
    @-rm -rf $(PKG_BUILD_DIR)
    @mkdir -p $(PKG_BUILD_DIR)
    $(foreach hook,$(Hooks/Prepare/Pre),$(call $(hook))$(sep))
    $(Build/Prepare)
    $(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))
    touch [email protected]

  $(call Build/Exports,$(STAMP_CONFIGURED))
  $(STAMP_CONFIGURED): $(STAMP_PREPARED)
    $(foreach hook,$(Hooks/Configure/Pre),$(call $(hook))$(sep))
    $(Build/Configure)
    $(foreach hook,$(Hooks/Configure/Post),$(call $(hook))$(sep))
    rm -f $(STAMP_CONFIGURED_WILDCARD)
    touch [email protected]

  $(call Build/Exports,$(STAMP_BUILT))
  $(STAMP_BUILT): $(STAMP_CONFIGURED)
    $(foreach hook,$(Hooks/Compile/Pre),$(call $(hook))$(sep))
    $(Build/Compile)
    $(foreach hook,$(Hooks/Compile/Post),$(call $(hook))$(sep))
    $(Build/Install)
    $(foreach hook,$(Hooks/Install/Post),$(call $(hook))$(sep))
    touch [email protected]

  $(STAMP_INSTALLED) : export PATH=$$(TARGET_PATH_PKG)
  $(STAMP_INSTALLED): $(STAMP_BUILT)
    $(SUBMAKE) -j1 clean-staging
    rm -rf $(TMP_DIR)/stage-$(PKG_NAME)
    mkdir -p $(TMP_DIR)/stage-$(PKG_NAME)/host $(STAGING_DIR)/packages $(STAGING_DIR_HOST)/packages
    $(foreach hook,$(Hooks/InstallDev/Pre),        $(call $(hook),$(TMP_DIR)/stage-$(PKG_NAME),$(TMP_DIR)/stage-$(PKG_NAME)/host)$(sep)    )
    $(call Build/InstallDev,$(TMP_DIR)/stage-$(PKG_NAME),$(TMP_DIR)/stage-$(PKG_NAME)/host)
    $(foreach hook,$(Hooks/InstallDev/Post),        $(call $(hook),$(TMP_DIR)/stage-$(PKG_NAME),$(TMP_DIR)/stage-$(PKG_NAME)/host)$(sep)    )
    if [ -f $(STAGING_DIR)/packages/$(STAGING_FILES_LIST) ]; then         $(SCRIPT_DIR)/clean-package.sh             "$(STAGING_DIR)/packages/$(STAGING_FILES_LIST)"             "$(STAGING_DIR)";     fi
    if [ -d $(TMP_DIR)/stage-$(PKG_NAME) ]; then         (cd $(TMP_DIR)/stage-$(PKG_NAME); find ./ > $(TMP_DIR)/stage-$(PKG_NAME).files);         $(call locked,             mv $(TMP_DIR)/stage-$(PKG_NAME).files $(STAGING_DIR)/packages/$(STAGING_FILES_LIST) &&             $(CP) $(TMP_DIR)/stage-$(PKG_NAME)/* $(STAGING_DIR)/;         ,staging-dir);     fi
    rm -rf $(TMP_DIR)/stage-$(PKG_NAME)
    touch [email protected]

  ifdef Build/InstallDev
    compile: $(STAMP_INSTALLED)
  endif

  define Build/DefaultTargets
  endef

  prepare: $(STAMP_PREPARED)
  configure: $(STAMP_CONFIGURED)
  dist: $(STAMP_CONFIGURED)
  distcheck: $(STAMP_CONFIGURED)
endef

<明日再续>

结语

若想与博文一起研究新技术,请关注我吧!

时间: 2024-11-10 10:53:54

OpenWRT开发之——BuildPackage剖析的相关文章

OpenWrt 开发版本主trunk MT7620N 无线驱动bug

环境: OpenWrt Development Trunk: svn co svn://svn.openwrt.org/openwrt/trunk/ BUG: 1. 无线无法建立连接. 2. 无线建立连接获取IP地址失败,断开连接. 出现问题的时候会伴随以下dmesg [ 3702.380000] ieee80211 phy0: rt2x00queue_write_tx_frame: Error - Dropping frame due to full tx queue 2 [ 3702.390

康威定律-软件之道:软件开发争议问题剖析

每个架构师都应该研究下康威定律 http://36kr.com/p/5042735.html 软件之道:软件开发争议问题剖析((美)AndyOram)  http://baike.baidu.com/view/11495670.htm

openwrt教程 第一章 物联网&amp;openwrt开发概述

转载请注明出处:http://blog.csdn.net/ns_code/article/details/28505569 序言 求两个正整数的最大公约数是一个很古老且很基本的问题,欧几里得在其著作<几何原本>中给出了高效的解法--辗转相除法,也叫做欧几里得算法.下面我们来看下求最大公约数的一些方法. 方法一 我们先来看欧几里得的辗转相除法.原理很简单,假设用f(x,y)表示x和y的最大公约数,我们令x>y,则有x=ky+b,如果一个数能够同时整除x和y,则必能同时整除b和y,而能够同时

我的openwrt开发相关文章

openwrt学习笔记: 在openwrt的学习过程中,走了非常多的弯路.一直以来有个期盼.希望能够出个简易教程,希望openwrt的同仁们能够更加高速的入手. . openwrt学习笔记(三十二):我的openwrt学习笔记(三十二):openwrt的UCI 2015.9.14 openwrt学习笔记(三十一):我的openwrt学习笔记(三十一):openwrt的vlan配置 2015.9.14 openwrt学习笔记(三十):   我的openwrt学习笔记(三十)webserver之uh

BAT解密:互联网技术发展之路(5)- 开发层技术剖析

BAT解密:互联网技术发展之路(5)- 开发层技术剖析 1. 开发框架 在系列文章的第2篇"BAT解密:互联网技术发展之路(2)- 业务怎样驱动技术发展"中我们深入分析了互联网业务发展的一个特点:复杂性越来越高. 复杂性添加的典型现象就是系统越来越多,不同的系统由不同的小组开发. 假设每一个小组用不同的开发框架和技术,将会带来非常多问题.典型的问题有: 1)技术人员之间没有共同的技术语言,交流合作少 2)每类技术都须要投入大量的人力和资源和熟练精通 3)不同团队之间人员无法高速流动,人

跟着佐大学Lede/OpenWrt培训班讲义-03如何配置OpenWrt开发环境?

备注: 此文是佐须之男"跟着佐大学OpenWrt开发"入门培训班教学视频演讲稿提纲,虽是内部资料但再三考虑后对外公布,通过公众的监督来鞭策自己.如对完整内容感兴趣,可以参加"跟着佐大学OpenWrt开发"入门培训班:http://forgotfun.org/2018/04/openwrt-training-2018.html. 课程试听地址: https://pan.baidu.com/s/13nCmoaXTEfKc9F9-bdtHcA 安装虚拟机,Virtualb

OpenWRT开发之——C++11的支持

前言 在上篇文章中博主尝试了在OpenWrt上用C++写个简单的程序测试了一下,可行. 博主这两天又了解了C++11,里面的新特性非常令我兴奋.比如shared_ptr, lambda, auto都是非常有用的特性.[点击了解C++11] 今天,博言主就尝试了一下. 正文 1. 检查gcc版本 据说,gcc在4.8版本之后就支持c++11了.我们先检查一下交叉编译器的版本. $ cd SDK    #进入OpenWrt的SDK路径 $ cd ./staging_dir/toolchain-mip

OpenWRT开发之——SDK

为了达到自己编写一个程序打包成ipk,并能在OpenWRT上运行的目的.我在网上找了些学习的资料. 本人参考的是:如何在OpenWRT上做开发 感谢该网友的耐心解答.虽然有现成的步骤,博主还是喜欢亲自实践一下,写下自己的实践过程. 第一步:生成SDK make menuconfig 选上 "Build the OpenWRT SDK" 在 trunk目录下,执行: $ make menuconfig 选择对应的"Target System"与"Target

OpenWRT开发之——远程debug

想要用gdb对OpenWrt进行远程调试.首先得在OpenWrt目标机上安装gdbserver. 1. 安装gdbserver gdbserver 可以用 ipk 包进行安装. 在OpenWrt的trunk目录下,运行 make menuconfig,进行系统进行裁剪. gdbserver在 Development 目录下. 将gdbserver选为M,保存退出. 可以打开 .config 进行查看: 可以看到 CONFIG_PACKAGE_gdbserver=m. 好了,再 make V=s