软件工程通用makefile写法学习总结

工程通用makefile简单实现

最近学习了一下makefile,总结了一些经验,自己试着写了一套简单通用的软件工程makefile,总结下来以后可能会用到。


我的工程目录是这样的结构:

顶层目录是app/,app/include/存放公共的头文件,app/lib/存放公共的.a,app/obj/存放下层模块编译出的.a。

app层下面有三个模块分别是init,mod1,mod2。各模块编译出的.o放在obj下面,然后链接成.a文件放到上一级目录的obj下面。

app层通过链接lib和obj下面所有的.a来生成可执行文件。


模块目录下面的makefile:

DIR_INC = ./inc ../mod1/inc ../mod2/inc
DIR_SRC = ./src
DIR_OBJ = ./obj
DIR_BIN = ../obj

TARGET = $(DIR_BIN)/libinit.a

INCLUDE += $(DIR_INC)
DEFINES +=

CC ?= gcc
CFLAGS ?= -Wall
AR ?= ar
ARFLAG ?= -cr

SRC = $(wildcard ${DIR_SRC}/*.c)
OBJ = $(patsubst %.c,${DIR_OBJ}/%.o,$(notdir ${SRC}))

all : $(TARGET)
$(TARGET):$(OBJ)
	$(AR) $(ARFLAG) $@ $(OBJ)
$(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
	$(CC) $(CFLAGS) $(addprefix -I, $(INCLUDE)) $(addprefix -D, $(DEFINES)) -c $< -o $@ 

.PHONY : clean
clean :
	rm -rf $(DIR_OBJ)/*.o


app目录下面的makefile:

DIR_APP = $(shell pwd)
DIR_MOD = ./mod1 ./mod2 ./init
DIR_OBJ = ./obj
DIR_BIN = ./bin
DIR_LIB = ./lib

TARGET = $(DIR_BIN)/app1

export INCLUDE = $(DIR_APP)/include
export CC ?= gcc
export CFLAGS ?= -Wall -g
export DEFINES ?= _DEBUG_

all :
	@for subdir in $(DIR_MOD); \
		do $(MAKE) -C $$subdir all || exit 1; done
	
	$(CC) -o $(TARGET) -Wl,--start-group $(DIR_OBJ)/*.a $(DIR_LIB)/*.a -Wl,--end-group 


.PHONY : clean
clean :
	@for subdir in $(DIR_MOD); \
		do $(MAKE) -C $$subdir clean || exit 1; done

	rm -rf $(DIR_BIN)/* $(DIR_OBJ)/*

运行结果如下,达到了预期效果。

make all


make clean


需要注意的事项:

1.模块目录下面的DIR_INC需要手工指定需要包含的头文件路径。

2.模块目录下面的TARGET指定为需要输出的.a,设计上是每个模块单独生成一个.a。

3.原本想要实现的效果是,在每个模块目录下面单独执行makefile,可以生成.a,无需依赖上层,这样就需要.c中包含头文件时指定相对路径,或者DIR_INC指定清楚需要的头文件路径。

4.模块下面的makefile,用了一些?=定义的变量,表示如果上层没有传递该变量下来,则使用本定义来执行。这样可以减少对上层makefile的依赖。

遇到的几个问题:

1.模块层makefile编译时,提示找不到头文件:

原因是:定义头文件的变量有很多项时,需要每一项都指定-I参数,需要使用加头函数$(addprefix -I, $(INCLUDE)),这样编译时每一项都会加-I参数。宏定义同理,都要加-D。

2.在app层编译可执行程序时,链接所有.a,提示函数未定义:

原因是:链接器在链接.a时,由于链接的顺序问题,导致有些.a中的函数定义找不到。此时需要使用-Wl,--start-group  *.a   -Wl,--end-group,意思是链接时,在中间包含的所有.a中查找符号定义,直到找到为止。


可以优化的地方:

1.不知道这样可以不可以,在app层makefile中,指定INCLUDE=$(shell find ./ -name inc),这样上层就可以把所有头文件路径传给模块层使用,模块层无需再手工修改需要包含的路径了。

2.可以在app层上再指定一层project层,大型工程可能会编译出很多可执行程序,可以集中控制app层编译。

3.编译选项之类的,可以在app层指定一个compilier.mk文件,专门定义清楚。


可以使用的调试手段:

1.nm filename

查看文件的函数符号表。U表示不是本文件定义的函数。一般是调用的其他文件的函数。T表示本文件定义的函数。

[cl@localhost obj]$ nm libinit.a

main.o:
                 U App1Lib
                 U App1Mod1
                 U App1Mod2
0000000000000000 T main
                 U puts

2.readelf -a filename

解析文件的符号表。


3.ar -t

查看.a链接了哪些文件。


常用makefile命令

1.makefile中的几个符号含义:
$@ 表示目标文件
$^   表示所有依赖的文件
$<   表示第一个依赖的文件
$?   表示比目标还要新的所有依赖文件
:=    赋值时只能定义前面已经定义好的变量
?=   如果前面没有定义该变量,则赋当前值,否则使用前面定义的值
+=   给变量追加值

2.makefile函数含义:
wildcard:展开通配符。$(wildcard *.c) => 目录下的所有.c文件。
patsubst:模式字符串替换。$(patsubst %.c,%.o,a.c b.c) => 将a.c b.c替换为a.o b.o。
addprefix:加头。$(addprefix -I, a.h b.h) => 结果为-Ia.h -Ib.c
notdir:去文件名。$(notdir xxx/a.c yyy/zzz/b.c) => 结果为a.c b.c


参考:

http://blog.csdn.net/haoel/article/details/2886/

http://www.cnblogs.com/Anker/p/3242207.html



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值