linux下configure,make(makefile),cmake命令详解-makefile和CMakeList.txt编写方法

64 篇文章 90 订阅

一、从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 头文件目录 库文件目录)

gcc编译多文件项目(包含静态库和动态库)

gcc和makefile用法总结(建议收藏)

接着前面讲,我们可以发现在Linux上使用源码安装软件时:

  1. 我们使用 ./configure来生成makefile
  2. 通过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 文件的例子。如果我们遇到的源文件的数量不是很多的话,可以选择这样的编译方式。如果源文件非常的多的话,就会遇到下面的这些问题。

  1. 编译的时候需要链接库的的问题。拿C语言来说,编译的时候 gcc 只会默认链接一些基本的C语言标准库,很多源文件依赖的标准库都需要我们手动链接。

下面列举了一些需要我们手动链接的标准库:

  • name1.c 用到了数学计算库 math 中的函数,我们得手动添加参数 -Im;
  • name4.c 用到了小型数据库 SQLite 中的函数,我们得手动添加参数 -lsqlite3;
  • name5.c 使用到了线程,我们需要去手动添加参数 -lpthread。

因为有很多的文件,还要去链接很多的第三方库。所以在编译的时候命令会很长,并且在编译的时候我们可能会涉及到文件链接的顺序问题,所以手动编译会很麻烦。

如果我们学会使用 Makefile 就不一样了,它会彻底简化编译的操作。把要链接的库文件放在 Makefile 中,制定相应的规则和对应的链接顺序。这样只需要执行 make 命令,工程就会自动编译。每次想要编译工程的时候就执行 make ,省略掉手动编译中的参数选项和命令,非常的方便。

  1. 编译大的工程会花费很长的时间。

如果我们去做项目开发,免不了要去修改工程项目的源文件,每次修改后都要去重新编译。一个大的工程项目可不止有几个的源文件,里面的源文件个数可能有成百上千个。例如一个内核,或者是一个软件的源码包。这些都是我们做开发经常会遇到的。要完成这样的文件的编译,我们消耗的时间可不是一点点。如果文件特别大的话我们可能要花上半天的时间。

对于这样的问题我们 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” 的值就可以了。

变量的基本赋值

知道了如何定义,下面我们来说一下 Makefile 的变量的四种基本赋值方式:

  • 简单赋值 ( := ) 编程语言中常规理解的赋值方式,只对当前语句的变量有效。
  • 递归赋值 ( = ) 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。
  • 条件赋值 ( ?= ) 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。
  • 追加赋值 ( += ) 原变量用空格隔开的方式追加一个新值。
简单赋值
x:=foo
y:=$(x)b
x:=new
test:
      @echo "y=>$(y)"
      @echo "x=>$(x)"

在 shell 命令行执行make test我们会看到:

y=>foob
x=>new

递归赋值
x=foo
y=$(x)b
x=new
test:
      @echo "y=>$(y)"
      @echo "x=>$(x)"

在 shell 命令行执行make test我们会看到:

y=>newb
x=>new

条件赋值
x:=foo
y:=$(x)b
x?=new
test:
      @echo "y=>$(y)"
      @echo "x=>$(x)"

在 shell 命令行执行make test我们会看到:

y=>foob
x=>foo

追加赋值
x:=foo
y:=$(x)b
x+=$(y)
test:
      @echo "y=>$(y)"
      @echo "x=>$(x)"

在 shell 命令行执行make test我们会看到:

y=>foob
x=>foo foob

不同的赋值方式会产生不同的结果,我们使用的时候应该根据具体的情况选择相应的赋值规则。

变量使用的范围很广,它可以出现在规则的模式中,也可以出现在规则的命令中或者是作为 Makefile 函数的参数来使用。总之,变量的使用在我们的 Makefile 编写中还是非常广泛的,可以说我们的 Makefile 中必不可少的东西。

其实变量在我们的 Makefile 中还是有很多种类的,它们的意义是不相同的。比如我们的环境变量,自动变量,模式指定变量等。其他的变量我们会在其他的文章里做介绍。

Makefile命令的编写

