【WiFi软件开发】Makefile实战和WiFi工具移植记录


前言

近期做了一个linux平台上WiFi工具acfg_tool的移植,借此机会对makefile和编译流程有了进一步的学习和理解,在此记录和分享。


一、Makefile学习

1.基本规则

Makefile基本规则的样式如下:

目标(target)... : 依赖(prerequiries)... 
	命令(command)

例如下面这条规则指以test.c为依赖,以test为目标,执行编译命令gcc test.c -o test

test:test.c
	gcc test.c -o test

2.gcc

接下来看下从一个hello.c文件到可执行文件out的编译流程:

  • C 源文件: hello.c
  • 预处理:生成预处理后的C源文件 hello.i
  • 编译:将C源文件翻译成汇编文件 hello.s
  • 汇编:将汇编文件汇编成目标文件 hello.o
  • 链接:将目标文件链接成可执行文件 out

makefile中如何实现上述过程呢?
gcc命令是GCC编译器里的一个前端程序,用来控制整个编译过程:分别调用预处理器、编译器和汇编器,完成编译的每一个过程,最后调用链接器,生成可执行文件。默认情况下,gcc命令会自动完成上述的整个编译过程。当然,gcc还提供了一系列参数,使用这些参数,可以让用户精准控制每一个编译过程。

-E :只做预处理,不编译
-S :只编译,将C程序编译为汇编文件
-c :只汇编,不链接。
-o :指定输出的文件名
-C :指定包含宏定义或其他预处理指令的头文件:gcc -C myheader.h -o myheader.i
-L :指定库文件的搜索路径
-l :指定要链接的库名称:gcc -L/usr/local/lib -o myprogram main.o -lmystaticlibrary
-D :定义一个宏:gcc -DDEBUG=1 -o myprogram main.c
-I :指定头文件的搜索路径:gcc -I/usr/local/include -o myprogram main.c

注:除了用-C参数指定头文件,makefile中.h文件也可以放在依赖处:

main: main.c main.h 
      gcc main.c -o main 

3.ar和静态库

在Makefile中,ar指令用于创建静态库(.a文件)。同样ar也提供了众多参数对创建静态库的过程进行控制:

参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
参数c:创建一个库。不管库是否存在,都将创建。
参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果.a文件缺少索引,可以使用ranlib命令添加)。
参数q:在文件包尾加入文件。
参数v:将文件插入库文件中。

下面给出两个创建静态库的示例:
示例1:

1、编译源代码生成.o 文件:		gcc -c add.c -o add.o
2、制作静态库:				ar rcs libname.a file1.o file2.o …
3、编译静态库到可执行文件中:	gcc test.c libname.a -o test

示例2:

  有源文件test.c和test.h:
  1、编译test.c生成目标文件:					gcc -o test.o -c test.c;
  2、将目标文件加入到静态库中:					ar rcv libmytest.a test.o;
  3、连接(如果库不在默认路径,需要用-L表明路径):	gcc -o test test.c -lmytest

4. $< $@ $^

  • $@ 表示目标文件
  • $^ 表示所有的依赖文件
  • $< 表示第一个依赖文件
  • $? 表示比目标还要新的依赖文件列表

示例:

链接文件main.o,stack.o,maze.o生成main:
main: main.o stack.o maze.o
	gcc $^ -o $@

编译所有.c文件生成对应.o文件:
%.o : %.c
    gcc -c $< -o $@

5.动态库

在GCC编译器中,-shared 选项用于生成共享库(动态库,.so文件)。

示例1:

gcc test_a.c test_b.c test_c.c -shared -o libtest.so

示例2:

1、将.c 文件生成.o 文件 (生成与位置无关的代码 -fPLC):	gcc -c add.c -o add.o -fPIC
2、使用 gcc -shared 制作动态库:						gcc -shared -o libmymath.so add.o sub.o div.o
3、编译可执行程序时指定所使用的动态库:					gcc test.c -o a.out -l mymath -L ./lib

静态库和动态库的区别:

  • 静态库: 编译到可执行程序中的库
  • 动态库: 在程序运行时被加载到内存中,以文件形式独立存在

6.make的执行

make命令会默认查找目录下名称为Makefile的文件进行执行,如果想执行特定的Makefile文件,可用命令:

make -f 文件名  目标

