/*
在makefile文件中定义的变量,有点类似C语言中的宏,它代表了一个文本字符串;在C语言中,在编译的预处理阶段,用到宏的地方都会用宏的定义进行精确的展开,而在makefile文件执行的时候,变量也会自动按它的值展开在所使用的位置。既然称作变量,当然就是可改变的,这就是它与C语言中的宏的不同之处。
在上面定义了一个变量MCU,并赋予它的初值为atmega128。
变量的赋值,有如下三种方法:
1). MCU = atmega128 //直接赋值
2). MCU := $(AVR) //直接赋值,但有限制
3). MCU+ = atmega128 //追加方式
讲下第二种,这句话的意思是把变量AVR的值赋予变量MCU,使用一个变量时需要用$(),这个要记住!“=”号前有个“:”,这要求变量AVR要在变量MCU定义之前被定义,这就是限制!
前面讲到它有点类似C语言中的宏,它代表了一个文本字符串,追加方式可以理解为两个字符串连接在一起。
这个变量的作用主要是传递MCU型号给编译器,好让它选择与MCU型号对应头文件等。
*/
# Output format. (can be srec, ihex, binary)
FORMAT = ihex
/* 定义一个变量FORMAT,值为ihex,作用是选择生成的烧录文件的格式,该变量可设置的值有srec, ihex, binary */
# Target file name (without extension).
TARGET = main
/* 定义一个变量TARGET,值为main,作用是告诉编译器最终目标的名字叫什么,在这里就讲下什么叫目标,请看下面:
foo.o: foo.c foo.h
gcc –c –g foo.c
foo.o也就是目标!foo.c foo.h是foo.o目标所依赖的文件,如果foo.c与foo.h当中有一个或以上文件比foo.o文件要新的话,就执行命令gcc –c –g foo.c,注意命令必须以Tab键开始!
将它一般化就如下:
target ... : prerequisites ...
command
target是一个目标文件,可以是中间代码文件(Object File),也可以是执行文件,还可以是一个标签(Label)。
prerequisites是要生成那个target所需要或依赖的文件或目标。
command是make需要执行的命令(以Tab键开始)。
说白一点就是,prerequisites中如果有一个或以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则,也就是Makefile中最核心的内容。
*/
# List C source files here. (C dependencies are automatically generated.)
SRC = app.c fun.c
/* 定义一个变量SRC,值为app.c fun.c,作用是选择需要编译的C源文件 */
# List Assembler source files here.
# Make them always end in a capital .S. Files ending in a lowercase .s
# will not be considered source files but generated files (assembler
# output from the compiler), and will be deleted upon "make clean"!
# Even though the DOS/Win* filesystem matches both .s and .S the same,
# it will preserve the spelling of the filenames, and gcc itself does
# care about how the name is spelled on its command-line.
ASRC =
/* 定义一个变量ASRC,值为空,作用是选择需要编译的汇编源文件 */
# Optimization level, can be [0, 1, 2, 3, s].
# 0 = turn off optimization. s = optimize for size.
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s
/* 定义一个变量OPT,值为s,作用是选择编译优化级别,该变量可设置的值有0, 1, 2, 3, s;
这里选择s,也就是告诉编译器,专门优化代码的大小*/
# Debugging format.
# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
DEBUG = stabs
/*定义一个变量DEBUG,值为stabs,作用是选择调试格式,可设置的值有stabs [缺省],dwarf-2;
而COFF调试文件要求选择stabs,以插入avr-objcopy文件运行。*/
# List any extra directories to look for include files here.
# Each directory must be seperated by a space.
EXTRAINCDIRS = ./include
/* 定义一个变量EXTRAINCDIRS,值为./include,作用是设置编译器编译时搜索包含文件的路径,可以设置多个,但要用空格分开。
假如上面的foo.c包含了foo.h文件,而foo.h文件在foo.c所在目录的子目录include中,那么EXTRAINCDIRS 设为 ./include,如果foo.h文件在foo.c所在目录的上一级目录的另一子目录iclude中,EXTRAINCDIRS 应设为 ../include
*/
# Compiler flag to set the C Standard level.
# c89 - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99 - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
CSTANDARD = -std=gnu99
/* 定义一个变量CSTANDARD,值为gnu99,作用是选择编译器版本
*/
# Place -D or -U options here
CDEFS =
# Place -I options here
CINCS =
/* 定义变量CDEFS和CINCS,值为空,作用是设置编译选项,前面命令gcc –c –g foo.c中–c –g就是编译选项,下面是更多的编译选项: */
# Compiler flags.
# -g*: generate debugging information 产生调试信息
# -O*: optimization level 优化级别
# -f...: tuning, see GCC manual and avr-libc documentation
# -Wall...: warning level 警告级别
# -Wa,...: tell GCC to pass this to the assembler. 传递参数给汇编器
# -adhlns...: create assembler listing 创建汇编列表
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS) $(CINCS)
CFLAGS += -O$(OPT)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wstrict-prototypes
CFLAGS += -Wa,-adhlns=$(<:.c=.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)
/* 定义变量CFLAGS,值为-g$(DEBUG),作用是设置编译选项,前面定义了DEBUG = stabs,
所以CFLAGS值应为-g stabs,之后对变量CFLAGS进行追加,完成之后,CFLAGS值为综合的编译选项!
讲下-adhlns=$(<:.c=.lst)的意思:<是一个临时变量,它的值是一些文件的列表,<:.c=.lst就是把<变量的值中所有.c扩展符替换为.lst,然后再赋予变量-adhlns,举个例子,假如变量<的值为app.c fun.c,那么<:.c=.lst则为app.lst fun.lst。
在这里并不打算细致讲解上面是什么意思
*/
# Assembler flags.
# -Wa,...: tell GCC to pass this to the assembler. 传递参数给汇编器
# -ahlms: create listing 创建列表
# -gstabs: have the assembler create line number information; note that
# for use in COFF files, additional information about filenames
# and function names needs to be present in the assembler source
# files -- see avr-libc docs [FIXME: not yet described there]
# 大概就是告诉汇编器产生行号、文件名、函数名信息,因为COFF调试文件
# 要用到
ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
/*定义变量ASFLAGS,值为-Wa,-adhlns=$(<:.S=.lst),-gstabs,作用是设置汇编选项,基本和上面差不多 */
#Additional libraries.
/* 额外库文件配置选项 */
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
/*定义变量PRINTF_LIB_MIN,值为-Wl,-u,vfprintf -lprintf_min,作用是设置printf版本为迷你版 */
# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
/*定义变量PRINTF_LIB_FLOAT,值为-Wl,-u,vfprintf -lprintf_flt,作用是设置printf版本为支持浮点数版 */
/* 上面的两个变量其实并不传给编译器,以下的是起作用的,如果要设置printf版本为迷你版,只需要PRINTF_LIB =$( PRINTF_LIB_MIN),下面空着是代表默认的标准版 */
PRINTF_LIB =
# Minimalistic scanf version
/* 有printf当然也有scanf,下面是它的配置选项,和printf差不多,跳过 */
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
SCANF_LIB =
/* 下面是数学库的配置选项,定义变量MATH_LIB,值为-lm */
MATH_LIB = -lm
# External memory options
/* 下面是外扩内存的配置选项 */
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
/*
大概就是MCU为ATmega128时,外扩了64 KB的 RAM,如果你想既想用它来做数据区,又想用它来做堆区(动态内存分配),就设置EXTMEMOPTS为这个值
*/
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
/*
大概就是MCU为ATmega128时,外扩了64 KB的 RAM,仅用它来做堆区(动态内存分配),就设置EXTMEMOPTS为这个值
*/
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
/* 一般MCU不扩外部RAM,就让它空着 */
EXTMEMOPTS =
# Linker flags.
# -Wl,...: tell GCC to pass this to linker. 传给链接器
# -Map: create map file 建立映射文件
# --cref: add cross reference to map file 增加交叉重映射文件
/* 首先解析一下什么叫编译和链接(一位前辈写的):
一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语**确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File |
|