参考 openwrt 官方文档
下面用<BUILDROOT>
表示 Openwrt 源码树顶层目录。Openwrt 所有的软件包都都保存在<BUILDROOT>/package
目录下,一个软件包占一个子目录。
软件包目录结构
例如: helloworld,如下所示
<BUILDROOT>/package/helloworld/Makefile
重点,它定义了如何构建一个Openwrt 软件包,包括从哪里下载源码,怎样编译和安装。
<BUILDROOT>/package/helloworld/files/
可选,一般存放配置文件和启动脚本。
<BUILDROOT>/package/helloworld/patches/
可选,一般存放 bug 修复或者程序优化的补丁。
两种软件包目录格式:
1、没有源码的 Openwrt 软件包目录下,构建 Openwrt 软件包的第一步是从指定的 URL 下载指定的源码包,
这个 URL 在 Makefile 中定义,如下所示:
PKG_SOURCE:=helloworld.tar.gz
PKG_SOURCE_URL:=http://www.example.com/
PKG_MD5SUM:=e06c222e186f7cc013fd272d023710cb
用于验证下载的软件包的完整性。
下载的helloworld.tar.gz 一般存放在<BUILDROOT>/dl/
目录,后
解压到<BUILDROOT>/build_dir/target-xxx/$(PKG_BUILD_DIR)
2、自己写的代码构建 Openwrt 软件包的方式是在<BUILDROOT>/package/helloworld/
目录下创
建一个子目录 src,用来保存自己写的源码文件,如下所示:
<BUILDROOT>/helloworld/src/
<BUILDROOT>/helloworld/src/helloworld.c
<BUILDROOT>/helloworld/src/Makefile
这种方式没有任何源码包需要从 Web 下载,这里的<BUILDROOT>/helloworld/src/Makefile
和通常的 GNU Makefile语法一样
Makefile
每个包都包含一个Makefile,内容也大致相同,首先导入几个文件进来,它们定义了一些变量、规则、函数
include $(TOPDIR)/rules.mk
PKG_*
自定义软件包的一些信息
include $(INCLUDE_DIR)/package.mk
对 PKG_*
没有定义到的基本信息补充,以及其他mk文件的导入。最重要的是定义BuildPackage宏在 Makefile 的最后一行被引用。
include $(INCLUDE_DIR)/kernel.mk
定义内核模块时才需要
include $(INCLUDE_DIR)/cmake.mk
使用 cmake 构建软件时才需要
BuildPackage 变量
PKG_*
定义了软件包相关的一些基本信息,如下所示:
PKG_NAME
-包的名称,可以通过menuconfig和ipkg看到。 不要使用下划线。
PKG_VERSION
-下载的或者自己写的软件包的版本号(必须)。
PKG_RELEASE
-Makefile 的版本号。
PKG_LICENSE
-软件包的许可,可以在SPDX形式下获得。
PKG_LICENSE_FILES
-包含许可文本的文件。
PKG_BUILD_DIR
- 软件包编译目录, 默认为 $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
。
PKG_SOURCE
-源码的文件名。
PKG_SOURCE_URL
-源码包下载地址。
PKG_HASH
- 用于验证下载的源码包的完整性,MD5或SHA256。最好是MD5。
PKG_MD5SUM
- 用于验证下载的源码包的完整性。
PKG_CAT
-源代码文件的解压方法 (zcat, bzcat,unzip等)。
PKG_BUILD_DEPENDS
-定义需要在该软件包之前编译的软件包或者一些版本的依赖。和下面的DEPENDS 语法相同。
PKG_INSTALL
-将其设置为1将调用包的原始make install,并将前缀设置为PKG_INSTALL_DIR。
PKG_INSTALL_DIR
-make install拷贝编译的文件。
可选择的从版本控制系统(git,bzr,svn等)获取源码。
PKG_SOURCE_PROTO
-用来获取源码(git, svn等)的协议。
PKG_SOURCE_URL
-要从中获取的源存储库。URL方案必须与PKG_SOURCE_PROTO一致(例如:git://),但大多数VCS现在接受http://或https:// URL。
PKG_SOURCE_VERSION
-必须指定,commit id 哈希或SVN版本 检查。
PKG_SOURCE_DATE
-一个类似于2017-12-25的日期,将被用于生成的tar包的名称中。
PKG_MIRROR_HASH
-源存储库签出生成的tarball的SHA256校验和(以前命名为PKG_MIRROR_MD5SUM)。详情见下文。
PKG_SOURCE_SUBDIR
-where the temporary source checkout should be stored,默认为$(PKG_NAME)-$(PKG_VERSION)
BuildPackage defines
Package
描述软件包在 menuconfig 和 ipkg 中的条目(其中的 xxx要S与PKG_NAME一致, 即出现在 menuconfig 中的标签以及.config 中的CONFIG_PACKAGE_xxx=y)如下图:
左边
PKG_NAME
包名字,右边是如下说的TITLE
描述内容
.config 中的CONFIG_PACKAGE_xxx=y)
SECTION
- 软件包的类型如(net、utilities等)
CATEGORY
- 该软件包出现在 menuconfig 中的哪个菜单下,如果是第一次使用,则 menuconfig 中会新创建一个菜单,如图:
SUBMENU
- 该软件包出现在CATEGORY
目录下的子目录。
TITLE
- 对该软件包的一个简短描述,如第一张图右半部分。
MENU:=1
目录项使能,结合 Package/config
使用。
DESCRIPTION
-(已弃用)对包的长描述
URL
- 在哪里可以找到该软件包的源码包
MAINTAINER
-定义该软件包的维护者的联系方式
DEPENDS
- 定义需要在该软件包之前编译/安装的软件包,或者其他一些依赖条件,比如版本依赖等,多个依赖之间使用空格分隔,下面列出其具体语法:
DEPENDS:=+foo
当前软件包和 foo 都会出现在 menuconfig 中,当前软件包被选中时,其依赖的软件
包 foo 也被自动选中;如果依赖额软件包 foo 先前没有选中而当前软件包取消选中时,其依赖分软
件包 foo 也会被取消选中。DEPENDS:=foo
只有当其依赖的软件包 foo 被选中时,当前软件包才会出现在 menuconfig 中。DEPENDS:=@FOO
软件包依赖符号 CONFIG_FOO,只有当符号 CONFIG_FOO 被设置时,该软件包才会
出现在 menuconfig 中。通常用于依赖于某个特定的 Linux 版本或目标(比如 CONFIG_LINUX_3_10、
CONFIG_TARGET_ramips)。DEPENDS:=+FOO:bar
当前软件包和软件包 bar 都会出现在 menuconfig 中。如果符号 CONFIG_FOO 被
设置,则当前软件包依赖于软件包 bar,否则不依赖DEPENDS:=@FOO:bar
如果符号 CONFIG_FOO 被设置,则当前软件包依赖于软件包 bar,否则不依赖,
并且只有其依赖包 bar 被选中时,当前软件包才会出现在 menuconfig 中
define Package/krb5-libs
SECTION:=net
CATEGORY:=Network
DEPENDS:=+libncurses +libss +libcomerr
TITLE:=Kerberos 5 Shared Libraries
URL:=http://web.mit.edu/kerberos/
endef
define KernelPackage/$(PKG_NAME)
描述内核模块在 menuconfig 和 ipk 中的条目(在 menuconfig 中看到的标签为 kmod-xxx)
SUBMENU
– 该模块出现在 menuconfig 中的 Kernel modules
菜单下的哪个子菜单。
TITLE
– 对该模块的一个简短描述
FILES
– 模块名称(全路径),多个模块文件之间用空格分开
AUTOLOAD
– 自动加载,例如:AUTOLOAD:=$(call AutoLoad,40,timesync can)表示自动加载 timesync.ko和 can.ko,其加载的优先级为 40
Package/conffiles (可选)
安装包程序的配置文件列表,每行一个,不要缩进。
define Package/$(PKG_NAME)/conffiles
/etc/config/a
/etc/config/b
endef
Package/description
包的描述
define Package/$(PKG_NAME)/description
a.
b.
endef
Package/config(可选)
可配置选项,需要 BuildPackage defines
中加上 MENU:=1
。
可以在配置时改变编译参数以及代码中宏定义。
结合cmake,目录关系 makefile、Config.in、src/CMakeLists.txt、src/hello.h
示例:
include $(INCLUDE_DIR)/cmake.mk
define Package/$(PKG_NAME)/config
source "$(SOURCE)/Config.in"
endef
TARGET_CFLAGS += $(if $(CONFIG_HELLO_DEBUG), -ggdb3)
CMAKE_OPTIONS += $(if $(CONFIG_HELLO_DEBUG),-DCMAKE_BUILD_TYPE=Debug)
CMAKE_OPTIONS += $(if $(CONFIG_HELLO_MACRO),-DCONFIG_HELLO_MACRO=1)
Config.in
定义
if PACKAGE_helloworld
config HELLO_DEBUG
bool "Enable debug version build."
default n
help
This option enables assert and gdb symbol.
Disabled by default.
config HELLO_MACRO
bool "Enable hello macro."
default y
help
MACRO value, enabled by default.
endif
src/CMakeLists.txt
OPTION(CONFIG_HELLO_MACRO "Enable HELLO_MACRO" 0)
add_compile_definitions(CONFIG_HELLO_MACRO=${CONFIG_HELLO_MACRO})
message(STATUS "CONFIG_HELLO_MACRO = ${CONFIG_HELLO_MACRO}")
src/hello.h
#if CONFIG_HELLO_MACRO
#define CURRENT_MODEL "Bxxxxx"
#else
#define CURRENT_MODEL "AXXXXX"
#endif
Build/Prepare(可选)
定义一组指令,比如用于给源码打补丁,创建编译目录,拷贝自己的源码到编译目录。对于从网上下载源
码包一般不需要定义,对于从自己写的代码构建软件包,一般按如下方式定义:
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/ -rf
$(CP) ./files/* $(PKG_BUILD_DIR)/ -rf
endef
Build/Configure(可选)
对于需要通过执行./ configure
的软件包可以在这里自定义。可参考 package/libs/openssl/Makefile
,
或使用$(call Build/ configure /Default,)
来传递标准配置脚本的附加参数。
define Build/Configure
[ -f $(STAMP_CONFIGURED) ] || { \
rm -f $(PKG_BUILD_DIR)/*.so.* $(PKG_BUILD_DIR)/*.a; \
find $(PKG_BUILD_DIR) -name \*.o | xargs rm -f; \
}
(cd $(PKG_BUILD_DIR); \
./Configure $(OPENSSL_TARGET) \
--prefix=/usr \
--openssldir=/etc/ssl \
$(TARGET_CPPFLAGS) \
$(TARGET_LDFLAGS) -ldl \
-DOPENSSL_SMALL_FOOTPRINT \
$(OPENSSL_NO_CIPHERS) \
$(OPENSSL_OPTIONS) \
)
# XXX: OpenSSL "make depend" will look for installed headers before its own,
# so remove installed stuff first
-$(SUBMAKE) -j1 clean-staging
+$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) \
MAKEDEPPROG="$(TARGET_CROSS)gcc" \
OPENWRT_OPTIMIZATION_FLAGS="$(TARGET_CFLAGS)" \
$(OPENSSL_MAKEFLAGS) \
depend
endef
define Build/Configure
$(call Build/Configure/Default,--with-linux-headers=$(LINUX_DIR))
endef
Build/Compile(可选)
如何编译源码,一般不用定义。可参考 package/libs/openssl/Makefile。
如果你想传递一些特殊的参数,例如$(call Build/Compile/Default,FOO=bar)
但对于内核模块需要这样定义
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
$(EXTRA_KCONFIG) \
modules
endef
Build/Install(可选)
如何安装编译后的源代码。默认是调用“make install”。同样,要传递特殊参数或目标,使用 $(调用Build/Install/Default, Install Install -foo)
。注意,您需要将所有必要的make参数放在这里。如果只需要在“install”参数中添加一些内容,不要忘记“install”本身。
Build/InstallDev(可选)
编译包需要的东西(静态库、头文件)。
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include
$(CP) $(PKG_INSTALL_DIR)/usr/include/openssl $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_INSTALL_DIR)/usr/lib/lib{crypto,ssl}.{a,so*} $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/{openssl,libcrypto,libssl}.pc $(1)/usr/lib/pkgconfig/
[ -n "$(TARGET_LDFLAGS)" ] && $(SED) 's#$(TARGET_LDFLAGS)##g' $(1)/usr/lib/pkgconfig/{openssl,libcrypto,libssl}.pc || true
endef
Build/Clean(可选)
make clean 中 过程中需要清除的东西。
Package/install
如何安装软件包。定义了一组命令,用来在嵌入式文件系统中创建目录或者拷贝相关文件到嵌入式文件系统或者 ipk。例如:
define Package/helloworld/install
echo "Here is Package/install"
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef
这里的$(1)
表示嵌入式根文件系统 rootfs 的根目录
$(INSTALL_DIR) $(1)/bin
表示在嵌入式根文件系统中创建目录 bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin
/表示将可执行程序$(PKG_BUILD_DIR)/helloworld
拷贝到嵌入式根文件系统的 bin 目录下
INSTALL_XXX
在<BUILDROOT>/rules.mk
中定义
INSTALL_DIR
用来安装目录,安装的目录权限为所属用户可读/写/执行,其他用户可读/执行。
INSTALL_DATA
用来安装数据文件,其权限为所属用户可读/写,其他用户可读
INSTALL_CONF
用来安装配置文件,其权限为所属用户可读/写,其他用户不能读/写/执行。
Package/preinst
定义一段需要在软件包安装前执行的脚本。不要忘记包含#!/bin/sh
。如果需要中止安装,返回false。
define Package/helloworld/preinst
#!/bin/sh
echo 'before install $(PKG_NAME)'
endef
Package/postinst
定义一段需要在软件包安装后执行的脚本。不要忘记包含#!/bin/sh
,可以使用 uci-default脚本。
Package/prerm
定义一段需要在软件包删除前执行的脚本。不要忘记包含#!/bin/sh
,如果需要中止删除,返回false。
Package/postrm
定义一段需要在软件包删除后执行的脚本。不要忘记包含#!/bin/sh
,
注意:这些脚本必须可以同时在嵌入式设备和主机上都能执行。
其中一些定义以“Package/”作为前缀,而另一些则简单地以“Build”作为前缀,这是因为可以从一个源生成多个包。OpenWrt在每个包Makefile都有一个源文件的假设下工作,但是您可以根据需要将该源文件拆分为任意多个包。因为你只需要编译一次源文件,所以有一个全局的“Build”定义集,但是你可以通过添加额外的BuildPackage调用来添加任意多的“Package/”定义-看看dropbear包的例子。
最后需要调用$(eval $(call BuildPackage,$(PKG_NAME)$(PKG_NAME)))
或者$(eval $(call KernelPackage,$(PKG_NAME)))
,前者用于应用程序,后者用于内核模块,如果有多个软件包需要针对每个软件包调用。
helloworld实例
hellokernel实例