openwrt软件包 4: 如何在一个Makefile中编译多个软件包

前言

openwrt软件包 1: 如何为自己的软件包编写makefile,并将其编译到镜像中
openwrt软件包 2: linux 内核软件包
openwrt软件包 3: openwrt下的 Linux 内核软件包

此前做了基础了解Makefile和openwrt各类软件包的编译, 现在是自己读一些Makefile的学习了

为什么同时编译多个软件包

做啥先问why

按我理解, 这个功能的作用主要是为了能够将有相同功能的模块放在一起管理, openwrt官方给的非常多包就会使用这个方法, 当然也会有一些复用的情况, 比如一些比较复杂的系统中其实共用一份基础代码, 基于这些基础的函数写不同的处理来编译给不同版本/不同硬件使用的软件包等等.

注意事项

虽然可以同时在一个Makefile中写规则编译不同的软件包, 但是请注意 不要混淆不同类型的软件包, 即此前提到的用于openwrt的软件包和作用于内核的软件包.

他们库不同, 在底层Makefile的编译形式也不尽相同, 同样对于顶层Makefile的编写也非常不同, 请千万千万不要混用

如何在一个Makefile中同时编译多个软件包

编译规则

首先还是基础的编译规则和架构, 请参考第一二三篇的内容, 编译多个Makefile的基础规则还是相同, 这里直接照搬第一篇中写的示例

include $(TOPDIR)/rules.mk
  
PKG_NAME:=test1
PKG_VERSION=1.0
PKG_RELEASE=1

include $(INCLUDE_DIR)/package.mk

#注意下面所有的的test1必须和PKG_NAME的值相同, 当然也可以直接用宏$(PKG_NAME)
define Package/test1
  SECTION:=examples
  CATEGORY:=Examples
  TITLE:=test1
endef