通过上个章节的描述,我们已经知道了 Makefile 的规则是什么,他是由依赖关系规则和命令组成的。所使用的命令是由 shell 命令行组成,他们是一条一条执行的。多个命令之间要使用分号隔开,Makefile 中的任何命令都要以tab键开始。多个命令行之间可以有空行和注释行,在执行规则时空行会被自动忽略。

通常系统中可能存在不同的 shell 。但是 make 处理 Makefile 过程时,如果没有明确的指定,那么对所有规则中的命令行的解析使用bin/sh来完成。执行过程中使用的 shell 决定了规则中的命令的语法和处理机制。当使用默认的bin/sh时,命令中出现的字符“#”到行末的内容被认为是注释。当然了“#”可以不在此行的行首,此时“#”之前的内容不会被作为注释处理。

命令回显

通常 make 在执行命令行之前会把要是执行的命令行输出到标准输出设备。我们称之为 “回显”,就好像我们在 shell 环境下输入命令执行时一样。如果规则的命令行以字符“@”开始,则 make 在执行的时候就不会显示这个将要被执行的命令。典型的用法是在使用echo命令输出一些信息时。

实例 1:

OBJ=test main list
all:
    @echo $(OBJ)

执行时将会得到test main list这条输出信息,如果在执行命令之前没有字符“@”,那么make的输出将是echo test main list

我们在执行 make 时添加上一些参数,可以控制命令行是否输出。当使用 make 的时候机加上参数-n或者是--just-print ,执行时只显示所要执行的命令,但不会真正的执行这个命令。只有在这种情况下 make 才会打印出所有的 make 需要执行的命令,其中包括了使用的“@”字符开始的命令。这个选项对于我们调试 Makefile 非常的有用,使用这个选项就可以按执行顺序打印出 Makefile 中所需要执行的所有命令。而 make 参数-s或者是--slient则是禁止所有的执行命令的显示。就好像所有的命令行都使用“@”开始一样。

命令的执行

当规则中的目标需要被重建的时候,此规则所定义的命令将会被执行,如果是多行的命令,那么每一行命令将是在一个独立的子 shell 进程中被执行。因此,多命令行之间的执行命令时是相互独立的,相互之间不存在以来。

在 Makefile 中书写在同一行中的多个命令属于一个完整的 shell 命令行,书写在独立行的一条命令是一个独立的 shell 命令行。因此:在一个规则的命令中命令行 “cd”改变目录不会对其后面的命令的执行产生影响。就是说之后的命令执行的工作目录不会是之前使用“cd”进入的那个目录。如果达到这个目的,就不能把“cd”和其后面的命令放在两行来书写。而应该把这两个命令放在一行上用分号隔开。这样才是一个完整的 shell 命令行。

实例 2:

foo:bar/lose
    cd bar;gobble lose >../foo

如果想把一个完整的shell命令行书写在多行上,需要使用反斜杠 ()来对处于多行的命令进行连接,表示他们是一个完整的shell命令行。例如上例我们也可以这样书写:

foo:bar.lose
    cd bar; \
    gobble lose > ../foo

make 对所有规则的命令的解析使用环境变量“SHELL”所指定的那个程序。在 GNU make 中,默认的程序时 “/bin/sh”。不像其他的绝大多数变量,他们的只可以直接从同名的系统环境变量那里获得。make 的环境变量 “SHELL”没有使用环境变量的定义。因为系统环境变量“SHELL”指定的那个程序被用来作为用户和系统交互的接口程序,他对于不存在直接交互过程的 make 显然不合适。在 make 环境变量中“SHELL”会被重新赋值;他作为一个变量我们也可以在 Makefile 中明确的给它赋值,变量“SHELL“的默认值时“/bin/sh”。

并发执行命令

GNU make 支持同时执行多条命令。通常情况下,同一时刻只有一个命令在执行,下一个命令只有在当前命令结束之后才能够开始执行。不过可以通过 make 命令行选项 “-j” 或者 “–jobs” 来告诉 make 在同一时刻可以允许多条命令同时执行。

如果选项 “-j” 之后存在一个整数,其含义是告诉 make 在同一时刻可以允许同时执行的命令行的数目。这个数字被称为job slots。当 “-j” 选项中没有出现数字的时候,那么同一时间执行的命令数目没有要求。使用默认的job solts,值为1,表示make将串行的执行规则的命令(同一时刻只能由一条命令被执行)。

