main.c
#include "speak.h"
int main(int argc , char **argv){
const char *str = "MakeFile";
toSpeak(str);
return 0;
}
speak.h
#include <stdio.h>
void toSpeak(const char *str);
speak.c
#include "speak.h"
void toSpeak(const char *str){
printf("hello:%s\n",str);
}
常规编译程序
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ gcc -c speak.c -o speak.o
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ gcc -c main.c -o main.o
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ gcc speak.o main.o -o helloMakeFile
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ls
helloMakeFile main.c main.o speak.c speak.h speak.o
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ./helloMakeFile
hello:MakeFile
Makefile 编写
程序的编译和链接
使用C、C++编写可执行程序,首先要把源文件编译成中间代码文件,Linux下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。
一个项目,拥有成百上千的源程序文件,编译链接这些源文件都是由规则的,Makefile闪亮登场!Makefile确定整个工程的编译规则,只需要一个make命令,就可以实现“自动化
Make 工作原理
通常在一个项目里,我们的规则是:
- 1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
- 2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。我们只需make脚本就会自动帮我们找到修改的文件,并且重新编译
- 3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
只要我们的Makefile写得够好,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。
Makefile的规则:
target ... : prerequisites ...
command
...
...
-
target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,暂不叙述。
-
prerequisites就是,要生成那个target所需要的文件或是目标。
-
command也就是make需要执行的命令。(任意的Shell命令)
重新编译上诉文件:
makefile
helloMakeFile : main.o speak.o
gcc main.o speak.o -o helloMakeFile
main.o : main.c speak.h
gcc -c main.c
speak.o : speak.c speak.h
gcc -c speak.c
clean :
rm helloMakeFile speak.o main.o
执行过程
// 执行makefile脚本
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ make
gcc -c main.c
gcc -c speak.c
gcc main.o speak.o -o helloMakeFile
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ls
helloMakeFile main.c main.o makefile speak.c speak.h speak.o
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ./helloMakeFile
hello:MakeFile
// 删除脚本产生的文件
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ make clean
rm helloMakeFile speak.o main.o
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ls
main.c makefile speak.c speak.h
Make 工作流程
- 1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“helloMakeFile”这个文件,并把这个文件作为最终的目标文件。
- 3、如果helloMakeFile文件不存在,或是helloMakeFile所依赖的后面的 .o 文件的文件修改时间要比helloMakeFile这个文件新,那么,他就会执行后面所定义的命令来生成helloMakeFile这个文件。
- 4、如果helloMakeFile所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。
- 5、当然,我们的C文件和H文件都存在,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件hello_demo了。
Make 变量
一个Makefile里我们发现经常会由重复的内容,如上面范例中的前两行中的
main.o speak.o
如果我们的工程需要加入一个新的[.o]文件,那么我们好几个地方都需要修改原来的makefile。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,完全可以理解成C语言中的宏。
变量定义: 变量名 = 值 使用shell script 的语法
如:
Cui = main.o speak.o
我们上面的makefile 就可以变成:
Cui = main.o speak.o
helloMakeFile : $(Cui)
gcc $(Cui) -o helloMakeFile
main.o : main.c speak.h
gcc -c main.c
speak.o : speak.c speak.h
gcc -c speak.c
clean :
rm helloMakeFile $(Cui)
更省事的方式,让Make 自动推导
make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。
只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个hello.o,那么hello.c,就会是hello.o的依赖文件。并且 gcc -c hello.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的新的makefile又出炉了。
Cui = main.o speak.o
helloMakeFile : $(Cui)
gcc $(Cui) -o helloMakeFile
main.o : main.c speak.h // main.c被自动推导出来,所以我们只要写上依赖的文件
gcc -c main.c // 执行语句被自动推导
speak.o : speak.c speak.h
gcc -c speak.c
clean :
rm helloMakeFile $(Cui)
简化版:
Cui = main.o speak.o
helloMakeFile : $(Cui)
gcc $(Cui) -o helloMakeFile
$(Cui) : speak.h # 这个地方会执行两次,第一个找main.o的时候,第二次找speak.o的时候
.PHONY : clean
clean :
rm helloMakeFile $(Cui)
拿clean举例,如果make完成后,自己另外定义一个名叫clean的文件,再执行make clean时,将不会执行rm命令。为了避免出现这个问题,需要.PHONY: clean
CMake,让Makefile 来得更猛男一点
-
你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
-
CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件。
-
Cmake 是帮助我们生成 makefile
安装cmake: sudo apt-get install cmake
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
- 1.编写 CMake 配置文件 CMakeLists.txt 。
- 2.执行命令 cmake PATH 生成 Makefile。其中, PATH 是 CMakeLists.txt 所在的目录。
- 3.使用 make 命令进行编译。
入门案例:
编写 CMakeLists.txt
#CMakeLists.txt 基本玩法
#CMake 最低版本要求
cmake_minimum_required(VERSION 3.1) # 自己安装的版本 3.16.3
#项目信息
project(HelloMakefile) # 项目名称
#指定生成的目标
add_executable(CmakeHelloMakefile speak.c main.c) # 生成目标文件 需要源文件
示例
main.c
#include "speak.h"
int main(int argc , char **argv){
const char *str = "MakeFile";
toSpeak(str);
return 0;
}
speak.h
#include <stdio.h>
void toSpeak(const char *str);
speak.cpp
#include "speak.h"
void toSpeak(const char *str){
printf("hello:%s\n",str);
}
CMakeLists.txt
#CMakeLists.txt 基本玩法
#CMake 最低版本要求
cmake_minimum_required(VERSION 3.1) # 自己安装的版本 3.16.3
#项目信息
project(HelloMakefile) # 项目名称
#指定生成的目标
add_executable(CmakeHelloMakefile speak.c main.c) # 生成目标文件 需要源文件
执行过程
uirui@cuirui-virtual-machine:~/text_demo/makefile_text$ cmake --version
cmake version 3.16.3
CMake suite maintained and supported by Kitware (kitware.com/cmake).
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ touch CMakeLists.txt
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ vi CMakeLists.txt
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ls
CMakeLists.txt main.c speak.c speak.h
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ cmake .
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/cuirui/text_demo/makefile_text
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ls
CMakeCache.txt cmake_install.cmake main.c speak.c
CMakeFiles CMakeLists.txt Makefile speak.h
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ make
Scanning dependencies of target CmakeHelloMakefile
[ 33%] Building C object CMakeFiles/CmakeHelloMakefile.dir/speak.c.o
[ 66%] Building C object CMakeFiles/CmakeHelloMakefile.dir/main.c.o
[100%] Linking C executable CmakeHelloMakefile
[100%] Built target CmakeHelloMakefile
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ls
CMakeCache.txt CmakeHelloMakefile CMakeLists.txt Makefile speak.h
CMakeFiles cmake_install.cmake main.c speak.c
cuirui@cuirui-virtual-machine:~/text_demo/makefile_text$ ./CmakeHelloMakefile
hello:MakeFile