在OpenWrt中添加package

Add new package to Openwrt

主要记录笔者学习官方教程"Hello, world!" package for OpenWrt的经历,并对在OpenWrt下加包的流程进行梳理。

  在开始之前我们先约定开发环境:

  • 开发的主目录为/home/buildbot
  • 使用的OpenWrt版本为v23.05.3
  • WSL2+Ubuntu20.04
  • gcc的版本是9.4.0

1. Preparing your OpenWrt build system for use

公司的网络可以直接连到OpenWrt官网并提供可观的下载速度,因此我们可以直接使用以下命令下载源码:

git clone https://git.openwrt.org/openwrt/openwrt.git source

这会把代码下载到source文件夹下,即/home/buildbot/source。此外在GitHub上也有OpenWrt的镜像。
进入source目录,切换到我们需要的分支上:

cd /home/buildbot/source
git checkout v23.05.3
make distclean

此时推荐更新我们的feeds包(在OpenWrtfeed是一组共享相同位置的包的集合。更近一步的信息参考OpenWrt Feeds),更新的命令如下:

./scripts/feeds update -a
./scripts/feeds install -a

可用的feed列表在feeds.conffeeds.conf.default下进行配置,默认的情况下feeds.conf.defaultopenwrt目录下,也就是本文中的source。我们可以看一下的它的内容:

src-git packages https://git.openwrt.org/feed/packages.git^063b2393cbc3e5aab9d2b40b2911cab1c3967c59
src-git luci https://git.openwrt.org/project/luci.git^b07cf9dcfc37e021e5619a41c847e63afbd5d34a
src-git routing https://git.openwrt.org/feed/routing.git^648753932d5a7deff7f2bdb33c000018a709ad84
src-git telephony https://git.openwrt.org/feed/telephony.git^86af194d03592121f5321474ec9918dd109d3057

由于某些原因对这些网址的访问将会变得艰难(是的,就是那个原因),因此在自己电脑上运行的时候可以考虑把网址修改一下,比如去gitee看看有没有好心人搬运,或者干脆自己动手。但好在公司的网络依然给力。feeds.conf可以由我们自己创建,我们自己写的包就可以放在这里。

接下来配置我们的交叉编译链:

make menuconfig

在菜单里我们可以选择Target System(目标架构),Subtarget(更细化的架构)和Target Profile(一些配置)。教程作者选用了Lantiq、XRX200、P2812HNU-F1,但是笔者什么也没有。选择结束之后保存你的配置并且退出,此时他会让你给配置文件命名。这里的建议是直接采用默认的.config,不然还要连带着修改很多别的地方。

以上结束之后开始编译我们的工具链:

make toolchain/install

按照教程作者的说法这个时候你可以去喝杯咖啡,但实际操作中耗时远比想象中更长。主要的时间浪费在下载Linux5.15的内核,在这一点上公司的网络也没有太大帮助。建议上午开始做这件事情,不然就等着拿餐补吧。

工具链会被放在staging_dir/host/staging_dir/toolchain/下面,所有一个建议是把他们添加到环境变量里:

export PATH=/home/buildbot/source/staging_dir/host/bin:$PATH

以上。

2. Creating a simple "Hello,world!" application

  出于SoC(Separation of Concerns)的目的我们单开一个文件夹写代码:

cd /home/buildbot
mkdir helloworld
cd helloworld
touch helloworld.c

文档的代码如下,当然你可以不按照它的写:

#include <stdio.h>
 
int main(void)
{
    printf("\nHello, world!\n\n");
    return 0;
}

接下来就是运行一下你的代码看看有没有问题,注意到这里还是使用本地的编译工具。此外编译选项建议加上-Wall-Werror(多加点小心总归是好事),对了,结束之后记得把这些生成文件删除掉,包代码里面不需要这些东西。

3. Creatint a package from your application

  接下里为我们的包创建feed

cd /home/buildbot
mkdir -p mypackages/examples/helloworld

OpenWrt构建系统中的每一个包都由一个包清单文件(package mnanifest file)描述,包清单文件描述了包的功能,源码位置,编译方式以及最终安装包中含有的文件,并可能包括一些配置脚本。所以接下来我们创建这个清单文件:

cd home/buildbot/mypackages/examples/helloworld
touch Makefile

一个可参考的Makefile如下:

include $(TOPDIR)/rules.mk

