前言
本人剛剛接觸 Waf ,加之翻譯水平一般,有什么錯誤大家見諒。
精確版本請看 原文地址
Waf 是一份用來幫助編譯軟件工程的軟件。本教程的目標是提供如何為一個使用 Waf 的工程設置腳本的簡要說明。
Waf 腳本與命令
軟件通常有保存在版本管理系統(git, subversion 等等)的 源文件(source files),以及描述如何處理這些文件的 編譯腳本(build scripts) (Makefiles,...)。一些 生成文件(build files) 通常由 源文件(source files) 轉換而得,但它們是可選的。在 Waf 中編譯腳本是那些命名為 'wscript' 的文件。
通常,一個工程包含下面若干階段:
- 配置(configure): 配置工程,找到依賴項的位置
- 編譯(build): 將源文件轉換為生成文件
- 安裝(install): 安裝生成文件
- 卸載(uninstall): 卸載生成文件
- 打包(dist): 生成源文件的存檔
- 清理(clean): 刪除生成文件
每一階段在 wscript 文件中都是以一個 Python 函數構造的,該函數使用 waflib.Context.Context 的一個實例作為函數。
讓我們從在文件夾 /tmp/myproject 下 新建一個 wscript 文件開始:
def configure(conf):
print("configure!")
def build(bld):
print("build!")
我們也需要一個 Waf 二進制文件,如: http://waf.googlecode.com/files/waf-1.6.1 , 並把該文件拷貝到工程目錄下:
$ cd /tmp/myproject
$ wget http://waf.googlecode.com/files/waf-1.6.1
我們只需簡單地將命令作為參數傳遞給 waf 即可運行此工程:
$ ./waf-1.6.1 configure build
configure!
build!
目標
編譯系統的一個重要組成部分是聲明目標的創建過程。這里有一個非常簡單的例子:
def build(bld):
tg = bld(rule='cp ${SRC} ${TGT}', source='wscript', target='foo.txt')
bld(rule='cp ${SRC} ${TGT}', source='foo.txt', target='bar.txt')
調用 bld(..) 創建了一個 任務生成器(task generator) ,它用來生成 任務(tasks) 。 任務則實際運行命令 cp。 命令直到所有腳本都被讀取后才會運行,這對計算編譯順序非常重要。
表達式 ${SRC} 和 ${TGT} 是快捷方式,用來避免文件名重復。更多的快捷方式可以通過使用 ${} 符合定義,該符號能從 bld.env 屬性讀取對應的值。
def build(bld):
bld.env.MESSAGE = 'Hello, world!'
bld(rule='echo ${MESSAGE}', always=True)
bld 對象是類 waflib.Build.BuildContext,它的 env 屬性是類 waflib.ConfigSet.ConfigSet 的一個實例。
這些值被保存在此對象中以便於共享/保存/加載。這里是如何在配置和編譯過程中共享數據來實現和上個例子同樣的事情:
def configure(cnf):
cnf.env.MESSAGE = 'Hello, world!'
def build(bld):
bld(rule='echo ${MESSAGE}', always=True)
腳本與工具
為讓一個腳本使用子目錄下的另一腳本,需要使用方法 waflib.Context.Context.recurse 及包含 wscript 文件夾的相對路徑。例如,調用 src/wscript 腳本中 build 函數,應該這樣寫:、
def build(bld):
bld.recurse('src')
Waf 通過特定模塊 Waf tools 提供了對特定語言和編譯器的支持。這些工具與 wscript 文件類似且提供如 configure 或者 build 函數。這里是一個C語言的簡單工程:
def options(opt):
opt.load('compiler_c')
def configure(cnf):
cnf.load('compiler_c')
def build(bld):
bld(features='c cprogram', source='main.c', target='app')
options 函數是另一個預定義的命令,用來設置命令行選項。它的參數是 waflib.Options.OptionsContext 的一個實例。 提供了工具 compiler_c用以檢測是否有 C 編譯器存在,並設置各種參數如 cnf.env.CFLAGS。
用 bld 聲明的任務生成器並沒有 規則(rule) 關鍵字,而是用一系列 特性(features) 來引用那些調用適當規則的方法。 在這個例子中,一個規則被調用以編譯文件,而另一個用來鏈接目標文件到二進制文件 app 。 還存在其他一些工具依賴的 特性(features) 如: javac,cs 或者 tex 。
一個同時使用C和C++的工程
下面是一個更復雜一些工程的腳本
def options(opt):
opt.load('compiler_c compiler_cxx')
def configure(cnf):
cnf.load('compiler_c compiler_cxx')
configure.check(features='cxx cxxprogram', lib=['m'], cflags=['-Wall'], defines=['var=foo'], uselib_store='M')
def build(bld):
bld(features='c cshlib', source='b.c', target='mylib')
bld(features='c cxx cxxprogram', source='a.c main.cpp', target='app', use=['M','mylib'], lib=['dl'])
方法 waflib.Tools.c_config.check 會內部執行編譯以檢測在操作系統中是否存在 libm 庫。然后它會定義變量如:
conf.env.LIB_M = ['m']conf.env.CFLAGS_M = ['-Wall']conf.env.DEFINES_M = ['var=foo']
通過聲明 use=['M', 'mylib'],程序 app 會繼承所有在配置過程中定義的 M 變量。該程序也會使用庫 mylib 並且編譯順序和依賴項都會更改以使 mylib 在 app 之前鏈接。
use 屬性也適用於其他語言如Java(jar 文件之間的依賴)或者C#(程序集之間的依賴)。
工程特定擴展
feature 關鍵字是高層次的對現有 Waf 方法的引用。例如: c feature 會添加方法 waflib.Tools.ccroot.apply_incpaths 以執行。要添加一個為所有C目標加入任務生成器路徑到包含路徑的新方法,可以采用如下聲明:
from waflib import Utils
from waflib.TaskGen import feature, before_method
@feature('c')
@before_method('apply_incpaths')
def add_current_dir_to_includes(self):
self.includes = Utils.to_list(self.includes)
self.includes.append(self.path)
def build(bld):
tg = bld(features='c', source='main.c', target='app')
這些 feature 方法被綁定到類 waflib.TaskGen.task_gen ,在這個例子中是對象 tg 的類。新的 feature 可以以相同的方式聲明:
from waflib.TaskGen import feature, after_method
@feature('debug_tasks')
@after_method('apply_link')
def print_debug(self):
print('tasks created %r' % self.tasks)
def build(bld):
tg = bld(features='c cprogram debug_tasks', source='main.c', target='app')
通過綁定新方法到 context 類, 聲明可以變得更加用戶友好。
from waflib.Build import BuildContext
def enterprise_program(self, *k, **kw):
kw['features'] = 'c cprogram debug_tasks'
return self(*k, **kw)
BuildContext.enterprise_program = enterprise_program
def build(bld):
# no feature line
bld.enterprise_program(source='main.c', target='app')
這些輔助代碼放到單獨文件中即可以成為一個 Waf 工具。為了便於部署,新的 Waf 工具甚至可以被添加到 Waf 文件中(參見 http://code.google.com/p/waf/source/browse/trunk/README)。
