资讯专栏INFORMATION COLUMN

Waf - a Python based build system

sunnyxd / 1380人阅读

摘要:以上便是所有的内容,可以看到涉及到的文件不算多。用也一样,一般是再等机器的轰鸣停止后继续工作流。这个抽象几乎相互独立,个人认为是很好的一个抽象。从声明式表达到生成的这项任务,由完成。

前言

看懂这篇文章需要一点使用waf的经验,不过也不费事,看看例子也够了。

构建系统简谈

软件构建系统不像是个很多人在研究的东西,所以在网络上很少能找到剖析某个构建系统原理、或者阐述构建系统principle的文章。看ns3的过程中接触到了waf,发现其文档waf book[https://waf.io/book/]很好的阐述了构建系统的一些基础知识,个人认为比cmake的文档好一些。因为其核心只有十几个文件,这个构建系统只需要一个10k+的waf文件,所以可以放到版本库里(像对python的评价一样,batteries included),唯一要求就是环境中有python,而这对一个开发人员来说显然不是一件困难的事情。

|-- Build.py
|-- ConfigSet.py
|-- Configure.py
|-- Context.py
|-- Errors.py
|-- Logs.py
|-- Node.py
|-- Options.py
|-- Runner.py
|-- Scripting.py
|-- Task.py
|-- TaskGen.py
|-- Tools [directory]
|-- Utils.py
|-- ansiterm.py
|-- extras
|-- fixpy2.py
`-- processor.py

以上便是所有waf的内容,可以看到涉及到的文件不算多。Tools下包含了很多语言的构建工具,比如c/c++/java/qt/ruby/tex等等,如果自己有能力定制,可以只保留自己项目里需要的tool,可以做到更小。(虽然个人认为没有必要)

核心抽象

如果是写编译语言的(c/c++/rust/go/fc/d),那么构建系统是每天都在用的。在敲击make之后,屏幕上出现了一系列的自动运行的命令,然后就是漫长的等待。用waf也一样,一般是./waf configure build clean dist...再等机器的轰鸣停止后继续工作流。waf提供了一些核心的抽象,从而能够表达出构建这个活动的几个关键方面:

像make clean dist类似,可以在构建命令后面自行添加指令,这种capibility由Context提供

构建系统最重要的功能就是按需构建,要判断出哪些文件要编译而哪些是不用的,这用到了TaskGen与Task的抽象

并行构建提升速度,由Runner来提供。

这3个抽象几乎相互独立,个人认为是很好的一个抽象。

Context

每一个跟在./waf后面的指令,都对应一个Context。如果是build/configure/list/step/install/uninstall,waf自行提供了对应的Context的子类用于执行这些命令,如果是其他的自定义函数,那么就会依托于Context本身,可以在自定义函数里用Context自定义的函数,比如recurse来遍历子目录执行子目录里的同名自定义函数。
如果项目根目录下的wscript有do_sth,就可以./waf do_sth

def do_sth(ctx):
    ctx.load("compiler_cxx")    # 加载工具
    ctx.recurse(["src","dep"])  # 遍历子目录,执行子目录下wscript里的do_sth
    ctx.exec_command("touch foo.txt")
    ctx.msg("hello")

这里函数参数ctx就是指向了Context的一个实例,而do_sth是作为Context上的一个方法而存在的,可以直观的理解为,我们为Context增加了一个自定义的do_sth方法,所以可以自由调用Context里本来提供的方法。
./waf build执行时绑定的Context是BuildConetxt,在Build.py里被定义,在waf build的时候,执行的是wscript里def build(bld)这个方法。举一个例子

def configure(conf):
    conf.load("compiler_cxx")
def build(bld):
    bld.shlib(source="a.cpp", target="mylib3")
    bld.program(source="main.cpp", target="app", use="mylib")
    bld.stlib(target="foo", source="b.cpp")
    # 直接调用bld
    bld(features = "c cprogram glib2",
        use      = "GLIB GIO GOBJECT",
        source   = "main.c org.glib2.test.gresource.xml",
        target   = "gsettings-test")

这里bld指向了BuildContext的一个实例,这意味着BuildContext里所有的方法都在这个函数里都是可用的,可以通过bld.xxx来调用。
值得注意的是,在Build.py中,可是找不到shlib/probram/stlib这3个方法的,但是在这里却调用成功没有报错,这全部依赖于conf.load("compiler_cxx")这一句。执行这句话后,就给bld指向的BuildContext实例绑定了shlib/program/stlib这3个方法。
那直接调用bld()呢?这个就要看Build.py里的BuildContex():__call__方法了。从这里开始,就涉及到TaskGen这个抽象了。

TaskGen & Task

最终需要执行的编译指令、中间代码生成等,每一条都对应一个task,我们不可能去一个一个的写task,而是希望以一种声明式的方法表达想要做的事情,这就是task_gen所完成的任务。从声明式表达到生成task的这项任务,由waf build完成。在执行的过程中,会对搜集到的每个task_gen执行一下post(),然后这个task_gen就生成了自己所有的task。作为一个灵活的构建系统,waf提供了很多方法来让我们hook到post()的过程中。对于每个task,到底该不该执行需不需要执行,它自己会追踪自己的依赖,职责分离,我很喜欢这个设计思路。
以前一小节为例,共在build(bld)里一共进行了4次调用,这意味着生成了4个task_gen的实例,在真正执行构建过程之前,会有一个地方对这4个实例各自调用一下post(),把所有的task_gen都消灭掉,变成task。至于怎么hook,这是个比较关键的点,如果理解了,就能很好的自定义waf了。
首先看看写好的wscript,它的声明式体现在什么地方呢?体现在函数参数里。得益于python的语言特点,可以随便加参数,然后在函数实现里用**kw来取这些值。这意味着可以随便加自己想要的key=value进去,这些加进去的参数是可以在自定义的hook过程中取到的,这算是可自定义的一个基础。(ruby自定义的能力更强,毕竟dsl是其强项,但可能限于ruby的流行程度以及发行版是否默认安装,让作者最后选择了python,不过也已经够用了)
在post()的过程中,会从task_gen.meths[]里依次取出方法来执行,hook的方式就是把自定义的方法塞到这个task_gen.meths[]之中。这只要在自定义的方法上加一个@TaskGen.taskgen_method的注解就能实现,还是挺简洁的吧?声明式中写的key=val,都能通过taskgen.key取到,这样一来,几乎就获得了无限的能力来自定义构建过程了。
在taskgen.meths[]里有几项预定义的方法,waf也提供了指令来让我们定制自己方法执行的位置。总而言之,想要什么内容,直接在wscript里以key=val的方式指定,然后在自己的方法里用getattr来取就行了。
这也只是个支持性框架,具体到某个语言(c/c++)是怎么做的,到后面再看。

Runner

waf自己会默认起和cpu core相同数量的进程来执行构建认任务,而且构建过程的输出也很清晰漂亮。waf也提供了lazy的模式,不是一下子把所有的task_gen都转化,所以也是用了一些技巧来达成这个目的。在看waf代码的过程中,能看到很多pythonic和近乎炫技的技法,可见作者真是把python语言玩弄于股掌之中。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45266.html

相关文章

  • 在 CentOS 5.8 上安装 Graphite

    摘要:在上安装标签空格分隔监控首先说一句,在上安装真的很坑爹。。。或如果上面这个方法无法安装那就用源码安装的方法。安装开始好几次就死在安装这个上面,版本的通过命令安装的不行,对应版本的包的名字叫做。 在 CentOS 5.8 上安装 Graphite 标签(空格分隔): 监控 monitor CentOS 5.8 Graphite 首先说一句,在 CentOS 5.8 上安装真的很坑爹...

    banana_pi 评论0 收藏0
  • Awesome Python

    摘要:漢字拼音 Awesome Python A curated list of awesome Python frameworks, libraries and software. Inspired by awesome-php. Awesome Python Environment Management Package Management Package Repositorie...

    fizz 评论0 收藏0
  • 你的 Docker 应用是安全的吗?

    摘要:如今,多样化的攻击手段层出不穷,传统安全解决方案越来越难以应对网络安全攻击。自适应安全平台集成了预测预防检测和响应的能力,为您提供精准持续可视化的安全防护。 近一年来,Docker 已经逐渐成为 container 界的事实标准,成为技术人员不可或缺的技能之一,就像 Docker 宣称的那样,「Build,Ship,and Run Any App,Anywhere」,容器极大简化了环境...

    darry 评论0 收藏0
  • Awesome Python II

    摘要: Caching Libraries for caching data. Beaker - A library for caching and sessions for use with web applications and stand-alone Python scripts and applications. dogpile.cache - dogpile.cache...

    lx1036 评论0 收藏0

发表评论

0条评论

sunnyxd

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<