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

在实际项目中,会有很多代码文件,它们间有复杂关系,并可能存放在不同的目录中。
光gcc命令一个个去编译有时是不现实的,Makefile的出现就是为了解决这个问题。
在这整理了我所碰到的一些情况,不是入门教程,但比较实用。如果真要全面学习,
    一定要看看陈皓的<<跟我一起写 Makefile>>,那才精典非常啊。

  一。 由繁到简写Makefile .

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

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

/*
操作系统: Oracle Linux 6.4
编译版本:
[root@ol64 test4]# gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)

例子目录结构:
[root@ol64 test4]# ls *
callso.c  main.c  Makefile

lib:
add.c  calc.h  Makefile  sub.c
*/
注意,例子代码文件附在 "  编译小结(3) 动态库(.so)编译及二种调用技巧" 一章中。

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

例1:
先上个标准的。
[root@ol64 test4]# cat Makefile
#author: xiongchuanliang 
CC = gcc
CFLAGS = -m64 -fPIC 
TARGET = demo01
OBJS = 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./lib
add.o: lib/add.c lib/calc.h
	$(CC) $(CFLAGS) -c lib/add.c
sub.o: lib/sub.c lib/calc.h
	$(CC) $(CFLAGS) -c lib/sub.c

.PHONY: clean
clean:
	-rm -f $(TARGET) $(OBJS)
        上面这个的Makfile就不细说。很规范的Makefile格式,特点是用一些宏代替了每一行要重复输入的东西。
 如果没看明白,看下面这个Makefile展开的结果就很明显了。
[root@ol64 test4]# make
gcc -m64 -fPIC  -c main.c -I./lib
gcc -m64 -fPIC  -c lib/add.c
gcc -m64 -fPIC  -c lib/sub.c
gcc -m64 -fPIC  -o demo01 main.o add.o sub.o
[root@ol64 test4]# ./demo01
add() = 8 
sub() = 2
MAIL:xcl_168@aliyun.com 
BLOG:http://blog.csdn.net/xcl168

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

.PHONY: clean
clean:
	-rm -f $(TARGET) $(OBJS)

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

[root@ol64 test4]# cat Makefile
#author: xiongchuanliang 
CC = gcc
CFLAGS = -m64 -fPIC -I./lib
TARGET = demo03
OBJS = main.o lib/add.o lib/sub.o

$(TARGET):$(OBJS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
main.o: main.c lib/calc.h
add.o: lib/add.c lib/calc.h
sub.o: lib/sub.c lib/calc.h

.PHONY: clean
clean:
	-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 = gcc
CXX = g++
CFLAGS = -m64 -fPIC -I./lib
TARGET = 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: clean
clean:
	-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]# make
main.c lib/add.c lib/sub.c
main.o lib/add.o lib/sub.o
main.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]# make
gcc -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]# ./demo05
add() = 8 
sub() = 2
MAIL:xcl_168@aliyun.com 
BLOG:http://blog.csdn.net/xcl168


[root@ol64 test4]# cat Makefile
#author: xiongchuanliang 
CC = gcc
CXX = g++
CFLAGS = -m64 -fPIC -I./lib
TARGET = 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: clean
clean:
	-rm -f $(TARGET) $(OBJS)
     个人理解这个Makefile是不管C还是C++文件,编译用"wildcard"宏,拼出文件串。
用"patsubst"宏串换得到".o" 分隔的字符串。
配合"$<"和"$@"匹配符自动推导功能。
然后桥归桥,路归路各自编译链接一下,目标就出来了。

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

这里我举了两个例子,各自体会吧。
例1:
[root@ol64 lib]# cat Makefile
#author: xiongchuanliang 
CC = gcc
CFLAGS = -m64 -fPIC 
SHARED = -shared 
TARGET = libcalc01.so
OBJS = add.o sub.o

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS) $(CFLAGS) $(SHARED)
add.o: add.c calc.h
sub.o: sub.c calc.h

.PHONY: clean
clean:
	rm -f $(TARGET) $(OBJS)
例2
[root@ol64 lib]# cat Makefile
#author: xiongchuanliang 
CC = gcc
CFLAGS = -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: clean
clean:
	-rm -f $(TARGET) $(OBJS)

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

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

//要注意下面Makefile中"EXPFUN"一行
[root@ol64 lib]# cat Makefile
#author: xiongchuanliang 
CC = gcc
CFLAGS = -m64 -fPIC 
SHARED = -shared 
EXPFUN = -Wl,--retain-symbols-file=dy.sym -Wl,--version-script=dy.map 
TARGET = libcalc.so
OBJS = add.o sub.o

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS) $(CFLAGS) $(SHARED) $(EXPFUN)
add.o: add.c calc.h
sub.o: sub.c calc.h

.PHONY: clean
clean:
	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.sym
sub

//可以用 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 = gcc
CFLAGS = -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: clean
clean:
	-rm -f $(TARGET) $(OBJS)


[root@ol64 lib]# make
ar -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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值