C_and_CPP 板


LINE

很久以前就想写一篇关於 BJam 的教学,这工具虽然冷门,但却是无庸置疑地强 大。因为我自己也不算是非常了解 BJam 内部的实作,所以这篇文章可能有些谬 误,也未能完整描述 BJam 的所有功能,若其它版友有更多心得,也欢迎提出讨 论。 网页版: http://electronic-blue.wikidot.com/doc:bjam-quickstart * * * * * * 前言 Boost.Jam(BJam)是包含在知名的 Boost C++ library 中,类似 make 的软体 建构工具。虽然 BJam 似乎只是拿来编译 Boost 的附属工具,但其实它的功能非 常强大,同时改善了许多 make 的缺点,相当适合用来建构 C++ 软体。 BJam 的前身是 FTJam,而 FTJam 又是从 Perforce Jam 发展出来的软体建构工 具。Jam 为了方便控制软体建构流程,本身就是一个程式语言,除了文字处理外 还有条件分支、回圈等功能。这篇文章的重点在於简单介绍使用 BJam 建构软体 的其本使用方法,因此不会讲得太深入。如果你想进一步了解这套工具,请参考 底下的参考资料。 如果你是 make 的惯用者,可能会质疑 BJam 究竟有何过人之处,毕竟 make 是 最流行的软体建构工具,而且使用上非常简单。然而 make 虽然行之有年,却也 有不少缺点: 1. 在 Makefile 中,相依性的描述和编译指令是写在一起的,因此当我们想使 用另一款编译器时,由於下参数的方式不同,我们往往需要修改 Makefile。 2. Makefile 无法针对多种不同的编译选项建构出所有的组合。比如在编译函式 库时,我们可以选择要编出静态或动态函式库,也可以选择是否加入除错资 讯以利除错。若我们希望一次建构出这四种组合,就只能在 Makefile 中辛 苦地列出四个 make target,然後使用不同的指令去编译。然而 BJam 却可 以轻易处理这类需求。(注1) 3. make 仅有简单的条件分支和字串处理功能,对於功能扩充上显得相当麻烦。 注1:事实上几乎所有的 IDE 都无法达成这件事,以 VC 来说,你必需新增四个 不同的组态并个别指定其中的编译选项。 安装方法 完整的 BJam 有两部份:一个是读入 Jamfile(相当於 Makefile)并依照相依性 进行编译连结的主程式 bjam,以及定义各种编译器如何运作的 boost-build 工 具集。两着都可以在 SourceForge.net 上下载。 安装 bjam 主程式 bjam 只是一个单一执行档,你可以直接下载编译好的版本(Boost 已提 供 Windows、OSX、Linux、FreeBSD 四种常见平台的执行档)。想要自行编译 bjam 也非常简单,打开命令列模式,进入原始码目录後直接执行 build.bat(若 你用的是 UN*X 系统,请执行 build.sh),它会自动找出系统上现有的编译器 并编出 bjam 执行档。 安装 boost-build boost-build 是与平台无关的工具定义文件。下载後解开到任何地方都可以,但 你需要设定 BOOST_BUILD_PATH 这个环境变数指向你安装的位置,这样 bjam 才 知道要去哪里读取它。 另外你需要去设定你所使用的编译器。请编辑 boost-build 底下的 user-config.jam 这个档案,并拿掉相对应的注解。比如说你想使用 GCC,找到 # using gcc ; 那 一行并把井号注解拿掉即可: using gcc ; 用 Visual C++ 的情况就是把 # using msvc ; 的注解拿掉,只要你安装这些编 译器时选择装在预设的路径下,BJam 就会自己去找到这些 compiler 来用,这样 够简单了吧。 有个需要特别注意的地方,因为 BJam 使用空白来区分符号(包含分号在内), 所以不管是 user-config.jam 或是等下我们要写的 Jamfile,请记得分号前面要 留空白,否则会产生语法错误,这是刚开始使用 BJam 时常发生的问题。 using gcc; # 错误,分号前要留空白 开始使用 BJam hello world 我们先从最简单的 hello world 开始吧。假设你的程式非常简单,只有一个 hello.cpp 原始码。要使用 BJam 来建构你的程式时,你需要在相同目录下放两 个档案:Jamfile 及 Jamroot。Jamfile 扮演的角色就像传统的 Makefile 一 样,是用来描述相依性的,而 Jamroot 则是用来指出目录树中的根目录位置。 目录中档案的相关位置如下: hello/ ├─ hello.cpp ├─ Jamfile └─ Jamroot 在这个简单的例子中,Jamroot 唯一的作用就是指出根目录的位置,所以我们不 需要编辑它,让它的内容保持空白即可(但这个空白档案仍有必要存在目录中)。 马上就来看看我们该如何写 Jamfile: exe hello : hello.cpp ; # 冒号和分号都要记得留空白唷 是的,只有一行。而且这行字的意思非常单纯:hello 这个执行档是由 hello.cpp 编译出来的结果。和 Makefile 不一样的是你不用去一一写明编译连结的指令, 那些 BJam 都会帮你处理得好好的。剩下要做的,就是去执行 bjam 把执行档编 出来而已。在同一个目录下执行 bjam,热腾腾的 hello.exe 马上出炉: D:\tmp\hello>bjam ...found 9 targets... ...updating 5 targets... MkDir1 bin MkDir1 bin\gcc-mingw-3.4.5 MkDir1 bin\gcc-mingw-3.4.5\debug gcc.compile.c++ bin\gcc-mingw-3.4.5\debug\hello.o gcc.link bin\gcc-mingw-3.4.5\debug\hello.exe ...updated 5 targets... D:\tmp\hello> 如你所见,BJam 会把编译好的结果依照使用工具及参数的不同,放到对应的目录 底下。这麽做有个好处:你可以同时使用不同的工具及参数进行编译,比如说你 想分别用 GCC 以及 Visual C++ 来编你的 hello.exe: bjam toolset=gcc toolset=msvc 你可以用逗号来结合选项。更进一步,因为 BJam 知道 gcc 和 msvc 都是指编译 器的意思,所以你可以省略前面的「toolset=」。以下两条指令和上面的指令效 果是相同的: bjam toolset=gcc,msvc bjam gcc msvc 另外,你可以用 variant=debug 或 variant=release,来选择你要编译 debug 版 或是 release 版。预设会编译出 debug 版本,你可以用如下的指令编译 release 版(相当於打开最佳化并关闭除错资讯): bjam variant=release 当然你也可以一次编两份出来,而「variant=」也可以省略: bjam debug release 当然也可以同时选用不同的编译器: bjam debug release gcc msvc 上述的指令会分别用 GCC 与 Visual C++ 编出 debug 版与 release 版,共四个 组合的执行档。 BJam 目标宣告格式 我们先稍微解释一下 BJam 中通用的目标宣告方式: TYPE TARGET : SOURCE-LIST : REQUIREMENTS : DEFAULT-BUILD : USAGE-REQUIREMENTS 各个栏位代表的意思如下: TYPE 目标的种类(注2),比如说 exe 表示执行档,lib 表示函式库。 TARGET 目标的名称。若没有特别指定,产生出来的执行档或函式库档名会和这个建 构目标的名称相同。 SOURCE-LIST 建构目标所需的原始档,可以是档案,也可以是其它目标(函式库)。 REQUIREMENTS 建构该目标的必要选项。 DEFAULT-BUILD 建构该目标的预设选项,和 REQUIREMENTS 的不同处在於列於此处的选项可 以在命令列中改写。 USAGE-REQUIREMENTS 这边所列的选项会传递给此目标的依赖者。比如说我们建出了一套函式库并 把表头档放在特定位置,那麽我们会希望在编译所有用到这套函式库的其它 目标时,都会把这个特定的表头档位置加入编译选项中。稍後我们会看到更 具体的例子。 这边的 REQUIREMENTS、DEFAULT-BUILD 和 USAGE-REQUIREMENTS 都是控制编译与 连结时要加入的参数,然而它们分别具有不同的意涵。以下我们会慢慢说明。 注2:其实在 Jam 这个语言中,这边的 TYPE 是一条 rule 的名称。所谓的 rule 有点类似一般程式语言中的 function,而在 Jam 中的 rule 还会与档案系 统中的档案产生关联,并可指定在更新该目标时应执行哪些指令。 编译参数设定 最常见的情况是把编译选项放在 REQUIREMENTS 栏位中,以下是个例子: exe hello : hello.cpp : <define>WIN32 <include>"C:/include" ; 如此一来,BJam 在编译 hello.cpp 时,就会帮你加入 WIN32 这个前置处理器的 定义,并且把 C:\include 加入表头档的搜寻路径中。注意到我们会把路径中的 backslash(\)改成 slash(/),因为就如大多数的程式语言一般,backslash 在 Jamfile 中也是跳脱字元,因此用 slash 来当作目录分隔字元会比较方便。 为了避免混乱,这篇文章中我会统一用 slash(/)来作为路径的分隔字元。 常用的参数如下: <include> 把指定的目录加入 include path 中。 <warnings> 设定编译器是否显示警告。比如说 <warnings>off 就是把警告功能关掉。可 接受的值有 on(预设的警告层级)、off(不显示任何警告)、all(显示所 有警告)。 <debug-symbols> 设定是否加入除错资讯。可接受的值为 on 或 off。 <define> 定义前置处理器的巨集。除了前面的例子,你也可以用 <define>A=B 的方式 定义常数。 <optimization> 最佳化选项。off 表示关闭,speed 表示对速度最佳化,space 表示对执行 档大小最佳化。 下面是另一个例子: exe hack : hack.cpp : <define>USE_DIRTY_METHOD <define>MAGIC_NUMBER=0xFF <warnings>off <optimization>speed ; 这个例子中定义了 USE_DIRTY_METHOD 以及 MAGIC_NUMBER 这两个前置处理器符 号,并关掉警告、打开最佳化。在 Jamfile 中断行或 tab 会被视为空白字元, 因此你可以把 Jamfile 的内容排版成如上例般适合阅读的型式。 除了在 Jamfile 中指定编译选项,你也可以在命令列中指定: bjam define=USE_DIRTY_METHOD define="MAGIC_NUMBER=0xff" warnings=off 需要注意的是,因为我们把这些选项写在 REQUIREMENTS 栏位中,因此这些选项 会成为编译时的「必要条件」,即使你在命令列中指定了不同的选项,BJam 还是 会忠实地遵守 Jamfile 中的设定。以下面的例子来看: exe test1 : test.cpp ; exe test2 : test.cpp : <optimization>speed ; 若你在命令列中指定 optimization=off,你会发现编译 test1 时不会打开最佳 化,但编译 test2 时仍会打开最佳化。若想把「打开最佳化」当作是预设设定, 但可接受使用者在命令列中修改,可以把它放在 DEFAULT-BUILD 栏位中: exe test : test.cpp : : <optimization>speed ; 注意到 <optimization>speed 选项放在由冒号分隔的第三个栏位中。 以下是另一个例子: exe hello : main.cpp func1.cpp func2.cpp : <debug-symbols>on : <optimization>speed ; 在这个例子中,<debug-symbol>on 被放在 REQUIREMENTS 内,所以不管如何下 指令,编出来的执行档一定会包含除错资讯,而 <optimization>speed 被放在 DEFAULT-BUILD 中,所以只是预设打开最佳化,但不强制打开。 bjam release REM 编出 release 版的 hello.exe,但仍会加入除错资讯 bjam debug REM 编出 debug 版的 hello.exe,不会打开最佳化 建构函式库 当然,除了执行档外,BJam 也能帮你建出函式库,语法和编译执行档几乎是一样 的: lib my_toolkit : func1.cpp func2.cpp ; 预设情况下会编译出动态连结函式库(在 Windows 上会产生 my_toolkit.lib 及 my_toolkit.dll 两个档案,UN*X 上则通常叫 libmy_toolkit.so)。当然,你也 可以指定连结的方式,比如说想产生静态函式库的情况: lib my_toolkit : func1.cpp func2.cpp : <link>static ; 同样的,你也可以在命令列指定连结的方式,而分别使用不同的连结方式产生动 态及静态函式库也易如反掌。以下的指令会分别编出八种版本:使用 GCC 及 VC、 debug 及 release、动态连结及静态连结。 bjam gcc msvc debug release link=shared,static 连结执行档与函式库 若你的 hello.exe 用到了 my_toolkit 提供的功能,在编译时只要把 my_toolkit 加到 hello 的原始档中即可: exe hello : hello.cpp my_toolkit ; lib my_toolkit : func1.cpp func2.cpp ; 通常你的执行档还会连结到系统上已编译好的函式库。这类函式库也可以用 lib 来宣告: exe hello : hello.cpp jpeg ; lib jpeg : : <name>jpeg <search>"C:/Lib" ; 这边因为 jpeg 是已经编好的函式库,因此我们不须要指定它的原始档,只需用 <name> 来指定它的名称,并用 <search> 来指定函式库的搜寻路径。你不需要完 整写出函式库的档案名称(比如说在 Windows 上可能叫 jpeg.lib,而在 UN*X 上可能是 libjpeg.a 或 libjpeg.so),BJam 会自动跟据你的平台及编译器去处 理名称的问题。 另外,当你在使用某套函式库时,通常也必须额外指定表头档的搜寻路径: exe prog1 : prog1.cpp png : <include>"C:/Include/png" ; exe prog2 : prog2.cpp png : <include>"C:/Include/png" ; lib png : : <name>png <search>"C:/Lib" ; 所有使用到 png 的执行档在编译时,都要加入特定的表头档搜寻路径,但这样一 一指定不但容易出错,也缺乏弹性。因此我们可以改成这样: exe prog1 : prog1.cpp png ; exe prog2 : prog2.cpp png ; lib png : : <name>png <search>"C:/Lib" : : <include>"C:/Include/png" ; 注意我们把 <include>"C:/Include/png" 写在 USAGE-REQUIREMENTS 栏位中,因 此所有使用到 png 的目标(prog1 以及 prog2)在编译时都会自动加入这个选项。 使用多个 Jamfile 当你的程式愈长愈大,往往会将不同的模组放在不同的目录中,编译出多个函式 库後,再让主程式去连结它们。在 Jamfile 中,我们也可以去参照其它 Jamfile 中的目标。 以下是我们的目录内容: project/ ├─ Jamroot ├─ include/ │ ├─ common.h │ └─ ... ├─ lib/ │ ├─ Jamfile │ ├─ func.h │ ├─ func.cpp │ └─ ... └─ src/ ├─ Jamfile ├─ main.cpp └─ ui.cpp 为了达成模组化及程式码再利用,我们把一些核心功能放在 lib 目录下并让它成 为一套函式库,而主程式则放在 src 底下。这两个目录中都会有一份 Jamfile 来说明档案的相依性。在 lib/Jamfile 中是这麽写的: lib func : func.cpp ... # source list : # requirements : # default build : <include>"." # usage requirements ; 因为编译 src 底下的主程式时,会需要用到 lib 内的表头档,所以我们会把 <include>"." 放在 USAGE-REQUIREMENTS 栏位内。BJam 会很聪明地去了解目录 间的开系,所以当你把工作目录切换到 src 底下并执行 bjam 时,<include>"." 会代换成 <include>"../lib"。 而在 src/Jamfile 则是这样: exe main : main.cpp ui.cpp ../lib//func ; 注意其中的 ../lib//func,它意指「位於 ../lib 目录下的 Jamfile 中,名叫 func 的目标」。因此在建构 main 这支主程式之前,bjam 会先把 lib 底下的 func 这套函式库建构出来,然後让 main 去连结它。 此外,放在 include 目录下的表头档,是 lib 以及 src 底下的程式码都可能会 用到的。我们可以在最上层的 Jamroot 中加入这项指定: project : requirements <include>"./include" ; build-project src//main ; 这麽一来,在编译 lib 及 src 底下的档案时,BJam 会自动加入 include 作 为表头档的路径。第二行的 build-project 则是指定我们在根目录下直接执行 bjam 时,预设建构的目标。 bjam 的命令列参数 最後,我们稍微介绍一下命令列下的 bjam 指令格式: bjam [-option [value]] [target | feature=value ...] target 就是列在 Jamfile 中的目标名称,若不指定 target,则 bjam 会建构 出 Jamfile 中所有能建出来的目标。当然我们也能用 feature=value 的方式来 指定编译选项,正如我们之前的范例那样。 除了指明目标及编译选项,bjam 还有一些其它选项可让我们控制它的行为: -a 不管原始档是否有更新,都重新建构目标。 -j n 同时执行 n 个指令,在多处理器或多核心的电脑上可加速建构。 -n 显示出所有的建构指令,但不实际执行。这个选项可以让使用者快速了解 BJam 到底做了什麽事。 -o file 同上,但把指令输出到档案中,这样就能以批次档的方式执行建构工作。 -q 在编译出错时立刻停止。预设情况下,bjam 遇到编译错误时,仍会把其它 不相依於该目标的其它工作完成。 结语 除了以上提到的功能外,BJam 也提供了单元测试(unit testing)及档案安装等 功能,而藉由 Jam 提供的程式功能,让 BJam 支援其它的程式语言或编译器也不 是太困难的事。更深入的议题请参考 Boost.Build V2 的说明文件。 BJam 的功能非常强大,同时具有跨平台、跨编译器的特性,很适合作为大型 C++ 专案的建构工具。然而 Jam 本身的语法颇为独特,形成一道门槛,加上文件明显 不足,使得它难以流行。这份文件说明了 BJam 最基本的功能,希望能让众多程式 设计师们事半功倍。 参考资料 Boost.Build V2 http://www.boost.org/doc/tools/build/index.html 这些文件主要介绍 Boost.Build 工具集。除了一般使用方法外,也有如何扩 充功能的说明。 Boost.Jam http://www.boost.org/doc/tools/jam/index.html 这一页介绍 bjam 指令,同时也介绍 Jam 这个语言以及 Boost 所加入的新功 能。 --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 219.87.151.2
1F:推 fuha:不得不推你的用心 05/06 18:45
2F:→ tinlans:其实我还是比较喜欢用 CMake。 05/06 19:20
3F:推 yoco315:赞美主,之前就一直找不到 bjam 的 tutorial QQ 05/06 21:00
4F:推 wangm4a1:很好 推! 05/06 21:42
5F:推 saxontai:推,感谢佛心分享 05/06 22:46
6F:推 sunneo: 太棒了! 05/06 22:54
7F:推 SHBK:很用心 05/06 23:06
8F:推 vizshala:这篇可以m了啦 05/07 15:39
9F:推 zlw:bjam似乎是用..tools\build\v2\tools\里的msvc.jam当作toolset 06/23 17:36
10F:→ zlw:的设定是msvc时的设定档,但里面看起来蛮复杂,想找到可以指定 06/23 17:37
11F:→ zlw:cl.exe 参数的地方,但不确定要放哪,有人知道吗? 06/23 17:38
12F:推 zlw:我悟了,多亏了这篇的指导。才想到要用 bjam -o123.txt 输出 06/23 18:18
13F:→ zlw:打开後终於看到实际的指令 cl /Zm800 -nologo 然後到 msvc.jam 06/23 18:19
14F:→ zlw:搜寻/Zm800只找到一行而已,在那後面加自己想要cl参数即可。 06/23 18:20
15F:推 zlw:多打了一个点,msvc.jam 路径在.tools\build\v2\tools\ 里才对 06/23 18:29
16F:→ zlw:.\tools\build\v2\tools\ 06/23 18:29







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:iOS站内搜寻

TOP