并行执行命令所带来的问题是显而易见的:

  • 多个同时执行的命令的输出信息将同时被输出到终端。当出现错误时很难根据一大堆凌乱的信息来区分那条命令执行错误。
  • 在同一时刻可能会存在多个命令执行的进程同时读取到标准输入,但是对于白哦准输入设备来说,在同一时刻只能存在一个进程访问它。就是说在某个时间点,make只能保证此刻正在执行的进程中的一个进程读取标准输入流。而其他的进程键的标准输入流将设置为无效。因此在此一时刻多个执行命令的进程中只有一个进程获得标准输入,而其他的需要读取标准输入流的进程由于输入流无效而导致致命的错误。

make命令参数和选项大汇总

我们在在执行 make 命令时,有的时候需要加上一下参数选项来保证我们的程序的执行,其实之前已经遇到过 make 在执行命令的时候需要添加上参数选项,比如只打印命令但不执行使用的参数是 “-n” ,还有只执命令不打印命令的参数选项是 “-s”,包含其它文件的路径参数选项是 "-include"等等。

我们现在列举一下 make 可以使用的参数选项,以及它们的功能是什么。

参数选项功能
-b,-m忽略,提供其他版本 make 的兼容性
-B,–always-make强制重建所有的规则目标,不根据规则的依赖描述决定是否重建目标文件。
-C DIR,–directory=DIR在读取 Makefile 之前,进入到目录 DIR,然后执行 make。当存在多个 “-C” 选项的时候,make 的最终工作目录是第一个目录的相对路径。
-dmake 在执行的过程中打印出所有的调试信息,包括 make 认为那些文件需要重建,那些文件需要比较最后的修改时间、比较的结果,重建目标是用的命令,遗憾规则等等。使用 “-d” 选项我们可以看到 make 构造依赖关系链、重建目标过程中的所有的信息。
–debug[=OPTIONS]make 执行时输出调试信息,可以使用 “OPTIONS” 控制调试信息的级别。默认是 “OPTIONS=b” ,“OPTIONS” 的可值为以下这些,首字母有效:all、basic、verbose、implicit、jobs、makefile。
-e,–enveronment -overrides使用环境变量定义覆盖 Makefile 中的同名变量定义。
-f=FILE,–file=FILE, --makefile=FILE指定文件 “FILE” 为 make 执行的 Makefile 文件
-p,–help打印帮助信息。
-i,–ignore-errors执行过程中忽略规则命令执行的错误。
-I DIR,–include-dir=DIR指定包含 Makefile 文件的搜索目录,在Makefile中出现另一个 “include” 文件时,将在 “DIR” 目录下搜索。多个 “-i” 指定目录时,搜索目录按照指定的顺序进行。
-j [JOBS],–jobs[=JOBS]可指定同时执行的命令数目,爱没有 “-j” 的情况下,执行的命令数目将是系统允许的最大可能数目,存在多个 “-j” 目标时,最后一个目标指定的 JOBS 数有效。
-k,–keep-going执行命令错误时不终止 make 的执行,make 尽最大可能执行所有的命令,直至出现知名的错误才终止。
-l load,–load-average=[=LOAD],–max-load[=LOAD]告诉 make 在存在其他任务执行的时候,如果系统负荷超过 “LOAD”,不在启动新的任务。如果没有指定 “LOAD” 的参数 “-l” 选项将取消之前 “-l” 指定的限制。
-n,–just-print,–dry-run只打印执行的命令,但是不执行命令。
-o FILE,–old-file=FILE, --assume-old=FILE指定 "FILE"文件不需要重建,即使是它的依赖已经过期;同时不重建此依赖文件的任何目标。注意:此参数不会通过变量 “MAKEFLAGS” 传递给子目录进程。
-p,–print-date-base命令执行之前,打印出 make 读取的 Makefile 的所有数据,同时打印出 make 的版本信息。如果只需要打印这些数据信息,可以使用 “make -qp” 命令,查看 make 执行之前预设的规则和变量,可使用命令 “make -p -f /dev/null”
-q,-question称为 “询问模式” ;不运行任何的命令,并且无输出。make 只返回一个查询状态。返回状态 0 表示没有目标表示重建,返回状态 1 表示存在需要重建的目标,返回状态 2 表示有错误发生。
-r,–no-builtin-rules取消所有的内嵌函数的规则,不过你可以在 Makefile 中使用模式规则来定义规则。同时选项 “-r” 会取消所有后缀规则的隐含后缀列表,同样我们可以在 Makefile 中使用 “.SUFFIXES”,定义我们的后缀名的规则。"-r" 选项不会取消 make 内嵌的隐含变量。
-R,–no-builtin-variabes取消 make 内嵌的隐含变量,不过我们可以在 Makefile 中明确定义某些变量。注意:"-R" 和 “-r” 选项同时打开,因为没有了隐含变量,所以隐含规则将失去意义。
-s,–silent,–quiet取消命令执行过程中的打印。
-S,–no-keep-going, --stop取消 “-k” 的选项在递归的 make 过程中子 make 通过 “MAKEFLAGS” 变量继承了上层的命令行选项那个。我们可以在子 make 中使用“-S”选项取消上层传递的 “-k” 选项,或者取消系统环境变量 “MAKEFLAGS” 中 "-k"选项。
-t,–touch和 Linux 的 touch 命令实现功能相同,更新所有的目标文件的时间戳到当前系统时间。防止 make 对所有过时目标文件的重建。
-v,version查看make的版本信息。
-w,–print-directory在 make 进入一个子目录读取 Makefile 之前打印工作目录,这个选项可以帮助我们调试 Makefile,跟踪定位错误。使用 “-C” 选项时默认打开这个选项。
–no-print-directory取消 “-w” 选项。可以是 用在递归的 make 调用的过程中 ,取消 “-C” 参数的默认打开 “-w” 的功能。
-W FILE,–what-if=FILE, --new-file=FILE, --assume-file=FILE设定文件 “FILE” 的时间戳为当前的时间,但不更改文件实际的最后修改时间。此选项主要是为了实现对所有依赖于文件 “FILE” 的目标的强制重建。
–warn-undefined-variables在发现 Makefile 中存在没有定义的变量进行引用时给出告警信息。此功能可以帮助我们在调试一个存在多级嵌套变量引用的复杂 Makefile。但是建议在书写的时候尽量避免超过三级以上的变量嵌套引用。