若没有指定make的目标,make的默认行为是执行Makefile的第一个target,该目标一般会带起多个目标的执行。目标中有=,或者-打头的不能指定为终极目标,因为这些字符的目标会被解析成命令行参数或者变量。

make的常用参数:

-n 打印命令,不执行
-q 寻找目标,找到什么都不输出,找不到打印错误error
-B 认为所有目标都需要重编译
-C <dir> 指定读取Makefile的目录,如果有多个“-C”参数,make 的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。
比如“make -C ~hchen/test -C prog”等价于“make -C ~hchen/test/prog”。
-I <dir> 指定一个被包含Makefile的搜索目标。可以使用多个“-I”参数来指定多个目录
-t 更新target的时间,组织生成目标的命令运行

示例1:以all为目标带动多个目标执行

在Makefile中,'all’通常被定义为默认目标。这意味着如果用户在命令行中不指定任何目标,那么’all’将被视为默认的构建目标。
例如,如果Makefile包含以下内容:

all: myprogram
myprogram: main.o myfunctions.o
    gcc -o $@ $^
main.o: main.c
    gcc -c -o $@ $<
myfunctions.o: myfunctions.c
    gcc -c -o $@ $<
clean:
    rm -f myprogram main.o myfunctions.o

当用户在命令行中运行’make’时,Makefile会首先检查’all’ 目标是否是最新的。由于’all’依赖于’myprogram’,Makefile会查看’myprogram’是否是最新的。由于’myprogram’依赖于’main.o’和’myfunctions.o’,Makefile会检查这些文件是否存在且是最新的。如果这些文件中的任何一个不是最新的,或者根本不存在,Makefile将执行相应的规则来生成或更新它们。最后,Makefile将链接’main.o’和’myfunctions.o’生成最终的可执行文件’myprogram’。
通过’all设置为默认目标,可以简化构建过程,只需运行’make’命令即可编译和链接程序。如果需要清理构建产物,可以运’make clean’命令。

示例2:模式规则

在Makefile中,%.o: %.c被称为模式规则(pattern rule),而不是一个普通的目标,因为它使用模式(pattern)来匹配多个可能的源文件和目标文件,而不是指定单一的具体文件。这种规则允许你编写一组通用的指令,这些指令可以应用于以特定模式命名的所有文件。

%.o: %.c
	$(CC) -c $(CFLAGS) $(LDFLAGS) $(COPTS) $< -o $@ $(Bx86_LIB)

all: $(OBJS)
	$(CC) -shared -o libacfg.so $(CFLAGS) -z relro $(OBJS)

上述代码中,尽管all在%.o后面,默认目标仍是all,%.o不是目标。

示例3:伪目标

.PHONY: clean
clean:
        rm a.txt

执行make clean命令,发现即使存在clean文件,最终还是执行了Makefile中的rm指令。声明clean是"伪目标"之后,make就不会去检查是否存在一个叫做clean的文件,而是每次运行都执行对应的命令。

二、acfg_tool代码结构和编译流程

1.工具介绍

acfg_tool是高通的一个用户态WiFi应用,它提供了一系列用户态接口用于配置驱动。它支持命令和文件两种方式进行WiFi配置,同时也支持netlinkioctl两种方式向驱动发送消息。

2.目录结构

acfg_tool代码结构如下所示:

[qca-acfg]$ tree
.
├── acfg_app_cmn
│   ├── acfg_config_file.c
│   ├── acfg_config_file.h
│   ├── acfg_tool.h
│   └── acfg_tool_util.c
├── acfg_cli.c
├── acfg_fw_rec
│   ├── acfg_fw_rec_app.c
│   ├── acfg_fw_rec.c
│   └── Makefile
├── acfg_profile_app
│   ├── acfg_profile_tool.c
│   └── Makefile
├── acfg_test_app
│   ├── acfg_sample.conf
│   ├── acfg_test.c
│   ├── acfg_tool_fn.c
│   └── Makefile
├── acfg_wsupp_event.h
├── acfg_wsupp.h
├── acfg_wsupp_if.h
├── acfg_wsupp_nw.h
├── Doxyfile
├── include
│   ├── acfg_api_cmds.h
│   ├── acfg_api.h
│   ├── acfg_misc.h
│   ├── acfg_types.h
│   ├── acfg_wsupp_types.h
│   ├── appbr_if.h
│   └── linux
│       └── ac_shims.h
├── LicenseChoice.txt
├── Makefile
├── NOTICE
├── Notice.txt
└── src
    ├── acfg_api_cmds.c
    ├── acfg_api_event.c
    ├── acfg_api_event.h
    ├── acfg_api_pvt.c
    ├── acfg_api_pvt.h
    ├── acfg_br.c
    ├── acfg_br.h
    ├── acfg_common.conf
    ├── acfg_config.c
    ├── acfg_ctrl.c
    ├── acfg_log.c
    ├── acfg_log.h
    ├── acfg_security.c
    ├── acfg_security.h
    ├── acfg_wireless.h
    ├── acfg_wsupp_api_pvt.h
    ├── appbr_if.c
    └── Makefile

