当前位置: 首页 > news >正文

看懂 Makefile 第一课:基础

1 Makefile 基础

1.1 Makefile 作用

​ 一个工程中的源文件、配置文件是不计其数的,因此在输入很长的命令或一系列固定命令来进行编译、解压缩、配置路径等功能时,使用 Linux 命令行是极其不方便的,而 Makefile 就是按照某些规则,进行自动编译,自动执行的脚本工具(类似于 shell 脚本),极大提高了开发效率,同时,它遵循了 IEEE 的 POSIX 标准。

​ GCC 是 Linux 下常用的编译工具,了解 Makefile 前,我们先来了解一下 GCC 编译和链接的过程:

  1. 预处理(gcc -E):由预处理器 CPP 完成,它会将 .c 文件预处理为 .i 文件( .i 文件的本质还是 .c 文件)。预处理会将宏定义进行简单的替换、同时判断条件编译的结果、复制粘贴 #include 头文件具体内容、删除注释、添加行号和文件标识,还会保留 #pragma 编译器指令;
  2. 编译(gcc -S):由编译器 CC 完成,它会将 .i 文件变为 .s 文件。编译器首先会检查语法,同时将 C 语言转换为等效,优化后的汇编语言,C 语言的可移植性从这里体现,对于不同的平台,只需要使用对应平台的编译器进行编译,同一套 C 语言源代码,就能生成在不同平台上的汇编语言文件。(Ps:编译器优化等级并非越高越好,常用的配置是 OS,约等于 O2 水平);
  3. 汇编(gcc -c):由汇编器 AS 完成,它会将 .s 文件变为 .o 文件。汇编语言将被转化为对应的机器语言(二进制文件),即目标文件,目标文件至少由代码段和数据段组成;
  4. 链接:由链接器 LD 完成。它会将动态库或静态库与执行文件进行链接,头文件中包含了函数的定义,而库中包含了函数对应的声明。将这些库、头文件,与被执行的二进制文件联合在一起的过程,便称为链接,链接过程中,头文件中的函数声明会通过 “函数签名” 找到对应的函数定义来执行,同时,main 函数的入口也是在这时确定下来的;

​ 动态库与静态库:

  • 静态库:用 .a 文件表示。静态库在编译过程中已经融合进程序中,相当于将用到的函数定义复制到可执行程序中,优点是生成的程序无需依赖任何外界库,缺点是占用内存较大,同时静态库一旦更新,需要重新进行编译;

    # 如果希望把源码 file1.c、flle2.c、...fileN.c 做成静态库文件,可以通过下面的命令把它们制作成静态库:
    gcc -c file1.c -I .../.../.../include -o file1.o
    gcc -c file2.c -I .../.../.../include -o file2.o
    ...
    gcc -c fileN.c -I .../.../.../include -o filen.oar -rcs libname.a file1.o file2.o ... filen.o
    
  • 动态库:用 .so 文件表示,windows 称为 .dll 文件。动态库在编译过程中不会被融合进程序中,而是在运行到相关函数时,才从库中调用对应的函数执行。优点是生成的程序占用内存较小,动态库更新,一般情况下不需要重新编译,缺点是程序运行时,依赖对应的动态库。若动态库和静态库同时存在,系统会优先调用动态库;

    gcc -shared -fPIC -o libname.so file1.c file2.c ... fileN.c
    
  • 库的使用:以动态库为例,常用的使用方式如下

    # -L 库函数的路径 -l库的名字:
    gcc main.c -o myapp -L lib_path -lname -lname# 需要注意的是引用多个库时,若库间有依赖关系,被依赖的库需要放在后面:
    # 例如有三个库:libgraphics.a 依赖 libpng.a ,libpng.a 依赖 libz.a ,那么正确的链接顺序:
    gcc main.o -lgraphics -lpng -lz# 同时动态库使用时,一般需要将库的路径加入到环境变量中:
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path_your_lib/.../...
    

​ 总的来说,编译过程主要检查了源文件的语法是否正确,并且编译出一个中间目标文件,而链接,就是链接器根据函数声明,寻找到函数定义的过程。可以看到,在编译、制作动态库、制作静态库时,命令行就并不方便了,而实现自动化编译,就是 Makefile 的一大作用。

​ 在 Linux 命令行输入 make ,Linux 系统会在当前路径下寻找名称为 makefile、Makefile 或 GUNmakefile 的文件,并执行该文件脚本,当然,如果你不想使用 Makefile 默认的命名,可以通过 -f--file 选项指定特定的 Makefile 文件。

1.2 target、prerequisites、command

