Makefile知识点温习

来源:互联网 发布:田原1968淘宝网 编辑:程序博客网 时间:2024/05/18 00:25

1.公司项目经常用Makefile,虽然可以看懂别人写的Makefile,但是有的时候编译有Makefile更方便,决定再次学习一遍。

常见Makefile

CC := gccSOURCE := $(wildcard *.c)OBJS := $(patsubst %.c %.o $(SOURCE))INCLUDE := /usr/lib/testLIBPATH := /usr/binLIBPATH_SUB +=  /usr/lib/x86_64-linux-gnuLINKS := -L$(LIBPATH) -L$(LIBPATH_SUB) -lpthread -lmysqlclient -lm CFLAGS := -g -Wall -fPIC $(INCLUDE) $(LINKS)TARGET := mainall : $(OBJS)CC $(CFLAGS) $(OBJS) -o $(TARGET).PHONE:cleanclean:rm -rf *.o $(TARGET)

---------------------------------------------------我是分割线------------------------------------------

1.在Makefile中我们经常看到 = := ?= +=这几个赋值运算符,那么他们有什么区别呢?

= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值

之前一直纠结makefile中“=”和“:=”的区别到底有什么区别,因为给变量赋值时,两个符号都在使用。网上搜了一下,有人给出了解答,但是本人愚钝,看不懂什么意思。几寻无果之下,也就放下了。今天看一篇博客,无意中发现作者对于这个问题做了很好的解答。解决问题之余不免感叹,有时候给个例子不就清楚了吗?为什么非要说得那么学术呢。^_^

      1、“=”

      make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:

            x = foo
            y = $(x) bar
            x = xyz

      在上例中,y的值将会是 xyz bar ,而不是 foo bar 。

      2、“:=”

      “:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

            x := foo
            y := $(x) bar
            x := xyz

      在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。



2.Makefile选项CFLAGS,LDFLAGS,LIBS

CFLAGS 表示用于 C 编译器的选项,

CXXFLAGS 表示用于 C++ 编译器的选项。
这两个变量实际上涵盖了编译和汇编两个步骤。

CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。

LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以抒那个包的lib路径加入的LDFALGS中试一下。

LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv

简单地说,LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件。不过使用时链接阶段这两个参数都会加上,所以你即使将这两个的值互换,也没有问题。

有时候LDFLAGS指定-L虽然能让链接器找到库进行链接,但是运行时链接器却找不到这个库,如果要让软件运行时库文件的路径也得到扩展,那么我们需要增加这两个库给"-Wl,R":

LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib

如果在执行./configure以前设置环境变量export LDFLAGS="-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib" ,注意设置环境变量等号两边不可以有空格,而且要加上引号(shell的用法)。那么执行configure以后,Makefile将会设置这个选项,链接时会有这个参数,编译出来的可执行程序的库文件搜索路径就得到扩展了。


3.

gcc编译参数-fPIC的一些问题

-fPIC作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code), 则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。


这里有一个-fPIC参数
PIC就是position independent code
PIC使.so文件的代码段变为真正意义上的共享
如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy.每个copy都不一样,取决于这个.so文件代码段和数据段内存映射的位置.


不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码)
如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了.(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)
我们总是用fPIC来生成so,也从来不用fPIC来生成a.
fPIC与动态链接可以说基本没有关系,libc.so一样可以不用fPIC编译,只是这样的so必须要在加载到用户程序的地址空间时重定向所有表目.

因此,不用fPIC编译so并不总是不好.
如果你满足以下4个需求/条件:
1.该库可能需要经常更新
2.该库需要非常高的效率(尤其是有很多全局量的使用时)
3.该库并不很大.
4.该库基本不需要被多个应用程序共享

如果用没有加这个参数的编译后的共享库,也可以使用的话,可能是两个原因:
1:gcc默认开启-fPIC选项
2:loader使你的代码位置无关

从GCC来看,shared应该是包含fPIC选项的,但似乎不是所以系统都支持,所以最好显式加上fPIC选项。参见如下


`-shared'
    Produce ashared object which can then be linked with other
    objects toform an executable.  Not allsystems support this
    option.  Forpredictable results, you must also specify the same
    set ofoptions that were used to generate code (`-fpic', `-fPIC',
    or modelsuboptions) when you specify this option.(1)



-fPIC 的使用,会生成 PIC代码,.so 要求为 PIC,以达到动态链接的目的,否则,无法实现动态链接。


