make主要用在代码移植性上。跨平台管理编译工作有好几种方法,每种的核心都是一个叫make的程序,这是一个跨平台的工具。
Make
假如你有一个程序叫做bar,它由bar.cpp和main.cpp两个C++源文件以及一个bar.h头文件组成
main.cpp
#include "bar.h"
int main(int argc, char *argv[]) {
Bar bar;
bar.SetBar(17);
bar.PutBar();
}
bar.h
#pragma once
#ifndef _BAR_00__
#define _BAR_00__
class Bar {
public:
void SetBar(int bar);
void PutBar();
private:
int m_bar;
};
#endif // ! _BAR_00__
bar.cpp
#include<stdlib.h>
#include<stdio.h>
#include "bar.h"
void Bar::SetBar(int bar) {
m_bar = bar;
}
void Bar::PutBar() {
char buf[16];
snprintf(buf, sizeof(buf) - 1, "% d", m_bar);
setenv("BAR", buf, 1);
}
bar对象维护了一个整数m_bar.m_bar的值由SetBar()决定,而PutBar()函数则把m_bar的值存到环境变量BAR里。
从创建一个简单脚本build.sh开始,把它编译链接为一个bar应用程序
#! /bin/sh
g++ -g -o bar bar.cpp main.cpp
随后执行
sh build.sh
make工具按照一定的依赖规则来执行一组命令,以获得需要的目标或结果。上面例子中,目标就是可执行文件bar,它依赖于源文件bar.cpp和main.cpp,而用来生成可执行文件的命令是
g++ -o bar bar.cpp main.cpp
make要求在一个文件里指定目标,依赖关系和命令,通常这个文件的名字是Makefile,下面这个makefile同样可以编译我们的例子
bar:bar.cpp main.cpp
g++ -g -o bar bar.cpp main.cpp
有了Makefile之后,只需要键入下列命令
make
然后make就会为我们生成bar程序
make如何处理shell脚本带来的问题:
- make没彻底消除可移植性,在windows上,make并不像linux活mac上那样是原生应用程序.
- makefile并没有解决指定不同编译器编译项目的问题。
- 命令行参数依然是硬编码
-
make只会在源码的修改时间比可执行文件的修改时间万,才重新编译bar
如何改进呢,重写makefile如下
bar:bar.o main.o
g++ -g -o bar main.o bar.o
bar.o:bar.cpp bar.h
g++ -g -c bar.cpp
main.o:main.cpp bar.h
g++ -g -c main.cpp
上面的makefile通过引入两个新目标bar.o和main.o解决了依赖性的问题。现在依赖关系有了明确的目标,例如当bar.cpp和bar.h发生修改时,bar.o应该重新编译。bar自己的依赖关系也发生了变化,它不在依赖于源文件,而是依赖于目标文件。所以touch文件main.cpp后再执行make时,会得到以下结果
touch main.cpp
make
g++ -g -c main.cpp
g++ -g -o bar bar.o main.o
注意到make只重新编译了目标main.o和bar,但是编译器和命令行参数仍旧是硬编码。这两个问题都可以用macro来解决
OBJS=bar.o main.o
CXXFLAGS=-g
barL $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJS)
bar.o:bar.cpp bar.h
$(CXX) $(CXXFLAGS) -c bar.cpp
main.o:main.cpp bar.h
$(CXX) $(CXXFLAGS) -c main.cpp)
如果你注意到所有的.o文件其实都只依赖于对应的.cpp文件和bar.h的话,我们可以进一步改进Makefile,通过额外的make机制如模式规则(pattern rules),Makefile可以简化为:
OBJS=bar.o main.o
CXXFLAGS= -g
bar:$(OBJS)
$(CXX) $(CXXFLAGS) -o $@(OBJS)
% o:%.cpp
$(CXX) $(CXXFLAGS) -c $ <
$(OBJS):bar.h
问题依然存在,把-g 提取出来放到CXXFLAGS里确实是一大进步,但是依然是Makefile里的硬编码,what’s more terrible,它和编译器脱离开来了,如果CXX不是使用g++,那么-g可能会在别的编译器上有完全不同的意思
未完待续……