在学习make之前,我们要知道为什么要使用make。在一个项目中,当我们改变某一个源程序时,若源文件较少,我们对其进行编译链接并生成新的目标文件并不复杂;然而,若我们的源文件很多,在一个大型的项目中时,若重新对这个项目进行编译链接无疑是耗时耗力,且其中很多的程序已经编译,无需再重新编译。make可以根据makefile文件提供的文件依赖,决定哪些需要重新编译,即利用其中的一个时间戳(在后面会讲到),从而节省时间。
例如,我们有一个test.h头文件,一个test.cpp源文件,还有一个main.cpp源文件,我们下面就讲解如何用make和makefile 生成一个可执行的目标文件;
首先,tesh.h头文件中声明了一个func函数:
#ifndef TEST_H #define TEST_H void func(char a); #endif
test.cpp源文件中对func函数进行定义:
#include <iostream> #include "test.h" using namespace std; void func(char a) { cout<<a<<endl; }
再定义一个main文件:
#include "test.h" #include<iostream> using namespace std; int main() { cout<<"please enter a char "<<endl; char ch; cin>>ch; cout<<"func(char)="<<func(ch)<<endl; return 0; }
上面定义了test.h头文件,及test.cpp和main.cpp的.cpp文件,下面首先通过g++命令编译看其结果:
若单纯的用g++,我们可以首先使用命令
g++ test.cpp -fPIC -shared -o test.so
生成test.so的动态链接库,然后链接这个库生成可执行目标文件:
g++ main.cpp -L. -ltest -I. -o test
这样我们就可以生成一个test的可执行文件。其中的一些参数大家可以去google或man g++查看其中的意义。
下面讲解如何通过make和makefile的搭配来实现:
我们要在源文件所在的目录中创建一个Makefile的文件,可以通过vim Makefile 命令实现。针对上面的例子我们可以写出Makefile如下:
CPP = g++ OBJS = main.cpp test.cpp test :$(OBJS) $(CPP) $(OBJS) -o $@
然后执行
make
./test
就可以生成了如同上面一样的结果。大家可以看出,Makefile的本质其实还是使用g++完成了编译过程。其中的CPP和OBJS相当于变量,方便我们添加文件。而$@代表的是第一个目标文件即test目标文件。而test :$(OBJS)表示test依赖于OBJS里的源文件。当make时,发现(OBJS)中的文件比test中更新,则更新test。这里面就涉及了linux的时间戳文件,大家可以去看CSDN中一篇博文<linux Makefile时间戳>.楼主浏览器貌似不能插入网址,不知道原因。。里面对linux的时间戳进行了详细的说明。关于Makefile时间戳的一个函数可以如下:
time_t GetModifiedTimestamp(char *path) { struct stat attr; if(stat(path,&attr) == -1) return 0; return attr.st_mtime; }
上面的代码是参考Cyberspace_TechNode的博文中的关于时间戳的函数。相应的我们也可以写个Makefile用于生成test.so的动态链接库。在此楼主就不写了。
关于Makefile模板,大家可以参考github上的一个我的师兄徐陈峰写的Makefile 模板。通过简单修改其中的一点参数即可简单的生成对应的如lib,bin等。其中的readme.txt有详细说明。
还有个师兄提议是不是可以考虑选择Cmake替代,因为Cmake是跨平台的,目前对Cmake的了解不是太多,希望大家能够给予意见及帮助!