Linux 系统中make和makefile如何使用
make命令
在linux系统中,make时一个用来编译的命令,项目开发和软件安装过程中我们经常会用到make或makeinstall。利用make工具我们就可以将大型开发项目分解成许多更易于管理的模块。
- make的工作原理
当make命令被执行时,它会扫描当前目录下Makefile或者makefile文件找到目标以及其依赖。
- make在当前目录下寻找“Makefile"或者”makefile“文件
- 若找到,查找文件中第一个目标文件.o
- 若目标文件不存在,根据依赖关系查找.s文件
- 若.s文件不存在,根据依赖关系查找.i文件
- 若.i文件不存在,根据依赖关系查找.c文件,此时.c文件一定存在,于是生成一个.o文件,再去执行
Makefile文件
Makefile文件有一系列的规则构成,每条规则形式如下:
<target>: <prerequisites>
[Tab]<commands>
第一行冒号前为目标,冒号后为前置条件;第二行必须由一个Tab键起首,后接命令;目标是必须的,不可省略;前置条件和命令是可选的,但是二者必须至少存在一个。
目标target
目标可以是文件名,指明make命令所要构建的对象;也可以是某个操作名称,称为伪目标
clean:
rm *.o
以上代码的目标是clean,命令是rm*.o
;
执行make clean
命令,实现对象文件的删除;
前置条件prerequisites
前置条件通常是一组文件名,用空格隔开;
命令commands
命令表示如何更新目标文件,由一行或多行shell命令组成;注意:shell命令一定是写在命令中,否则会被make忽略,每行命令前必须有一个Tab键;每行命令在一个独立的shell中执行,shell之间没有继承关系,因此上一行的变量赋值在下一行无效;若前后两行命令有共享数据,可以写在同一行,用分号隔开。
CMake
CMake是开源、跨平台的构建工具,可以让我们通过编写简单的配置文件CMakelist.txt去生成本地的Makefile文件,这个配置文件是独立于运行平台和编译器的,这样就不用亲自去编写Makefile文件了,而且配置文件可以直接拿到其他平台上使用,无需修改,非常方便。
CMakelist.txt编写样例
- 同一目录下多个源文件
对于下面这种文件结构,适用于小型项目,文件数量较少,并不是规范的分类管理习惯。
这时CMakelist.txt内容如下:
cmake_minimum_required (VERSION 2.8)
project (demo)
add_executable(main main.c testFunc.c)
第一行的意思是要求CMake的最低版本是2.8;第二行表示本工程信息,也就是工程名叫做demo;第三行比较关键,表示最终要生成的elf文件的名字叫做main,使用的源文件是main.c,testFunc.c。在终端下切换到main.c所在的目录下,然后输入以下命令运行CMake,
cmake .
这样就会在当前目录下生成makefile文件,接着在终端输入make
并回车,main.c文件就编译完成了,这时我们需要的elf文件main成功生成。
可以类推,如果在同一个目录下有多个源文件,那么只要在add_executable
里把所有的源文件都添加进去即可。但是如果源文件的数量过多,cmake提供了一个命令可以把指定目录下的所有源文件存储在一个变量中,这个命令就是aux_source_directory(dir var)
第一个参数是指定目录,第二个参数是存放源文件列表的变量。那么,我们的CMakelist.txt文件也可以写成下面这种形式:
cmake_minimum_required (VERSION 2.8)
project (demo)
aux_source_directory(. SRC_LIST)
add_executable(main ${SRC_LIST})
使用aux_source_directory
把当前目录下的源文件列表存放在变量SRC_LIST
中,然后在add_executable
调用SRC_LIST
(注意调用变量的写法)。
- 不同目录下多个源文件
一般情况下,当程序文件比较多时,我们会进行分类管理,根据代码的不同功能放置在不同的目录下,这样方便查找,那么这种情况下该如何编写CMakelist.txt文件呢?整理好的文件结构如下:
这时,还是在main.c目录下新建CMakelist.txt文件,内容调整为下面所示:
cmake_minimum_required (VERSION 2.8)
project (demo)
include_directories (test_func test_func1)
aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)
add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})
这次使用了一个新命令include_directories
,该命令是用来向工程中添加多个指定头文件的搜索路径,路径之间用空格分开。因为main.c源文件中包含了testFunc.h和testFunc1.h,如果不用这个命令来指定头文件所在位置,就会无法编译,当然也可以在main.c里面使用include来指定路径,只是这种写法不太优雅,如下:
#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"
- 正规一点的组织结构
正规一点来说,我们会把源文件放在src目录下,把头文件放到include文件下,生成的对象文件存放在build目录下,最终输出的elf文件会放到bin目录下,这样整个结构更加清晰。比如下面这种结构:
这时,我们在最外层目录下新建一个CMakelist.txt文件,内容为:
cmake_minimum_required (VERSION 2.8)
project (demo)
add_subdirectory (src)
这里出现了一个新的命令add_subdirectory
,这个命令可以向当前工程添加存放源文件的子目录,这里指定src目录下存放了源文件,当执行cmake
时就会进入src目录下取寻找src目录下的CMakelist.txt文件,所以在src目录下也建立一个CMakelist.txt:
aux_source_directory (. SRC_LIST)
include_directories (../include)
add_executable (main ${SRC_LIST})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
这里有出现了一个新命令set
,是用于定义变量的,这里是把存放elf文件的位置设置为工程根目录下的bin目录。EXECUTABLE_OUTPUT_PATH和PROJECT_SOURCE_DIR是CMake自带的预定义变量,分别表示为:目标二进制可执行文件存放位置;工程根目录
下面来运行cmake
,不过这次我们将目录切换到buid目录下,然后输入以下命令:
cmake ..
Makefile文件会在build目录下生成,然后接着在build目录下运行make
,这时文件已经编译好了,回到bin目录下,发现可执行文件main已经生成。解释一下为什么要在build目录下运行cmake
,从之前几个例子可以知道,如果在最外层运行cmake
,运行时啥呢工程的附带文件就会和源码文件混在一起,这样会对程序的目录结构造成污染,而在build目录下运行cmake
,生成的附带文件只会呆在build目录下,如果我们不想要这些文件了可以直接清空该目录,非常方便。
- 动态库和静态库的编译控制
有时我们只需要编译出动态库,静态库,然后等着让其他程序去使用,这种情况我们该怎么使用cmake?
我们会在build目录下运行cmake,并把生成的库文件存放在lib目录下。最外层的CMakelist.txt内容如下:
cmake_minimum_required (VERSION 2.8)
project (demo)
add_subdirectory (lib_testFunc)
lib_testFunc目录下的CMakelist.txt如下,
aux_source_directory (. SRC_LIST)
add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
这里有出现了新的命令和预定变量,add_library
: 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件);set_target_properties
: 设置输出的名称,还有其它功能,如设置库的版本号等;LIBRARY_OUTPUT_PATH
: 库文件的默认输出路径,这里设置为工程目录下的lib目录
- 对库进行链接
既然我们已经生成了库,那么就进行链接测试下。把build里的文件都删除,然后在在工程目录下新建src目录和bin目录,在src目录下添加一个main.c和一个CMakeLists.txt,整体结构如下,
工程目录下CMakelist.txt文件修改如下:
cmake_minimum_required (VERSION 2.8)
project (demo)
add_subdirectory (lib_testFunc)
add_subdirectory (src)
src目录下的CMakelist.txt如下:
aux_source_directory (. SRC_LIST)
# find testFunc.h
include_directories (../lib_testFunc)
link_directories (${PROJECT_SOURCE_DIR}/lib)
add_executable (main ${SRC_LIST})
target_link_libraries (main testFunc)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
这里出现两个新的命令,link_directories
: 添加非标准的共享库搜索路径,target_link_libraries
: 把目标文件与库文件进行链接