* [追加赋值](#_443)
一、从Linux上使用源码安装程序说起
一、程序的组成部分
Linux下程序大都是由以下几部分组成:
二进制文件:也就是可以运行的程序文件
库文件:就是通常我们见到的lib目录下的文件
配置文件:这个不必多说,都知道
帮助文档:通常是我们在Linux下用man命令查看的命令的文档
二、Linux下程序的存放目录
Linux程序的存放目录大致有三个地方:
/etc, /bin, /sbin, /lib :系统启动就需要用到的程序,这些目录不能挂载额外的分区,必须在根文件系统的分区上
/usr/bin,/usr/sbin,/usr/lib:操作系统核心功能,可以单独分区
/usr/local/bin,/usr/local/sbin,/usr/local/lib,/usr/local/etc,/usr/local/man:这个用于安装第三方程序,分别对应了二进制文件、库文件、配置文件、帮助文档的目录
通常来说我们安装程序就安装在 /usr/local目录下
三、编译安装源程序
1、使用如下命令查看当前是否安装了gcc编译器,没有可以先用yum安装gcc
gcc --version #查看是否安装gcc
2、解压源码包,例如:
tar -xvf nginx-1.7.7.tar.gz #解压源码包
3、进入解压好的源码包:
cd nginx-1.7.7 #进入源码包
4、执行configure文件,此文件有两个功能:1、让用户选定编译特性;2、检查编译环境。configure执行后将生成MakeFile文件。例如:
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf
其中我们通过–prefix制定了安装路径,通过–conf-path制定了配置文件的具体位置。注意:不是所有的程序的configure参数都是一样的 可以使用 ./configure --help查看详细参数说明。如果该程序所依赖的库在当前系统中没有安装,则会导致configure最后报错,遇到这种情况就需要你先安装依赖库。
5、执行make命令,编译程序
make
6、编译成功后就可以安装了,执行如下命令
sudo make install
到此程序就算安装完成了,但是不要忘了还有后续的配置哦
四、配置程序
1、修改PATH环境变量,以能够识别此程序的二进制文件路径;
修改/etc/profile文件,在文件中 添加
export PATH=$PATH:/path/to/somewhere#记得是可执行文件所在的目录,路径中不要包含可执行文件。
然后执行 :
source /etc/profile #是我们的修改生效
2、默认情况下,系统搜索库文件的路径/lib, /usr/lib; 要增添额外搜寻路径(注意:有的程序不提供库文件,那就不需要此设置了)
在/etc/ld.so.conf.d/中创建以.conf为后缀名的文件,而后把要增添的路径直接写至此文件中;然后执行如下命令使其生效
ldconfig
3、如果程序提供了库文件,也会相应的提供头文件,一般在安装目录的include目录下,系统默认扫描头文件的路径是:/usr/include。我们可以在/usr/include下用链接连接到我们安装程序的头文件。
ln -s /usr/local/nginx/include /usr/include/yourname
4、可能程序还提供了帮助文档,一般是安装目录下的man目录,为了我们可以使用man命令查看我们程序的帮助文档,我们需要:在/etc/man.config中添加一条MANPATH,指向我们的文档目录
./configure是用来检测你的安装平台的目标特征的。比如它会检测你是不是有CC或GCC,
并不是需要CC或GCC,它是个shell脚本。
make是用来编译的,它从Makefile中读取指令,然后编译。
make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置。
1、configure ,这一步一般用来生成 Makefile,为下一步的编译做准备,你可以通过在 configure 后加上参数来对安装进行控制,比如
代码:
./configure --prefix=/usr
上面的意思是将该软件安装在 /usr 下面,执行文件就会安装在 /usr/bin (而不是默认的 /usr/local/bin),
资源文件就会安装在 /usr/share(而不是默认的/usr/local/share)。
同时一些软件的配置文件你可以通过指定 --sys-config= 参数进行设定。
有一些软件还可以加上 --with、–enable、–without、–disable 等等参数对编译加以控制,
你可以通过允许 ./configure --help 察看详细的说明帮助。
2、make ,这一步就是编译,大多数的源代码包都经过这一步进行编译
(当然有些perl或python编写的软件需要调用perl或python来进行编译)。
如果 在 make 过程中出现 error ,你就要记下错误代码(注意不仅仅是最后一行),
然后你可以向开发者提交 bugreport(一般在 INSTALL 里有提交地址),
或者你的系统少了一些依赖库等,这些需要自己仔细研究错误代码。
make 的作用是开始进行源代码编译,以及一些功能的提供,
这些功能由他的 Makefile 设置文件提供相关的功能,比如 make install 一般表示进行安装,
make uninstal 是卸载,不加参数就是默认的进行源代码编译。
make 是 Linux 开发套件里面自动化编译的一个控制程序,
他通过借助 Makefile 里面编写的编译规范(语法很多,类似一个可以运行的脚本程序。
反正我是看不懂,所以你也别问我怎么编写)。进行自动化的调用 gcc 、ld 以及运行某些需要的程序进行编译的程序。
一般情况下,他所使用的 Makefile 控制代码,由 configure 这个设置脚本根据给定的参数和系统环境生成。
3、make insatll ,这条命令来进行安装(当然有些软件需要先运行 make check 或 make test
来进行一些测试),这一步一般需要你有 root 权限(因为要向系统写入文件)
二、makefile和CMakeList.txt编写方法
前面讲完了在Linux上使用源码安装软件,在讲下面的内容之前,我们需要对C/C++的编译过程及gcc的使用有一定了解。可以参考下面的博客:
linux下的C/C++编译环境构建(gcc make cmake 头文件目录 库文件目录)
接着前面讲,我们可以发现在Linux上使用源码安装软件时:
- 我们使用 ./configure来生成makefile
- 通过make命令来执行makefile里面的编译规则,得到可执行文件和库文件
当然,因为是软件作者已经给我们配置好了makefile里面的内容,所以我们不需要自己来写编译规则。
对于一个项目,有时候我们需要自己编写makefile,因此下面我们讲解一下makefile的编写方法。
Makefile教程:Makefile文件编写1天入门
Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目。一旦写编写好 Makefile 文件,只需要一个 make 命令,整个工程就开始自动编译,不再需要手动执行 GCC 命令。
一个中大型 C/C++ 工程的源文件有成百上千个,它们按照功能、模块、类型分别放在不同的目录中,Makefile 文件定义了一系列规则,指明了源文件的编译顺序、依赖关系、是否需要重新编译等。
这套 Makefile 入门教程针对有 C/C++ 基础的读者,如果你还不了解 C/C++,请转到《C语言教程》《C++教程》进行学习。Makefile 文件可以很复杂,它的语法甚至不比C语言简单;本教程化繁为简,以口语化和通俗化的手法来讲解每个知识点,同时附带大量实例,让读者学以致用。
一个 Linux C/C++ 程序员必须会编写 Makefile 文件,否则就玩不转中大型项目。
Makefile文件是什么?
我们教程主要是讲的是 Makefile 。很多 Linux(Unix) 做开发的初学者不了解 Makefile 是什么,甚至大部分 Windows 开发工程师对 Makefile 都特别陌生。这个其实很正常,如果你是在 Windows 下作开发的话不需要去考虑这个问题,因为 Windows 下的集成开发环境(IDE)已经内置了 Makefile,或者说会自动生成 Makefile,我们不用去手动编写。
Linux 中却不能这样,需要我们去手动的完成这项工作。Linux 下可以学习的开发语言有很多,常见的有 C/C++语言、python、java 等等。如果你想要在 Linux(Unix) 下做开发的话,不了解 Makefile 是一件非常失败的事情,甚至说你就成为不了一个合格的 Linux 开发工程师。不懂 Makefile,就操作不了多文件编程,就完成不了相对于大的工程项目的操作。如果你想在 Linux(Unix) 环境下做开发的话,Makefile 是必须掌握的一项技能。
那么,究竟什么是 Makefile 呢?
Makefile 可以简单的认为是一个工程文件的编译规则,描述了整个工程的编译和链接等规则。其中包含了那些文件需要编译,那些文件不需要编译,那些文件需要先编译,那些文件需要后编译,那些文件需要重建等等。编译整个工程需要涉及到的,在 Makefile 中都可以进行描述。换句话说,Makefile 可以使得我们的项目工程的编译变得自动化,不需要每次都手动输入一堆源文件和参数。
以 Linux 下的C语言开发为例来具体说明一下,多文件编译生成一个文件,编译的命令如下所示:
gcc -o outfile name1.c name2.c …
outfile 要生成的可执行程序的名字,nameN.c 是源文件的名字。这是我们在 Linux 下使用 gcc 编译器编译 C 文件的例子。如果我们遇到的源文件的数量不是很多的话,可以选择这样的编译方式。如果源文件非常的多的话,就会遇到下面的这些问题。
- 编译的时候需要链接库的的问题。拿C语言来说,编译的时候 gcc 只会默认链接一些基本的C语言标准库,很多源文件依赖的标准库都需要我们手动链接。
下面列举了一些需要我们手动链接的标准库:
- name1.c 用到了数学计算库 math 中的函数,我们得手动添加参数 -Im;
- name4.c 用到了小型数据库 SQLite 中的函数,我们得手动添加参数 -lsqlite3;
- name5.c 使用到了线程,我们需要去手动添加参数 -lpthread。
因为有很多的文件,还要去链接很多的第三方库。所以在编译的时候命令会很长,并且在编译的时候我们可能会涉及到文件链接的顺序问题,所以手动编译会很麻烦。
如果我们学会使用 Makefile 就不一样了,它会彻底简化编译的操作。把要链接的库文件放在 Makefile 中,制定相应的规则和对应的链接顺序。这样只需要执行 make 命令,工程就会自动编译。每次想要编译工程的时候就执行 make ,省略掉手动编译中的参数选项和命令,非常的方便。
- 编译大的工程会花费很长的时间。
如果我们去做项目开发,免不了要去修改工程项目的源文件,每次修改后都要去重新编译。一个大的工程项目可不止有几个的源文件,里面的源文件个数可能有成百上千个。例如一个内核,或者是一个软件的源码包。这些都是我们做开发经常会遇到的。要完成这样的文件的编译,我们消耗的时间可不是一点点。如果文件特别大的话我们可能要花上半天的时间。
对于这样的问题我们 Makefile 可以解决吗?当然是可以的,Makefile 支持多线程并发操作,会极大的缩短我们的编译时间,并且当我们修改了源文件之后,编译整个工程的时候,make 命令只会编译我们修改过的文件,没有修改的文件不用重新编译,也极大的解决了我们耗费时间的问题。
这其实是我们遇到的比较常见的问题,当然可能遇到的问题还会有很多,比如:工程文件中的源文件的类型很多,编译的话需要选择的编译器;文件可能会分布在不同的目录中,使用时需要调价路径。这些问题都可以通过 Makefile 解决。并且文件中的 Makefile 只需要完成一次,一般我们只要不增加或者是删除工程中的文件,Makefile 基本上不用去修改,编译时只用一个 make 命令。为我们提供了极大的便利,很大程度上提高编译的效率。
Makefile文件中包含哪些规则?
想要书写一个完整的 Makefile文件,需要了解 Makefile 的相关的书写规则。我们已经知道了 Makefile 描述的是文件编译的相关规则,它的规则主要是两个部分组成,分别是依赖的关系和执行的命令,其结构如下所示:
targets : prerequisites
command
或者是
targets : prerequisites; command
command
相关说明如下:
- targets:规则的目标,可以是 Object File(一般称它为中间文件),也可以是可执行文件,还可以是一个标签;
- prerequisites:是我们的依赖文件,要生成 targets 需要的文件或者是目标。可以是多个,也可以是没有;
- command:make 需要执行的命令(任意的 shell 命令)。可以有多条命令,每一条命令占一行。
注意:我们的目标和依赖文件之间要使用冒号分隔开,命令的开始一定要使用Tab
键。
通过下面的例子来具体使用一下 Makefile 的规则,Makefile文件中添代码如下:
test:test.c gcc -o test test.c
上述代码实现的功能就是编译 test.c 文件,通过这个实例可以详细的说明 Makefile 的具体的使用。其中 test 是的目标文件,也是我们的最终生成的可执行文件。依赖文件就是 test.c 源文件,重建目标文件需要执行的操作是gcc -o test test.c
。这就是 Makefile 的基本的语法规则的使用。
使用 Makefile 的方式:首先需要编写好 Makefile 文件,然后在 shell 中执行 make 命令,程序就会自动执行,得到最终的目标文件。
通过上面的例子我们可以了解到,Makefile 的规则很简单,但这并不是 Makefile 的全部,这个仅仅是它的冰山一角。仅仅靠一个规则满足不了我们对于大的工程项目的编译。甚至几个文件的编译都会出现问题,所以要学习的东西还有很多。
简单的概括一下Makefile 中的内容,它主要包含有五个部分,分别是:
1) 显式规则
显式规则说明了,如何生成一个或多的的目标文件。这是由 Makefile 的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
2) 隐晦规则
由于我们的 make 命名有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 Makefile,这是由 make 命令所支持的。
3) 变量的定义
在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。
4) 文件指示
其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像C语言中的 include 一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像C语言中的预编译 #if 一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
5) 注释
Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,这个就像 C/C++ 中的“//”一样。如果你要在你的 Makefile 中使用“#”字符,可以用反斜框进行转义,如:“#”。
Makefile的工作流程
简单了解一下 Makefile 书写规则之后,再来深入研究一下 Makefile 的是怎样工作的?当我们在执行 make 条命令的时候,make 就会去当前文件下找要执行的编译规则,也就是 Makefile 文件。我们编写 Makefile 的时可以使用的文件的名称 “GNUmakefile” 、“makefile” 、“Makefile” ,make 执行时回去寻找 Makefile 文件,找文件的顺序也是这样的。
我们推荐使用 Makefile(一般在工程中都这么写,大写的会比较的规范)。如果文件不存在,make 就会给我们报错,提示:
make:*** 没有明确目标并且找不到 makefile。停止
Makefile的工流程
Makefile 的具体工作流程可以通过例子来看一下:创建一个包含有多个源文件和 Makefile 的目录文件,源文件之间相互关联。在 Makefile 中添加下面的代码:
main:main.o test1.o test2.o gcc main.o test1.o test2.o -o main main.o:main.c test.h gcc -c main.c -o main.o test1.o:test1.c test.h gcc -c test1.c -o test1.o test2.o:test2.c test.h gcc -c test2.c -o test2.o
在我们编译项目文件的时候,默认情况下,make 执行的是 Makefile 中的第一规则(Makefile 中出现的第一个依赖关系),此规则的第一目标称之为“最终目标”或者是“终极目标”。
在 shell 命令行执行的 make 命令,就可以得到可执行文件 main 和中间文件 main.o、test1.o 和 test2.o,main 就是我们要生成的最终文件。通过 Makefile 我们可以发现,目标 main"在 Makefile 中是第一个目标,因此它就是 make 的终极目标,当修改过任何 C 文件后,执行 make 将会重建终极目标 main。
它的具体工作顺序是:当在 shell 提示符下输入 make 命令以后。 make 读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标”,开始处理第一个规则(终极目标所在的规则)。在我们的例子中,第一个规则就是目标 “main” 所在的规则。规则描述了 “main” 的依赖关系,并定义了链接 “.o” 文件生成目标 “main” 的命令;make 在执行这个规则所定义的命令之前,首先处理目标 “main” 的所有的依赖文件(例子中的那些 “.o” 文件)的更新规则(以这些 “.o” 文件为目标的规则)。
对这些 “.o” 文件为目标的规则处理有下列三种情况:
- 目标 “.o” 文件不存在,使用其描述规则创建它;
- 目标 “.o” 文件存在,目标 “.o” 文件所依赖的 “.c” 源文件 “.h” 文件中的任何一个比目标 “.o” 文件“更新”(在上一次 make 之后被修改)。则根据规则重新编译生成它;
- 目标 “.o” 文件存在,目标 “.o” 文件比它的任何一个依赖文件(“.c” 源文件、“.h” 文件)“更新”(它的依赖文件在上一次 make 之后没有被修改),则什么也不做。
通过上面的更新规则我们可以了解到中间文件的作用,也就是编译时生成的 “.o” 文件。作用是检查某个源文件是不是进行过修改,最终目标文件是不是需要重建。我们执行 make 命令时,只有修改过的源文件或者是不存在的目标文件会进行重建,而那些没有改变的文件不用重新编译,这样在很大程度上节省时间,提高编程效率。小的工程项目可能体会不到,项目工程文件越大,效果才越明显。
当然 make 命令能否顺利的执行,还在于我们是否制定了正确的的依赖规则,当前目录下是不是存在需要的依赖文件,只要任意一点不满足,我们在执行 make 的时候就会出错。所以完成一个正确的 Makefile 不是一件简单的事情。
清除工作目录中的过程文件
我们在使用的时候会产生中间文件会让整个文件看起来很乱,所以在编写 Makefile 文件的时候会在末尾加上这样的规则语句:
.PHONY:cleanclean: rm -rf *.o test
其中 “*.o” 是执行过程中产生的中间文件,“test” 是最终生成的执行文件。我们可以看到 clean 是独立的,它只是一个伪目标(在《Makefile伪目标》的章节中详细介绍),不是具体的文件。不会与第一个目标文件相关联,所以我们在执行 make 的时候也不会执行下面的命令。在shell 中执行 “make clean” 命令,编译时的中间文件和生成的最终目标文件都会被清除,方便我们下次的使用。
Makefile通配符的使用
Makefile 是可以使用 shell 命令的,所以 shell 支持的通配符在 Makefile 中也是同样适用的。 shell 中使用的通配符有:“*”,“?”,“[…]”。具体看一下这些通配符的表示含义和具体的使用方法。
通配符 | 使用说明 |
* | 匹配0个或者是任意个字符 |
? | 匹配任意一个字符 |
[] | 我们可以指定匹配的字符放在 “[]” 中 |
通配符可以出现在模式的规则中,也可以出现在命令中,详细的使用情况如下。
实例 1:
.PHONY:clean clean: rm -rf *.o test
这是在 Makefile 中经常使用的规则语句。这个实例可以说明通配符可以使用在规则的命令当中,表示的是任意的以 .o 结尾的文件。
实例 2:
test:*.c gcc -o $@ $^
这个实例可以说明我们的通配符不仅可以使用在规则的命令中,还可以使用在规则中。用来表示生所有的以 .c 结尾的文件。
但是如果我们的通配符使用在依赖的规则中的话一定要注意这个问题:不能通过引用变量的方式来使用,如下所示。
OBJ=*.c test:$(OBJ) gcc -o $@ $^
我们去执行这个命令的时候会出现错误,提示我们没有 “.c" 文件,实例中我们相要表示的是当前目录下所有的 “.c” 文件,但是我们在使用的时候并没有展开,而是直接识别成了一个文件。文件名是 ".c”。
如果我们就是相要通过引用变量的话,我们要使用一个函数 “wildcard”,这个函数在我们引用变量的时候,会帮我们展开。我们把上面的代码修改一下就可以使用了。
OBJ=$(wildcard *.c) test:$(OBJ) gcc -o $@ $^
这样我们再去使用的时候就可以了。调用函数的时候,会帮我们自动展开函数。
还有一个和通配符 “*” 相类似的字符,这个字符是 “%”,也是匹配任意个字符,使用在我们的的规则当中。
test:test.o test1.o gcc -o $@ $^ %.o:%.c gcc -o $@ $^
“%.o” 把我们需要的所有的 “.o” 文件组合成为一个列表,从列表中挨个取出的每一个文件,“%” 表示取出来文件的文件名(不包含后缀),然后找到文件中和 "%"名称相同的 “.c” 文件,然后执行下面的命令,直到列表中的文件全部被取出来为止。
这个属于 Makefile 中静态模规则:规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。跟我们的多规则目标的意思相近,但是又不相同。
Makefile变量的定义和使用
变量对于我们来说是不陌生的,在学习各种编程语言时会经常用到。就拿C语言来说,变量的使用是十分常见的,变量可以用来保存一个值或者是使用变量进行运算操作。Makefile 中的变量也是这样,我们可以利用它来表示某些多处使用而又可能发生变化的内容,不仅可以节省重复修改的工作,还可以避免遗漏。
变量的定义
Makefile 文件中定义变量的基本语法如下:
变量的名称=值列表
Makefile 中的变量的使用其实非常的简单,因为它并没有像其它语言那样定义变量的时候需要使用数据类型。变量的名称可以由大小写字母、阿拉伯数字和下划线构成。等号左右的空白符没有明确的要求,因为在执行 make 的时候多余的空白符会被自动的删除。至于值列表,既可以是零项,又可以是一项或者是多项。如:
VALUE_LIST = one two three
调用变量的时候可以用 “
(
V
A
L
U
E
L
I
S
T
)
"
或
者
是
"
(VALUE_LIST)" 或者是 "
(VALUELIST)“或者是”{VALUE_LIST}” 来替换,这就是变量的引用。实例:
OBJ=main.o test.o test1.o test2.o test:$(OBJ) gcc -o test $(OBJ)
这就是引用变量后的 Makefile 的编写,比我们之前的编写方式要简单的多。当要添加或者是删除某个依赖文件的时候,我们只需要改变变量 “OBJ” 的值就可以了。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
V
A
L
U
E
L
I
S
T
)
"
或
者
是
"
(VALUE_LIST)" 或者是 "
(VALUELIST)“或者是”{VALUE_LIST}” 来替换,这就是变量的引用。实例:
OBJ=main.o test.o test1.o test2.o test:$(OBJ) gcc -o test $(OBJ)
这就是引用变量后的 Makefile 的编写,比我们之前的编写方式要简单的多。当要添加或者是删除某个依赖文件的时候,我们只需要改变变量 “OBJ” 的值就可以了。