C++进阶3:Makefile入门
0、作用
Makefile文件告诉Make怎样编译和连接成一个程序。
1、Makefiile基本语法与执行
1.1 示例
编译一个单文件HelloWorld.cpp
(1)编写Makefile
HelloWorld: HelloWorld.cpp
g++ HelloWorld.cpp-o HelloWorld
clean:
rm HelloWorld
(2)编译
make
(3)清空
make clean
1.2 构成
Makefile主要由多条规则构成,每条规则由三部分构成:目标(target)、依赖(prerequiries)和命令(command)。
1.3 格式
按如下格式编写Makefile
目标(target): 依赖(prerequiries)...
命令(command)
(1)目标(target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,诸如‘clean’(仅仅表达动作的目标称为假想目标)。
(2)依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。
(3)命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。
注意:
每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。这是不小心容易出错的地方。
1.4 说明:
(1)默认情况下,make最先执行第一条。
(2)使用make 目标名的方式,执行指定的规则。
2、Makefile多文件编译
2.1 示例
(1)string.h
#ifndef_STRING_H_#define_STRING_H_#include<iostream>usingnamespace std;#include<string.h>classString{public:String(constchar* cstr=NULL);String(const String& str);
String&operator=(const String& str);~String();char*c_str()const{return m_data;}private:char* m_data;};
ostream&operator<<(ostream& os,const String& str);#endif// _STRING_H_
(2)string.cpp
#include"String.h"String::String(constchar* cstr/*= NULL*/){if(cstr){
m_data=newchar[strlen(cstr)+1];strcpy(m_data, cstr);}else{
m_data=newchar[1];*m_data='\0';}}String::String(const String& str){
m_data=newchar[strlen(str.m_data)+1];strcpy(m_data, str.m_data);}
String& String::operator=(const String& str){//检测是否自我赋值if(this==&str)return*this;delete[] m_data;
m_data=newchar[strlen(str.m_data)+1];strcpy(m_data, str.m_data);return*this;}String::~String(){delete[] m_data;}
ostream&operator<<(ostream& os,const String& str){
os<< str.c_str();return os;}
(3)StringTest.cpp
#include"String.h"intmain(){
String s1;
Strings2("hello");
Strings3(s1);//拷贝构造函数
cout<< s3<< endl;
s3= s2;//拷贝赋值函数
cout<< s3<< endl;return0;}
(4)makefile
StringTest:StringTest.o String.o
g++-o StringTest StringTest.o String.o
StringTest.o:StringTest.cpp String.h
g++-c StringTest.cpp
String.o:String.cpp String.h
g++-c String.cpp
clean:
rm StringTest StringTest.o String.o
2.2 说明
make执行规则之前,检查依赖是否存在或者是否最新的。如果不是则执行依赖对应的规则,创建或者更新依赖。
3、使用变量简化makefile
**优点:**每次增加新的文件,需要在nakefile的很多地方增加依赖,容易导致遗漏。可以使用变量可以简化,避免这种出错的可能。
变量定义:变量=字符串
变量使用:$(变量名)
3.1 示例
(1)makefile
OBJS= StringTest.o String.o
StringTest:$(OBJS)
g++-o StringTest $(OBJS)
StringTest.o:StringTest.cpp String.h
g++-c StringTest.cpp
String.o:String.cpp String.h
g++-c String.cpp
clean:
rm StringTest $(OBJS)
在makefile文件中使用名为objects, OBJECTS, objs, OBJS, obj, 或 OBJ的变量代表所有OBJ文件已是约定成俗。
3.2 说明
变量是定义一个字符串,在多处替代该字符串使用。
4、命令自动推导
编译.o文件这类非常普遍并且常用,规则也比较简单
文件名.o:文件名.cpp 头文件
g++-c 文件名.cpp
make提供一种简化写法,可以自动推导出该规则
文件名.o:头文件
这种简化规则称为隐含规则,非简化规则成为具体规则。
4.1 示例
(1)makefile
OBJS= StringTest.o String.o
StringTest:$(OBJS)
g++-o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h
clean:
rm StringTest $(OBJS)
(2)小知识
通常,规则按照目标进行分组。规则也可以按照依赖分组。例如,例子中String.o和StringTest.o都依赖String.h。那么,可以这两个合并到一个规则中。
OBJS= StringTest.o String.o
StringTest:$(OBJS)
g++-o StringTest $(OBJS)
StringTest.o String.o:String.h
clean:
rm StringTest $(OBJS)
按照依赖分组规则可以减少规则数量,规则按照目标分组更符合我们日常思维习惯。
5、假想动作
表达动作的目标称为假想目标。通常规则会生成或者更新与目标的同名文件,但是假想目标不生成文件,只是作为几个命令组成特殊规则的名称。例如例子中的clean,只是执行清理动作。**如果,makefile同级目录存在与假象目标同名的文件(例如:clean),那么会导致命令不会被执行。**所以需要把目标显示声明为假想目标。
5.1 示例
(1)makefile
OBJS= StringTest.o String.o.PHONY: all clean
all:StringTest
StringTest:$(OBJS)
g++-o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h
clean:
rm StringTest $(OBJS)
(2)常用假想目标
6、通配符与变量
编译.o文件可以写成更通用的方式,使用我们之前已经定义好的变量$(OBJS),自动推导出需要生成的规则。
6.1 makefile
OBJS= StringTest.o String.o.PHONY: all clean
all:StringTest
StringTest:$(OBJS)
g++-o StringTest $^
$(OBJS):%.o:%.cpp
$(CXX)-c $(CXXFLAGS) $<-o $@.PHONY: clean
clean:
rm StringTest $(OBJS)
6.2 说明
(1)通配符
通配符主要用于匹配文件名,makefile中使用%作为通配符。从匹配目标格式的目标名中依据通配符抽取部分字符串,再按照抽取字符串分配到每一个依赖格式中产生依赖名。例如,使用%.o:%.cpp。
(2)自动变量
自动变量是在规则每次执行时都基于目标和依赖产生新值的变量。下面是常用的自动变量。
(3)预定义变量
预定义变量是makefile已经定义好的变量,用户可以在makefile文件中改变变量的值。
程序名变量
程序运行参数的变量
7、其他
注释#
换行
回显命令@echo
8、总结
9、常见makefile的用法汇总
#规则:(命令构成的一组操作)
#默认只执行第一条规则
#若想执行其他规则,make+命令名字
#目标:依赖
# 命令
##g++ overload.o Test.o
#只执行第一条规则,然后先找依赖在下面存在不存在,如果存在,先执行依赖,再执行第一条,依赖是串联规则执行的条件
#表示前面的TARGET,$^表示前面的OBJECTS,$<表示第一个依赖。
#前面的总的定义
TARGET=Test #生成的文件名
OBJECTS= overload.o\
Test.o#变量做替换的#CXX=clang CXX可以改成其他的编程方式,例如gcc等等
CXXFLAGS=-g #可以变成调试版本的.PHONY: clean
#虚假目标,用来无视其他干扰,只执行makefile里面的命令
#第一条规则,也是总的规则
all:$(TARGET)
$(TARGET):$(OBJECTS)
@echo 生成可执行文件$(TARGET)
$(CXX) $(CXXFLAGS) $^-o $@#overload.o:overload.cpp
# $(CXX) $(CXXFLAGS)-c $<#Test.o:Test.cpp
# $(CXX) $(CXXFLAGS)-c $<
#%表示通配符
#通配符推导
$(OBJECTS):%.o:%.cpp
$(CXX) $(CXXFLAGS)-c $<
clean:
@echo 清空文件
$(RM) Test*.o