Makefile中常见的错误信息

make 执行过程中所产生错误并不都是致命的,特别是在命令行之前存在 “-”、或者 make 使用 “-k” 选项执行时。make 执行过程的致命错误都带有前缀字符串 “***”。错误信息都有前缀,一种是执行程序名作为错误前缀(通常是 “make”);另外一种是当 Makefile 本身存在语法错误无法被 make 解析并执行时,前缀包含了 Makefile 文件名和出现错误的行号。

在下述的错误列表中,省略了普通前缀:

[FOO] Error NN
[FOO] signal description

这类错误并不是 make 的真正错误。它表示 make 检测到 make 所调用的作为执行命令的程序返回一个非零状态(Error NN),或者此命令程序以非正常方式退出(携带某种信号)。
如果错误信息中没有附加 “***” 字符串,则是子过程的调用失败,如果 Makefile 中此命令有前缀 “-”,make 会忽略这个错误。

missing separator. Stop.
missing separator (did you mean TAB instead of 8 spaces?). Stop.

错误的原因:不可识别的命令行,make 在读取 Makefile 过程中不能解析其中包含的内容。GNU make在读取 Makefile 时根据各种分隔符(:, =, [TAB]字符等)来识别 Makefile 的每一行内容。这些错误意味着 make 不能发现一个合法的分隔符。
出现这些错误信息的可能的原因是(或许是编辑器,绝大部分是ms- windows的编辑器)在 Makefile 中的命令之前使用了4个(或者8个)空格代替了 [Tab] 字符。这种情况,将产生上述的第二种形式产生错误信息。且记,所有的命令行都应该是以 [Tab] 字符开始的。

commands commence before first target. Stop.
missing rule before commands. Stop.