7 directories, 48 files

其中,src下主要包含acfg_tool中需要的接口,编译后会生成一个.so动态库,acfg_profile_app和acfg_test_app下的代码会分别生成bin文件:acfg_set_profile和acfg_tool,命令行也是主要通过这两个工具对无线驱动进行配置。

3.编译过程和输出

首先在qca-acfg的前一级目录的Makefile中,编译目标添加了qca-acfg/src路径,且all目标会依次执行makemake installromfs命令。

obj-y   += applications/app/qca-acfg/src
all:
	mkdir -p $(TARGET_ROMFS_PATH)/etc/wlan
	for dir in $(obj-y) ; do $(MAKE) -C $$dir || exit 1 ; done
	for dir in $(obj-y) ; do $(MAKE) -C $$dir installromfs || exit 1 ; done

qca-acfg的编译入口为src目录下的Makefile文件(并非qcs-acfg根目录下的Makefileqcs-acfg根目录下的Makefile.c文件不参与编译):

all: $(OBJS)
	...
	$(CC) -shared -o libacfg.so $(CFLAGS) -z relro $(OBJS)
	$(MAKE) -C ../acfg_profile_app
	$(MAKE) -C ../acfg_test_app

编译生成动态库libacfg.so后,分别进入acfg_profile_appacfg_test_app目录进行编译,acfg_set_profileacfg_tool会分别依赖动态库libacfg.so完成编译并生成。

LDFLAGS += -L$(PWD)/../src -lacfg

qca-acfg/acfg_profile_app/目录下:
all:
	@echo -e "\nBuilding ACFG PROFILE APP..."
	$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) $(COPTS) -o $(ACFG_PROFILE_BINARY)

qca-acfg/acfg_test_app/目录下:
all:
	@echo -e "\nBuilding ACFG TESTAPP..."
	$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) $(COPTS) -o $(ACFG_BINARY)  $(Bx86_LIB)

回到src目录下的Makefile文件,完成src路径下的all目标后,会执行make installromfs,将生成的动态库和bin文件复制到对应目录中,完成安装。

installromfs:
	...
	-cp -v $(ACFG_TEST_DIR)/$(ACFG_BINARY) $(TARGET_ROMFS_PATH)/sbin
	-cp -v $(ACFG_TEST_DIR)/$(ACFG_CONF_FILE) $(TARGET_ROMFS_PATH)/etc/wlan
	-cp -v acfg_common.conf $(TARGET_ROMFS_PATH)/etc/wlan
	-cp -v $(ACFG_PROFILE_TEST_DIR)/$(ACFG_PROFILE_BINARY) $(TARGET_ROMFS_PATH)/sbin
	-cp -v libacfg.so $(TARGET_ROMFS_PATH)/lib

综上,qca-acfg完成编译,共生成一个动态库libacfg.so和两个bin文件acfg_set_profile、acfg_tool


总结

以上就是这次WiFi工具移植过程中对makefile学习的总结,欢迎指点交流学习。

学习链接:
https://zhuanlan.zhihu.com/p/648106991
https://zhuanlan.zhihu.com/p/654905927
https://blog.csdn.net/2301_77312300/article/details/131502680
https://blog.csdn.net/weixin_44617175/article/details/130873856
https://blog.csdn.net/pingxiaozhao/article/details/122378076
https://blog.csdn.net/tjcwt2011/article/details/127227905

bug记录:
https://blog.csdn.net/Liuwe1Ye/article/details/139814384

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值