1 交叉编译简介
1.1 什么是交叉编译
- 本地编译:在当前编译平台下,编译出来的程序只能放到当前平台下运行
- 在当前编译平台下,编译出来的程序运行在体系结构不同的另外一种目标平台上,但是编译平台 本身不能运行该程序
1.2 为什么会有交叉编译
-
Speed:目标平台的运行速度比主机慢很多,嵌入式的硬件没有太高的性能
-
Capability:编译过程非常消耗资源,嵌入式系统没有足够的内存或磁盘来完成编译
-
Availability:即使资源充足,但是第一个目标平台的运行的编译器也需要交叉编译获得
-
Flexibility:一个完整的编译环境有很多依赖包,会占用目标平台的资源,交叉编译使我们不需要花时间将各种 支持包移植到目标平台上
1.3 交叉编译链
交叉编译链:
一系列软件开发工具的集合,通常包含以下组件:
- 编译器:在嵌入式开发里,通常指的就是GCC
- Binutils:一些二进制工具的集合,比如链接器ld,汇编器as
- C library:常见的C库实现有,glibc,uClibc,musl
- 调试器:GDB
SDK中通常包含预编译好的工具链
如果要改变C库的某些特性,则需要修改配置并重新编译工具链
2 GCC与编译基础
2.1 GCC简介
-
GCC的前身是 GNU C Compiler,随着时间的推移, 已经发展到支持许多语言,如C(gcc)、C++ (g++)、Objective-C、Java(gcj)。它现在被称 为 GNU Compiler Collection
-
GCC是“GNU Toolchain”的一个关键组成部分,GNU Toolchain包括以下的这些工具:
- GNU Compiler Collection (GCC):一个支持许多语言的编译器套件,如C/C++和 Objective-C/C++
- GNU Make:一个用于编译和构建应用程序的自动化工具
- GNU Binutils: 一套二进制实用工具,包括链接器和汇编器
- GNU Debugger(GDB)
- GNU Autotools: 一个构建系统,包括 Autoconf, Autoheader, Automake 和 Libtool
- GNU Bison: 一个parser生成器(类似于lex和yacc)
-
GCC是可移植的,可以在许多操作平台上运行
- 所有类Unix操作系统
- windows
- 交叉编译
2.2 GCC的使用
gcc test.c
: 会自动生成a.out
可执行文件,使用时.a.out
gcc test.c -o test
:指定gcc输出的文件名,使用时./test
gcc -I -L -l
:-I
指定头文件搜索路径-L
指定头文件搜索路径-l
指定库文件
2.3 GCC编译过程
- 预处理(Preprocessing):
cpp xxx.c > xxx.i
,预处理后生成.i
文件 - 编译(Compiler):
gcc –S xxx.i
编译后生成.s
文件 - 汇编(Assembler):
as -o xxx xxx.s
编译后生成.o
文件 - 链接(Linker):
ld xxx xxx.o library
,链接后生成可执行文件
3 Makefile基础
3.1 什么是Make
-
GNU Make是一个构建自动化(build automation)工具,可以从源代码自动构建生成
-
可执行程序 Make定义了一种用于描述源代码、中间文件和可执行文件之间关系的语言
-
这些描述关系保存在一个名为Makefile的文件里面
3.2 Makefile示例
-
Makefile包含了一系列用于编译的描述规则(rule)
-
每个规则包含了3个部分:
- 目标 target
- 依赖项 prerequisites
- 指令 commands(tab缩进)
target: prereq1 rereq2 commands
-
Make当make在解析makefile的时候,遵循如下的逻辑
- 如果在命令行调用make时,没有指定target,默认从第一条规则开始解析(test)
- 在解析每条规则时,先检查是否有跟prerequisite相关的规则
- 若有,则先解析prerequisite规则(test.o)
- 解析完所有prerequisite规则后,再回来解析target对应的规则(test)
-
当make在解析每条规则时(以test.o为例),遵循以下逻辑
- 如果存在某个prerequisite存在更新(修改时间比较比target新,或target不存在),则按照command更新target(gcc -c test.c)
- 若没有prerequisite存在更新,则什么都不执行,直接返回
-
逻辑自上而下,执行自下而上(递归)
3.3 Makefile基本规则
- Make默认会打印每条command,如果不希望看到,可以在每条 command前加
@
- makefile的文件名可以取名为:makefile、Makefile、GNUMakefile
- 当我们执行make指令时,默认读取同目录下的makefile
- 每一条command必须用一个
tab
进行缩进(不能是空格) - 注释以
#
开头 - 一条语句比较长的情况下,可以使用 \ 进行换行
3.4 Makefile语法规则
3.4.1 伪目标
-
不代表具体文件的target,称之为伪目标(Phony target):比如all、clean等
-
伪目标必定每次都会执行更新(因为伪目标不会生成对应的文件)
-
如果有一个文件名为clean,要怎么处理:使用特殊目标 .PHONY,显示的告诉make那些目标是伪目标
.PHONY: all clean all: test test:test.c gcc -o test test.c clean: rm test
-
一些约定俗称的伪目标:
all
:执行建立应用程序的所有任务install
:把编译的二进制文件安装到系统中clean
:删除从源代码生成的二进制文件distclean
:删除所有不属于源文件的生成文件TAGS
:创建一个供编辑使用的标签表info
:从Texinfo源码中创建GNU信息文件check
:运行与该应用程序相关的测试
3.4.2 自动变量
- 自动变量(Automatic Variables)
$@
target文件名$*
去掉后缀的target文件名$<
第一个prerequisite文件名$^
所有prerequisite文件名,并去掉重复的文件名$+
所有prerequisite文件名$?
所有比target更新的prerequisite
3.4.3 模式规则
-
模式规则(Pattern Rules):通过后缀名去匹配文件,一条规则就能描 述所有c文件与o文件的关系
all: test %: %.c gcc -o $@ $< clean: rm test
3.4.4 预设变量
-
可以通过设置预设变量的值,改变默认规则的行为
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c CC = gcc OUTPUT_OPTION = -o $@ %.o: %.c $(COMPILE.c) = $(OUTPUT_OPTION) $<
- CC:C编译器的名称,默认gcc
- CFLAGS:C编译器的选项,也指定头文件(.h文件)的路径,无默认值
- CFLAGS = -g -O2 -Wall -Werror
- CFLAGS = -I /usr/include –I /path/include
- CPPFLAGS:C预编译器的选项,无默认值
- CPPFLAGS = " -Werror -Wno-unused-but-set-variable" || exit 1
- LDFLAGS:指定库路径和库文件
- LDFLAGS = -L /var/xxx/lib –L /opt/mysql/lib -Wl,R /var/xxx/lib -Wl,R /opt/mysql/lib
3.5 变量与宏
-
变量定义:直接对变量 进行赋值(变量的值包 含右边的所有字符,头部的空格会被删除,尾部的空格会被保留)
LIBRARY = libio.a missing_file: touch $(LIBRARY) ls -l | grep '$(LIBRARY)'
-
变量展开:要获取变量 的值,需要把变量名包 裹在\(()里面(也可以使用\){},但多见于旧 Makefile中
-
简单赋值(:=)- 立即展开
-
递归赋值(=) - 变量被使用时展开
-
条件赋值(?=)- 变量未被定义时展开
-
追加赋值(+=)- 追加到原变量值的尾部
var1 = hello $(word) var2 := hello $(word) var3 = hello var3 += $(word) word := world hello: @echo var1 = $(var1) @echo var2 = $(var2) @echo var3 = $(var3)
-
宏事实上只是定义变量的另外一种方式, 它们的本质是一样的
-
为了便于区分,我们称通过赋值符号定义 的为变量,通过define定义的为宏
define print_hello @echo "hello world" endef hello: $(print_hello)
-
变量主要用在条件语句中,通过不同的取值,控制编译的行为
-
make提供4种可用的条件语句
-
包含语句:Make支持通过include语句把其它的makefile包含进来
-
要注意变量或宏之间的覆盖关系
3.6 函数
-
Make提供了一系列内置函数以及支持用户自定义函数
-
函数跟变量的调用跟变量引动的形式类似,但包括一个或者多个参数,用逗号分隔
$(function-name arg1[, argn])
3.6.1 字符函数
-
$(filter pattern ...,text)
- 返回text中与任何pattern相匹配的词,删除任何不 匹配的词,起到过滤的作用。text包含多个以空格分 隔的字符串
- filter函数可用于在一个变量中分离出不同类型的字 符串(如文件名)
sources := foo.c bar.c baz.s ugh.h foo: $(sources) cc $(filter %.c %.s,$(sources)) -o foo
-
$(subst search-string,replace-string,text)
- 起到文本替换的作用。可用于.c和.o文件名之间的替换
sources := count_words.c counter.c lexer.c objects := $(subst .c,.o,$(sources))
-
$(findstring string, text)
- 在text中查找string,如果能找到,则返回string,否 则返回为空
-
$(strip text)
- 去除text头部和尾部的空格,同时若text内部出现连续 多个空字符,会将之合并成一个
3.6.2 文件名函数
$(wildcard pattern...)
:返回与pattern匹配的文件名$(suffix name...)
:从name中提取每一个文件名的后缀$(basename name...)
:从name中提取每一个文件名,并去掉后缀
objects := $(subst .c,.o,$(wildcard *.c))
foo: $(objects)
$(CC) -o foo $(objects)
3.6.3 流程控制函数
$(if condition,then-part,else-part)
:若condition为非空字符串,则返回then-part,否则返回else-part$(error text)
:出发编译错误并把text中的内容打印出来$(foreach variable,list,body)
:对于list中每一个字符串,调用一次body,并把在调用body前把此字符串赋值给variable
3.6.4 shell函数
$(shell command)
:在shell中执行command中包含的指令,并返回指令的输出结果
DATE := $(shell date)
print_date:
@echo $(DATE)
3.6.5 自定义函数
$(call macro-name[, param1...])
:- 自定义函数其实只是宏定义的一种特殊形式,通过使用call函数调用宏, 可以增加参数列表
- 每一个参数通过\((1),\)(2)...这种形式进行引用
define print_func
@echo "param1 = $(1)"
@echo "param2 = $(2)"
endef
hello:
$(call print_func,hello,world)
标签:文件名,target,GNU,交叉,Makefile,编译,test,变量
From: https://www.cnblogs.com/mobbu/p/18334436