# Name, version and release number
# The name and version of your package are used to define 
# the variable to point to the build directory of your package : $(PKG_BUILD_DIR)
PKG_NAME:=helloworld
PKG_VERSION:=1.0
PKG_RELEASE:=1

# Source settings (i.e. where to find the source codes)
# This is a custom variable, used below
SOURCE_DIR:=/home/wjm/projects/buildbot/helloworld

include $(INCLUDE_DIR)/package.mk

# Package definition; instructs on how and where our package will appear in the overall
# configuration menu ('make menuconfig')
define Package/helloworld
	SECTION:=examples
	CATEGORY:=Examples
	TITLE:=Hello,World!
endef

# Package description; a more verbose description on what our package does
define Package/helloworld/description
	A simple "Hello, world!" -application.
endef

# Package preparation instructions; create the build directory and copy the source code.
# The last command is necessary to ensure our preparation instructions remain compatible
# with the patching system.
define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	cp $(SOURCE_DIR)/* $(PKG_BUILD_DIR) -r
	$(Build/Patch)
endef

# Package build instructions; invoke the target-specific compiler to first compile the source file,
# and then to link the file into the final executable
# define Build/Compile
# 	$(TARGET_CC) $(TARGET_CFLAGS) -o $(PKG_BUILD_DIR)/helloworld.o -c $(PKG_BUILD_DIR)/helloworld.c
# 	$(TARGET_CC) $(TARGET_LDFLAGS) -o $(PKG_BUILD_DIR)/$1 $(PKG_BUILD_DIR)/helloworld.o
# endef

# Package build instructions; invoke the GNU make tool to build our package
define Build/Compile
	$(MAKE) -C $(PKG_BUILD_DIR) \
	CC="$(TARGET_CC)" \
	CFLAGS="$(TARGET_CFLAGS)" \
	LDFLAGS="$(TARGET_LDFLAGS)"
endef

# Package install instructions; create a directory inside the package to hold our executable,
# and then copy the executable we built previously into the folder
define Package/helloworld/install
	$(INSTALL_DIR) $(1)/usr/bin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/usr/bin
endef

# This command is always the last, it uses the definitions and variables we give above in order to
# get the job done
$(eval $(call BuildPackage,helloworld))

注意到这里的cp指令其实比教程中多了一个-r,考虑到实际的包往往结构复杂,所以这里未雨绸缪一下(单押成功)。关于Build/Compile,教程提供的是注释掉的版本,在后面会解释为什么会做这样的修改。此外make对空格极不友好,比如CC="$(TARGET_CC)"中在赋值号左右加上空格就会报错,因为它会把这些理解成选项。

4. Inculding your package feed into OpenWrt build system

OpenWrtfeeds.conf来表示在固件配置阶段提供的feeds,那么为了把我们自己的feed包括进去,我们需要创建feeds.conf

cd /home/buildbot/source
touch feeds.conf

接下来我们修改feeds.conf来指定本地的package feed

src-link mypackages /home/buildbot/mypackages

既然加入了新的feed,那么当然要更新一下啦:

cd /home/buildbot/source
./scripts/feeds update mypackages
./scripts/feeds install -a -p mypackages

feeds系统会自动检测清单文件内的变化,并在需要时进行更新。

5. Building, deploying and testing your applicatin

  为了将我们的包集成进来,运行make menuconfig并选中我们的包。在离开菜单之后运行下述命令编译我们的包:

make package/helloworld/compile

如果一切顺利,那我们将在bin/package/<arch>/mypackages文件夹下看到helloworld_1.0-1_<arch>.ipk

之后是部署我们的包,因为笔者手头并没有合适的设备,所以我们略过这里...

6. Migrating to use GNU make in your application

  考虑到实际包的代码不会像我们的helloworld一样简单,所以把它的编译方式硬编码到清单文件中其实并不合适,所以我们会在源码中额外提供一个Makefile

cd /home/buildbot/helloworld
touch Makefile

一个可参考的Makefile如下:

# Global target; when 'make' si run without arguments, this is what it should do
all:helloworld

# These variables hold the name of the compilation tool, the compilation flags and the link flags
# We make use of these variables in the package manifest
CC = gcc
CFLAGS = -Wall
LDFLAGS = 

# This variable identifies all header files in the directory; we use it to create a dependency chain 
# between the object files and the source files
# This approach will re-build your application whenever any header file changes. In a more complex application,
# such behavior is often undesirable
DEPS = $(wildcard *.h)

# This variable holds all source files to consider for the build; we use a wildcard to pick all files 
SRC = $(wildcard *.c)

# This variable holds all object file names, constructed from the source file names using pattern substitution
OBJ = $(patsubst %.c, %.o, $(SRC))

# This rule builds individual object files, and depends on the corresponding C source files and the header files
%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)

# To build 'helloworld', we depend on the object files, and link them all into a single executable using the 
# compilation tool
# We use automatic variables to specify the final executable name 'helloworld', using '$@' and the '$^' will hold 
# the names of all the dependencies of this rule
helloworld: $(OBJ)
	$(CC) -o $@ $^ $(LDFLAGS)

# To clean build artifacts, we specify a 'clean; rule, and use PHONY to indicate that this rule never matches with 
# a potential file in the directory
.PHONY: clean
clean:
	rm -f helloworld *.o

接可以跑一下make看看有没有问题(再次提醒没有把握的不要乱加空格),一切安好的话可以更新一下清单文件,也就是我们第三部分提供的Makefile(如果你一开始用的就是更新过的Makefile,那就什么都不用做)。

接下来测试一下我们的包:

cd /home/buildbot/source
make package/helloworld/{clean,compile}

如果这一步遇到问题的话可能有两种情况:

  • 更新feeds
      cd /home/buildbot/source
      ./scripts/feeds update mypackages
      ./scripts/feeds install -a -p mypackages
    
  • 记得删除源码目录下的目标文件和可执行文件。

7. Patching your application: Adding new files

  考虑到程序的修改需求,OpenWrt支持patch,完成这项任务的工具是Quilt。关于Quilt可以参考这篇文档Working with patches,在开始后续操作之前最好先配置一下。

还记得第一部分中添加的环境变量吗?如果你做了这件事你会发现在bin下面存在一个quilt,但是在笔者的操作中会发现这个quilt的功能并不完善,所以笔者还是建议sudo一下(sudo,启动!)。

cd /home/buildbot/source/
make package/helloworld/{clean,prepare} QUILT=1
cd build_dir/target-.../helloworld-1.0/
quilt push -a

在这里quilt报错是很正常的事情,因为我们还没有patch。稳住。

quilt new 100-add_module_files.patch

patch的名字来源于OpenWrt的惯例:一个可能含有某些意义的数字+一段简短的描述。这个命令的输出将告诉你patch被创建并且处于patch stack的顶端。

接下来是我们patch的源码:

quilt add functions.c
quilt add functions.h

touch functions.c
quilt edit functions.c

touch functions.h
quilt functions.h

具体的内容如下:

//functions.c
int add(int a, int b)
{
    return a + b;
}
//functions.h
int add(int, int);

如果你参考了quilt文档的默认配置,那使用的编辑器应该是nano。写完代码之后用Ctrl+o保存,Enter确定文件名,Ctrl+x退出。

可以使用quilt diff 查看前后的差别,如果确认修改正确,使用quilt refresh接受。

OpenWrt中,patch在源码路径创建,然后迁移到所属的package中。为了迁移我们的patch,执行如下的命令:

cd /home/buildbot/source
make package/helloworld/update

为了确保我们的patch被正确应用了:

cd /home/buildbot/source
make package/helloworld/{clean,prepare}
ls -la build_dir/target-<arch>_<subarch>_<clib>_<clibversion>/

通过上述命令我们可以看到我们的patch被应用了,并且新的文件在构建目录中。

8. Patching your application: Editing existing files

  因为我们不是总需要创建新的文件,很多时候只是在之前的文件上修修补补,所以有了这么一节 😃。这一节我们的目的是修改helloworld.c,首先创建一个新的patch

cd /home/buildbot/source
make package/helloworld/{clean,prepare} QUILT=1
cd build_dir/target-.../helloworld-1.0/
quilt push -a
quilt new 101-use_module.patch

因为只是修改,所以:

quilt edit helloworld.c

新的代码如下:

#include <stdio.h>
#include "functions.h"
 
int main(void)
{
    int result = add(2, 3);
 
    printf("\nHello, world!\nThe sum is '%d'", result);
    return 0;     
}

修改之后别忘了quilt diffquilt refresh

最后,更新我们的包包:

cd /home/buildbot/source
make package/helloworld/update

完结撒花!!!

posted @ 2024-08-02 17:13  邵玄  阅读(157)  评论(0编辑  收藏  举报