在实际项目中,会有很多代码文件,它们间有复杂关系,并可能存放在不同的目录中。
光gcc命令一个个去编译有时是不现实的,Makefile的出现就是为了解决这个问题。
在这整理了我所碰到的一些情况,不是入门教程,但比较实用。如果真要全面学习,
一定要看看陈皓的<<跟我一起写 Makefile>>,那才精典非常啊。
四。 用Makefile编译静态库(.a)
一。 由繁到简写Makefile
这里的"繁",我其实更认为是"烦",有时文件一多,Makefile会写得很烦的。
为了简化这些工作,很有必要多了解一些实用的技巧。
我在这提供了4个例子,展示如何一步步的简化编写工作。
例1:
先上个标准的。
如果没看明白,看下面这个Makefile展开的结果就很明显了。
例2
除了用宏定义省去一些重复东西外,Makefile还提供了一些匹配符简化编写,如用 $< 匹配符简化。
$< : 扩展成依靠,取列表中的第一个依靠文件名.
例3
例3这个Makefile相比上二个例子,特别的地方在于采用下面两行来做命令的自动推导,
它能依列表的.o文件,自动推导出.c文件并编译和链接。
使文件一下子简化了很多。
$(TARGET):$(OBJS)
$(CC) $(CFLAGS) -o $(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有时确实挺方便。
$@ 扩展成当前规则的目的文件名,
$< 扩展成依靠,取列表中的第一个依靠文件名.
$^ 扩展成整个依靠的列表(除掉了里面所有重 复的文件名)。
因为匹配符还算常见,剩下的就看不懂了吧。 不懂没关系,我用个小例子再展示一下。
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++代码文件并不鲜见,又要混在一起编译,应当怎么办呢?
来吧,看我怎么弄。
用"patsubst"宏串换得到".o" 分隔的字符串。
配合"$<"和"$@"匹配符自动推导功能。
然后桥归桥,路归路各自编译链接一下,目标就出来了。
三。用Makefile编译动态库(.so)
只简单展示下动态库的Makefile怎么写,关于动态库的编译知识,
看看我写的"编译小结(3) 动态库(.so)编译及二种调用技巧"吧.
这里我举了两个例子,各自体会吧。
例1:
看看我写的"编译小结(4) 说说静态库(.a)"吧。
光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