编译小结(5) Makefile项目实用例子

来源:互联网 发布:网络机顶盒升级包 编辑:程序博客网 时间:2024/05/19 10:16
在实际项目中,会有很多代码文件,它们间有复杂关系,并可能存放在不同的目录中。
光gcc命令一个个去编译有时是不现实的,Makefile的出现就是为了解决这个问题。
在这整理了我所碰到的一些情况,不是入门教程,但比较实用。如果真要全面学习,
    一定要看看陈皓的<<跟我一起写 Makefile>>,那才精典非常啊。

  一。 由繁到简写Makefile .

  二。 如何应付多路径,多语言(C/C++)混杂环境下的Makfile.

  三。 用Makefile编译动态库(.so)
  四。 用Makefile编译静态库(.a)

/*操作系统: Oracle Linux 6.4编译版本:[root@ol64 test4]# gcc --versiongcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)例子目录结构:[root@ol64 test4]# ls *callso.c  main.c  Makefilelib:add.c  calc.h  Makefile  sub.c*/
注意,例子代码文件附在 " 编译小结(3) 动态库(.so)编译及二种调用技巧" 一章中。

一。 由繁到简写Makefile
  这里的"繁",我其实更认为是"烦",有时文件一多,Makefile会写得很烦的。
为了简化这些工作,很有必要多了解一些实用的技巧。
我在这提供了4个例子,展示如何一步步的简化编写工作。

例1:
先上个标准的。
[root@ol64 test4]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -m64 -fPIC TARGET = demo01OBJS = main.o add.o sub.o$(TARGET): $(OBJS)$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)main.o: main.c lib/calc.h$(CC) $(CFLAGS) -c main.c -I./libadd.o: lib/add.c lib/calc.h$(CC) $(CFLAGS) -c lib/add.csub.o: lib/sub.c lib/calc.h$(CC) $(CFLAGS) -c lib/sub.c.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)
        上面这个的Makfile就不细说。很规范的Makefile格式,特点是用一些宏代替了每一行要重复输入的东西。
 如果没看明白,看下面这个Makefile展开的结果就很明显了。
[root@ol64 test4]# makegcc -m64 -fPIC  -c main.c -I./libgcc -m64 -fPIC  -c lib/add.cgcc -m64 -fPIC  -c lib/sub.cgcc -m64 -fPIC  -o demo01 main.o add.o sub.o[root@ol64 test4]# ./demo01add() = 8 sub() = 2MAIL:xcl_168@aliyun.com BLOG:http://blog.csdn.net/xcl168

例2  
 除了用宏定义省去一些重复东西外,Makefile还提供了一些匹配符简化编写,如用 $< 匹配符简化。
 $< : 扩展成依靠,取列表中的第一个依靠文件名.
[root@ol64 test4]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -m64 -fPIC TARGET = demo02OBJS = main.o add.o sub.o$(TARGET):main.o add.o sub.o$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)main.o: main.c lib/calc.h$(CC) $(CFLAGS) -c $< -I./libadd.o: lib/add.c lib/calc.h$(CC) $(CFLAGS) -c $< sub.o: lib/sub.c lib/calc.h$(CC) $(CFLAGS) -c $<.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)

例3
例3这个Makefile相比上二个例子,特别的地方在于采用下面两行来做命令的自动推导,
它能依列表的.o文件,自动推导出.c文件并编译和链接。
使文件一下子简化了很多。
    $(TARGET):$(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)

[root@ol64 test4]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -m64 -fPIC -I./libTARGET = demo03OBJS = main.o lib/add.o lib/sub.o$(TARGET):$(OBJS)$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)main.o: main.c lib/calc.hadd.o: lib/add.c lib/calc.hsub.o: lib/sub.c lib/calc.h.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)
小技巧: 
得到 "main.o: main.c lib/calc.h" 这种东西是很费事的,在这分享一个不那麻烦的方法。
用 gcc -MM 文件名.c 即可得文件的依赖性。 
如引用的头文件之类可能在别的目录,和编译一样,加上 "-I" 参数即可。
 gcc -I目录 -MM 文件名.c
