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可能会在别的编译器上有完全不同的意思

未完待续……