前言
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相似, 只是注意, 在写各个包的规则时, 写上对应的, 你想要这个规则应用到哪个包上.
具体可以参考给出的例子, 这篇已经很长啦, 不再赘述了.