makefile管理编译工程 自动生成.h头文件依赖

🌌 365bet有app吗 ⏳ 2026-10-05 19:23:08 👤 admin 👁️ 8250 💖 349
makefile管理编译工程 自动生成.h头文件依赖

makefile管理编译工程 自动生成.h头文件依赖

工程目录结构 每一个功能模块建立一个文件夹,然后该文件夹下建立include,lib,src文件夹。include存放对外接口,lib存放编译好的静态库,src存放源码.c和.h文件。 每个功能模块有单独的makefile进行编译管理。 顶层路径下建立一个管理所有功能模块的makefile。这个makefile负责主函数的编译链接工作。

这里给出一个用我写的Makefile框架管理工程的例子。可对比后面完整项目工程框架,实际使用中只需要进行简单配置即可。

Makfile实现

先写一个最简单的,能把整个工程编译出来。再考虑后续makefile的简洁,易配置等特性。 cJSON

build = ../build/cJSON

CFLAGS = $(CFLAGS_ENV)

INCLUDE = -I./include -I./src

src = $(wildcard ./src/*.c)

obj = $(src:.c=.o)

lib/libcJSON.a:$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@

.PHONY: clean

clean:

-rm $(build)/src/*.o ./lib/*.a

gsoap

build = ../build/gsoap

INCLUDE = -I../openssl/include -I./include -I./src

CFLAGS = -g -fPIC -DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACE -DDBUG -DWITH_NO_C_LOCALE $(CFLAGS_ENV)

src = $(wildcard ./src/*.c)

obj = $(src:.c=.o)

lib/libgsoap.a:$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@

.PHONY: clean

clean:

-rm $(build)/src/*.o ./lib/*.a

onvif

build = ../build/onvif

INCLUDE = -I/usr/local/ssl/include -I./include -I./src -I../gsoap/include -I../cJSON/include

CFLAGS = -g -fPIC -DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACE -DDBUG -DWITH_NO_C_LOCALE $(CFLAGS_ENV)

src = $(wildcard ./src/*.c)

obj = $(src:.c=.o)

lib/libonvif.a:$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@

.PHONY: clean

clean:

-rm $(build)/src/*.o ./lib/*.a

主函数makefile

LDLAGS = -lpthread -ldl -lm

main: gsoap/lib/libgsoap.a onvif/lib/libonvif.a openssl/lib/libssl.a openssl/lib/libcrypto.a cJSON/lib/libcJSON.a

$(CC) -o $@ -Xlinker "-(" $^ -Xlinker "-)" $(LDLAGS)

gsoap/lib/libgsoap.a:gsoap/src/*.c

cd gsoap && make && cd ..

onvif/lib/libonvif.a:onvif/src/*.c

cd onvif && make && cd ..

cJSON/lib/libcJSON.a:cJSON/src/*.c

cd cJSON && make && cd ..

.PHONY: clean

clean:

cd gsoap && make clean && cd ../onvif && make clean && cd ../cJSON && make clean

环境变量设置

build/目录为空,无法添加到远程仓库。通过这是环境变量来实现选择编译器和创建build/目录。

#!/bin/bash

#export CC=arm-himix100-linux-gcc

export CC=gcc

echo ">> [toolchain] $CC"

export CFLAGS_ENV="-ffunction-sections"

# -Wall -O2

export obj_dir=$(ls -l | grep "^d" | awk '{print $9}' | sed 's/build//g')

for i in $obj_dir;

do

if [ ! -d "./build/$i/src" ]; then

mkdir -p ./build/$i/src

fi

done

if [ $CC == "arm-himix100-linux-gcc" ]; then

cp ./openssl/lib_arm_gcc/*.a ./openssl/lib

elif [ $CC == "gcc" ]; then

cp ./openssl/lib_gcc/*.a ./openssl/lib

fi

修改优化makefile

上面的makefile文件已经能把工程编译出来了,修改.c文件,会重新编译对应的.c。但是如果我们修改头文件,运行make是不会有任何更新。所以需要在makefile里面增加.h的依赖关系。 鉴于前面都是所有文件编译都是自动识别的,考虑到如果工程很大,手动输入文件名称是一件很笨的事,需要用makefile来自动生成头文件的依赖关系。 原理:通过gcc -MM main.c命令,可以得到main.c这个文件所依赖的其它文件。如:

#include

#include "test.h"

#include "main.h"

那么,生成main.o所依赖的文件就包括main.c,test.h,main.h。格式如下:

main.o: main.c test.h main.h

显然这是我们需要的依赖关系,而且这个依赖关系很完美。那么makefile是如何做的呢? makefile通过这个命令得到依赖关系,然后把这个依赖关系保存到对应的.d文件中,在需要的地方进行导入。在保存之前,由于后面会生成.d文件,.d文件也应该随着.c导入头文件的变化而变化。所以.d是依赖于.c的,因此,makefile会对命令得到的依赖关系进行简单的修改:

main.o main.d : main.c test.h main.h

这个依赖关系等价于:

main.o: main.c test.h main.h

main.d: main.c test.h main.h

这里对于.o和.d目标的生成,运行makefile的匹配符号来完成。我看网上的教程都是不带前缀的。不带前缀会使我们将.c和.d和.o分开存放时造成错误。当对obj和dep添加前缀的话,又会导致匹配失败。所以还是在匹配的时候,通过addprefix来处理前缀问题。 至于.d文件的作用,前面已经讲了,在.c修改导入头文件后,依赖关系改变了,这个时候就是通过这个.d文件来动态的表示改变的依赖关系。否则,如果依赖关系一直不变,那么我在main.c中又导入了fun.h,那么我修改fun.h的话,这个时候运行make,那么将不会有任何更新。

通过这个方法,对上面的makefile进行修改。 cJSON

target := libcJSON.a

build := ../build/cJSON

outdir := lib

srcdir := src

INCLUDE := -I./include -I./src

CFLAGS := $(CFLAGS_ENV)

src := $(wildcard $(srcdir)/*.c)

obj := $(src:.c=.o)

dep := $(src:.c=.d)

$(outdir)/$(target):$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@

$(addprefix $(build)/, %.d): %.c

@set -e; rm -f $@; \

$(CC) -MM $(CPPFLAGS) $(INCLUDE) $< > $@.$$$$; \

sed 's,\($(*F)\)\.o[ :]*,$(build)/$( $@; \

rm -f $@.$$$$

include $(addprefix $(build)/, $(dep))

.PHONY: clean

clean:

-rm $(addprefix $(build)/, $(dep)) $(addprefix $(outdir)/, $(target)) $(addprefix $(build)/, $(obj))

gsoap

target := libgsoap.a

build := ../build/gsoap

outdir := lib

srcdir := src

INCLUDE := -I../openssl/include -I./include -I./src

CFLAGS := -g -fPIC -DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACE -DDBUG -DWITH_NO_C_LOCALE $(CFLAGS_ENV)

src := $(wildcard $(srcdir)/*.c)

obj := $(src:.c=.o)

dep := $(src:.c=.d)

$(outdir)/$(target):$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@

$(addprefix $(build)/, %.d): %.c

@set -e; rm -f $@; \

$(CC) -MM $(CPPFLAGS) $(INCLUDE) $< > $@.$$$$; \

sed 's,\($(*F)\)\.o[ :]*,$(build)/$( $@; \

rm -f $@.$$$$

include $(addprefix $(build)/, $(dep))

.PHONY: clean

clean:

-rm $(addprefix $(build)/, $(dep)) $(addprefix $(outdir)/, $(target)) $(addprefix $(build)/, $(obj))

onvif

target := libonvif.a

build := ../build/onvif

outdir := lib

srcdir := src

INCLUDE := -I/usr/local/ssl/include -I./include -I./src -I../gsoap/include -I../cJSON/include

CFLAGS := -g -fPIC -DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACE -DDBUG -DWITH_NO_C_LOCALE $(CFLAGS_ENV)

src := $(wildcard $(srcdir)/*.c)

obj := $(src:.c=.o)

dep := $(src:.c=.d)

$(outdir)/$(target):$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@

$(addprefix $(build)/, %.d): %.c

@set -e; rm -f $@; \

$(CC) -MM $(CPPFLAGS) $(INCLUDE) $< > $@.$$$$; \

sed 's,\($(*F)\)\.o[ :]*,$(build)/$( $@; \

rm -f $@.$$$$

include $(addprefix $(build)/, $(dep))

.PHONY: clean

clean:

-rm $(addprefix $(build)/, $(dep)) $(addprefix $(outdir)/, $(target)) $(addprefix $(build)/, $(obj))

上面修改后的makefile支持我们修改.h文件后对工程对应的编译更新。 并且我用了很多变量。运用变量编写makefile非常的有利于我们对makefile的修改维护。 并且会让我们makefile看起来更加的清晰。

由于用了很多变量,这3个makefile的很大一部分内容都是一样。这使我们可以用makefile的include。 将相同的内容写到另外一个makefile中,然后通过include将那个makefile导入。这个运用和C语言的#define宏定义类似。会对文本原样导入。 这样就得到最终的三个看起来很简洁的4个makefile。 cJSON

target := libcJSON.a

build := ../build/cJSON

outdir := lib

srcdir := src

make_run_mk_dir := ..

INCLUDE := -I./include -I./src

CFLAGS := $(CFLAGS_ENV)

include $(make_run_mk_dir)/make_run.mk

gsoap

target := libgsoap.a

build := ../build/gsoap

outdir := lib

srcdir := src

make_run_mk_dir := ..

INCLUDE := -I../openssl/include -I./include -I./src

CFLAGS := -g -fPIC -DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACE -DDBUG -DWITH_NO_C_LOCALE $(CFLAGS_ENV)

include $(make_run_mk_dir)/make_run.mk

onvif

target := libonvif.a

build := ../build/onvif

outdir := lib

srcdir := src

make_run_mk_dir := ..

INCLUDE := -I/usr/local/ssl/include -I./include -I./src -I../gsoap/include -I../cJSON/include

CFLAGS := -g -fPIC -DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACE -DDBUG -DWITH_NO_C_LOCALE $(CFLAGS_ENV)

include $(make_run_mk_dir)/make_run.mk

make_run.mk

src := $(wildcard $(srcdir)/*.c)

obj := $(src:.c=.o)

dep := $(src:.c=.d)

$(outdir)/$(target):$(addprefix $(build)/, $(obj))

ar -rc $@ $^

$(addprefix $(build)/, %.o):%.c

$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@

$(addprefix $(build)/, %.d): %.c

@set -e; rm -f $@; \

$(CC) -MM $(CPPFLAGS) $(INCLUDE) $< > $@.$$$$; \

sed 's,\($(*F)\)\.o[ :]*,$(build)/$( $@; \

rm -f $@.$$$$

include $(addprefix $(build)/, $(dep))

.PHONY: clean

clean:

-rm $(addprefix $(build)/, $(dep)) $(addprefix $(outdir)/, $(target)) $(addprefix $(build)/, $(obj))

这样就非常的简单明了了。我们只需要修改target、build、CFLAGS和INCLUDE即可。剩下的问题makefile会帮我自动处理。

让Makefile变得更好

通过上面的内容,可以自己来定义的Makefile的结构。接下来我定义了一下我的工程的Makefile的结构,是makefile使用起来更加的容易。 通过前面Makefile的编写,整个Makefile功能已经很完善了,能够处理我们一般的问题。 现在,通过修改工程的结构,让我们来更简单的使用Makefile。并且在配置Makefile的时候能够更加集中的处理。将需要配置的内容放到Makefile的最顶层和最底层。最顶层的内容存放:通用的配置和各个需要编译的模块内容。最底层存放:编译处理的具体实施。中间的Makefile只是做一个过度作用,连接顶层目录和Makefile和底层路径的Makefile。 这样做,整个Makefile结构更加的清晰明了,更易于配置。 我的工程放在github上了。 工程中的所有.c和.h已经删除,只保留了结构和Makefile,供参考。 这里展示部分makefile内容,整个工程makefile结构请参考上面的github链接。 顶层makefile

all:

@make -C $(MOD_DIR)

@make -C $(APP_DIR)

@echo "------------------------------"

@echo "make all done."

@echo "------------------------------"

.PHONY: clean gsoap_clean onvif_clean cJSON_clean clean_all

clean_all: clean

-@rm -rf $(BUILD_DIR)

clean:

-@make -C $(APP_DIR) clean

-@make -C $(MOD_DIR) clean

@echo "------------------------------"

@echo "clean all doen."

@echo "------------------------------"

gsoap_clean:

-@make -C $(MOD_DIR) gsoap_clean

onvif_clean:

-@make -C $(MOD_DIR) onvif_clean

cJSON_clean:

-@make -C $(MOD_DIR) cJSON_clean

生成程序makefile

#------------------------------

# 生成程序名称

#------------------------------

target := main

#------------------------------

# 生成程序输出路径

#------------------------------

outdir := $(ROOT_DIR)

#------------------------------

# .c源文件

#------------------------------

src := $(wildcard *.c)

#------------------------------

# 链接参数

#------------------------------

LDLAGS := -lpthread -ldl -lm

#------------------------------

# 链接时需要的头文件

#------------------------------

INCLUDE := -I$(MOD_DIR)/onvif/include

#------------------------------

# 模块路径

#------------------------------

MOD += cJSON

MOD += gsoap

MOD += onvif

MOD += openssl

#------------------------------

# 链接时需要的库

#------------------------------

LIB += $(foreach d, $(addsuffix /lib, $(addprefix $(MOD_DIR)/, $(MOD))), $(wildcard $(d)/*.a))

all: $(outdir)/$(target)

$(outdir)/$(target): $(LIB) $(src)

$(CC) -o $(outdir)/$(target) $(src) $(INCLUDE) -Xlinker "-(" $(LIB) -Xlinker "-)" $(LDLAGS)

.PHONY: clean

clean:

-@rm -f $(outdir)/$(target)

相关文章