​ 首先 Makefile 有三种元素,目标(target)、依赖(prerequisites)和动作(command)。

  • target:通俗讲就是一个代号,可以是纯操作的代号,也可以是具体的目标文件;
  • prerequisites:完成目标所需要的条件,可以是其他文件、目标等;
  • command:具体的命令,让系统执行的操作,如 gcc,rm -rf 等。

​ 常用基本语法:

# Makefile 使用 # 来进行注释
# 总目标,Makefile 中的第一个目标就是总目标,后面跟的是实现这个目标需要的依赖。
# 依赖可以是工作路径下的某个文件,也可以是某个目标,若依赖某目标,则需要先执行这个被依赖的目标。
# 例如总目标 all 就会先执行 target1 中的动作,结束后接着执行all目标的动作。
all: depend1 depend2 target1 action1 # 这个目标要实行的动作,例如 gcc、rm -rf 等。action2	# 注意,动作前必须是 TAB 而不是空格。target1:actiontarget2:action# 运行 Makefile 时,默认在控制台输出执行的动作,如果不需要打印信息,可以将 @ 加在 action 之前。
# 引用其他 Makefile ,类似于 C 语言中的 #include<>
include Makefile_name
-include Makefile_name_warning_ignored# Makefile 会先从当前工作路径下寻找 Makefile_name 文件是否存在,我们也可以通过 -I,--include-dir 选项来添加路径。
# 同时 Makefile 也会在 <prefix>/include(一般是/usr/local/bin 或 /usr/include)路径中进行寻找。
# 若没有找到,会生成警告信息,如果想让 make 无视无法读取的文件继续执行,在 include 前加一个 - 号即可。
# 伪目标:一般用于标识纯操作类型的“目标”,具体原因会在 1.3 Makefie 的运行机制中解释。
.PHONY: phonytarget1 phonytarget2 cleanHelloWorld.o: HelloWorld.c HelloWorld.hgcc HelloWorld.c -o HelloWorld.ophonytarget1:actionphonytarget2:actionclean:action
# 赋值语法:
VAR_NAME = testvalue    # 变量被强制赋值为 testvalue。
VAR_NAME ?= testvalue1  # 若变量未被赋值,就赋值为 testvalue1,若在此语句前已被赋值,那么忽略该语句。VAR_NAME +=testvalue2   # 在变量后面追加 testvalue2 值,例如变量之前被赋值为 testvalue1。# 此时变量的值就变成了testvalue1testvalue2,多用于增加 PATH 路径时使用。# 要取出某个变量中的值,可以用 ${变量} 的形式来取出其中的值。
http://www.lryc.cn/news/618863.html

相关文章:

  • 企业培训笔记:宠物信息管理--实现宠物信息的添加
  • c#,vb.net全局多线程锁,可以在任意模块或类中使用,但尽量用多个锁提高效率
  • 行业分享丨SimSolid 在汽车零部件开发中应用的可行性调研及实践
  • 基于Hadoop的汽车价格预测分析及评论情感分析可视化系统
  • 海信IP108H(53U1M)_S905L-B主控-无线SV6051P/8822CS(通刷咪咕mg100_mg101)线刷固件包
  • grpc浅入门
  • 一键生成 Android 适配不同分辨率尺寸的图片
  • 什么是 Spring MVC?
  • AuthController类讲解
  • 龙舌兰人造植物、Apple Watch保护壳、厨房水槽收纳架、家居磁性挂钩等亚马逊热销单品,正在外观专利TRO维权!
  • 备战国赛算法讲解——马尔科夫链,2025国赛数学建模B题详细思路模型更新
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-会议记录
  • Linux网络--2.2、TCP接口
  • 5 重复匹配
  • 51 单片机分层架构的模块依赖关系图
  • 详细解释RBFT和NoxBFT及RAFT的差异
  • PCIe Electrical Idle Sequences ( EIOS and EIEOS )
  • Java 22 新特性:字符串模板(String Templates)让拼接更优雅、更安全
  • 机械学习--TF-IDF实战--红楼梦数据处理
  • 什么是iOS超级签名?为何它能解决企业签名的“掉签”难题?
  • 如何在idea中导入外来文件
  • provide()函数和inject()函数
  • 力扣-394.字符串解码
  • Spark Core 3.3.2 略讲~
  • Java设计模式-快速入门
  • DEA模型MATLAB实现(CCR、BCC、超效率)
  • 优选算法 力扣 18. 四数之和 双指针算法的进化 优化时间复杂度 C++ 题解 每日一题
  • 基于.net高校财务管理系统/c#/asp.net/sql server 设计开发
  • GIT使用攻略
  • IEEE 2025 | 重磅开源!SLAM框架用“法向量+LRU缓存”,将三维重建效率飙升72%!