嵌入式Linux(四):GCC编译C代码 Makefile编译文件编写
前言
编写嵌入式Linux应用程序时,编译工具使用的一般是交叉编译版本的GCC,使用方式跟GCC一样。本文,记录使用GCC编译的相关命令,以及编写Makefile文件来编译项目。
(一):GCC编译为可执行程序全过程
1.1 四个阶段
C语言代码编译链接成一个可执行程序,需要经过四个阶段。
(1) 预编译
展开宏,查找头文件目录,main.c -> main.i
(2) 编译
将C/C++代码转换为汇编代码,mian.i->main.s
(3) 汇编
将汇编代码编译为二进制机器码,main.s->main.o
(4) 链接
将多个目标文件(.o)链接成一个可执行程序,main.o->可执行APP
1.2 四个阶段GCC对应的指令
/*hello.c*/
#include <stdio.h>
int main(int args,char **argv){
if(args>2){
printf("hello %s !\n",argv[1]);
}else{
printf("hello world!\n");
}
return 0;
}
//预编译
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语言项目
2.1 完整编译一个项目
//先汇编再链接
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
./test
2.1.1 main.c
/*main.c*/
#include <stdio.h>
#include "sub.h"
int main(int args,char **argv){
printf("this main!\n");
sub_func();
return 0;
}
2.1.2 sub.c
/*sub.c*/
#include "sub.h"
#include <stdio.h>
void sub_func(){
printf("this sub function!\n");
}
2.1.3 sub.h
/*sub.h*/
void sub_func();
2.2 尖括号与双引号
#include <xxx.h>
系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。
#include "xxx.h"
用户自定义的头文件用双引号括起来,编译器首先会在用户目录下查找,然后再到 C++ 的安装目录(Linux 中可以通过环境变量来设定)中查找,最后在系统文件中查找。
当然,在用gcc编译时,也可指定第一个首先搜索头文件的目录
//从当前路径首先搜索头文件
gcc -c -o main.o main.c -I ./
-I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录
-L /home/hello/lib表示将/home/hello/lib目录作为第一个寻找库文件的目录
2.3 制作静态库文件(.a)
使用静态库链接程序时,会将动态库打包到程序里,可以脱离原来的静态库文件运行程序。
//制作目标文件
gcc -c -o sub.o sub.c
//生成静态库
ar crs libsub.a sub.o
//使用静态库编译,需要指定静态库的绝对路径
gcc -o test main.o libsub.a
//测试
./test
2.4 制作动态库 (.so)
使用动态库编译程序时,并没有将动态库添加到可执行程序里,所以程序运行时,需要依赖外部的动态库。可以将动态库移动到 /lib 或 /usr/lib
//生成动态库
gcc -shared -o libsub.so sub.o
//使用动态库编译程序
gcc-o test main.o -lsub -L ./
//测试 需要将动态库移动到 /lib /usr/lib 或者 export LD_LIBRARY_PATH:$LD_LIBRARY_PATH:./
sudo cp libsub.so /lib
./test
(三):Makefile 编写
3.1 Makefile规则
3.1.1 目标依赖
当依赖比目标更新时,执行
目标文件:依赖文件
【TAB】命令
3.1.2 通配符
%.o
$@ 表示目标
$< 第1个依赖
$^ 所有依赖
3.1.3 变量
A := XXX #及时变量
B = XXX #延时变量,使用时才确定
C ?= XXX #延时变量,只有变量第一次定义才有效
A += hello #附加
3.2 函数
3.2.1 filter
选择指定模板的数据
C = a b c d/
D = $(filter %/,$(c))
3.2.2 filter
选择指定模板的数据
C = a b c d/
D = $(filter %/,$(c))
@echo D = $(D)
3.2.3 filter-out
剔除指定模板的数据
C = a b c d/
D = $(filter-out %/,$(c))
@echo D = $(D)
@echo E = $(E)
3.2.4 wildcard
#取出当前路径所有.c
files = $(wildcard *.c)
files2 = a.c b.c c.c d.c e.c
//取出files2存在的文件
files3 = $(wildcard $(files2))
3.2.5 wildcard
取出并替换
//%.c替换为%.d
dep_files = $(patsubst %.c,%d,$(files2))
3.3 完整的Makefile编译程序
#所有程序文件
objs = main.o test.o
#选择头文件
dep_files := $(patsubst %,.%.d,$(objs))
dep_files := $(wildcard $(dep_files))
#链接
test: $(objs)
gcc -o test $^
#导入头文件路径
ifneq ($(dep_files),)
include $(dep_files)
endif
#汇编
%.o:%.c
gcc -c -o $@ $< -MD -MF .$@.d
#清空汇编文件与程序
clean:
rm *.o test
#删除头文件链接
distclean:
rm $(dep_files)
.PHONY:clean
结语
这些笔记主要是记录韦东山老师视频里关于GCC和Makefile里讲的内容。下面贴下链接
https://www.bilibili.com/video/BV1w4411B7a4?p=39&vd_source=9385b7f8c739b9e3ef3f21ddaebd2eb9