Makefile 可能是以命令行开始:以 [Tab] 字符开始,但不是一个合法的命令行(例如,一个变量的赋值)。命令行必须和规则一一对应。
产生第二种的错误的原因可能是一行的第一个非空字符为分号,make 会认为此处遗漏了规则的 “target: prerequisite” 部分。

No rule to make target ‘XXX’.
No rule to make target 'XXX ', needed by ‘yyy’.

无法为重建目标“XXX”找到合适的规则,包括明确规则和隐含规则。
修正这个错误的方法是:在 Makefile 中添加一个重建目标的规则。其它可能导致这些错误的原因是 Makefile 中文件名拼写错误,或者破坏了源文件树(一个文件不能被重建,可能是由于依赖文件的问题)。

No targets specified and no makefile found. Stop.
No targets. Stop.

第一个错误表示在命令行中没有指定需要重建的目标,并且 make 不能读入任何 Makefile 文件。第二个错误表示能够找到 Makefile 文件,但没有终极目标或者没有在命令行中指出需要重建的目标。这种情况下,make 什么也不做。

Makefile ‘XXX’ was not found.
Included makefile ‘XXX’ was not found.

没有使用 “-f” 指定 Makefile 文件,make 不能在当前目录下找到默认 Makefile(makefile 或者 GNUmakefile)。使用 “-f” 指定文件,但不能读取这个指定的 Makefile 文件。

warning: overriding commands for target ‘XXX’
warning: ignoring old commands for target ‘XXX’

对同一目标 “XXX” 存在一个以上的重建命令。GNU make 规定:当同一个文件作为多个规则的目标时,只能有一个规则定义重建它的命令(双冒号规则除外)。如果为一个目标多次指定了相同或者不同的命令,就会产生第一个告警;第二个告警信息说新指定的命令覆盖了上一次指定的命令。

Circular XXX <- YYY dependency dropped.

规则的依赖关系产生了循环:目标 “XXX” 的依赖文件为 “YYY”,而依赖 “YYY” 的依赖列表中又包含 “XXX”。

Recursive variable ‘XXX’ references itself (eventually). Stop.

make 的变量 “XXX”(递归展开式)在替换展开时,引用它自身。无论对于直接展开式变量(通过:=定义的)或追加定义(+=),这都是不允许的。

Unterminated variable reference. Stop.

变量或者函数引用语法不正确,没有使用完整的的括号(缺少左括号或者右括号)。

insufficient arguments to function ‘XXX’. Stop.

函数 “XXX” 引用时参数数目不正确。函数缺少参数。

missing target pattern. Stop.
multiple target patterns. Stop.
target pattern contains no ‘%’. Stop.
mixed implicit and static pattern rules. Stop.

不正确的静态模式规则。
第一条错误的原因是:静态模式规则的目标段中没有模式目标;
第二条错误的原因是:静态模式规则的目标段中存在多个模式目标;
第三条错误的原因是:静态模式规则的目标段目标模式中没有包含模式字符“%”;
第四条错误的原因是:静态模式规则的三部分都包含了模式字符“%”。正确的应该是只有后两个才可以包含模式字符“%”。

warning: -jN forced in submake: disabling jobserver mode.

这一条告警和下条告警信息发生在:make 检测到递归的 make 调用时,可通信的子 make 进程出现并行处理的错误。递归执行的 make 的命令行参数中存在 “-jN” 参数(N的值大于1),在有些情况下可能导致此错误,例如:Makefile 中变量 “MAKE” 被赋值为 “make –j2”,并且递归调用的命令行中使用变量 “MAKE”。在这种情况下,被调用 make 进程不能和其它 make 进程进行通信,其只能简单的独立的并行处理两个任务”。

warning: jobserver unavailable: using -j1. Add ‘+’ to parent make rule.

为了现实 make 进程之间的通信,上层 make 进程将传递信息给子 make 进程。在传递信息过程中可能存在这种情况,子 make 进程不是一个实际的 make 进程,而上层make却不能确定子进程是否是真实的 make 进程。它只是将所有信息传递下去。上层 make 采用正常的算法来决定这些。当出现这种情况,子进程只会接受父进程传递的部分有用的信息。子进程会产生该警告信息,之后按照其内建的顺序方式进行处理。