一样能得到一个很标准的Makefile行.
[root@ol64 test4]# gcc -I./lib -MM main.c
main.o: main.c lib/calc.h
这样是不是很爽呢.

例4
  如果说觉得前面三种方法还是麻烦的话,不怕,看看下面这个。你会觉得Makefile有时确实挺方便。
[root@ol64 test4]# cat Makefile#author: xiongchuanliang CC = gccCXX = g++CFLAGS = -m64 -fPIC -I./libTARGET = demo04%.o: %.c$(CC) $(CFLAGS) -c $< -o $@ SOURCES = $(wildcard *.c)  $(wildcard lib/*.c) OBJS = $(patsubst %.c,%.o,$(SOURCES))$(TARGET) : $(OBJS)$(CXX) $(CFLAGS) -o $(TARGET) $^.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)
首先我把几个匹配符说明下:
$@ 扩展成当前规则的目的文件名, 
$< 扩展成依靠,取列表中的第一个依靠文件名.
$^ 扩展成整个依靠的列表(除掉了里面所有重 复的文件名)。

因为匹配符还算常见,剩下的就看不懂了吧。 不懂没关系,我用个小例子再展示一下。
[root@ol64 test4]# cat Makefile#author: xiongchuanliang SOURCES = $(wildcard *.c)  $(wildcard lib/*.c) OBJS = $(patsubst %.c,%.o,$(SOURCES))NODIR = $(notdir $(OBJS))all:@echo $(SOURCES)@echo $(OBJS)@echo $(NODIR)[root@ol64 test4]# makemain.c lib/add.c lib/sub.cmain.o lib/add.o lib/sub.omain.o add.o sub.o
参数说明:
wildcard 扩展通配符
notdir   去除路径
patsubst 替换通配符

