SylixOS Makefile 源代码解析
word版下载地址:https://download.****.net/download/qq_28637193/10871601
原来是在word上写的,粘贴复制过来了,格式显示的不是很好。
SylixOS Makefile 源代码解析
SylixOS 是由base,bsp,app三部分组成,一下将分别介绍着三部分中的Makefile以及他们之间Makefile相互联系。
1.SylixOS base 中的makefile
1.1Base中Makefile的结构
图 1
Makefile文件中内容:
图 2
COMPONENTS 是宏定义,宏定义的内容会根据在IDE中创建base工程时选择的模块而变化。
all 和 clean 命令,当执行make all 时候回执行all:命令,make clean 会执行clean 命令后面的命令。在IDE中使用 build project 时默认是make all。使用clean project 会使用 make clean 命令。
@是不显示后面的命令 即for不显示。
for target in $(COMPONENTS); do make -C $$target all; done
这条命令是通过for循环 在宏定义COMPONENTS 文件夹中遍历执行makefile。
libsylixos 中makefile
进入到libsylixos文件夹后找到makefile文件,如图4:
图 3
图 4
第24行 $(shell) 会执行 shell 后面的shell命令,如果在上一级目录中存在config.mk 则#include ../config.mk,如果不存在,则判断当前文件夹中是否有config.mk,如果存在定义#include config.mk。如果当前文件夹中也不存在则CONFIG_MK_EXIST 为空。
41行的BUILD_LITE_TARGET 是定义是否编译 sylixos 的lite版本。
47-70 行是根据是否是lite版本的配置
图 5
第77行的$(SPACE) 代表空格, “subst from,to,text ”这条命令的意思是将text中的from 替换成to。所以77行的意思是将路径中的空格进行转义替换成“\ ”(window允许在文件名中使用空格)。后面的其他行是根据配置加入所需要的mk文件。
libsylixos.mk
在makefile的 #include 包含的是mk文件,make将会逐个编译,这里取
libsylixos.mk文件,其它mk文件与之相似。
图 6
在第25行的 CLEAR_VARS_MK 变量,这个是在 SylixOS/mktemp文件夹中的header.mk文件。
图 7
在这个文件夹中放的是sylixos的mk模板。
打开header.mk文件
图 8
在53行就是前面的CLEAR_VARS_MK 变量。 $(MKTEMP)中的MKTEMP则是在前面makefile中介绍base路径转义那里赋值的,clear-vars.mk,clear-vars.mk与header.mk同一文件夹中。
回到libsylixos.mk中第30行LOCAL_TARGET_NAME是编译出来的目标文件, libsylixos.a是编译出来的静态库。
第38行 LOCAL_ARM_SRCS 是arm架构需要编译的代码,后面类似代码是根据不同架构不同功能选择编译的代码。会再common.mk中使用这个宏定义,图9中第55,59行就是根据不同体系结构选择不同的源代码编译。定义的LOCAL_ARCH_SRCS最后会69到72行被使用
图 9
图 10
最后会在common.mk 的134行相关部分会被编译成.o文件。
再次回到libsylixos.mk文件,如图11:
图 11
第1857行是首先按照ARCH 定义的内容过滤 x86 x64 如果存在LOCAL_INC_PATH 就把相应的头文件加入。LOCAL_DSYMBOL 是编译符号
LOCAL_DEPEND_LIB 是依赖库的名称,LOCAL_DEPEND_LIB_PATH 是依赖库的路径, LOCAL_USE_CXX 是否使用c++,LOCAL_USE_CXX_EXCEPT 是否使用c++ 异常,LOCAL_USE_GCOV 是否使用代码覆盖率。
图 12
如图12 ,第1892行。-mfloat-abi=name 是gcc编译arm命令gcc手册解释
Specifies which floating-point ABI to use. Permissible values are: ‘soft’, ‘softfp’ and ‘hard’.
Specifying ‘soft’ causes GCC to generate output containing library calls for floating-point operations. ‘softfp’ allows the generation of code using hardware floating-point instructions, but still uses the soft-float calling conventions. ‘hard’ allows generation of floating-point instructions and uses FPU-specific calling conventions.
The default depends on the specific target configuration. Note that the hard-float and soft-float ABIs are not link-compatible; you must compile your entire program with the same ABI, and link with a compatible set of libraries.
-mfpu=name
This specifies what floating-point hardware (or hardware emulation) is available on the target. Permissible names are: ‘auto’, ‘vfpv2’, ‘vfpv3’, ‘vfpv3-fp16’, ‘vfpv3-d16’, ‘vfpv3-d16-fp16’, ‘vfpv3xd’, ‘vfpv3xd-fp16’, ‘neon-vfpv3’, ‘neon-fp16’, ‘vfpv4’, ‘vfpv4-d16’, ‘fpv4-sp-d16’, ‘neon-vfpv4’, ‘fpv5-d16’, ‘fpv5-sp-d16’, ‘fp-armv8’, ‘neon-fp-armv8’ and ‘crypto-neon-fp-armv8’. Note that ‘neon’ is an alias for ‘neon-vfpv3’ and ‘vfp’ is an alias for ‘vfpv2’.
The setting ‘auto’ is the default and is special. It causes the compiler to select the floating-point and Advanced SIMD instructions based on the settings of -mcpu and -march.
If the selected floating-point hardware includes the NEON extension (e.g. -mfpu=neon), note that floating-point operations are not generated by GCC’s auto-vectorization pass unless -funsafe-math-optimizations is also specified. This is because NEON hardware does not fully implement the IEEE 754 standard for floating-point arithmetic (in particular denormal values are treated as zero), so the use of NEON instructions may lead to a loss of precision.
You can also set the fpu name at function level by using the target("fpu=") function attributes (see ARM Function Attributes) or pragmas (see Function Specific Option Pragmas).
回到libsylixos.mk文件的1895行,“:”前部是目标文件,后部是需要文件,也就是使用armVfpV7MAsm.S 编译生成armVfpV7MAsm.o文件。这里有个宏定义OBJPATH ,OBJPATH 在header.mk头文件中,如图13:
图 13
OBJPATH 最终是根据DEBUG_LEVEL 决定的,这个DEBUG_LEVEL 是在config.mk 文件中定义,如图14,图15:
图 14
图 15
查看config.mk内容 里面全部宏定义正是根据base 创建时生成。
回到libsylixos.mk第1896行$(dir [email protected]) 中的 [email protected] 指的是目标文件名称,-d是判断是否为目录,也就是判断(OBJPATH)/libsylixos.a/SylixOS/arch/arm/fpu/v7m/是否存在,如果不存在调用mkdir -p 创建文件目录(-p 是先检查是否存在目录,存在就不新建)。
第1898行的 __DEP 宏定义位于header.mk头文件中,如图16:
图 16
DEPPATH 宏定义由上面第58行DEBUG_LEVEL决定,DEBUG_LEVEL是在config.mk文件中配置的。再看第75行的__TARGET,
$(subst <from>,<to>,<text>)
名称:字符串替换函数——subst。
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。
$(word <n>,<text>)
名称:取单词函数——word。
功能:取字符串<text>中第<n>个单词。(从一开始)
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。
示例:$(word 2, foo bar baz)返回值是“bar”。
$(addprefix <prefix>,<names...>)
名称:加前缀函数——addprefix。
功能:把前缀<prefix>加到<names>中的每个单词前面。
返回:返回加过前缀的文件名序列。
示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”
$(addsuffix <suffix>,<names...>)
名称:加后缀函数——addsuffix。
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列。
示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
$(basename <names...>)
名称:取前缀函数——basename。
功能:从文件名序列<names>中取出各个文件名的前缀部分。
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。
示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar $< 表示第一个依赖文件 hacks”。
在编译release版本时,OBJPATH 等于 Release/obj ,
header.mk头中第75行的 subet 命令 的意思是将目标文件路径中的“/”替换成为空格,所以
Release/obj/libsylixos.a/SylixOS/arch/arm/fpu/v7m/armVfpV7MAsm.o 被替换成为Release obj libsylixos.a SylixOS arch arm fpu v7m armVfpV7MAsm.o
word 是取第三个字,也就是libsylixos.a,所以__TARGET 值为libsylixos.a
$< 表示第一个依赖文件,内容为/SylixOS/arch/arm/fpu/v7m/armVfpV7MAsm.S
basename是取前缀所以变为/SylixOS/arch/arm/fpu/v7m/armVfpV7MAsm
由addsuffix处理后变为/SylixOS/arch/arm/fpu/v7m/armVfpV7MAsm.d,
DEPPATH 值是Release/dep/,所以在通过addprefix 处理加上前缀就变为Release/dep/libsylixos.a SylixOS/arch/arm/fpu/v7m/armVfpV7MAsm.d 如图17
图 17
回到libsylixos.mk的1900行 宏定义AS 定义在gcc.mk中。
图 18
图 19
TOOLCHAIN_PREFIX 宏定义在config.mk中,如图20
图 20
回到libsylixos.mk 第1900行_ASFLAGS_WITHOUT_FPUFLAGS 定义在mktemp文件夹中的libsylixos.mk文件中
图 21
这里面宏定义一部分在本文件中,一部分定义在mktemp文件夹中的arch.mk中,如图
图 22
SylixOS bsp makefile
以zynq700为例,如图21:
图 23
包含makefile,config.mk和其他以bsp开头的mk文件,例如bspzc702.mk,bspzc706.mk是针对相同芯片但是外设不同做出的定制mk,方便编译生成针对不同板子的系统文件。
打开makefile文件,如图22:
图 24
首先是寻找config.mk文件,SYLIXOS_BASE_PATH 宏定义就是在config.mk里。第49行是将路径里的空格替换成“\ ”,各种宏定义在config.mk 或者base 的config.mk 或mktemp中。
图 25
图25 中77行到87行是选择要编译不同板子的mk文件。
以bspzc702.mk为例,如下图26:
图 26
第35行是要编译的特定板子的代码,第41,42行分别是依赖库和依赖库的路径
第51行是要编译生成的目标文件。第56行包含了 commsrc.mk
在commsrc.mk中包含了共用的代码。在第63行中的COMMON_SRCS 就定义在commsrc.mk中。
图 27
如上图27,第69行是依赖的头文件路径,78行是编译时所需要的符号,第85,86行分别是c语言,c++编译参数 。第100行是指定链接脚本。
SylixOS App makefile
以qtum程序为例,首先打开项目下的makefile
图 28
Makefile头部还是与bsp基本相同的内容。
图 29
第78到91行包含了要编译文件。
打开libunivalue.mk 文件
图 30
第30行指定了编译的目标文件,是编程生成libunivalue.so动态库。
第35行是要编译的源代码,第43行是编译源代码要用到的头文件的路径。
第54行是要编译文件中药使用的符号。第62行是C语言编译命令
第63行是c++编译命令,-std=c++11是使用c++11标准编译,-static是禁止使用动态库。
图 31
第68,69行分别是依赖的库,和库的路径。第73和74行是否使用c++和c++异常。第80是是否使用代码覆盖率检查。
打开libbitcoin_common.mk 文件,
图 32
、
此文件的最终目标是生成libbitcoin_common.so 动态库
图 33
LOCAL_SRCS 是要编译的文件
图 34
LCOAL_INC_PATH是依赖的头文件路径
图 35
LOCAL_DSTMBOL是编译文件时需要的符号
图 36
LOCAL_DEPEND_LIB 是依赖的库,可以看到依赖leveldb.so,cryptopp.so,boost_thread-mt.so等库。
以leveldb.so 为例,它是由 lilevldb.mk编译生成,如下图
图 37
再以boost_thread-mt.so为例它是由libboost项目编译出来的动态库,所以在上面图36的依赖路径第171行加入libboost的路径。
再次查看
在libboost项目中,如下图
图 38
可以看到libboost_thread-mt.mk 编译生成的动态库。
由于编译代码时候需要多个依赖库,不同的依赖库之间可能还有顺序要求,LOCAL_DEPEND_TARGET 用来控制依赖的库是否存在,如果不存在先编译依赖的库。
附录
Gcc 编译命令
-x language filename
设定文件所使用的语言,使后缀名无效,对以后的多个有效.也就是根据约定C语言的后缀名称是.c的,而C++的后缀名是.C或者.cpp,如果你很个性,决定你的C代码文件的后缀名是.pig 哈哈,那你就要用这个参数,这个参数对他后面的文件名都起作用,除非到了下一个参数的使用。
可以使用的参数吗有下面的这些
`c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `assembler-with-cpp'.
看到英文,应该可以理解的。
例子用法:
gcc -x c hello.pig
-x none filename
关掉上一个选项,也就是让gcc根据文件名后缀,自动识别文件类型
例子用法:
gcc -x c hello.pig -x none hello2.c
-c
只**预处理,编译,和汇编,也就是他只把程序做成obj文件
例子用法:
gcc -c hello.c
他将生成.o的obj文件
gcc -c test.s
将汇编输出文件test.s编译输出test.o文件。
-S
只**预处理和编译,就是指把文件编译成为汇编代码。
例子用法:
gcc -S hello.c
他将生成.s的汇编代码,你可以用文本编辑器察看
gcc -S test.i
将预处理输出文件test.i汇编成test.s文件
-E
只**预处理,这个不生成文件,你需要把它重定向到一个输出文件里面.
例子用法:
gcc -E hello.c >; pianoapan.txt
gcc -E hello.c | more
慢慢看吧,一个hello word 也要与处理成800行的代码
gcc -E test.c -o test.i
-o
制定目标名称,缺省的时候,gcc 编译出来的文件是a.out,很难听,如果你和我有同感,改掉它,哈哈
例子用法:
gcc -o hello.exe hello.c (哦,windows用习惯了)
gcc -o hello.asm -S hello.c
-pipe
使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,可能有些问题
gcc -pipe -o hello.exe hello.c
-ansi
关闭gnu c中与ansi c不兼容的特性,**ansi c的专有特性(包括禁止一些asm inline typeof关键字,以及UNIX,vax等预处理宏,
-fno-asm
此选项实现ansi选项的功能的一部分,它禁止将asm,inline和typeof用作关键字。
-fno-strict-prototype
只对g++起作用,使用这个选项,g++将对不带参数的函数,都认为是没有显式的对参数的个数和类型说明,而不是没有参数.
而gcc无论是否使用这个参数,都将对没有带参数的函数,认为城没有显式说明的类型
-fthis-is-varialble
就是向传统c++看齐,可以使用this当一般变量使用.
-fcond-mismatch
允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前两个参数)或者 signed char(后两个参数)
-include file
包含某个代码,简单来说,就是便以某个文件,需要另一个文件的时候,就可以用它设定,功能就相当于在代码中使用#include<filename>;
例子用法:
gcc hello.c -include /root/pianopan.h
-imacros file
将file文件的宏,扩展到gcc/g++的输入文件,宏定义本身并不出现在输入文件中
-Dmacro
以字符串“1”定义 MACRO 宏
相当于C语言中的#define macro
-Dmacro=defn
以字符串“DEFN”定义 MACRO 宏
相当于C语言中的#define macro defn
-Umacro
取消对 MACRO 宏的定义
相当于C语言中的#undef macro
-undef
取消对任何非标准宏的定义
-Idir
在你是用#include"file"的时候,gcc/g++会先在当前目录查找你所制定的头文件,如果没有找到,他回到缺省的头文件目录找,如果使用-I制定了目录,他
回先在你所制定的目录查找,然后再按常规的顺序去找.
对于#include<file>;,gcc/g++会到-I制定的目录查找,查找不到,然后将到系统的缺省的头文件目录查找
-I-
就是取消前一个参数的功能,所以一般在-Idir之后使用
-idirafter dir
在-I的目录里面查找失败,讲到这个目录里面查找.
-iprefix prefix
-iwithprefix dir
一般一起使用,当-I的目录查找失败,会到prefix+dir下查找
-nostdinc
使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头文件的位置
-nostdin C++
规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创libg++库使用
-C
在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序,用这个很方便的
-M
生成文件关联的信息。包含目标文件所依赖的所有源代码你可以用gcc -M hello.c来测试一下,很简单。
-MM
和上面的那个一样,但是它将忽略由#include<file>;造成的依赖关系。
-MD
和-M相同,但是输出将导入到.d的文件里面
-MMD
和-MM相同,但是输出将导入到.d的文件里面
-Wa,option
此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然后传递给会汇编程序
-Wl.option
此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序.
-llibrary
制定编译的时候使用的库
例子用法
gcc -lcurses hello.c
使用ncurses库编译程序
-Ldir
制定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然
编译器将只在标准库的目录找。这个dir就是目录的名称。
-O0
-O1
-O2
-O3
编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
例子用法:
gcc -O1 test.c -o test
使用编译优化级别1编译程序。级别为1~3,级别越大优化效果越好,但编译时间越长
-g
只是编译器,在编译的时候,产生调试信息。
-gstabs
此选项以stabs格式声称调试信息,但是不包括gdb调试信息.
-gstabs+
此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息.
-ggdb
此选项将尽可能的生成gdb的可以使用的调试信息.
-static
此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么
动态连接库,就可以运行.
-share
此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-traditional
试图让编译器支持传统的C语言特性
-IDIRECTORY
指定额外的头文件搜索路径DIRECTORY
-LDIRECTORY
指定额外的函数库搜索路径DIRECTORY
-lLIBRARY
连接时搜索指定的函数库LIBRARY
-m486
针对 486 进行代码优化
-shared
生成共享目标文件。通常用在建立共享库时
-static
禁止使用共享连接
-w
不生成任何警告信息
-Wall
生成所有警告信息
-save-temps
一次获得全部的中文输出文件,正常的进行编译连接,.i、.s、.o为后缀,文件名相同
-fsyntax-only
不会执行预处理、编译、汇编、连接,只会测试输入文件的语法是否正确
-std
指定C方言,如:-std=c99,gcc默认的方言是GNU C