cmake和CMakeList.txt

通过上面编写makefile,我们可以发现对于大型项目,其实makefile的编写也是非常复杂的。因此就有了cmake。

cmake就可以更加简单的生成makefile文件给上面那个make用。当然cmake还有其他更牛X功能,就是可以跨平台生成对应平台能用的makefile,我们就不用再自己去修改了。

可是cmake根据什么生成makefile呢?它又要根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。

到最后CMakeLists.txt文件谁写啊?亲,是你自己手写的。

CMakeLists.txt的编写方法

可以参考下面几篇博客:

(转载+整理)超详细的cmake教程

CMake教程(一)

【C/C++】从零开始的cmake教程

【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
 CMake 是个跨平台的自动化建构系统,它用组态档控制建构过程(build process)的方式和 Unix 的 Make 相似,只是 CMake 的组态档取名为 CmakeLists.txtCmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。CMake 可以编译源代码、制做程式库、产生适配器(wrapper)、还可以用任意的顺序建构执行档。CMake 支援 in-place 建构(二进档和源代码在同一个目录树中)和 out-of-place 建构(二进档在别的目录里),因此可以很容易从同一个源代码目录树中建构出多个二进档。CMake 也支援静态与动态程式库的建构。   “CMake”这个名字是“cross platform make”的缩写。虽然名字中含有“make”,但是CMake和Unix上常见的“make”系统是分开的,而且更为高阶。 编辑本段 历史   CMake是为了解决美国国家医学图书馆出资的Visible Human Project专案下的Insight Segmentation and Registration Toolkit (ITK) 软件的跨平台建构的需求而创造出来的,其设计受到了Ken Martin开发的pcmaker所影响。pcmaker当初则是为了支援Visualization Toolkit这个开放源代码的三维图形和视觉系统才出现的,今日VTK也采用了CMake。在设计CMake之时,Kitware公司的Bill Hoffman采用了pcmaker的一些重要想法,加上更多他自己的点子,想把GNU建构系统的一些功能整合进来。CMake最初的实作是在2000年中作的,在2001年初有了急速的进展,许多改良是来自其他把CMake整合到自己的系统中的开发者,比方说,采用CMake作为建构环境的VXL社群就贡献了很多重要的功能,Brad King为了支援CABLE和GCC-XML这套自动包装工具也加了几项功能,奇异公司的研发部门则用在内部的测试系统DART,还有一些功能是为了让VTK可以过渡到CMake和支援(“美国Los Alamos国家实验室”&“洛斯阿拉莫斯国家实验室”)的Advanced Computing Lab的平行视觉系统ParaView而加的。 编辑本段 功能介绍   组态档是用一种建构软件专用的特殊编程语言写的CMake脚本。   内建C语言、C++、Fortran、Java的自动相依性分析功能。   经由CMake脚本语言支援SWIG、Qt、FLTK。   内建对微软Visual Studio .NET和过去的Visual Studio版本的支援,可以产生后缀为.dsp、.sln和.vcproj的文档。   用传统的时间标签侦测档案内容的改变。   支援平行建构(在多台电脑上同时建构)   在许多操作系统上进行跨平台编译,包括Linux、POSIX相容的系统(AIX、*BSD、HP-UX、IRIX、MinGW/MSYS、Solaris系统)、Mac OS X和微软Windows 95/98/NT/2000/XP等。   产生可以给Graphviz用的全局相依图。   已经和Dart、CTest和CPack等软件测试和释出的工具整合。 编辑本段 应用软件   Bullet Physics Engine   KDE (starting with version 4)   The Visualization Toolkit   Insight Segmentation and Registration Toolkit   ParaView   DevIL - Open Image Library   OpenSceneGraph   Scribus   Drishti   PvPGN   Chicken   ParadisEO   Quantum GIS   OPENCV 编辑本段 cmake的安装   下载cmake   安装cmake   tar –zxvf cmake-2.8.3.tar.gz   ./configure--prefix=/usr/local/cmake/   gmake   make install 注意: 包含win版linux版 源码及bin. 版本为v2.8.4

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值