define Build/Prepare
        echo "build prepare"
        mkdir -p $(PKG_BUILD_DIR)
        $(CP) ./src/* $(PKG_BUILD_DIR)
endef

TARGET_CFLAGS += -Wall -Werror -D_LINUX
define Build/Compile
	CC="$(TARGET_CC)" \
	CFLAGS="$(TARGET_CFLAGS)" \
	LDFLAGS="$(TARGET_LDFLAGS)" \
	LIBS="$(TARGET_LIBS)" \
	$(MAKE) -C $(PKG_BUILD_DIR) all
endef

define Package/test1/install
        $(INSTALL_DIR) $(1)/usr/bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/test1 $(1)/usr/bin
endef

$(eval $(call BuildPackage,test1))

格式还是类似, 注意TAB空格的使用.

关于openwrt软件包和用于内核的软件包

两种类型的软件包写法相似, 此处以openwrt下软件包为例, 内核软件包想要通过一个Makefile编译多个软件包可以参考着修改.

如何编译多个软件包

好接下来就是关于如何真正的编写一个Makefile来同时编译多个软件包, 我们还是以openwrt的原生代码为例:
path:openwrt/package/system/procd/Makefile

#
# Copyright (C) 2014-2016 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=procd
PKG_RELEASE:=2

PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/procd.git
PKG_MIRROR_HASH:=a7e42525ae65eb1342e593a714e88bc59e46467cbb5a7fd7d7aca4a9815b7c0d
PKG_SOURCE_DATE:=2023-06-25
PKG_SOURCE_VERSION:=2db836553e8fc318143b38dbc6e12b8625cf5c33
CMAKE_INSTALL:=1

PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=

PKG_MAINTAINER:=John Crispin <john@phrozen.org>

PKG_BUILD_FLAGS:=lto
PKG_ASLR_PIE_REGULAR:=1
PKG_CONFIG_DEPENDS:= \
	CONFIG_TARGET_INIT_PATH CONFIG_KERNEL_SECCOMP CONFIG_PROCD_SHOW_BOOT \
	CONFIG_KERNEL_NAMESPACES CONFIG_PACKAGE_procd-ujail CONFIG_PACKAGE_procd-seccomp

include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk

ifeq ($(DUMP),)
  STAMP_CONFIGURED:=$(strip $(STAMP_CONFIGURED))_$(shell echo $(CONFIG_TARGET_INIT_PATH) | $(MKHASH) md5)
endif

CMAKE_OPTIONS += -DEARLY_PATH="$(TARGET_INIT_PATH)"

define Package/procd/Default
  SECTION:=base
  CATEGORY:=Base system
  DEPENDS:=+ubusd +ubus +libjson-script +ubox +libubox \
	  +libubus +libblobmsg-json +libjson-c +jshn
  TITLE:=OpenWrt system process manager
  USERID:=:dialout=20 :audio=29
endef

define Package/procd
  $(call Package/procd/Default)
  VARIANT:=default
  CONFLICTS:=procd-selinux
endef

define Package/procd-selinux
  $(call Package/procd/Default)
  DEPENDS += +libselinux
  TITLE += with SELinux support
  PROVIDES:=procd
  VARIANT:=selinux
endef

define Package/procd-ujail
  SECTION:=base
  CATEGORY:=Base system
  DEPENDS:=@KERNEL_NAMESPACES +@KERNEL_UTS_NS +@KERNEL_IPC_NS +@KERNEL_PID_NS \
	  +libubox +libubus +libuci +libblobmsg-json
  TITLE:=OpenWrt process jail helper
endef

define Package/procd-seccomp
  SECTION:=base
  CATEGORY:=Base system
  DEPENDS:=@SECCOMP +libubox +libblobmsg-json
  TITLE:=OpenWrt process seccomp helper + utrace
endef

define Package/uxc
  SECTION:=base
  CATEGORY:=Base system
  DEPENDS:=+procd-ujail +libubus +libubox +libblobmsg-json +blockd +PACKAGE_uxc:rpcd
  TITLE:=OpenWrt container management
  MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
endef

define Package/procd/config
menu "Configuration"
	depends on PACKAGE_procd || PACKAGE_procd-selinux

config PROCD_SHOW_BOOT
	bool
	default n
	prompt "Print the shutdown to the console as well as logging it to syslog"

endmenu
endef

ifeq ($(BUILD_VARIANT),selinux)
  CMAKE_OPTIONS += -DSELINUX=1
endif

ifeq ($(CONFIG_PROCD_SHOW_BOOT),y)
  CMAKE_OPTIONS += -DSHOW_BOOT_ON_CONSOLE=1
endif

ifdef CONFIG_PACKAGE_procd-ujail
  CMAKE_OPTIONS += -DJAIL_SUPPORT=1
endif

SECCOMP=$(if $(CONFIG_PACKAGE_procd-seccomp),1,0)
CMAKE_OPTIONS += -DSECCOMP_SUPPORT=$(SECCOMP) -DUTRACE_SUPPORT=$(SECCOMP)

define Package/procd/install
	$(INSTALL_DIR) $(1)/sbin $(1)/etc $(1)/lib/functions

	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{init,procd,askfirst,udevtrigger,upgraded} $(1)/sbin/
	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libsetlbf.so $(1)/lib
	$(INSTALL_BIN) ./files/reload_config $(1)/sbin/
	$(INSTALL_CONF) ./files/hotplug*.json $(1)/etc/
	$(INSTALL_DATA) ./files/procd.sh $(1)/lib/functions/
	$(INSTALL_BIN) ./files/service $(1)/sbin/service
endef

Package/procd-selinux/install = $(Package/procd/install)

define Package/procd-ujail/install
	$(INSTALL_DIR) $(1)/sbin
	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ujail $(1)/sbin/
endef

define Package/procd-seccomp/install
	$(INSTALL_DIR) $(1)/sbin $(1)/lib
	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libpreload-seccomp.so $(1)/lib
	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/utrace $(1)/sbin/
	$(LN) utrace $(1)/sbin/seccomp-trace
	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libpreload-trace.so $(1)/lib
endef

define Package/uxc/conffiles
/etc/uxc
endef

define Package/uxc/install
	$(INSTALL_DIR) $(1)/sbin
	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/uxc $(1)/sbin/
	$(INSTALL_DIR) $(1)/etc/init.d
	$(INSTALL_BIN) ./files/uxc.init $(1)/etc/init.d/uxc
endef

$(eval $(call BuildPackage,procd))
$(eval $(call BuildPackage,procd-selinux))
$(eval $(call BuildPackage,procd-ujail))
$(eval $(call BuildPackage,procd-seccomp))
$(eval $(call BuildPackage,uxc))

$(eval $(call BuildPackage, <package_name>))

在 OpenWrt 的 Makefile 中,$(eval $(call BuildPackage, <package_name>)) 这种语法用于在构建过程中调用特定的宏或函数来构建软件包。每一行代码的作用是定义一个软件包并将其添加到构建过程中。

语法:$(call <macro>, <args>)

$(call ...) 是一个用于调用宏(macro)的语法。
是宏的名称,宏类似于函数或模板,它会根据传入的参数执行一些操作。
是传给宏的参数。

$(eval ...) 是 OpenWrt Makefile 系统中的一个关键功能,它用于执行动态生成的 make 命令。$(eval ...) 允许在 Makefile 中执行动态生成的内容,实际上就是在 Makefile 中插入一个新的命令行。通常,$(eval ...) 用于将宏扩展为实际的 make 规则或者其他配置。

BuildPackage 宏:

BuildPackage 是 OpenWrt 中的一个宏,用于创建一个标准的软件包构建规则。它通常在 OpenWrt 的构建系统中用于定义一个软件包的构建过程。BuildPackage 宏会根据传入的包名(如 procd、procd-selinux 等)自动生成构建所需的目标规则,处理如何获取源代码、编译代码、安装文件等。

$(call BuildPackage, procd):会生成并执行构建 procd 软件包的规则。
$(call BuildPackage, procd-selinux):会生成并执行构建 procd-selinux 软件包的规则。
以此类推,其他包如 procd-ujail、procd-seccomp 和 uxc 也会生成相应的构建规则。

所以这些 $(eval $(call BuildPackage, ...)) 行的作用就是:

注册 和 定义 需要构建的多个软件包。
对于每个软件包,BuildPackage 宏会创建一组构建规则,确保该软件包在构建过程中能够正确地被获取、编译并安装到目标文件系统中。
这些宏调用帮助 OpenWrt 在构建时自动处理构建过程,而不需要手动编写每个软件包的构建规则。

在上面提到的例子中

$(eval $(call BuildPackage, procd))
$(eval $(call BuildPackage, procd-selinux))
$(eval $(call BuildPackage, procd-ujail))
$(eval $(call BuildPackage, procd-seccomp))
$(eval $(call BuildPackage, uxc))

这些语句会确保在构建过程中,procd、procd-selinux、procd-ujail、procd-seccomp 和 uxc 软件包都会被构建,系统会根据这些规则来管理它们的编译和安装。

PKG_NAME

虽然这篇叫做编译多个软件包, 但这个多个软件包就是相对于上一个$(eval $(call BuildPackage, <package_name>))的说明, Makefile中还是只有一个PKG_NAME变量, 这个变量决定你最后生成的软件包, 一般在编译生成文件下可以看到, 比如举例用的这个软件包openwrt/build_dir/target-aarch64-openwrt-linux-musl_musl/procd-XXX这里XX就看你的Makefile中PKG_相关宏的一些设置了.

也请注意, 这个名字需要和openwrt中这个软件包的路径名字一样, openwrt是根据这个名字来找到对应的软件包的.

同样的PKG_NAME只有一个, 控制编译这整个软件包的还是这个PKG_NAME的CONFIG, 各种类型软件包的CONFIG_XXX_$PKG_NAME中XXX的部分并不相同, 请在之前文章对应内容中查找.

我们把这个PKG_NAME称为主包, 而上面调用package生成的则是这个主包的子功能或者变种

其他注意

以上两点就是主要的注意点, 可以理解为在一个Makefile的主包中有多个子包. 这些子包的写法都和普通Makefile相似, 只是注意, 在写各个包的规则时, 写上对应的, 你想要这个规则应用到哪个包上.

具体可以参考给出的例子, 这篇已经很长啦, 不再赘述了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值