OpenWrt软件包创建
OpenWrt官方Wiki

相关知识


https://lingxiankong.github.io/2014-01-06-linux-install.html
Openwrt软件源码包的结构
- Makefile(必选)
- 提供了下载、编译、安装的步骤
- patches(可选)
- 补丁文件,用于对源码打补丁
- files(可选)
- 配置文件目录
Makefile语法
引入文件
OpenWrt使用三个Makefile的子文件,分别为:
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
由这些Makefile子文件确定软件包加入OpenWrt的方式和方法。$(TOPDIR)/rules.mk一般在Makefile的开头,$(INCLUDE_DIR)/kernel.mk文件对于软件包为内核时是不可缺少的,$(INCLUDE_DIR)/package.mk一般在软件包的基本信息完成后再引入。
示例Makefile
1 | include $(TOPDIR)/rules.mk |
这是最为关键的BuildPackage宏。它是在**$(INCLUDE_DIR)/package.mk文件里定义的。BuildPackage宏只要求一个参数,即要编译的软件包名**,在本例中是“bridge”。所有其他信息都通过宏来获得,这提供了一种内在的简洁性。比如BuildPackage需要软件包的一大串描述信息,我们并不要向它传递冗长的参数,因为我们已经约定描述信息定义在DESCRIPTION宏,BuildPackage从里面读取就可以了。
软件包信息
软件包的信息均以PKG_开头,其意思和作用如下:
- PKG_NAME -软件包的名字, 在 menuconfig 和 ipkg 显示
- PKG_VERSION -软件包的版本,主干分支的版本正是我们要下载的
- PKG_RELEASE -这个 makefile 的版本
- PKG_BUILD_DIR -编译软件包的目录
- PKG_SOURCE -要下载的软件包的名字,一般是由 PKG_NAME 和 PKG_VERSION 组成
- PKG_SOURCE_URL -下载这个软件包的链接
- PKG_MD5SUM -软件包的 MD5 值
- PKG_CAT -解压软件包的方法 (zcat, bzcat, unzip)
- PKG_BUILD_DEPENDS -需要预先构建的软件包,但只是在构建本软件包时,而不是运行的时候。它的语法和下面的DEPENDS一样。
PKG_*变量定义了从何处下载这个软件包;@SF是表示从sourceforge网站下载的一个特殊关键字。md5sum用来检查从网上下载的软件包是否完好无损。PKG_BUILD_DIR定义了软件包源代码的解压路径。
BuildPackage相关的宏
应用程序的编译包以Package/开头。然后接着软件名,在Package定义中的软件名可以与软件包名不一样,而且可以多个定义。下面使用$(PKG_NAME)只是做一个标志,并非真正使用$(PKG_NAME),如Package/$(PKG_NAME)。
- SECTION 表示包的类型,预留。
- CATRGORY 表示分类,在make menuconfig的菜单下将可以找到。
- TITLE 用于软件包的简短描述。
- DESCRIPTION 用于软件包的详细描述,已放弃使用。如果使用DESCRIPTION将会提示“error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description”。
- URL 表示软件包的下载位置。
- MAINTAIER 表示维护者,选项。
- DEPENDS 表示与其他软件的依赖。运行本软件依赖的其他包。如果存在多个依赖,则每个依赖需要用空格分开。依赖前使用+号表示默认为显示,即对象没有选中时也会显示,使用@则默认为不显示,即当依赖对象选中后才显示。
在用户空间的应用程序软件包中没有内核驱动模块的AUTOLOAD参数。如果应用软件需要在boot时自动运行,则需要在/etc/init.d中增加相应的脚本文件。脚本文件需要START参数,说明在boot时的优先级,如果在boot过程启动后再关闭,则需要进一步设置STOP参数。如果STOP参数存在,其值必须大于START。脚本文件需要start()和stop()两个函数,start()是执行程序,stop()是关闭程序。关闭程序一般需要执行killall命令。由/etc/rc.d/S10boot知道,装载内核驱动模块的优先级为10,需要使用自己设计的内核驱动模块的程序其START的值必须大于10。同样由/etc/rc.d/S40network知道,使用网络通信的程序其START的值必须大于40。
- Package/$(PKG_NAME)/conffiles 本包安装的配置文件,一行一个。如果文件结尾使用/,则表示为目录。用于备份配置文件说明,在sysupgrade命令执行时将会用到。
- Package/$(PKG_NAME)/description 软件包的详细描述,取代前面提到的DESCRIPTION详细描述。
- Build/Prepare 编译准备方法,对于网上下载的软件包不需要再描述。对于非网上下载或自行开发的软件包必须说明编译准备方法。一般的准备方法为:
1 | define Build/Prepare |
按OpenWrt的习惯,一般把自己设计的程序全部在src目录下。
- Build/Compile 编译方法,没有特别说明的可以不予以定义。如果不定义将默认使用编译方法Build/Compile/Default。
自行开发的软件包可以考虑使用下面的定义。
1 | define Build/Compile |
- Package/$(PKG_NAME)/install 软件包的安装方法,包括一系列拷贝编译好的文件到指定位置。调用时会带一个参数,就是嵌入式系统的镜像文件系统目录,因此**$(1)表示嵌入式系统的镜像目录**。一般可以采用下面的方法:
1 | define Package/$(PKG_NAME)/install |
INSTALL_DIR、INSTALL_BIN在$(TOPDIR)/rules.mk文件定义,所以本Makefile必须引入$(TOPDIR)/rules.mk文件。
INSTALL_DIR:=install -d -m0755 意思是创建所属用户可读写和执行,其他用户可读可执行的目录。
INSTALL_BIN:=install -m0755 意思是编译好的文件存放到镜像文件目录。
如果用户空间的应用软件在boot时要自动运行,则需要在安装方法说明中增加自动运行的脚本文件安装和配置文件安装方法。
例如:
1 | define Package/mountd/install |
安装文件放在files子目录下,不要与源代码文件目录src混在一起,以提高可读性。使用清晰的文件扩展名,更方便安装识别文件。
- Package/$(PKG_NAME)/preinst 软件包安装前处理方法,使用脚本语言,因此定义的第一行需要下面的格式
#!/bin/sh
调用时带入的参数为嵌入式系统的镜像目录。 - Package/$(PKG_NAME)/postinst 软件包安装后处理方法,使用脚本语言。
- Package/$(PKG_NAME)/prerm 软件包删除前处理方法,使用脚本语言。
- Package/$(PKG_NAME)/postrm 软件包删除后处理方法,使用脚本语言。
注意:对于所有在pre/post, install/removal脚本中使用的变量,都应该使用”$$“代替”$“。这是告诉make暂时不要解析这个变量,而是把它当成普通字符串以及用”$“代替”$$“
内核驱动模块包定义
Linux分为内核空间和用户空间。开发者开发的内核部分可以直接加入Linux的Kernel程序,也可以生成内核模块以便需要时装入内核。OpenWrt一般希望开发者生成内核模块,在Linux启动后自动装载或手工使用insmod命令装载。内核模块使用KernelPackage开头,其他与一般应用软件包基本相同。
在内核驱动模块定义中增加了:
- SUBMENU 表示子菜单位置,在$(INCLUDE)/kernel.mk对内核模块定义了CATEGORY为kernel modules,所以内核模块在menuconfig中的主菜单为kernel modules,然后有下一级子菜单$(SUBMENU)。在子菜单下可以看到以kmod-$(PKG_NAME)项目。
- DEFAULT 表示直接编入内核或产生内核模块,y表示直接编入内核,m表示产生内核模块。
- AUTOLOAD 表示自动装入内核,一般表示方法为:
AUTOLOAD:=$(call AutoLoad, $(PRIORITY),$(AUTOLOAD_MODS))
AutoLoad的第一个参数$(PRIORITY)为优先级,01为最优先,99为最后装载。有关自动装载可以在/etc/modules.d目录下看到,第二个参数$(AUTOLOAD_MODS)模块名,每个模块名以空格符分隔。即可同时装载多个内核模块。
在开发过程最好不要使用自动装载,经过严格调试后再使用,可以减轻调试的工作量。
使用定义
完成前面定义后,必须使用eval函数实现各种定义。其格式为:
对于一般应用软件包
$(eval $(call Package, $(PKG_NAME)))
或对于内核驱动模块
$(eval $(call KernelPackage, $(PKG_NAME)))
如果一个软件包有多个程序,例如:一个应用程序有自己的内核驱动模块,上面使用PKG_NAME需要灵活变通。eval函数可以设计多个。也可以当成多个软件包处理。
参考引用
开机自启动配置
创建文件夹
1
2
3mkdir -p package/helloworld/files
cd package/helloworld/files
touch helloworld编写脚本
修改helloworld 目录下的Makefile