系列文章:
automake學(xué)習(xí)筆記 - helloworld
automake學(xué)習(xí)筆記 - 模塊化編譯
automake學(xué)習(xí)筆記 - 安裝與發(fā)布
automake學(xué)習(xí)筆記 - 交叉編譯
雖然之前已經(jīng)用過一段時(shí)間的automake,但是總覺得對它的理解不過充分,只是知其然而不知其所以然。于是下定決心好好啃文檔,并將學(xué)的的東西記錄下來。
這篇文章用一個(gè)簡單的log工具的編譯先對automake做一個(gè)hello world級別的介紹羡蛾。
代碼
我們的demo有6個(gè)文件cout_log_interface.h, cout_log_interface.cpp, log_interface.h, easy_log.h, easy_log.cpp, main.cpp
簡單介紹下代碼吧,首先有個(gè)簡單的Log類:
class EasyLog {
public:
EasyLog(std::shared_ptr<LogInterface> interface);
void Info(const std::string& tag, const std::string& log);
void Debug(const std::string& tag, const std::string& log);
void Warn(const std::string& tag, const std::string& log);
void Error(const std::string& tag, const std::string& log);
private:
std::string GetLog(const std::string& tag, const std::string& log) const;
std::shared_ptr<LogInterface> interface_;
};
它的實(shí)現(xiàn)十分簡單岂津,就是將所有的操作代理給LogInterface:
EasyLog::EasyLog(shared_ptr<LogInterface> interface)
: interface_(interface)
{
}
void EasyLog::Info(const string& tag, const string& log)
{
interface_->DoLog(kInfo, tag, GetLog(tag, log));
}
void EasyLog::Debug(const string& tag, const string& log)
{
interface_->DoLog(kDebug, tag, GetLog(tag, log));
}
void EasyLog::Warn(const string& tag, const string& log)
{
interface_->DoLog(kWarn, tag, GetLog(tag, log));
}
void EasyLog::Error(const string& tag, const string& log)
{
interface_->DoLog(kError, tag, GetLog(tag, log));
}
std::string EasyLog::GetLog(const string& tag, const string& log) const
{
return "[" + tag + "]" + " " + log;
}
LogInterface是一個(gè)純虛類硝皂,然后LogLevel是一個(gè)枚舉體:
enum LogLevel {
kInfo,
kDebug,
kWarn,
kError
};
class LogInterface {
public:
virtual void DoLog(LogLevel level, const std::string& tag, const std::string& log) = 0;
};
我們再寫一個(gè)使用標(biāo)準(zhǔn)輸出打印log的LogInterface:
class COutLogInterface : public LogInterface {
public:
virtual void DoLog(LogLevel level, const std::string& tag, const std::string& log);
};
它的實(shí)現(xiàn)就是使用cout打印log:
void COutLogInterface::DoLog(LogLevel level, const string& tag, const string& log) {
cout<<log<<endl;
}
當(dāng)然需要有個(gè)main函數(shù):
int main()
{
EasyLog log(std::make_shared<COutLogInterface>());
log.Debug("test", "testlog");
return 0;
}
Makefile.am
automake使用Makefile.am配置工程的源碼,它的內(nèi)容如下:
bin_PROGRAMS = easylog
easylog_SOURCES = cout_log_interface.cpp \
easy_log.cpp \
main.cpp
bin_PROGRAMS 指定了要編譯生成的目標(biāo)程序的名字,在這里我們最終編譯出來的目標(biāo)程序的文件名是easylog
之后的 easylog_SOURCES 指定了需要參與編譯的源代碼疫剃。
如果需要同時(shí)編譯多個(gè)目標(biāo)程序的話可以用下面的方式分別指定各個(gè)目標(biāo)程序的源代碼
bin_PROGRAMS = program_a program_b
program_a_SOURCES = code_a.cpp
program_b_SOURCES = code_b.cpp
configure.ac
指定了源代碼還不夠桨菜,因?yàn)閍utomake不僅僅可以用來生成編譯c/c++的makefile仍侥,還可以用來編譯生成其他許許多多語言的makefile要出,所以還需要指定編譯器和依賴文件等。automake使用configure.ac配置這些東西,本例子的configure.ac是這么寫的:
AC_INIT([easylog], [0.0.1], [466474482@qq.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX_11
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
簡單分析一下configure.ac的內(nèi)容:
- AC_INIT
指定了工程的名字农渊、版本號患蹂、和bug的報(bào)告郵箱
- AM_INIT_AUTOMAKE
指定了一些選項(xiàng),-Wall和-Werror指定編譯的時(shí)候?qū)⑺械膚arning當(dāng)做error來報(bào)錯(cuò), foreign告訴automake這里不用遵循GNU標(biāo)準(zhǔn)。GNU軟件包應(yīng)該包括一些附加文件去描述如修改項(xiàng)砸紊,作者等信息传于。在這里我們不需要automake去檢查這些附加文件的存在。
- AC_CONFIG_HEADERS
我們在AC_INIT中配置了版本號等信息,c/c++中一般需要用宏來定義它們,這里就指定了生成的配置宏的頭文件名醉顽。配置了這里,automake就會自動(dòng)幫我們生成config.h頭文件,里面定義了一些VERSION之類的宏
- AC_PROG_CXX
該宏用于檢查系統(tǒng)中是否有g(shù)++編譯器
- AX_CXX_COMPILE_STDCXX_11
檢查系統(tǒng)的c++11編譯支持
- AC_CONFIG_FILES
指定了需要configure生成的Makefile,autoreconf的時(shí)候會通過Makefile.am生成Makefile.in沼溜。而configure的時(shí)候會通過Makefile.in生成Makefile。因?yàn)镸akefile.am和configure.ac在同級目錄,所以直接寫Makefile就好了游添。在后面我會介紹當(dāng)Makefile.am和configure.ac不在同級目錄的時(shí)候需要怎么配置
- AC_OUTPUT
這是一個(gè)結(jié)束標(biāo)志,實(shí)際上它是一個(gè)腳本命令用來創(chuàng)建AC_CONFIG_HEADERS和AC_CONFIG_FILES所配置的文件
生成Makefile
首先要安裝autoconf
sudo apt-get install autoconf
然后使用下面的命令生成configure
autoreconf --install
除了configure之外系草,它還會生成一些其他的文件,當(dāng)然現(xiàn)在我們不需要去管這些文件
之后就能使用configure腳本去生成Makefile和config.h等
./configure
編譯工程
Makefile都已經(jīng)生成了唆涝,現(xiàn)在就可以使用make命令編譯工程啦
編譯成功之后就能在當(dāng)前目錄看到easylog程序找都。我們可以運(yùn)行它:
./easylog
得到下面輸出:
[test] testlog
在build目錄中編譯項(xiàng)目
現(xiàn)在我們編譯生成的.o文件和目標(biāo)程序都混在源代碼中間,看起來很不舒服。我們可以創(chuàng)建一個(gè)build目錄廊酣。然后進(jìn)入build目錄執(zhí)行下面命令
../configure
這樣就在build目錄下生成Makefile了,于是現(xiàn)在我們在build中使用make命令編輯工程就會發(fā)現(xiàn)編譯產(chǎn)生的.o文件和目標(biāo)文件都在build中而不會污染源代碼了能耻。
使用autoscan生成configure.ac
如果直接手寫configure.ac的話是比較困難的,很容易漏掉一些依賴項(xiàng)沒有檢查。所以就出現(xiàn)了autoscan這個(gè)工具,它可以幫我們檢查工程中的依賴項(xiàng)生成configure.ac的模板,然后我們只需要在它生成的模板上略加改動(dòng)就可以了亡驰。
我們在工程目錄下使用autoscan命令,會得到下面的兩個(gè)文件:
autoscan.log
configure.scan
autoscan.log是一個(gè)日志文件,通過它我們可以知道一些配置為什么會被需要
而configure.scan就是生成出來的configure.ac的模板了,在easylog工程目錄使用autoscan,生成的configure.scan內(nèi)容如下
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([log_interface.h])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
看是不是和我們之前手寫的很像?我們只有在上面進(jìn)行一些小的修改就能得到最終我們需要的configure.ac了
這里有兩個(gè)宏我們是沒有見過的
- AC_PREREQ
用于檢查autoconf的最低版本
- AC_CONFIG_SRCDIR
用一個(gè)項(xiàng)目中一定存在的文件去確定源碼目錄的有效性,這是一個(gè)安全檢查宏晓猛。configure有一個(gè)--srcdir的參數(shù)可以指定源碼目錄,這個(gè)宏就可以檢查出源碼目錄是否不小心配置錯(cuò)了
Demo項(xiàng)目
可以在這里查看完整的項(xiàng)目代碼