[工具]Makefile的本质
lipiwang 2024-11-27 17:16 11 浏览 0 评论
对于初学者来说,理解makefile的本质对于后续学习如何编写makefile意义重大。要记住,从更抽象的层次,更高的维度来审视即将要学习的内容,可以极大的降低学习的难度,简化学习的历程,从而达到事半功倍的效果。
Makefile是make构建工具的脚本而已,so easy!
Make是Linux中广泛使用的自动化构建工具,Makefile则是make工具的构建脚本,它们可以帮助开发人员自动化构建过程,提高开发效率和代码质量。
Make和Makefile的关系,类似于bash和bash shell脚本的关系。Make工具解析Makefile文件并构建工程,bash解析shell脚本并执行。
本质上,makefile是一种用于自动化编译和构建代码的脚本文件,其主要作用是简化程序或软件的构建过程,避免手动操作繁琐的编译、链接、打包等步骤,从而提高构建的速度和可靠性。Make工具根据makefile中的规则自动执行构建命令,生成目标文件、库文件或可执行文件等。Makefile的语法比较简单,但是由于其强大的灵活性和可扩展性,可以构建复杂的项目和应用。
从hello和edit工程看Makefile的本质
Hello world的源程序hello.c如下:
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
要运行这个hello world程序,就要先把hello.c源文件编译成可执行文件,然后在运行。
步骤如下:
[root@ ~/work/lpp/makefile/helloworld/test1]ls
hello.c
[root@ ~/work/lpp/makefile/helloworld/test1]gcc hello.c -o hello
[root@ ~/work/lpp/makefile/helloworld/test1]ls
hello hello.c
[root@ ~/work/lpp/makefile/helloworld/test1]./hello
hello, world
[root@ ~/work/lpp/makefile/helloworld/test1]
当修改了hello.c文件,就需要重新输入gcc -o hello hello.c命令进行重新编译,然后就可以再次运行了。
Hello工程只包含一个源文件,重新编译会比较简单,而对于包含多个源文件和头文件的edit工程来说,重新编译就会复杂一些。edit工程包含如下文件:
- 3个头文件: buffer.h、command.h、defs.h
- 8个源文件:
- command.c, 包含头文件defs.h、command.h
- display.c,包含头文件defs.h、buffer.h
- files.c,包含头文件defs.h、buffer.h、command.h
- insert.c,包含头文件defs.h、buffer.h
- kbd.c,包含头文件defs.h、command.h
- main.c,包含头文件defs.h
- search.c,包含头文件defs.h、buffer.h
- utils.c,包含头文件defs.h
那我们如何编译整个工程呢?
方法1: 在命令行上手动输入编译命令
gcc -o edit main.c kbd.c command.c display.c insert.c search.c files.c utils.c
[root@ ~/work/lpp/makefile/edit/test1]ls
buffer.h command.c command.h defs.h display.c files.c insert.c kbd.c main.c search.c utils.c
[root@ ~/work/lpp/makefile/edit/test1]gcc -o edit main.c kbd.c command.c display.c insert.c search.c files.c utils.c
[root@ ~/work/lpp/makefile/edit/test1]ls
buffer.h command.c command.h defs.h display.c edit files.c insert.c kbd.c main.c search.c utils.c
[root@ ~/work/lpp/makefile/edit/test1]./edit
The edit project...
In main.c:main()...
In files.c:files()...
In command.c:command()...
In display.c:display()...
In insert.c:insert()...
In kbd.c:kbd()...
In search.c:search()...
In utils.c:utils...
[root@ ~/work/lpp/makefile/edit/test1]
方法1的问题: 如果每次编译都需要重新输入gcc命令来编译,效率低而且容易出错,对于大型的有成百上千源文件的工程,通过手动输入进行编译的方法更是行不通的。
方法2: 通过shell脚本来进行编译
为了提高效率,可以写一个shell脚本,使用这个shell脚本来编译。
例如: 可以写一个build.sh脚本,
#!/bin/sh
gcc -o edit main.c kbd.c command.c display.c \
insert.c search.c files.c utils.c
每次需要重新编译的时候,只要重新运行这个build.sh脚本就可以了,效率明显提高。
[root@ ~/work/lpp/makefile/edit/test2]ls
buffer.h build.sh command.c command.h defs.h display.c files.c insert.c kbd.c main.c search.c utils.c
[root@ ~/work/lpp/makefile/edit/test2]./build.sh
[root@ ~/work/lpp/makefile/edit/test2]ls
buffer.h build.sh command.c command.h defs.h display.c edit files.c insert.c kbd.c main.c search.c utils.c
[root@ ~/work/lpp/makefile/edit/test2]
方法2的问题: 如果有文件做了修改,如何编译?
- 如果编译之后又对main.c做了修改,又要把所有的源文件编译一遍,那些没有修改的7个源文件也要跟着重新编译,这会浪费时间。
- 在大型的项目中,通常有几十到上百个的源文件,如果每次都要对所有的源文件进行重新编译的话,全部编译可能需要几个小时,效率太低了,另外只改一个源文件就全部重新编译也是不合理的。
为了解决文件修改的问题,可以对edit的编译过程进行优化,就是把各个源文件编译为目标文件。
gcc -c main.c
gcc -c kbd.c
gcc -c command.c
gcc -c display.c
gcc -c insert.c
gcc -c search.c
gcc -c files.c
gcc -c utils.c
如果对main.c做了修改,只需重新编译main.c和重新连接edit,这就就可以节约时间,提高效率。
gcc -c main.c
gcc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o
但优化方案仍然存在问题:
- 每次编译命令都不一样,很容易出错,比如修改了三个源文件,可能有一个忘了重新编译,结果编译完了修改没生效,运行时产生Bug。
- 更复杂的问题是,如果修改了defs.h,还得需要确认哪个源文件包含了defs.h。
通过方法1和方法2来实现对一个软件项目编译的人工维护或半自动化维护,效率非常低而且容易出错,是不可取的,为什么不同计算机来自动的维护编译过程呢?这正是makefile或make工具产生的原因。
Makefile产生的历史
Makefile的历史可以追溯到1976年,当时Unix操作系统的开发团队在开发Unix第七版时,面临着不断增长的源代码和编译时间过长的问题。为了解决这些问题,他们发明了一种名为Make的工具,用于自动化构建Unix操作系统。 Make工具的核心思想是通过规则来描述源代码和目标文件之间的依赖关系,根据依赖关系自动推导出编译顺序,并执行编译命令生成目标文件。这种思想被广泛应用于软件开发中,成为了自动化构建的基石。
后来,Make工具被移植到了其他操作系统和编程语言中,并发展出了许多变种和扩展,其中最著名的就是GNU Make。 GNU Make是Richard Stallman和Roland McGrath在1985年开发的一种Make工具,它在原有的Make基础上增加了许多新特性,包括更加灵活的规则语法、条件语句、函数和变量扩展等。GNU Make的出现推动了Makefile的发展,成为了当今最为流行的自动化构建工具之一。
通过Makefile自动构建edit工程
我们可以通过写一个Makefile文件来描述整个工程的编译工作,然后在目录下运行make命令来自动编译。 Make命令会自动读取当前目录下的makefile文件,完成相应的编译步骤。
例如: /root/work/lpp/makefile/edit/test4/
edit: main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
gcc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o: main.c defs.h
gcc -c main.c
kbd.o: kbd.c defs.h command.h
gcc -c kbd.c
command.o: command.c defs.h command.h
gcc -c command.c
display.o: display.c defs.h buffer.h
gcc -c display.c
insert.o : insert.c defs.h buffer.h
gcc -c insert.c
search.o: search.c defs.h buffer.h
gcc -c search.c
files.o: files.c defs.h buffer.h command.h
gcc -c files.c
utils.o: utils.c defs.h
gcc -c utils.c
clean:
rm -rf edit *.o
Makefile描述了整个edit工程的编译规则,也就是如何对edit工程进行编译和链接等。Make是一个命令工具,它根据Makefile中的信息来自动化构建edit工程,极大的提高了软件开发的效率。可以把Makefile看做是make命令的脚本,类似于shell和shell脚本之间的关系。
实验1: 当目标文件edit不存在时,运行make命令会生成它。
[root:/home/linux/work/work/lpp/makefile/edit/test4]# ls
buffer.h command.h display.c insert.c main.c search.c
command.c defs.h files.c kbd.c Makefile utils.c
[root:/home/linux/work/work/lpp/makefile/edit/test4]# make
gcc -c main.c
gcc -c kbd.c
gcc -c command.c
gcc -c display.c
gcc -c insert.c
gcc -c search.c
gcc -c files.c
gcc -c utils.c
gcc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
[root:/home/linux/work/work/lpp/makefile/edit/test4]#
实验2: 当目标文件edit已经生成时,如果修改了edit的依赖,重新运行make就会重新编译。
[root:/home/linux/work/work/lpp/makefile/edit/test4]# touch main.c
[root:/home/linux/work/work/lpp/makefile/edit/test4]# make
gcc -c main.c
gcc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
[root:/home/linux/work/work/lpp/makefile/edit/test4]#
通过以上可以看出,make和makefile工具极大的简化了程序或软件的构建过程,避免手动操作繁琐的编译、链接、打包等步骤,提高了构建的速度和可靠性。
- 上一篇:Makefile 简介
- 下一篇:使用makefile编译文件,生成什么文件
相关推荐
- linux实例之设置时区的方式有哪些
-
linux系统下的时间管理是一个复杂但精细的功能,而时区又是时间管理非常重要的一个辅助功能。时区解决了本地时间和UTC时间的差异,从而确保了linux系统下时间戳和时间的准确性和一致性。比如文件的时间...
- Linux set命令用法(linux cp命令的用法)
-
Linux中的set命令用于设置或显示系统环境变量。1.设置环境变量:-setVAR=value:设置环境变量VAR的值为value。-exportVAR:将已设置的环境变量VAR导出,使其...
- python环境怎么搭建?小白看完就会!简简单单
-
很多小伙伴安装了python不会搭建环境,看完这个你就会了Python可应用于多平台包括Linux和MacOSX。你可以通过终端窗口输入"python"命令来查看本地是否...
- Linux环境下如何设置多个交叉编译工具链?
-
常见的Linux操作系统都可以通过包管理器安装交叉编译工具链,比如Ubuntu环境下使用如下命令安装gcc交叉编译器:sudoapt-getinstallgcc-arm-linux-gnueab...
- JMeter环境变量配置技巧与注意事项
-
通过给JMeter配置环境变量,可以快捷的打开JMeter:打开终端。执行jmeter。配置环境变量的方法如下。Mac和Linux系统在~/.bashrc中加如下内容:export...
- C/C++|头文件、源文件分开写的源起及作用
-
1C/C++编译模式通常,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码;而.h文件则被称...
- linux中内部变量,环境变量,用户变量的区别
-
unixshell的变量分类在Shell中有三种变量:内部变量,环境变量,用户变量。内部变量:系统提供,不用定义,不能修改环境变量:系统提供,不用定义,可以修改,可以利用export将用户变量转为环...
- 在Linux中输入一行命令后究竟发生了什么?
-
Linux,这个开源的操作系统巨人,以其强大的命令行界面而闻名。无论你是初学者还是经验丰富的系统管理员,理解在Linux终端输入一条命令并按下回车后发生的事情,都是掌握Linux核心的关键。从表面上看...
- Nodejs安装、配置与快速入门(node. js安装)
-
Nodejs是现代JavaScript语言产生革命性变化的一个主要框架,它使得JavaScript从一门浏览器语言成为可以在服务器端运行、开发各种各样应用的通用语言。在不同的平台下,Nodejs的安装...
- Ollama使用指南【超全版】(olaplex使用方法图解)
-
一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...
- linux移植(linux移植lvgl)
-
1uboot移植l移植linux之前需要先移植一个bootlader代码,主要用于启动linux内核,lLinux系统包括u-boot、内核、根文件系统(rootfs)l引导程序的主要作用将...
- Linux日常小技巧参数优化(linux参数调优)
-
Linux系统参数优化可以让系统更加稳定、高效、安全,提高系统的性能和使用体验。下面列出一些常见的Linux系统参数优化示例,包括修改默认配置、网络等多方面。1.修改默认配置1.1修改默认编辑器默...
- Linux系统编程—条件变量(linux 条件变量开销)
-
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等...
- 面试题-Linux系统优化进阶学习(linux系统的优化)
-
一.基础必备优化:1.关闭SElinux2.FirewalldCenetOS7Iptables(C6)安全组(阿里云)3.网络管理服务||NetworkManager|network...
- 嵌入式Linux开发教程:Linux Shell
-
本章重点介绍Linux的常用操作和命令。在介绍命令之前,先对Linux的Shell进行了简单介绍,然后按照大多数用户的使用习惯,对各种操作和相关命令进行了分类介绍。对相关命令的介绍都力求通俗易懂,都给...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)