赞
踩
工程目录结构
每一个功能模块建立一个文件夹,然后该文件夹下建立include
,lib
,src
文件夹。include
存放对外接口,lib
存放编译好的静态库,src
存放源码.c和.h文件。
每个功能模块有单独的makefile进行编译管理。
顶层路径下建立一个管理所有功能模块的makefile。这个makefile负责主函数的编译链接工作。
这里给出一个用我写的Makefile框架管理工程的例子。可对比后面完整项目工程框架,实际使用中只需要进行简单配置即可。
先写一个最简单的,能把整个工程编译出来。再考虑后续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文件已经能把工程编译出来了,修改.c文件,会重新编译对应的.c。但是如果我们修改头文件,运行make是不会有任何更新。所以需要在makefile里面增加.h的依赖关系。
鉴于前面都是所有文件编译都是自动识别的,考虑到如果工程很大,手动输入文件名称是一件很笨的事,需要用makefile来自动生成头文件的依赖关系。
原理:通过gcc -MM main.c命令,可以得到main.c这个文件所依赖的其它文件。如:
#include <stdio.h>
#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)/$(<D)/\1.o $@ : ,g' < $@.$$$$ > $@; \ 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)/$(<D)/\1.o $@ : ,g' < $@.$$$$ > $@; \ 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)/$(<D)/\1.o $@ : ,g' < $@.$$$$ > $@; \ 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)/$(<D)/\1.o $@ : ,g' < $@.$$$$ > $@; \ 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结构更加的清晰明了,更易于配置。
我的工程放在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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。