//wildcard命令把后面指定路径下的所有后缀是c的文件全部连接成一个空格分隔的字符串
src=$(wildcard *.c ./include/*.c)

//去掉字符串中的路径,只保留文件名
dir=$(notdir $(src))

patsubst函数有三个参数。
第一个是一个需要匹配的式样,
第二个表示用什么来替换它,
第三个是一个需要被处理的由空格分隔的列表。
//把所有列表中.c结尾的替换为.o结尾
obj=$(patsubst %.c,%.o,$(dir))

     理解了参数后,本例中的Makefile个人理解为用"wildcard"宏,拼出文件串。
 再用"patsubst"宏把.c或.cpp扩展名的文件串换为".o" 分隔的字符串。
 配合"$<"和"$@"匹配符自动推导功能。
 使用gcc -c编译,用gcc -o链接上,目标就编译出来了。

二。如何应付多路径,多语言(C/C++)混杂环境下的Makfile.
     开发中同时存在C和C++代码文件并不鲜见,又要混在一起编译,应当怎么办呢?
  来吧,看我怎么弄。
[root@ol64 test4]# makegcc -m64 -fPIC -I./lib -c main.c -o main.o gcc -m64 -fPIC -I./lib -c lib/add.c -o lib/add.o gcc -m64 -fPIC -I./lib -c lib/sub.c -o lib/sub.o g++ -m64 -fPIC -I./lib -o demo05 main.o lib/add.o lib/sub.o[root@ol64 test4]# ./demo05add() = 8 sub() = 2MAIL:xcl_168@aliyun.com BLOG:http://blog.csdn.net/xcl168[root@ol64 test4]# cat Makefile#author: xiongchuanliang CC = gccCXX = g++CFLAGS = -m64 -fPIC -I./libTARGET = demo05#OBJS = main.o lib/add.o lib/sub.o%.o: %.c$(CC) $(CFLAGS) -c $< -o $@ %.o: %.cpp$(CXX) $(CFLAGS) -c $< -o $@ SOURCES = $(wildcard *.c *.cpp)  $(wildcard lib/*.c) OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))$(TARGET) : $(OBJS)$(CXX) $(CFLAGS) -o $(TARGET) $^.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)
     个人理解这个Makefile是不管C还是C++文件,编译用"wildcard"宏,拼出文件串。
用"patsubst"宏串换得到".o" 分隔的字符串。
配合"$<"和"$@"匹配符自动推导功能。
然后桥归桥,路归路各自编译链接一下,目标就出来了。

三。用Makefile编译动态库(.so)
        只简单展示下动态库的Makefile怎么写,关于动态库的编译知识,
看看我写的"编译小结(3) 动态库(.so)编译及二种调用技巧"吧.

这里我举了两个例子,各自体会吧。
例1:
[root@ol64 lib]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -m64 -fPIC SHARED = -shared TARGET = libcalc01.soOBJS = add.o sub.o$(TARGET): $(OBJS)$(CC) -o $(TARGET) $(OBJS) $(CFLAGS) $(SHARED)add.o: add.c calc.hsub.o: sub.c calc.h.PHONY: cleanclean:rm -f $(TARGET) $(OBJS)
例2
[root@ol64 lib]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -m64 -fPIC SHARED = -shared TARGET = libcalc02.so%.o: %.c$(CC) $(CFLAGS) -c $< -o $@ SOURCES = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SOURCES))$(TARGET) : $(OBJS)$(CXX) $(CFLAGS) $(SHARED) -o $(TARGET) $^.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)

        如果你做的动态库有跨平台的需求,还可以看下面的例子。

  Windows下的动态库,通常会定义一个def文件,来定义要指定导出的函数。
   gcc也可以,通过 --retain-symbols-file 与  --version-script 两个参数控制。 

//要注意下面Makefile中"EXPFUN"一行[root@ol64 lib]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -m64 -fPIC SHARED = -shared EXPFUN = -Wl,--retain-symbols-file=dy.sym -Wl,--version-script=dy.map TARGET = libcalc.soOBJS = add.o sub.o$(TARGET): $(OBJS)$(CC) -o $(TARGET) $(OBJS) $(CFLAGS) $(SHARED) $(EXPFUN)add.o: add.c calc.hsub.o: sub.c calc.h.PHONY: cleanclean:rm -f $(TARGET) $(OBJS)//控制动态导出符号 对应 :--version-script //global表示要导出的符号,local表示不导出的,*表示都不导出[root@ol64 lib]# cat dy.map{global:     sub;local: *;};//控制静态导出符号 对应 :--retain-symbols-file [root@ol64 lib]# cat dy.symsub//可以用 readelf 来查看导出函数 [root@ol64 lib]# readelf -s libcalc.so //例子中没有指定要导出add函数,当被调用时,会报下面的错。[root@ol64 lib]# ./../callso[main] cadd() failed! /xcl/test4/lib/libcalc.so: undefined symbol: add

四。 用Makefile编译静态库(.a)

  只简单展示下静态库的Makefile怎么写,关于静态库的编译知识,
看看我写的"编译小结(4) 说说静态库(.a)"吧。
[root@ol64 lib]# cat Makefile#author: xiongchuanliang CC = gccCFLAGS = -maix64 -fPIC TARGET = libcalc01.a%.o: %.c$(CC) $(CFLAGS) -c $< -o $@ SOURCES = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SOURCES))$(TARGET) : $(OBJS)ar -X64 -rcs $(TARGET) $(OBJS) $^.PHONY: cleanclean:-rm -f $(TARGET) $(OBJS)[root@ol64 lib]# makear -X64 -rcs libcalc01.a add.o sub.o add.o sub.o

     ar要注意编译成64位时,要加对应的参数,可用man ar查出。  这个很容易被忽视,特别是在UNIX下。


搞定收工。

MAIL: xcl_168@aliyun.com

BLOG:http://blog.csdn.net/xcl168

原创粉丝点击