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可能會在別的編譯器上有完全不同的意思
未完待續……