一个简单的例子:
ab.out : a.o b.o g++ -o ab.out o/a.o o/b.o a.o : a.cpp g++ -c a.cpp -o o/a.o b.o : b.cpp g++ -c b.cpp -o o/b.o clean: rm -f o/a.o o/b.o
Linux下动态链接库默认后缀名是".so",静态链接库默认后缀名是“.a”。
-l 参数指定程序要链接的库名,这个库名就是把库文件名头lib和尾.so去掉的子字符串,如链接到库 libmysqlpp.so 就是 -lmysqlpp ,如此类推。
/lib 和 /usr/lib 和 /usr/local/lib 这三个目录里的库直接用 -l 参数就能直接链接。
那在此三者之外目录里的库呢?那就需要使用 -L 参数:
比如想链接 /usr/soft/libtest.so 这个库,就是 -L/usr/soft -ltest
-include 用来包含头文件,但一般情况下已经在源码里用#include指令实现了,所以这个很少用。
-I 参数是用来指定头文件目录,当前目录和缺省目录(/usr/include)不用指定。
simple_server_objects = socket/ServerSocket.o socket/Socket.o server.o simple_client_objects = socket/ClientSocket.o socket/Socket.o client.o all : server client server: $(simple_server_objects) g++ -o server $(simple_server_objects) -lmysqlpp -lmemcached -lmysqlclient -L/usr/local/lib client: $(simple_client_objects) g++ -o client $(simple_client_objects) Socket.o: socket/Socket.cpp g++ -c socket/Socket.cpp -o socket/Socket.o ServerSocket.o: socket/ServerSocket.cpp g++ -c socket/ServerSocket.cpp -o socket/ServerSocket.o ClientSocket.o: socket/ClientSocket.cpp g++ -c socket/ClientSocket.cpp -o socket/ClientSocket.o server.o: server.cpp g++ -c server.cpp -o server.o -I/usr/local/mysql/include simple_client_main.o: simple_client_main.cpp g++ -c client.cpp -o client.o clean: rm -f *.o socket/*.o server client
编译(compile)部分只需要指明头文件,可能会用到 -I 参数。链接(link)部分需要链接库,可能会用到 -l 和 -L
将动态链接库路径(例如说是 /usr/local/lib )放到变量 LD_LIBRARY_PATH 里。可以用命令 export 来临时测试:export LD_LIBRARY_PATH=/usr/local/lib
如果问题解决,
可以在 ~/.bashrc 或者 ~/.bash_profile 中加入 export 语句,前者在每次登陆和每次打开 shell 都读取一次,后者只在登陆时读取一次。我的习惯是加到 ~/.bashrc 中,在该文件的未尾,可采用如下语句来使设置生效:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
修改完后,记得关掉当前终端并重新打开一个新的终端,从而使上面的配置生效。
比如说,在使用 boost/thread.hpp 库的时候,就需要加上 -lboost_thread -L/usr/local/lib
Linux动态链接库的搜索路径按优先级排序为:
1.编译目标代码时指定的动态库搜索路径;
在编译时通过gcc 的参数”-Wl,-rpath,”指定。当指定多个动态库搜索路径时,路径之间用冒号”:”分隔。
2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;
/etc/ld.so.conf的第一行有个引用命令:include ld.so.conf.d/*.conf
因此,最优雅的方式是在ld.so.conf.d目录下创建一个你的程序依赖的配置文件,配置文件内容为程序依赖的动态链接库的路径,一个路径一行。
添加完配置文件后执行ldconfig使其生效。
4.默认的动态库搜索路径/lib;
5.默认的动态库搜索路径/usr/lib;
1、可以用 LD_LIBRARY_PATH 环境变量指定,这个类似于 PATH 机制,比较直观,而且,可以放到 bashrc 中固化下来,也可以放到自己的 .bashrc 中只对本用户起作用;
2、如果启用了 ld.so.cache 的话,系统会在 /etc/ld.so.cache 中存储所有可引用的动态链接库。这个文件的内容可以通过 /etc/ld.so.conf 来指定;这个是比较固定的机制,对全局所有用户都有影响;不过更改设置后需要 root 调用 ldconfig 来刷新一下。
3、默认的标准库路径,这个似乎不用设置就可以。包括 /lib 和 /usr/lib。当然,如果是64位系统,还包括 /lib64 和 /usr/lib64。
4、其它情况,如果只想对某一个特定的应用程序起作用的话,可以在编译时指定搜索路径。gcc 的 -Wl 和 -rpath 参数。
1 #定义编译选项 2 CC = g++ 3 LINK = g++ 4 CFLAGS = -Wall 5 6 #定义头文件目录,链接库目录,链接文件 7 INCLUDE_PATH = -Iinclude 8 LIB_PATH = -Llib -L/usr/local/lib -Lsrc/lib 9 LIBS = -lboost_thread -lboost_system -Llib #-ljsoncpp 10 11 #定义项目代码根目录及所有文件夹目录 12 SRC_DIR = src 13 VPATH = $(SRC_DIR) 14 VPATH += $(SRC_DIR)/base 15 VPATH += $(SRC_DIR)/data 16 VPATH += $(SRC_DIR)/include 17 VPATH += $(SRC_DIR)/operate 18 VPATH += $(SRC_DIR)/operate/detail 19 20 #找出所有 .cpp 文件和相应的 .o 文件(带目录) 21 SRC_FILES = $(foreach n, $(VPATH),$(wildcard $(n)/*.cpp)) 22 OBJ_FILES = $(SRC_FILES:.cpp=.o) 23 24 #把所有的 .o 文件放到定义好的输出文件夹中统一管理 25 OUTPUT_DIR := debug 26 OUTPUT_OBJS = $(addprefix $(OUTPUT_DIR)/,$(subst $(SRC_DIR)/, ,$(OBJ_FILES))) 27 28 #创建存放 .o 文件的目录结构 29 $(shell mkdir -p "$(OUTPUT_DIR)") 30 $(shell mkdir -p "$(OUTPUT_DIR)/base") 31 $(shell mkdir -p "$(OUTPUT_DIR)/data") 32 $(shell mkdir -p "$(OUTPUT_DIR)/operate") 33 $(shell mkdir -p "$(OUTPUT_DIR)/operate/detail") 34 35 #更新 ctags 36 #$(shell ctags -R --c++-kinds=+p --fields+iaS --extra=+q .) 37 38 #定义输出目标名 39 TARGET = server 40 41 #链接 42 $(TARGET) : $(OUTPUT_OBJS) 43 $(LINK) $(OUTPUT_OBJS) -o $@ $(LIB_PATH) $(LIBS) 44 45 #编译 46 $(OUTPUT_DIR)/%.o : %.cpp 47 $(CC) -c $< -o $@ 48 49 #清除 50 .PHONY:clean 51 clean: 52 -rm -rf $(OUTPUT_DIR)/* 53 -rm -rf $(TARGET)
备注:大体的步骤如上,不过需要提前生成存放 .o 文件的目录结构,可以通过 $(shell mkdir -p "$(OUTPUT_DIR)") 等一系列命令来生成,另外需要事先定义好一些变量,如头文件目录,链接库目录,链接库名称等。该通用步骤可以使MAKEFILE写起来很简单,不过感觉效率不高,太多依赖自动查找,另外头文件改动后,也要先 clean 再 make,这一点才是最致命的。待续。
GCC头文件的环境变量是 export CPLUS_INCLUDE_PATH=/root/new_s3/src/include
加入到GCC环境变量相比使用 -I 来包含头文件的区别是,在使用 -MM 生成依赖关系时,前者会忽略生成指定路径的头文件的依赖关系。
二、自动依赖
#定义编译选项 CC = g++ LINK = g++ CFLAGS = -g -Wall -O0 #定义头文件目录,链接库目录,链接文件 #INCLUDE_PATH = -Isrc/include -Isrc/include/mysql LIB_PATH = -Lsrc/lib LIBS = -lboost_thread -lboost_system -ljsoncpp -lmysqlpp -lmysqlclient #定义项目代码根目录及所有文件夹目录 SRC_DIR = src/Server VPATH = $(SRC_DIR) VPATH += $(SRC_DIR)/base VPATH += $(SRC_DIR)/data VPATH += $(SRC_DIR)/include VPATH += $(SRC_DIR)/operate #设置GCC编译时的查找的头文件目录。如果在 -MM 时用-I来指定,那么会生成依赖关系,如BOOST库等,但一般情况下BOOST库是不会修改的。 export CPLUS_INCLUDE_PATH=src/include:src/include/mysql #找出所有 .cpp 文件和相应的 .o 文件(带目录) SRC_FILES = $(foreach n, $(VPATH),$(wildcard $(n)/*.cpp)) ALL_FILES = $(foreach n, $(VPATH),$(wildcard $(n)/*.h $(n)/*.cpp)) OBJ_FILES = $(notdir $(SRC_FILES:.cpp=.o)) .PHONY:all all : depend server #生成依赖关系文件,这里不用伪目标,这样只有当代码被改动时,依赖关系文件才会被重新生成 #.PHONY:depend depend:$(ALL_FILES) #@export CPLUS_INCLUDE_PATH=src/include:src/include/mysql && $(CC) $(CFLAGS) -MM $(SRC_FILES) > depend -include depend #链接 server : $(OBJ_FILES) $(LINK) $(OBJ_FILES) -o $@ $(LIB_PATH) $(LIBS) #清除 .PHONY:clean clean: -rm -rf $(TARGET) -rm -rf *.o
该方法缺点:每次都需要生成所有源文件的依赖关系文件,即使是改动一个源文件的情况下。
time make -j4 使用4线程来编译程序
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o test
上面是GCC编译的四个阶段,其中第一个阶段产生的 *.i 文件是中间文件,一般用不到,所以一般不专门去生成它。 *.s 文件是汇编文件, *.o 是机器语言。最后一个是可执行的二进制文件。
关于警告,参考:http://developer.51cto.com/art/200609/32317_1.htm
选项-O
用法:#gcc -O1 test.c -o test
作用:使用编译优化级别1编译程序。级别为1~3,级别越大优化效果越好,但编译时间越长。
通配符可以使用在变量中,如 objects = *.o ,不过 *.o 并不会展开,Makefile 中的变量,就是C/C++中的宏,如果需要展开的话,即 objects 的值是所有 .o 文件的集合,可以:
objects = $(wildcard *.o)
Makefile 默认只在当前目录下寻找依赖文件和目标文件,如果定义了VPATH变量,则会到变量中所指示的目录中去寻找,VPATH变量的值,应该用空格或冒号分开,(在WINDOWS下是用空格或分号),与之类似的还有一个vpath关键字,它灵活,可以指定在不同的目录里搜索不同类型的文件,使用方法有三种:
vpath <pattern> <dir> #为模式指定目录
vpath <pattern> #清除某种模式的搜索目录
vpath #清除所有模式的搜索目录
<pattern>中使用 % ,来匹配0或若干个字符。如:vpath %.h src/include
伪目标也可以做为依赖:
.PHONY : cleanall cleanobj cleandiff cleanall : cleanobj cleandiff rm program cleanobj : rm *.o cleandiff : rm *.diff
files = a.o b.o c.i
$(filter %.o,$(files)) : %.o : %.cpp
感觉 $(filter %.o,$(files)) : %.cpp 与 $(file) : %.o : %.cpp 效果一样。
$(files: .o = .d) #把变量$(files) 中的所有以 .o 结尾(后面是空格或结束符)字符串换成 .d
gcc 的 -M 和 -MM 选项,可以生成依赖关系,前者会包括标准库的头文件,后者不带。
命令行前如果使用 @ 符号,则这个命令不会被 make 显示出来,如 @echo 正在编译……,会输出“正在编译……”
可以通过 make -n 或 make --just-print 来查看而不执行命令。 make -s 或 make --slient 是全面禁止显示命令。$(subst <from>,<to>,<text>) #字符串替换,返回处理后的字符串 $(patsubst <from>,<to><text>) #模式字符串替换,相当于 $(var:%.c=%.o),返回处理后的字符串 $(strip <string>) #去首尾空格,返回处理后的字符串 $(findstring <find>,<text>) #查找字符串,找到则返回<find>,否则返回空字符串 $(filter <pattern>,<text>) #过滤出text中符合模式的字符串,如:$(filter %.c %.cpp,$(source)) $(filter-out <pattern>,<text>) #和上面相反,返回不符合模式的字串,同样可以有多个模式 $(sort <text>) #给字符串中的单词排序,注意它会去掉重复的单词 $(word <n>,<text>) #取出第n个单词 $(wordlist <s>,<e>,<text>) #取出第s到e个单词,包括第s和第e的单词 $(words <text>) #统计单词个数 $(firstword <text>) #返回第一个单词,相当于 $(word 1,<text>)
文件函数:
$(dir <names>) #返回文件名序列的目录部分 $(nodir <names>) #返回文件名序列的文件部分 $(suffix <names>) #返回文件名序列的后缀部分 $(basename <names>) #返回文件名序列的前缀部分(返回最后一个小数点前的字串) $(addsuffix <suffix>,<names>) #为文件名序列中每个单词加后缀 $(addprefix <prefix>,<names>) #为文件名序列中每个单词加前缀 $(join <list1>,<list2>) #连接单词,如 $(join aaa bbb,111 222 333) 返回 aaa111 bbb222 333
$(foreach <var>,<list>,<cmd>) 把<list>中的单词逐一取出来,放到<var>中,然后执行<cmd>,如:
names := a b c dfiles := $(foreach n,$(names),$(n).o)
$@ 目标文件集 $< 第一个依赖项 $? 所有比目标新的依赖项 $^ 所有依赖集合 $+ 同上,只是不去重复 另外,加上D或F,分别表示目录部分或文件部分,如 $(@F) 当一个模式匹配包含有斜杠(实际也不经常包含)的文件时,那么在进行模式匹配时,目 录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。在进行"茎"的传递时,我 们需要知道这个步骤。例如有一个模式"e%t",文件"src/eat"匹配于该模式
生成动态链接库与静态链接库的方法(先生成 *.o 文件,直接通过源文件生成亦可):
静态链接库其实就可以看成是 *.o 文件的简单打包:
ar -crv libtest.a test.o 或: ar -r libtest.a test.cpp -r选项是必须的,表示插入到备份文件,并且有则替换 -c表示创建备份文件 -v表示显示详情
动态链接库优于静态链接库,无论是在内存使用和磁盘使用上(多进程时),既可以使用源码文件直接生成,也可以使用 *.o 文件生成(*.o 文件的生成必须使用 -fPIC 参数,而从*.o 文件生成动态链接库则不用再重复此参数):
gcc -c -fPIC test.cpp && gcc test.o -o libtest.so -shared
或: gcc test.cpp -o libtest.so -shared -fPIC
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
通过源码安装新版本的GCC,要保证已经安装了GCC编译器:
1、下载源码包
下载GCC源码包
http://www.multiprecision.org/mpc 下载 mpc 最新版
ftp://ftp.gnu.org/gnu/gmp 下载最新版
http://ftp.gnu.org/gnu/mpfr/ 下载 mpfr 最新版
2、依次安装 gmp 、mpfr、mpc
./configure --prefix=/usr/local/gmp-5.1.1&& make -j4 && make install
./configure --prefix=/usr/local/mpfr-3.1.2 --with-gmp=/usr/local/gmp-5.1.1&& make -j4 && make install
./configure --prefix=/usr/local/mpc-1.0 --with-gmp=/usr/local/gmp-5.1.1 --with-mpfr=/usr/local/mpfr-3.1.2&& make -j4 && make install
3、安装GCC
//export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/mpc-1.0/lib:/usr/local/gmp-5.1.1/lib:/usr/local/mpfr-3.1.2/lib
./configure --prefix=/usr/local/gcc-4.8 --enable-threads=posix --disable-checking --disable-multilib --enable-languages=c,c++ --with-gmp=/usr/local/gmp-5.1.1 --with-mpfr=/usr/local/mpfr-3.1.2 --with-mpc=/usr/local/mpc-1.0 --with-gmp-lib=/usr/local/gmp-5.1.1/lib --with-mpfr-lib=/usr/local/mpfr-3.1.2/lib --with-mpc-lib=/usr/local/mpc-1.0/lib
make -j4 && make install
4、做软链接
ln -s /usr/local/gcc-4.8/bin/g++ /bin/g++2
cp /usr/local/mpc-1.0/lib/libmpc.so /usr/local/gmp-5.1.1/lib/libgmp.so /usr/local/mpfr-3.1.2/lib/libmpfr.so /usr/local/lib/
5、测试
#include <memory> class User{}; int main() { auto i = "Hello"; std::shared_ptr<User> p; }
g++2 -std=c++0x test.cpp -o test
如果出现:/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found