嵌入式 Linux 学习笔记(03):交叉编译、GCC 与 Makefile

交叉编译原理、GCC 编译过程与库制作、Makefile 从基本规则到工程实例。

📚 本文是《嵌入式 Linux 应用开发学习笔记》系列第 03 / 7 篇:交叉编译、GCC 与 Makefile。返回系列目录。

第1章 交叉编译

获取源码后,想要给ARM板编译出hello程序

arm-buildroot-linux-gnueabihf-gcc -o hello hello.c

若hello程序如下

01 #include <stdio.h>
02
03 /* 执行命令: ./hello weidongshan
04 * argc = 2
05 * argv[0] = ./hello
06 * argv[1] = weidongshan
07 */
08
09 int main(int argc, char **argv)
10 {
11 		if (argc >= 2)
12 			printf("Hello, %s!\n", argv[1]);
13 		else
14 			printf("Hello, world!\n");
15 		return 0;
16 }
  1. 头文件在哪

    进入交叉编译器的目录里,执行:find -name “stdio.h”,它位于一个“include”目录下的根目录里。这个“include”目录,就是要找的路径(系统目录)

    或者自己指定

  2. 怎么指定头文件目录

    编译时,加上“-I <头文件目录>”这样的选项。

  3. 怎么确定交叉编译时库文件

    进入交叉编译器的目录里,执行:find -name lib,可以得到 xxxx/lib、

    xxxx/usr/lib,一般来说这 2 个目录就是要找的路径。如果有很多类似的 lib,

    进去看看,有很多 so 文件的目录一般就是要找的路径

  4. 怎么指定自己的库文件目录、库文件

    编译时,加上“-L <库文件目录>”这样的选项,用来指定库目录;

    编译时,加上“-labc”这样的选项,用来指定库文件 libabc.so。

第2章 GCC编译器使用

2.1GCC编译过程

image-20250807212639382

通过不同的编译选项可以控制这些过程

image-20250807212755548

image-20250807213106882

2.1.2编译多个文件

  1. 一起编译、链接

    gcc -o test main.c sub.c

  2. 分开编译,统一链接

    gcc -c -o main.o main.c

    gcc -c -o sub.o sub.c

    gcc -o test main.o sub.o

2.1.3制作使用动态库

step1:制作、编译

gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -shared -o libsub.so sub.o sub2.o sub3.o(可以使用多个.o 生成动态库)
gcc -o test main.o -lsub -L /libsub.so/所在目录/

step2:运行

  1. 先把 libsub.so 放到 Ubuntu 的/lib 目录,然后就可以运行 test 程序
  2. 如果不想把 libsub.so 放到/lib,也可以放在某个目录比如/a,然后执行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/a 
./test

2.1.4制作使用静态库

gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
ar crs libsub.a sub.o sub2.o sub3.o(可以使用多个.o 生成静态库)
gcc -o test main.o libsub.a (如果.a 不在当前目录下,需要指定它的绝对或相对路径)

执行 arm-buildroot-linux-gnueabihf-gcc -c -o sub.o sub.c 交叉编译需要在最后面加上-fPIC 参数

第3章 Makefile的使用

3.1Makefile大纲

3.1.1为什么需要Makefile

修改源文件或头文件,只需要重新编译牵涉到的文件就可以重新生成APP

3.1.2Makefile基本规则

目标文件(target)…: 依赖文件(prerequiries)…
<tab>命令(command)

3.1.3一步步完善Makefile

1.如果没有makefile

使用gcc -o test a.c b.c

前三步统称为编译,最后一步链接

image-20250808143112824

很简单,但是对所有文件都再处理一次,即使b.c没有修改,a.c修改了

优化上一步可以a.c和b.c分开编译,最后链接

gcc -c -o a.o a.c

gcc -c -o b.o b.c

gcc -o test a.o b.o

如何判断哪些文件被修改了?通过比较时间

目标文件(target)…: 依赖文件(prerequiries)…
<tab>命令(command)

当依赖文件比目标文件信,就执行命令

2.最简单的Makefile
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

执行make命令,此时修改a.c,只执行两条gcc命令

3.引入通配符

相似规则太多,可以使用通配符

test:a.o b.o c.o
	gcc -o test $^
	//$^表示所有依赖
%.o:%.c
	gcc -c -o $@ $<
	//$@表示目标文件 $<表示依赖文件
  • %.o:通配符
  • $@:表示目标
  • $<:表示第一个依赖文件
  • $^:表示所有依赖文件
4.引入clean、.PHONY

垃圾文件太多

test: a.o b.o c.o
	gcc -o test $^
%.o : %.c
	gcc -c -o $@ $<
clean:
	rm *.o test
.PHONY: clean	

make:执行第一个目标

make [目标]:执行指定目标

假想目标.PHONY

5.即时变量、延时变量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
A := $(C)				#A的值即时确定,在定义时即确定,赋值						   #为C,C现在为空
B = $(C)				#B的值使用到才确定
C = abc					#C赋值字符串abc

#D = 100ask
# 若 D 未定义则设为 weidongshan,已定义则保持原值
D ?= weidongshan		

# 默认目标 all:打印各变量最终值
all:
	@echo A = $(A)   # A 为空(在第 1 行已固定)
	@echo B = $(B)   # B 为 abc123(C 后面被追加 123)
	@echo D = $(D)   # D 为 weidongshan(未被覆盖)

# C += 123      # 将 123 追加到 C 末尾,使 C 变成 abc123
C += 123
6.Makefile函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#A赋值 a b b
A = a b c 
#对A中每一个元素,取出来赋值给f,把f改为这种形式
B = $(foreach f, $(A), $(f).o)

#C赋值
C = a b c d/

#对C中符合%/格式的内容,过滤留下来
D = $(filter %/, $(C))

#对C中符合%/格式的内容,过滤出去
E = $(filter-out %/, $(C))

#*.c格式的文件是否存在,把存在的列出来
files = $(wildcard *.c)

files2 = a.c b.c c.c d.c e.c  abc

#找到file2中存在的文件列出来
files3 = $(wildcard $(files2))

#找到file2中符合%.c的文件,将其改为%.d
dep_files = $(patsubst %.c,%.d,$(files2))

all:
	@echo B = $(B)
	@echo D = $(D)
	@echo E = $(E)
	@echo files = $(files)
	@echo files3 = $(files3)
	@echo dep_files = $(dep_files)

image-20250808154303640

3.2Makefile实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
objs = a.o b.o c.o
#把objs所有文件加前缀.加后缀.d
dep_files := $(patsubst %,.%.d, $(objs))
#找到dep_files中存在的文件列出来
dep_files := $(wildcard $(dep_files))

#编译参数
CFLAGS = -Werror -Iinclude

test: $(objs)
	gcc -o test $^

#如果dep_files不为空,就把这个文件包含
ifneq ($(dep_files),)
include $(dep_files)
endif

%.o : %.c
	gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d

clean:
	rm *.o test

distclean:
	rm $(dep_files)
	
.PHONY: clean	
使用 Hugo 构建
主题 Stack 由 Jimmy 设计