---------------------------------------------------------我是分割线---------------------------------------------------------


%.c是GUNmake语法层,表示的是所有以".c"结尾的文件;而*.c则是linux shell 语法层的,比如:rm -f *.o 类似的。作用效果一样,只是一个是GUNmake的一个是shell的。Makefile  $@, $^, $< 
$@  表示目标文件
$^  表示所有的依赖文件
$<  表示第一个依赖文件
$?  表示比目标还要新的依赖文件列表

Makefile中指示符“include”、“-include”和“sinclude”的区别

如果指示符include指定的文件不是以斜线开始(绝对路径,如/usr/src/Makefile...),而且当前目录下也不存在此文件;

make将根据文件名试图在以下几个目录下查找:首先,查找使用命令行选项-I或者--include-dir指定的目录,如果找到指定的文件,则使用这个文件;

否则继续依此搜索以下几个目录(如果其存在):/usr/gnu/include/usr/local/include/usr/include

当在这些目录下都没有找到include指定的文件时,make将会提示一个包含文件未找到的告警提示,但是不会立刻退出。而是继续处理Makefile的后续内容。

当完成读取整个Makefile后,make将试图使用规则来创建通过指示符include指定的但未找到的文件,当不能创建它时(没有创建这个文件的规则),

make将提示致命错误并退出。会输出类似如下错误提示:

Makefile:错误的行数:未找到文件名:提示信息(No such file ordirectory

Make *** No rule to make target ‘’.Stop

通常我们在Makefile中可使用-include来代替include,来忽略由于包含文件不存在或者无法创建时的错误提示(-的意思是告诉make,忽略此操作的错误。

make继续执行)。像下边那样:

-includeFILENAMES...

使用这种方式时,当所要包含的文件不存在时不会有错误提示、make也不会退出;除此之外,和第一种方式效果相同。以下是这两种方式的比较:

使用includeFILENAMES...make程序处理时,如果FILENAMES列表中的任何一个文件不能正常读取而且不存在一个创建此文件的规则时make程序

将会提示错误并退出。

使用-includeFILENAMES...的情况是,当所包含的文件不存在或者不存在一个规则去创建它,make程序会继续执行,只有真正由于不能正确完成终极

目标的重建时(某些必需的目标无法在当前已读取的makefile文件内容中找到正确的重建规则),才会提示致命错误并退出。

为了和其它的make程序进行兼容。也可以使用sinclude来代替-includeGNU所支持的方式)。

Linux系统平台下关于GCC编译及使用的方法 (-Werror,它要求GCC将所有的警告当成错误进行处理 )  

警告:

1、-pedantic 选项,那么使用了扩展语法的地方将产生相应的警告信息

2、-Wall 使用它能够使GCC产生尽可能多的警告信息

3、-Werror,它要求GCC将所有的警告当成错误进行处理

-static选项,强制使用静态链接库

加速:

使用管道代替编译中临时文件,

-pipe 加速编译

-c 通知GCC取消链接步骤,即编译源码并在最后生成目标文件;

-Dmacro 定义指定的宏,使它能够通过源码中的#ifdef进行检验;

-E 不经过编译预处理程序的输出而输送至标准输出;

-g3 获得有关调试程序的详细信息,它不能与-o选项联合使用;  

-Idirectory 在包含文件搜索路径的起点处添加指定目录;

-llibrary 提示链接程序在创建最终可执行文件时包含指定的库;

-O、-O2、-O3 将优化状态打开,该选项不能与-g选项联合使用;

-S 要求编译程序生成来自源代码的汇编程序输出;

-v 启动所有警报;  

-Wall 在发生警报时取消编译操作,即将警报看作是错误;  

-Werror 在发生警报时取消编译操作,即把报警当作是错误;  

-w 禁止所有的报警。

gcc编译链接动态库时,很有可能编译通过,但是执行时,找不到动态链接库,那是

因为-L选项指定的路径只在编译时有效,编译出来的可执行文件不知道-L选项后面的值,

当然找不到。可以用ldd <your_execute>看看是不有 ‘not found’在你链接的库后面,

解决方法是通过-Wl,rpath=<your_lib_dir>,使得execute记住链接库的位置

makefile .d的解释 

Makefile 一般要用隐晦规则

gcc编译器有个功能 gcc -MM first.c   这样会输出first.o : first.c *.h

基于这个原因.这个.d出来了.也就是把每个源文件都gcc -MM下把输出从定向到一个对应的.d文件




0 0
原创粉丝点击