KEIL_GCC_MAKEFILE

IDE是什么

IDE指集成开发环境(Integrated Development Rnvironment)。
我们开发的STM32F103等单片机程序时使用keil就是一种IDE。
使用IDE,很容易操作,点点鼠标就可完成:

  • 添加文件
  • 指定文件路径(头文件路径、库文件路径)
  • 指定链接库
  • 编译、链接
  • 下载、调试

IDE的背后是命令

使用GitBash执行命令的话,GitBash采用类似Linux的文件路径表示方法(比如/d/abc,而非d:\abc),命令行中windows格式的路径名要加上双引号,比如“.\objects\main.o”

使用dos命令执行命令的话,不需要加双引号。

有两套主要的编译器

armcc
ARM公司的编译器,keil使用的就是armcc
gcc
GNU工具链,Linux等开源软件经常使用gcc

gcc分类

  • arm-linux-gcc:给ARM芯片编译程序
  • gcc:在x86编译程序

GCC编译过程详解

KEIL_GCC_MAKEFILE_第1张图片

gcc hello.c //输出一个名为a.exe文件的可执行呈,然后可执行a.exe
gcc -o hello hello.c //输出名为hello.exe的可执行程序,然后执行./hello.exe
gcc -o hello hello.c -static //静态链接

gcc -c -o hello.o hello.c //先编译
gcc -o hello hello.c //再链接

gcc常用选项

  • -v查看gcc编译器的版本,显示gcc执行时的详细过程
  • -o :指定输出文件名为file,这个名称不能跟源文件同名
  • -E:只预处理,不会汇编、编译、链接
  • -S:只编译,不会汇编、链接
  • -c:只编译和汇编,不会链接

gcc编译过程

一个C/C++文件要经过预处理、编译、汇编和链接才能变成可执行文件。

预处理
C/C++源文件中,以’#'开头的命令被称为预处理命令,如包含命令#include、宏定义命令#define、条件编译命令#if、#ifdef等。
预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些东西输出到一个“.i”文件中等待进一步处理。
但不会检查语法错误。

编译
编译就是把C/C++代码(比如上述的.i文件)翻译成汇编代码,会检查语法错误。

汇编
汇编就是将汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现为ELF目标文件。反汇编就是将机器代码转换成汇编代码,这在调试程序时常常用到。

链接
链接就是将上步生成的OBJ文件和系统库的OBJ文件、库文件链接起来,最终生成了可以在特定平台运行的只执行文件。

hello.c(预处理)->hello.i(编译)->hello.s(汇编)->hello.c(链接)->hello

详细的每一步命令如下:

gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o

一般常用

gcc -c -o hello.o hello.c
gcc -o hello hello.o
PREFIX=arm-linux-gnueabihf-
CC=$(PREFIX)gcc
LD=$(PREFIX)ld
AR=$(PREFIX)ar
OBJCOPY=$(PREFIX)objcopy
OBJDUMP=$(PREFIX)objdump

led.img : start.S uart.c main.c
	$(CC) -nostdlib -g -c -o start.o start.S
	$(CC) -nostdlib -g -c -o uart.o uart.c
	$(CC) -nostdlib -g -c -o main.o main.c
	
	$(LD) -T stm32mp157.lds -g start.o main.o uart.o -o led.elf
	
	$(OBJCOPY) -O binary -S led.elf led.bin
	$(OBJDUMP) -D -m arm led.elf > led.dis

clean:
	rm -f led.dis led.bin led.elf led.img *.o *.log

Makefile的引入及规则

使用keil,mdk,avr等工具开发程序时点击鼠标就可以编译了,它的内部机制是什么?它是怎么组织管理程序?怎么决定编译哪一个文件?

答:实际上Windows工具管理程序的内部机制,也是Makefile,我们在Linux下开发裸板程序的时候,使用Makefile组织管理这些程序。

gcc -o test a.c b.c

这条命令虽然简单,但是它完成的功能不简单。
我们来看看它做了哪些事情,我们知道.c程序,得到可执行程序它们之间要经过四个步骤:

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

对于上面那行代码,要经过:

  1. 对于a.c:执行预处理、编译、汇编的过程,a.c->xxx.s->xxx.o文件
  2. 对于b.c:执行预处理、编译、汇编的过程,b.c->yyy.s->yyy.o文件
  3. 最后将xxx.o和yyy.o链接在一起得到一个test应用程序。

第一次编译 a.c 得到 xxx.o 文件,这是很合乎情理的, 执行完第一次之后,如果修改 a.c 又再次执行:gcc -o test a.c b.c,对于 a.c 应该重新生成 xxx.o,但是对于 b.c 又会重新编译一次,这完全没有必要,b.c 根本没有修改,直接使用第一次生成的 yyy.o 文件就可以了。

缺点:对所有的文件都会在处理一次,即使b.c没有经过修改,b.c也会重新编译一次,当文件比较少时,这没有什么问题,当文件非常多的时候,就会带来效率问题,只修改一个文件,却让所有文件都重新编译一次。

对于这些源文件,我们应该分别处理:先分别编译它们,再链接在一起。

gcc -c -o a.o a.c
gcc -c -o b.o b.c

gcc -o test a.o b.o

使用这个方式,当修改a.c之后,a.c会重新编译然后把它们链接在一起,b.c就不需要重新编译。

问题就变为了如何知道哪些文件被更新了/被修改了?

比较时间:比较a.o和a.c的时间,如果a.c的时间比a.o的时间更加新的话,就表明a.c被修改了;同理b.o和b.c也会进行通用的比较;还有test和a.o与b.o的时间,如果a.o或者b.o的时间比test更新的话,就表明应该重新生成test。

Makefile基本语法

makefile最基本的语法是规则

目标: 依赖1 依赖2 ...
	命令

当依赖比目标新,执行它们下面的命令

test : a.o b.o
	gcc -o test a.o b.o

a.o : a.c
	gcc -c -o a.o a.c

b.o : b.c
	gcc -c -o b.o b.c
touch a.c b.c

命令’touch’用于修改文件的时间戳,如果文件存在,则更新它们的访问时间和修改时间为当前时间,这仅仅更新文件的时间戳,而不修改文件内容。
如果文件不存在,这创建两个空文件。

你可能感兴趣的:(stm32,Makefile)