Makefile整理代码

Makefile

Makefile 是一个包含一系列规则的文本文件,用于告诉构建工具 make 如何构建软件项目。它描述了项目中每个源文件之间的依赖关系,并指定了如何将源文件编译成目标文件,以及如何链接这些目标文件以生成最终的可执行文件或库文件。

包含内容

  • 变量定义: 定义了一系列变量,用于存储编译器、编译选项、目标文件名等信息。
  • 规则定义: 每个规则包含了目标文件、依赖文件和执行命令。Make 工具根据这些规则来判断哪些文件需要重新编译,哪些文件已经是最新的,无需重新编译。
  • 注释: Makefile 支持注释,用于对 Makefile 中的内容进行说明和解释。
  • 内置函数和指令: Makefile 中可以使用一些内置函数和指令来处理变量、执行命令、进行条件判断等操作,这些函数和指令提供了丰富的功能,可以方便地编写复杂的构建规则。

基本语法

target: dependencies
command
  • target 要生成的目标文件的名称。
  • dependencies 目标文件依赖的文件列表
  • command 生成目标文件的命令

变量定义

CC = gcc 
CFLAGS = -Wall
target: dependencies
$(CC) $(CFLAGS) -o target dependencies

通配符

sources = $(wildcard *.c)
objects = $(sources:.c=.o)
target: $(objects)
$(CC) $(CFLAGS) -o target $(objects)

伪目标

.PHONY: clean
clean:
rm -f target *.o
  • .PHONY: 表示伪目标,不是真正的文件,告诉 Make 不要查找这个文件名。

自动变量

objects = $(sources:.c=.o)
target: $(objects)
$(CC) $(CFLAGS) -o target $^
  • $^: 所有的依赖文件列表。

条件判断

DEBUG = 1
ifeq ($(DEBUG), 1)
CFLAGS += -g
else
CFLAGS += -O2
endif
  • ifeq: 如果条件成立则执行。

include与export

include config.mk
  • include: 包含其他 Makefile。
  • 在 Makefile 中用于将配置选项传递给被调用的子 Makefile 或其他命令,这样,子进程就可以使用该变量

编译选项变量

CFLAGS = #-c     #编译选项的变量
CFLAGS  += #-g #-Wall -fno-stack-protector -rdynamic -g 
#-g 生成调试信息,该信息可以被调试器用来进行源代码级别的调试。这对于在开发过程中识别和修复错误非常有用。
#-Wall: 启用所有常见的编译器警告。这有助于发现潜在的代码问题或不规范的写法。
#-fno-stack-protector: 禁用堆栈保护。堆栈保护是一项安全特性,用于检测堆栈溢出攻击。
#-rdynamic: 生成一个可加载的动态链接库(shared object),使得在程序运行时,动态链接库中的符号也能被解析。这在一些动态加载模块的场景中很有用。

CFLAGS  += #-I$(LIBMTN_CFG_PATH)/include
#-I: 这是编译器的选项,用于指定头文件的搜索路径。在这里,-I 后面跟着的是头文件的路径。在 -I 选项和头文件路径之间,是可以有空格的

CFLAGSL += #-L$(LIBMTN_PATH)
#-L: 这是编译器的选项,用于指定库文件的搜索路径。在这里,-L 后面跟着的是库文件的路径。

LDLIBS  += #-lpthread
#用于指定链接时需要使用的库。类似于 CFLAGS 和 LDFLAGS,LDLIBS 允许你设置链接阶段的选项。

LDFLAGS += #-L$(ROOTDIR)/lib/$(openssl_dir) -lssl -lcrypto -lcrypt
#-L$(ROOTDIR)/lib/$(openssl_dir): 这是编译器的选项,用于指定链接时需要使用的库文件的搜索路径。在这里,-L 后面跟着的是库文件搜索路径
#-lssl -lcrypto -lcrypt: 这是编译器的选项,用于指定链接时需要使用的库。在这里,-l 后面跟着的是库文件的名称,编译器会在指定的搜索路径中查找并链接这些库。
#具体来说,-lssl 表示链接 OpenSSL 的 SSL 库,-lcrypto 表示链接 OpenSSL 的 Crypto 库,而 -lcrypt 则表示链接系统的加密库。
#LDFLAGS: 这是一个 Makefile 中的变量,用于指定链接阶段的选项。类似于 CFLAGS,LDFLAGS 允许你设置链接时的选项。

链接软硬
ln -snf /tmp/serverkey.pem …/…/romfs/etc/serverkey.pem

for与if的使用(demo):轮询自路径,并进行自动编译
#方法1

for target in $(SUBDIRS); do \
echo "Building $(SUBDIRS)..."; \
$(MAKE) -C $$target; \
done

#方法2:轮询子路径是否存在Makefile

if [ -f $@/Makefile ]; then \
	$(MAKE) -C $@ ; \
	$(CC) main.c -o main.o; \
	else \
	echo "No Makefile in $@, skipping..."; \
	echo "No Makefile in $@, skipping..."; \
	fi

Makefile整理实例

tree命令查看代码结构:

├── char_driver
│   ├── helloDev.c
│   ├── Makefile
│   └── test.c
├── char_ioctl
│   ├── char_ioctl_driver.c
│   ├── char_ioctl_user
│   ├── char_ioctl_user.c
│   └── Makefile
├── dns_src
│   ├── dns.c
│   ├── dns_excute.c
│   └── Makefile
├── GDB_debug
│   ├── backtrace.c
│   ├── gdb_server_proxy.c
│   ├── hello.c
│   ├── Makefile
│   ├── multiprocess_demo_2.c
│   ├── multiprocess_demo.c
│   ├── multithread_demo.c
│   └── Segmentation_fault.c
├── hook_fuction
│   ├── hook.c
│   └── Makefile
├── ifconfig_achieve
│   ├── ifconfig_1.c
│   ├── ifconfig_wlan_information.c
│   ├── ifconfig_wlan_ip.c
│   └── Makefile
├── ip_addr_exchange
│   ├── ip_addr_exchange.c
│   └── Makefile
├── main.c
├── Makefil
├── Makefile
├── mk_test
├── net_device_driver
│   ├── 1111111.c
│   ├── Makefile
│   ├── net_device.c
│   ├── net_device_dirver.c
│   ├── net_device_tun_driver.c
│   ├── net_device_tun_user_tx.c
│   ├── net_device_user1
│   ├── net_device_user2
│   ├── net_device_user2.c
│   ├── net_device_user.c
│   ├── net_device_user_tap
│   ├── net_device_user_tap.c
│   ├── net_device_user_tun.c
│   ├── openvpn-as_2.12.0-2e834031-Ubuntu20_amd64.deb
│   ├── tap_demo
│   ├── tap_demo1
│   ├── tap_demo1.c
│   ├── tap_demo.c
│   ├── tcpdump1.pcap
│   ├── tcpdump2.pcap
│   ├── tcpdump3.pcap
│   └── tcpdump4.pcap
├── net_device_driver2
│   ├── Makefile
│   ├── net_device_tun_driver.c
│   ├── net_device_tun_user
│   ├── net_device_tun_user.c
│   ├── tcpdump1.pcap
│   ├── tcpdump2.pcap
│   ├── tcpdump3.pcap
│   └── tcpdump.pcap
├── netlink_socket
│   ├── Makefile
│   ├── Module.symvers
│   ├── netlink_socket_dirver.c
│   └── netlink_socket_user.c
├── ping
│   ├── all
│   ├── Makefile
│   ├── ping.h
│   └── ping_system.c
├── ping_combin_bag.c
├── process_communication
│   ├── Makefile
│   ├── name_pipe_1.c
│   ├── name_pipe.c
│   ├── pipe.c
│   ├── queue_1.c
│   ├── queue.c
│   ├── semaphores_consumer.c
│   ├── semaphores_producer.c
│   ├── shared_memory_modify.c
│   ├── shared_memory_touch.c
│   ├── signals.c
│   └── signals_send.c
├── str_function
│   ├── Makefile
│   ├── str_function.c
│   └── str_function.h
├── tcp_test
│   ├── Makefile
│   ├── tcp_client.c
│   └── tcp_sever.c
└── udp_test
    ├── Makefile
    ├── udp_client.c
    └── udp_server.c

如上虽然有很多的代码,主要分为两部分:

  1. 纯应用层代码
  2. 驱动代码与应用层的结合代码

驱动与应用层结合代码Makefile:char_driver

CC = gcc
CFLAGS = -o

ifneq ($(KERNELRELEASE),)
obj-m := helloDev.o
else
PWD := $(shell pwd)
#KDIR:= /lib/modules/4.4.0-31-generic/build
KDIR := /lib/modules/`uname -r`/build
all:
	make -C $(KDIR) M=$(PWD)
	$(CC) test.c $(CFLAGS) test.o
clean:	
	rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~ *.a *.cmd* *.order
endif

纯应用层代码:根目录Makefile为例

SUBDIRS := char_driver GDB_debug hook_fuction char_ioctl dns_src ifconfig_achieve ip_addr_exchange net_device_driver \
 net_device_driver2 netlink_socket process_communication udp_test tcp_test ping str_function
MAKE = make
CC = gcc
C = -c
.PHONY: all $(SUBDIRS)

all: $(SUBDIRS)
#这里 $@ 表示当前规则的目标,即次级目录的名字。
#方法1
$(SUBDIRS):
	if [ -f $@/Makefile ]; then \
	$(MAKE) -C $@ ; \
	$(CC) main.c -o main.o; \
	else \
	echo "No Makefile in $@, skipping..."; \
	echo "No Makefile in $@, skipping..."; \
	fi
#文件创建用法
	mkdir -p ./mk_test || exit $?;
	cp ./main.o ./mk_test/ || exit $?;
#创建链接,当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,
#我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link)它就可以,不必重复的占用磁盘空间。
#ln -snf /tmp/serverkey.pem ../../romfs/etc/serverkey.pem

#方法2
# for target in $(SUBDIRS); do \
#     echo "Building $(SUBDIRS)..."; \
#     $(MAKE) -C $$target; \
# done

clean:
	for dir in $(SUBDIRS); do \
        if [ -f $$dir/Makefile ]; then \
            $(MAKE) -C $$dir clean; \
        else \
            echo "No Makefile in $$dir, skipping..."; \
        fi \
    done
	rm -rf ./mk_test/main.o

如上Makefile解释说明:
方法1与方法2分别用了if与for语句,达到的效果相同,想要用哪种就可以取消注释进行编译

疑问

$$target,可以写成$$(target)吗?
在 Makefile 中,$$ 是用来转义 $ 符号的,以防止被 Make 解释成 Makefile 变量或命令行变量。
因此,在 for 循环中,$$target 是正确的写法,用于引用 shell 中的变量。
如果写成 $$(target),那么 Make 会将 $(target) 解释为 Makefile 变量,而不是 shell 中的变量。
所以正确的写法是 $$target,这样 shell 才能正确地解释并执行循环。
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值