Scons使用入门
1.簡單編譯
? ? 源文件:hello.cpp
?用SCons編譯它,需要在一個名為SConstruct的文件:
? ? 這個短小的配置文件給了SCons兩條信息:你想編譯什么(一個可執(zhí)行程序),你編譯的輸入文件(hello.cpp)。Program是一個編譯器方法(builder_method),一個Python調(diào)用告訴SCons,你想編譯一個可執(zhí)行程序。 Program編譯方法是SCons提供的許多編譯方法中一個。另一個是Object編譯方法,告訴SCons從指定的源文件編譯出一個目標(biāo)文件,在SConstruct中為:
? ? 使用SCons,編譯之后想要清除不需要增加特殊的命令或目標(biāo)名。你調(diào)用SCons的時候,使用-c或--clean選項,SCons就會刪除合適的編譯產(chǎn)生的文件。?
? ? 當(dāng)你調(diào)用Program編譯方法的的時候,它編譯出來的程序名字是和源文件名是一樣的。下面的從hello.cpp源文件編譯一個可執(zhí)行程序的調(diào)用將會在POSIX系統(tǒng)里編譯出一個名為hello的可執(zhí)行程序,在windows系統(tǒng)里會編譯出一個名為hello.exe的可執(zhí)行程序。如果你想編譯出來的程序的名字與源文件名字不一樣,你只需要在源文件名的左邊聲明一個目標(biāo)文件的名字就可以了:
Program('new_hello','hello.cpp')
2. SConstruct文件
SConstruct文件實際上就是一個Python腳本。你可以在你的SConstruct文件中使用Python的注釋:
# Arrange to build the "hello" program.
重要的一點是SConstruct文件并不完全像一個正常的Python腳本那樣工作,其工作方式更像一個Makefile,那就是在SConstruct文件中SCons函數(shù)被調(diào)用的順序并不影響SCons你實際想編譯程序和目標(biāo)文件的順序。換句話說,當(dāng)你調(diào)用Program方法,你并不是告訴SCons在調(diào)用這個方法的同時馬上就編譯這個程序,而是告訴SCons你想編譯這個程序:
指定默認(rèn)的目標(biāo)文件
? ? ?指定了默認(rèn)的target,如果在命令行中沒有顯示指定target,那么scons將編譯默認(rèn)的target,多次調(diào)用Default是合法的,實例:
? ? 如果在Default中傳入?yún)?shù)None,那么將會清楚所有默認(rèn)的target:
Default(None)
3. 編譯多個源文件
通常情況下,你需要使用多個輸入源文件編譯一個程序。在SCons里,只需要就多個源文件放到一個Python列表中就行了,如下所示:
?你可以使用Glob函數(shù),定義一個匹配規(guī)則來指定源文件列表,比如*,?以及[abc]等標(biāo)準(zhǔn)的shell模式。如下所示:
為了更容易處理文件名長列表,SCons提供了一個Split函數(shù),這個Split函數(shù)可以將一個用引號引起來,并且以空格或其他空白字符分隔開的字符串分割成一個文件名列表,示例如下:
SCons允許使用Python關(guān)鍵字參數(shù)來標(biāo)識輸出文件和輸入文件。輸出文件是target,輸入文件是source,示例如下:
如果需要用同一個SConstruct文件編譯多個程序,只需要調(diào)用Program方法多次:
多個程序之間共享源文件是很常見的代碼重用方法。一種方式就是利用公共的源文件創(chuàng)建一個庫文件,然后其他的程序可以鏈接這個庫文件。另一個更直接,但是不夠便利的方式就是在每個程序的源文件列表中包含公共的文件,示例如下:
4. 編譯和鏈接庫文件
(1)編譯靜態(tài)庫:
你可以使用Library方法來編譯庫文件:
Library('foo', ['f1.cpp', 'f2.cpp', 'f3.cpp'])
除了使用源文件外,Library也可以使用目標(biāo)文件
Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
你甚至可以在文件List里混用源文件和目標(biāo)文件
Library('foo', ['f1.cpp', 'f2.o', 'f3.c', 'f4.o'])
使用StaticLibrary顯示編譯靜態(tài)庫
StaticLibrary('foo', ['f1.cpp', 'f2.cpp', 'f3.cpp'])
(2)編譯動態(tài)庫:
?
? 如果想編譯動態(tài)庫(在POSIX系統(tǒng)里)或DLL文件(Windows系統(tǒng)),可以使用SharedLibrary:
SharedLibrary('foo', ['f1.cpp', 'f2.cpp', 'f3.cpp'])
(3)鏈接庫文件:
鏈接庫文件的時候,使用$LIBS變量指定庫文件,使用$LIBPATH指定存放庫文件的目錄:
Library('foo', ['f1.cpp', 'f2.cpp', 'f3.cpp'])
Program('prog', LIBS=['foo', 'bar'], LIBPATH='.')注意到,你不需要指定庫文件的前綴(比如lib)或后綴(比如.a或.lib),SCons會自動匹配。
默認(rèn)情況下,鏈接器只會在系統(tǒng)默認(rèn)的庫目錄中尋找?guī)煳募?。SCons也會去$LIBPATH指定的目錄中去尋找?guī)煳募?LIBPATH由一個目錄列表組成,如下所示:
Program('prog', LIBS='m', LIBPATH=['/usr/lib', '/usr/local/lib'])
5. 節(jié)點對象?
編譯方法返回目標(biāo)節(jié)點列表
? ? 所有編譯方法會返回一個節(jié)點對象列表,這些節(jié)點對象標(biāo)識了那些將要被編譯的目標(biāo)文件。這些返回出來的節(jié)點可以作為參數(shù)傳遞給其他的編譯方法。例如,假設(shè)我們想編譯兩個目標(biāo)文件,這兩個目標(biāo)有不同的編譯選項,并且最終組成一個完整的程序。這意味著對每一個目標(biāo)文件調(diào)用Object編譯方法,如下所示:
? ? 這樣指定字符串名字的問題就是我們的SConstruct文件不再是跨平臺的了。因為在Windows里,目標(biāo)文件成為了hello.obj和goodbye.obj。一個更好的解決方案就是將Object編譯方法返回的目標(biāo)列表賦值給變量,這些變量然后傳遞給Program編譯方法:?
顯示創(chuàng)建文件和目錄節(jié)點
? ? 在SCons里,表示文件的節(jié)點和表示目錄的節(jié)點是有清晰區(qū)分的。SCons的File和Dir函數(shù)分別返回一個文件和目錄節(jié)點:
? ? 通常情況下,你不需要直接調(diào)用File或Dir,因為調(diào)用一個編譯方法的時候,SCons會自動將字符串作為文件或目錄的名字,以及將它們轉(zhuǎn)換為節(jié)點對象。只有當(dāng)你需要顯示構(gòu)造節(jié)點類型傳遞給編譯方法或其他函數(shù)的時候,你才需要手動調(diào)用File和Dir函數(shù)。有時候,你需要引用文件系統(tǒng)中一個條目,同時你又不知道它是一個文件或一個目錄,你可以調(diào)用Entry函數(shù),它返回一個節(jié)點可以表示一個文件或一個目錄:
xyzzy=Entry('xyzzy')
?將一個節(jié)點的文件名當(dāng)作一個字符串
? ? 如果你不是想打印文件名,而是做一些其他的事情,你可以使用內(nèi)置的Python的str函數(shù)。例如,你想使用Python的os.path.exists判斷一個文件是否存在:
GetBuildPath:從一個節(jié)點或字符串中獲得路徑
? ? env.GetBuildPath(file_or_list)返回一個節(jié)點或一個字符串表示的路徑。它也可以接受一個節(jié)點或字符串列表,返回路徑列表。如果傳遞單個節(jié)點,結(jié)果就和調(diào)用str(node)一樣。路徑可以是文件或目錄,不需要一定存在:
6. 依賴性
隱式依賴:$CPPPATH Construction變量
#include <iostream>
#include "hello.h"
using namespace std;int main()
{cout << "Hello, " << string << endl;return 0;
}并且,hello.h文件如下:?
#define string "world"
? ? 在這種情況下,我們希望SCons能夠認(rèn)識到,如果hello.h文件的內(nèi)容發(fā)生改變,那么hello程序必須重新編譯。我們需要修改SConstruct文件如下:?
7. 環(huán)境
(1)外部環(huán)境
? ? 外部環(huán)境指的是在用戶運行SCons的時候,用戶環(huán)境中的變量的集合。這些變量在SConscript文件中通過Python的os.environ字典可以獲得。你想使用外部環(huán)境的SConscript文件需要增加一個import os語句。
(2)構(gòu)造環(huán)境
? ? 一個構(gòu)造環(huán)境是在一個SConscript文件中創(chuàng)建的一個唯一的對象,這個對象包含了一些值可以影響SCons編譯一個目標(biāo)的時候做什么動作,以及決定從那一個源中編譯出目標(biāo)文件。SCons一個強大的功能就是可以創(chuàng)建多個構(gòu)造環(huán)境,包括從一個存在的構(gòu)造環(huán)境中克隆一個新的自定義的構(gòu)造環(huán)境。
創(chuàng)建一個構(gòu)造環(huán)境:Environment函數(shù)
? ? 默認(rèn)情況下,SCons基于你系統(tǒng)中工具的一個變量集合來初始化每一個新的構(gòu)造環(huán)境。當(dāng)你初始化一個構(gòu)造環(huán)境時,你可以設(shè)置環(huán)境的構(gòu)造變量來控制一個是如何編譯的。例如:
從一個構(gòu)造環(huán)境中獲取值
? ? 你可以使用訪問Python字典的方法獲取單個的構(gòu)造變量:?
? ? 一個構(gòu)造環(huán)境實際上是一個擁有方法的對象。如果你想直接訪問構(gòu)造變量的字典,你可以使用Dictionary方法:?
?默認(rèn)的構(gòu)造環(huán)境:DefaultEnvironment函數(shù)
? ? 你可以控制默認(rèn)構(gòu)造環(huán)境的設(shè)置,使用DefaultEnvironment函數(shù):
DefaultEnvironment(CC='/usr/local/bin/gcc')
? ? 這樣配置以后,所有Program或者Object的調(diào)用都將使用/usr/local/bin/gcc編譯目標(biāo)文件。注意到DefaultEnvironment返回初始化了的默認(rèn)構(gòu)造環(huán)境對象,這個對象可以像其他構(gòu)造環(huán)境一樣被操作。所以如下的代碼和上面的例子是等價的:?
env=DefaultEnvironment()
env['CC']='/usr/local/bin/gcc'
多個構(gòu)造環(huán)境?
? ? 構(gòu)造環(huán)境的真正優(yōu)勢是你可以創(chuàng)建你所需要的許多不同的構(gòu)造環(huán)境,每一個構(gòu)造環(huán)境對應(yīng)了一種不同的方式去編譯軟件的一部分或其他文件。比如,如果我們需要用-O2編譯一個程序,編譯另一個用-g,我們可以如下做:
拷貝構(gòu)造環(huán)境:Clone方法?
? ? 有時候你想多于一個構(gòu)造環(huán)境對于一個或多個變量共享相同的值。當(dāng)你創(chuàng)建每一個構(gòu)造環(huán)境的時候,不是重復(fù)設(shè)置所有共用的變量,你可以使用Clone方法創(chuàng)建一個構(gòu)造環(huán)境的拷貝。Environment調(diào)用創(chuàng)建一個構(gòu)造環(huán)境,Clone方法通過構(gòu)造變量賦值,重載拷貝構(gòu)造環(huán)境的值。例如,假設(shè)我們想使用gcc創(chuàng)建一個程序的三個版本,一個優(yōu)化版,一個調(diào)試版,一個其他版本。我們可以創(chuàng)建一個基礎(chǔ)構(gòu)造環(huán)境設(shè)置$CC為gcc,然后創(chuàng)建兩個拷貝:
替換值:Replace方法
? ? 你可以使用Replace方法替換已經(jīng)存在的構(gòu)造變量:
在沒有定義的時候設(shè)置值:SetDefault方法
? ? 有時候一個構(gòu)造變量應(yīng)該被設(shè)置為一個值僅僅在構(gòu)造環(huán)境沒有定義這個變量的情況下。你可以使用SetDefault方法,這有點類似于Python字典的set_default方法:
? ? 調(diào)用該SConscript函數(shù)有兩種方法: ?
? ? 第一種方法是明確指定一個或多個 scripts 作為第一個參數(shù)??梢詫蝹€腳本指定為字符串; 多個腳本則必須指定為列表(顯式或由函數(shù)創(chuàng)建 Split),例子:
SConscript('SConscript') ? ? ? ? ? ? ? ? ? ? ? ? ? ?#在當(dāng)前目錄中運行SConscript? SConscript('src / SConscript') ? ? ? ? ? ? ? ? ? ? ?#在src目錄中運行SConscript? SConscript(['src / SConscript','doc / SConscript'])# 執(zhí)行多個腳本
? 第二種方法是將(子)目錄名稱列表指定為 dirs=subdirs 參數(shù)。在這種情況下,scons將 在每個指定目錄中執(zhí)行名為SConscript的輔助配置文件 。您可以 通過提供可選的name = keyword參數(shù)來指定除了名為SConscript以外script。例子:
? ?可選exports參數(shù)提供變量名稱列表或要導(dǎo)出到script的命名值字典。這些變量僅在本地導(dǎo)出到指定 script(s)的變量,并且不會影響Export函數(shù)使用的全局變量池 。子腳本Script必須使用Import函數(shù)來導(dǎo)入變量。例子:
? ? ?如果提供variant_dir參數(shù),Sconscript位于源碼目錄之下,就像位于variant_dir目錄下一樣,例子一:
SConscript('src/SConscript', variant_dir = 'build')
等價于:
VariantDir('build', 'src') ? ? ?# 指定obj文件的目錄
SConscript('build/SConscript')
? 例子二:
SConscript('SConscript', variant_dir = 'build')
等價于:
VariantDir('build', '.')
SConscript('build/SConscript')
? ? ?如果沒有提供variant_dir參數(shù),那么參數(shù)duplicate參數(shù)將會被忽略,這個參數(shù)表示是否備份目標(biāo)文件。 ?
?(3)執(zhí)行環(huán)境
? ? 一個執(zhí)行環(huán)境是SCons在執(zhí)行一個外部命令編譯一個或多個目標(biāo)文件時設(shè)置的一些值。這和外部環(huán)境是不同的。
控制命令的執(zhí)行環(huán)境
? ? 當(dāng)SCons編譯一個目標(biāo)文件的時候,它不會使用你用來執(zhí)行SCons的同樣的外部環(huán)境來執(zhí)行一些命令。它會使用$ENV構(gòu)造變量作為外部環(huán)境來執(zhí)行命令。這個行為最重要的體現(xiàn)就是PATH環(huán)境變量,它決定了操作系統(tǒng)將去哪里查找命令和工具,與你調(diào)用SCons使用的外部環(huán)境的不一樣。這就意味著SCons將不能找到你在命令行里執(zhí)行的所有工具。PATH環(huán)境變量的默認(rèn)值是/usr/local/bin:/bin:/usr/bin。如果你想執(zhí)行任何命令不在這些默認(rèn)地方,你需要在你的構(gòu)造環(huán)境中的$ENV字典中設(shè)置PATH,最簡單的方式就是當(dāng)你創(chuàng)建構(gòu)造環(huán)境的時候初始化這些值:
path=['/usr/local/bin', '/usr/bin']
env=Environment(ENV={'PATH':PATH})
? ? 以這種方式將一個字典賦值給$ENV構(gòu)造變量完全重置了外部環(huán)境,所以當(dāng)外部命令執(zhí)行的時候,設(shè)置的變量僅僅是PATH的值。如果你想使用$ENV中其余的值,僅僅只是設(shè)置PATH的值,你可以這樣做:?
env['ENV']['PATH']=['/usr/local/bin','/bin','/usr/bin']
? ? 注意SCons允許你用一個字符串定義PATH中的目錄,路徑用路徑分隔符分隔:?
?env['ENV']['PATH']='/usr/local/bin:/bin:/usr/bin'
從外部環(huán)境獲得PATH值
? ? 你可能想獲得外部的PATH來作為命令的執(zhí)行環(huán)境。你可以使用來自os.environ的PATH值來初始化PATH變量:
import os
env=Environment(ENV={'PATH':os.environ['PATH']})
? ? 你設(shè)置可以設(shè)置整個的外部環(huán)境:
import os
env=Environment(ENV=os.environ)
在執(zhí)行環(huán)境里增加PATH的值
? ? 常見的一個需求就是增加一個或多個自定義的目錄到PATH變量中:
env=Environment(ENV=os.environ)
env.PrependENVPath('PATH','/usr/local/bin')
env.AppendENVPath('LIB','/usr/local/lib')
本文參考:https://blog.csdn.net/andyelvis/article/category/948141
?
總結(jié)
- 上一篇: 交换机分布缓存_缓存比普通交换机也大许多
- 下一篇: 基于SSM框架的狼途汽车门店管理系统的设