IDE指集成开发环境(Integrated Development Rnvironment)。
我们开发的STM32F103等单片机程序时使用keil就是一种IDE。
使用IDE,很容易操作,点点鼠标就可完成:
使用GitBash执行命令的话,GitBash采用类似Linux的文件路径表示方法(比如/d/abc,而非d:\abc),命令行中windows格式的路径名要加上双引号,比如“.\objects\main.o”
使用dos命令执行命令的话,不需要加双引号。
armcc
ARM公司的编译器,keil使用的就是armcc
gcc
GNU工具链,Linux等开源软件经常使用gcc
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 //再链接
一个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
使用keil,mdk,avr等工具开发程序时点击鼠标就可以编译了,它的内部机制是什么?它是怎么组织管理程序?怎么决定编译哪一个文件?
答:实际上Windows工具管理程序的内部机制,也是Makefile,我们在Linux下开发裸板程序的时候,使用Makefile组织管理这些程序。
gcc -o test a.c b.c
这条命令虽然简单,但是它完成的功能不简单。
我们来看看它做了哪些事情,我们知道.c程序,得到可执行程序它们之间要经过四个步骤:
对于上面那行代码,要经过:
第一次编译 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最基本的语法是规则
目标: 依赖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’用于修改文件的时间戳,如果文件存在,则更新它们的访问时间和修改时间为当前时间,这仅仅更新文件的时间戳,而不修改文件内容。
如果文件不存在,这创建两个空文件。