上面的例子中,指明了咱们的目标从$object中获取,“%.o”代表要全部以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foobar”,并为其加下“.c”的后缀,因而,咱们的依赖目标就是“foo.cbar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示全部的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是oo.o bar.o”)。因而,上面的规则展开后等价于下面的规则:javascript
这是一个进行了提高的列子:java
使用了filter进行单独过滤处理。c++
-
号,会标记全部后面的命令都是返回成功,也就是忽略失败。数组
@
号,会在make的时候,只是执行命令,而不输出命令自己。架构
make的 -n
参数,加上这个参数以后,只会展开命令,而不真的执行,能够帮助调试脚本。jvm
.PHONY:clean
用来定一个伪目标,好比这里定义了一个清理操做。ide
[Tab]键后面跟着的会被看成命令来执行。多个命令有依赖关系的时候,要写在一行,而且用分号分隔。函数
=、:=、?=、+=
的区别,=
能够在前面的代码用后面的变量,:=
则不能够。?=
被赋值的变量若是没有定义过,就是等于后面的值,若是定义过,则什么都不作。+=
用于追加。学习
override
定义了一个覆盖操做。ui
有两个变量,一个是SHELL,一个是 MAKEFLAGS ,这两个变量无论你是否export,其老是要传递到下层Makefile中,其余环境变量须要export 导出一下。可是make命令中的有几个参数并不往下传递,它们是“-C”,“-f”,“-h”“-o”和“-W”(有关Makefile参数的细节将在后面说明)
MAKEFILES这个环境变量,执行make的时候,会进行一个相似include的动做。通常不建议使用.
里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
隐晦规则。因为咱们的make有自动推导的功能,因此隐晦的规则可让咱们比较粗糙地简略地书写Makefile,这是由make所支持的。
变量的定义。在Makefile中咱们要定义一系列的变量,变量通常都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
文件指示。其包括了三个部分,一个是在一个Makefile中引用另外一个Makefile,就像C语言中的include同样;另外一个是指根据某些状况指定Makefile中的有效部分,就像C语言中的预编译#if同样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
注释。Makefile中只有行注释,和UNIX的Shell脚本同样,其注释是用“#”字符,这个就像C/C++中的“//”同样。若是你要在你的Makefile中使用“#”字符,能够用反斜框进行转义,如:“#”。
变量替换
理解为bar的值等于,foo里面的值将.o替换为.c以后。
变量再当变量
这个表达式a=的值等于z;
多层变量配合函数
“$($($(z)))”扩展为“$($(y))”,而其再次被扩展为“$($(subst 1,2,$(x)))”。$(x)的值是“variable1”,subst函数把“variable1”中的全部“1”字串替换成“2”字串,因而,“variable1”变成“variable2”,再取其值,因此,最终,$(a)的值就是$(variable2)的值—— “Hello”。
变量名组合
这里的“$a_$b”组成了“first_second”,因而,$(all)的值就是“Hello”。
“函数”和“条件语句”一同使用
这个示例中,若是定义了“do_sort”,那么:foo := $(sort a d b g q c),因而$(foo)的值就是“a b c d g q”,而若是没有定义“do_sort”,那么:foo := $(sort a d bg q c),调用的就是strip函数。
某个目标的局部变量
在这个示例中,无论全局的$(CFLAGS)的值是什么,在prog目标,以及其所引起的全部规则中(prog.o foo.o bar.o的规则),$(CFLAGS)的值都是“-g”
$@
表示规则中的目标文件集。在模式规则中,若是有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,若是一个目标是"foo.a(bar.o)",那么,"$%“就是"bar.o”,"$@“就是"foo.a”。若是目标不是函数库文件(Unix下是
[.a],Windows下是[.lib]),那么,其值为空。
$<
依赖目标中的第一个目标名字。若是依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
全部比目标新的依赖目标的集合。以空格分隔。
$^
全部的依赖目标的集合。以空格分隔。若是在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+
这个变量很像"$^",也是全部依赖目标的集合。只是它不去除重复的依赖目标。
$*
这个变量表示目标模式中"%“及其以前的部分。若是目标是"dir/a.foo.b”,而且目标的模式是"a.%.b",那么,"$“的值就是"dir /a.foo”。这个变量对于构造有关联的文件名是比较有较。
若是目标中没有模式的定义,那么"$“也就不能被推导出,可是,若是目标文件的后缀是 make所识别的,那么”$“就是除了后缀的那一部分。
例如:若是目标是"foo.c”,由于".c"是make所能识别的后缀名,因此,"$“的值就是"foo”。这个特性是GNU make的,颇有可能不兼容于其它版本的make,因此,你应该尽可能避免使用"$“,除非是在隐含规则或是静态模式中。若是目标中的后缀是make所不能识别的,那么”$"就是空值。
$(@D)
表示"$@“的目录部分(不以斜杠做为结尾),若是”$@“值是"dir/foo.o”,那么"$(@D)“就是"dir”,而若是"$@“中没有包含斜杠的话,其值就是”."(当前目录)。
$(@F)
表示"$@“的文件部分,若是”$@“值是"dir/foo.o”,那么"$(@F)“就是"foo.o”,"$(@F)“至关于函数”$(notdir $@)"。
"$(*D)" "$(*F)"
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,"$(*D)“返回"dir”,而"$(*F)“返回"foo”
"$(%D)" "$(%F)"
分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不一样的目录颇有用。
"$(<D)" "$(<F)"
分别表示依赖文件的目录部分和文件部分。
"$(^D)" "$(^F)"
分别表示全部依赖文件的目录部分和文件部分。(无相同的)
"$(+D)" "$(+F)"
分别表示全部依赖文件的目录部分和文件部分。(能够有相同的)
"$(?D)" "$(?F)"
分别表示被更新的依赖文件的目录部分和文件部分。
最后想提醒一下的是,对于"$<",为了不产生没必要要的麻烦,咱们最好给$后面的那个特定字符都加上圆括号,好比,"$(< )“就要比”$<"要好一些。
还得要注意的是,这些变量只使用在规则的命令中,并且通常都是"显式规则"和"静态模式规则"(参见前面"书写规则"一章)。其在隐含规则中并无意义。
误区一:gcc只能编译c代码,g++只能编译c++代码
二者均可以,可是请注意:
1.后缀为.c的,gcc把它看成是C程序,而g++看成是c++程序;后缀为.cpp的,二者都会认为是c++程序,注意,虽然c++是c的超集,可是二者对语法的要求是有区别的,例如:
若是按照C的语法规则,OK,没问题,可是,一旦把后缀改成cpp,马上报三个错:
分别对应前面红色标注的部分。可见C++的语法规则更加严谨一些。
2.编译阶段,g++会调用gcc,对于c++代码,二者是等价的,可是由于gcc命令不能自动和C++程序使用的库联接,因此一般用g++来完成连接,为了统一块儿见,干脆编译/连接通通用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。
误区二:gcc不会定义__cplusplus宏,而g++会
实际上,这个宏只是标志着编译器将会把代码按C仍是C++语法来解释,如上所述,若是后缀为.c,而且采用gcc编译器,则该宏就是未定义的,不然,就是已定义。
误区三:编译只能用gcc,连接只能用g++
严格来讲,这句话不算错误,可是它混淆了概念,应该这样说:编译能够用gcc/g++,而连接能够用g++或者gcc -lstdc++。由于gcc命令不能自动和C++程序使用的库联接,因此一般使用g++来完成联接。但在编译阶段,g++会自动调用gcc,两者等价。
误区四:extern "C"与gcc/g++有关系
实际上并没有关系,不管是gcc仍是g++,用extern "c"时,都是以C的命名方式来为symbol命名,不然,都以c++方式命名。
-l参数 和 -L参数
就是用来指定程序要连接的库,-l参数紧接着就是库名。
-L参数跟着的是库文件所在的目录名
-include和-I参数
-include用来包含头文件,但通常状况下包含头文件都在源码里用#include xxxxxx实现。
-I参数是用来指定头文件目录,/usr/include目录通常是不用指定的,gcc知道去那里找.
#include有两种形式,例如以下:
用尖括号表示的是包含系统的头文件,用双引号包含的是用户本身的头文件。
下面是使用#include时的一些规则:
1)使用<>包含的头文件通常会先搜索-I选项后的路径(即用gcc编译时的-I选项),以后就是标准的系统头文件路径。
2)而用""号包含的头文件会首先搜索当前的工做目录,以后的搜索路径才是和<>号包含的头文件所搜索的路径同样的路径。
3)在unix系统中,通常标准的头文件路径为:
4)通常有两条独立的头文件搜索路径链。一条是-I后面指示的路径,另外一条是系统头文件路径和以-prefix, -withprefix,和-idirafter后操做的目录。
5)若是gcc编译的是c++的程序,那么在搜索上面所说的目录前,预处理器会首先搜索/usr/include/g++v3目录,v3是你的gcc中c++的版本。
6)在头文件中运行增长路径名,例如:#include <sys/time.h>,那么就会在搜索的系统目录的sys目录下寻找time.h文件。
7)通常会用斜线来做为目录的分割符,甚至有些系统使用不一样的字符做为分割符(例如反斜线)。
相关环境变量
有大量的环境变量可供设置以影响 GCC 编译程序的方式。利用这些变量的控制也可以使用合适的命令行选项。一些环境变量设置在目录名列表中。这些名字和 PATH 环境变量使用的格式相同。特殊字符 PATH_SEPARATOR (安装编译程序的时候定义)用在目录名之间。在 UNIX 系统中,分隔符是冒号,而 Windows 系统中为分号。
C_INCLUDE_PATH
编译 C 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -isystem 选项同样。会首先查找 -isystem 指定的全部目录。
==> 也见 CPATH 、 CPLUS_INCLUDE_PATH 和 OBJC_INCLUDE_PATH 。
COMPILER_PATH
该环境变量指定一个或多个目录名列表,若是没有指定 GCC_EXEC_PREFIX 定位子程序,编译程序会在此查找它的子程序。
==> 也见 LIBRARY_PATH 、 GCC_EXEC_PREFIX 和 -B 命令行选项。
CPATH
编译 C 、 C++ 和 Objective-C 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -l 选项同样。会首先查找 -l 指定的全部目录。
==> 也见 C_INCLUDE_PATH 、 CPLUS_INCLUDE_PATH 和 OBJC_INCLUDE_PATH 。
CPLUS_INCLUDE_PATH
编译 C++ 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -isystem 选项同样。会首先查找 -isystem 指定的全部目录。
==> 也见 CPATH 、 C_INCLUDE_PATH 和 OBJC_INCLUDE_PATH 。
DEPENDENCIES_OUTPUT
为文件名设置该环境变量会让预处理程序将基于依赖关系的 makefile 规则写入文件。不会包括系统头文件名字。
若是环境变量设置为单名,被看做是文件名字,而依赖关系规则的名字来自源文件名字。若是定义中有两个名字,则第二个名字是用做依赖关系规则的目标名。 设置该环境变量的结果和使用命令行选项 -MM 、 -MF 和 -MT 的组合是同样的。
==> 也见 SUNPRO_DEPENDENCIES 。
GCC_EXEC_PREFIX
若是定义了该环境变量,它会做为编译程序执行的全部子程序名字的前缀。例如,若是将变量设置为 testver 而不是查找 as ,汇编器首先会在名字 testveras 下查找。若是在此没有找到,编译程序会继续根据它的普通名进行查找。可在前缀名中使用斜线指出路径名。
GCC_EXEC_PREFIX 的默认设置为 prefix /lib/gcc-lib/ ,这里的 prefix 是安装编译程序时 configure 脚本指定的名字。该前缀也用于定位标准链接程序文件,包含进来做为可执行程序的一部分。
若是使用 -B 命令行选项,会重写该设置。
==> 也见 COMPILER_PATH 。
warning: "unused parameter xxxx"警告
第一种方法
在UNUSED(param2)语句不产生任何目标代码,消除对未使用的变量的警告,并明确文件,不要使用变量的代码。
第二种方法
warning: unused parameter ‘mcb’
举例:
说明:由于函数参数中的mcb,在该函数中没有被使用,因此产生warning
修改:对没使用的参数使用 para=para;
最优美的方法
-fpic告诉编译器将源代码编译成共享的object文件,PIC(Position-Independent Code)意思是函数都是相对地址,这是共享库所须要的。
编译时”-lsum“的方式,是不可以区分当前是静态连接仍是动态连接的。若是在同一个目录下同时有静态连接库和动态连接库,则系统默认会引用动态连接库,若是想使用静态连接库则须要在编译时加上”-static“参数(具体方法可自行百度
共享库,有两种形式,第一种就是在上一篇文章中说到的“动态连接库”,而共享库的另外一种形式,则被称之为“动态加载库”,也就是我刚才提到的用“dlopen”方式来玩的。动态加载库在编译的时候,应该是不须要去-l引用lib,而是在可执行程序中,能够自已决定加载库的时机。好比程序跑着跑着,忽然想用libabc.so库里的一个叫abc的函数了,这时就能够用dlopen去打开这个库,而后使用dlsym去找到abc的函数指针并调用便可。
编译时-rdynamic用来通知连接器将全部符号添加到动态符号表中(目的是可以经过使用 dlopen 来实现向后跟踪)。-ldl 代表必定要将 dllib 连接于该程序
-MMD -MP -MF ./obj/local/armeabi/objs/jvm/jni-jvm.o.d
生成依赖说明文件。
-ffunction-sections, -fdata-sections会使compiler为每一个function和data item分配独立的section。 --gc-sections会使ld删除没有被使用的section。
-fstack-protector:
启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码。
-no-canonical-prefixes 生成其余gcc 组件的相对路径时不生成规范化的
-march=armv5te
-march 指定的指令集的版本 指定架构
hard-float 是直接生成浮点运算的指令(若是有的话);soft-float 是用库模拟浮点运算(若是有的话)。
-mthumb参数是编译全编译成thumb指令集,-mthumb-interwork是指thumb的code用thumb指令集,其余的用arm指令集
-fomit-frame-pointer 编译选项
忽略帧指针。这样在程序就不须要保存,安装,和恢复ebp了。这样ebp也就是一个
free的register了,在函数中就能够随便使用了。
-fno-strict-aliasing,则编译器认为任何 指针均可能指向同一个内存区域。所以对*b赋值,编译器认为有可能会影响a里面的值了。因此编译器给printf那一行传递参数的时候就认为寄存器里的 a[0],a[1]值已经不必定正确了,只有内存里的才可靠,因而只能老老实实从栈里取值了。
-finline-limit=n对伪指令数超过n的函数,编译程序将不进行内联展开
加上GCC編譯參數–noexecstack , 用以讓程式載入後Stack會透過MMU設定不可執行. (Hardware-based No eXecute (NX)),如此可避免像是透過Stack-Overflow將程式碼注入的軟體攻擊問題.
‘-Wformat’ This option warns about the incorrect use of format strings in functions
such as printf and scanf, where the format specifier does not agree with the
type of the corresponding function argument.
因为Android 的gdbserver 会让程序停在linker 的启动进程。因此通常都是显示??,若是须要查看能够 set sysroot adl_src/out/target/product/generic/symbols/ 或者,直接break main 下断点,而后continue执行过去。