升级

2008年10月31日 17:14

计划周末对服务器做一次升级。blog及其他服务暂停2天。

scons简介

2008年10月28日 23:00

云风在blog上写了一组《IDE 不是程序员的唯一选择》的文章。题目很吸引人,以至于我一直以为他要写什么鸿篇巨制,可惜到最后只出现了一组《GNU make入门指南》。(笑)

被IDE绑定的确是一件很悲哀的事情。作为一个程序员,当然应该搞清楚程序编译链接的整个流程,makefile给出了很好的一个路径,让我们能够了解这一点。

但是,如果觉得程序员就应该比拼“手写汇编代码”、“用记事本写程序”,那就大大的错了。程序员需要了解细节,但不意味着程序员都是傻瓜。为什么要放着好好的提高生产力的工具不用,一切从零做起呢?(当然了,上面这句话并不能成为使用盗版的理由。)所以寻找并了解一些优秀的工具,也是我们这些程序员所需要做的事情之一。

GNU make 当然是一个很好的工具,云风已经讲了很多,我就不啰嗦了。我今天想介绍另一个优秀的自动构建工具scons

scons是一个Python写的自动化构建工具,从构建这个角度说,它跟GNU make是同一类的工具。它有什么好处呢?在它自己的网站上,当然写了一大堆了,快速、稳定、强大、跨平台、可扩展……。不过我们还是从自己的角度来看看它到底好在哪里。

刚刚提到scons从目的而言跟GNU make是同一类的工具。但是实际上,它的思想是跟GNU make完全不同的。GNU make的核心是“依赖关系”,我要做的事情,就是告诉系统,一个目标依赖什么东西,并且,当被依赖的东西发生变化时,我要做什么。这样做可以解决相当多的问题,但是也带来了一个最大的问题:我如何判别这个目标依赖什么?

对于一个两个,甚至十几个文件,我当然还比较容易搞清楚,谁依赖谁。但是当文件有成百上千个时,要分清楚谁依赖谁可就没这么容易了。尤其是C/C++头文件的依赖,如果手工分析的话,工程量可是不小。为了解决这个问题,GNU又提供了另外一套工具:Automake,使用程序来分析依赖性,然后辅助你产生makefile。

于是乎,就有人想了,既然如此,我干吗费那劲,用程序分析依赖性,然后生成一个文件,再交给另外一个程序去处理呢?既然依赖性需要用程序来分析,那么就直接交给构建工具本身去做不就好了吗?对的,这是一个非常自然的思路,于是,Java世界有了Ant,而Python世界有了scons。

scons就是这样一个构建工具:你告诉它要做的任务,以及完成这个任务需要的输入,以及这个任务产生的输出,怎么做这个任务(当然其中就包括依赖性分析),就交给工具本身完成。

说了这么多,我们来看看一个现实世界的scons是什么样子的。

我们假设有一个C程序,由三个文件组成:

//-----func.cpp
int add(int x, int y)
{
    return x+y;
}

//----func.h
#ifndef __FUNC_H__
#define __FUNC_H__
extern int add(int x, int y);
#endif

//-----main.cpp
#include 
#include "func.h"

int main()
{
    printf("2+3=%d\n", add(2, 3));
}
然后我们写一个SConstruct文件(类似于GNU make的Makefile文件,是scons的默认文件名):
Program('add_main', ['main.cpp', 'func.cpp'])

然后执行scons,将会输出以下信息:

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o main.o -c main.cpp
g++ -o func.o -c func.cpp
g++ -o add_main main.o func.o
scons: done building targets.

这时,我们就会得到一个add_main的可执行程序。

如果执行scons -c,我们会看到:

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed main.o
Removed func.o
Removed add_main
scons: done cleaning targets.

生成的可执行程序连带中间结果都被清除了。

这里我们可以看到,scons只需要描述任务,并不需要指定依赖关系,甚至我们都没有指出头文件,但是你修改func.h的时候仍然会触发构建。这是因为scons内部有个scanner,可以帮助扫描包含文件的关系。(我们可以编写自己的构建任务,当然也可以编写自己的scanner,有兴趣的可以看帮助,这里就多说了)

我们还发现,这里我们根本没有指定编译器,也没有指定编译选项,但scons仍然很聪明的选择了g++(这是Linux上的结果,如果是Windows,默认会选择cl也就是Visual C++),并且给出了正确的编译选项。事实上,这是因为scons内置提供了很多编译器及其对应选项的选择,然后对于不同的平台,会有一个默认项。我们当然也可以自己选择编译环境,比如在Windows下,我同时安装了VC和mingW,但是我想用mingW来编译而不是VC,就可以这样指定:

import os
env = Environment(ENV=os.environ, tools=['mingw'])
env.Program('add_main', ['main.cpp', 'func.cpp'])

这里出现了一个Environment的概念,Environment可以设置编译的环境。这是一个简介,所以对于它我们就不多说了,感兴趣的可以自行查阅资料。嘿嘿。

在这里我们看到了一句熟悉的语句:import os。是的,SConstruct文件就是一个非常标准的Python程序,所以,Python能做什么,scons就能做什么。很好很强大阿,哈哈。(这里顺便说一句,我们也可以认为Makefile是shell程序,但是因为shell有平台相关性问题,我们很难写出一个通用平台的Makefile,但是我们还是写的出一个通用平台的SConstruct的。)

Program只是scons支持的构建任务其中的一种,用于根据后缀名自动构建C、C++、D和Fortran的可执行程序。scons还支持另外几十种构建目标,这里可以查看支持的列表。如果这里找不到的,还可以自己编写Builder和Scanner。

接下来我想给出一个一直提但是一直没有给出结果的东西,就是对C++程序的单元测试。

还是以刚刚那个小例子为例。我们对add函数作一个单元测试。我们知道,单元测试不是程序的一部分,所以需要一个独立的main函数。而被测试的单元是一样的。

现在假设我们有一个测试函数:(用的是boost的test库,这个库的使用方法不再赘述)

//--------test_main.cpp
#include "func.h"
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include 

BOOST_AUTO_TEST_CASE( add_test )
{
    BOOST_CHECK( add(2, 2) == 4 );
}
然后我们写一个SConstruct:
#所有的需要测试的单元文件(去除两个主文件)
import glob
obj_files = glob.glob('*.cpp')
obj_files.remove('main.cpp')
obj_files.remove('test_main.cpp')

common = Object(obj_files)

Program('add_main', ['main.cpp'] + common)
Program('unittest', ['test_main.cpp'] + common, LIBPATH='/usr/lib', LIBS=['libboost_unit_test_framework'] )

Alias('test', 'unittest')
Default('add_main')

这里出现了几个新玩意儿,一个是Object,其实也很好理解,Object就是将指定的文件编译成目标文件(.o或者.obj),然后我们用了2个Program,表示要生成两个可执行文件。在生成的时候,我们将通用的common附加到构建输入中。另一个是Default,这是表示默认的构建。当我们输入scons时,将构建add_main,而我们输入scons unittest时,则构建unittest。但是输入unittest感觉不太方便,我们想输入scons test来编译,但希望输出的文件名仍然是unittest,于是我们增加了Alias,将unittest取了一个别名叫test,这时,我们输入scons test,仍然会构建unittest。

我们注意到构建unittest时,使用了附加的信息,比如额外的库、额外的路径等等。还有为了方便起见,我们使用了Python标准库的glob函数展开文件通配符。

从这个例子我们大约可以感受到scons的强大威力了。至于进一步的深入,就看各位自己的了。

依赖性处理的Demo

2008年10月09日 16:49

这是我为了验证插件机制依赖性写的一个原型代码。本来想写一些文字说明的,后来发现实在没什么好写,就直接贴代码吧,如果有问题可以问。

class Element:
def __init__(self, name, deps):
self.name = name
self.deps = deps

def getIndexFromName(elementList, name):
for index, elem in enumerate(elementList):
if elem.name == name:
return index
return -1

def dependProcess(elementList):
stack = []
for index, elem in enumerate(elementList):
print "now processing Element %s[%d]" % (elem.name, index)

tracker = []
if elem.name not in stack:
result = elementDependProcess(elementList, index, tracker, stack)
if result == False:
#circle dependence
return None
else:
print "%s is already scaned, skip it" % elem.name

#when elment duplicated in stack list, just keep the last one
#and reverse it to make the dependency order right.
stack.reverse()
newstack = []
for elem in stack:
if elem not in newstack:
newstack.append(elem)
return newstack

def elementDependProcess(elementList, startIndex, tracker, stack):
ins = elementList[startIndex]
name = ins.name

stack.append(name)

print "curr element=", name, ", tracker=", tracker
if name in tracker:
print "%s is circle dependence" % name
return False
tracker.append(name)

if ins.deps == None:
return True
else:
for dep in ins.deps:
index = getIndexFromName(elementList, dep)
if index == -1:
#Cannot find element in list
print "Cannot find %s in elementList" % dep
return False
else:
result = elementDependProcess(elementList, index, tracker, stack)
if result == False:
return False
else:
tracker.remove(dep) #restore the init state
return True

if __name__ == "__main__":
elementList = [
Element("a", None),
Element("b", ["d"]),
Element("c", ["a", "b"]),
Element("d", ["a", "e"]),
Element("e", None)
]

loadList = dependProcess(elementList)
print "Load List=", loadList

The Diva Dance

2008年10月06日 13:51

昨天看了最新一期的《我猜》,看到有人居然又唱了《第五元素》里的《The Diva Dance》,虽然只有一句,不过还是勾起了我的回忆。忍不住要推荐一下这段音乐。

每个看过第五元素的人应该都会对那个蓝色的外星歌唱家Diva印象深刻。没错,她出场演唱的那段歌剧就是《The Diva Dance》。但是,这段歌剧实际上是由两个部分组成,第一部分是葛塔诺·多尼采蒂的歌剧Lucia di Lammermoor(拉莫莫尔的露西亚)第三幕中的一段,第二部分的花腔就是我们通常所说的The Diva Dance。

The Diva Dance实际上是一段虚拟的唱段,为了表现外星人的特别,唱段里使用了很广的音域以及快速的高低音变换,这种变换地球人是做不到的,所以没有办法真实的唱出来。事实上,电影中的这段唱,是人声+电脑辅助处理完成的,如果仔细的听,可以听到电脑处理的痕迹。(不知道10年之后的今天再做这段音乐的话能不能做得更加自然一点?)

网上传说俄罗斯的高音王子Vitas曾经翻唱过The Diva Dance。听过Vitas的Opera 2的惊艳表演之后,我倒是真的很期待听到真人版的The Diva Dance,可惜Vitas只翻唱了前一段Lucia di Lammermoor,我最期待的部分并没有出现。当然还是有人会尝试这段超高难度的花腔,但都做了或多或少的简化了。

这就是原始完整版本的The Diva Dance。在第二部分,中间有一段可以很明显的听出是电脑处理的。

这是潘美辰的翻唱,可以明显感受到高音部分很干涩并且有失音准(毕竟不是专业唱美声的),而且在高低音转换部分做了很多的简化。但是我觉得有勇气挑战这段就很厉害了:

这就是Vitas翻唱的Lucia di Lammermoor,他的高音很迷人,不过注意一下他的唱法并不是传统的歌剧唱法:

2008苏州大闸蟹聚会纪要

2008年10月03日 21:45

第二届苏州大闸蟹聚会于2008年10月1日在TR家举行,为期一天。

今年聚会规模有所扩大,增加了从南京赶来的WingSun和尚在腹中的小刺猬。

今年聚会的形式也更多样,除了传统的XBox360上的甩牛和专业代关厕所门之外,还增加了Wii上的网球对打、BT的疯兔2还有装反d专用吉他英雄等。另外还有毫不起眼的NDSL、没怎么玩的iPhone、键盘超级难用的EeePC、1700元的耳塞、新鲜出路的House MD S05E02,以及Nikon、Canon、Sony单反相机若干。让各个层次的BT们都能找到自己的所爱。

今年聚会的主题更明确,在大闸蟹时间,众BT们快速讨论了狗屎皮计划、萝莉养成计划、新版群机器人计划、RPI远景计划等各项计划,明确了BT群下一阶段的工作。

今年聚会的成果更显著,我们解决了圣上的DocBook生成问题,研究了2F ssh连接速度慢的问题,升级了令狐的Wii。

这是一次关键的聚会,成功的聚会,胜利的聚会!祝贺此次聚会圆满落幕!(在我确实拿到自己的Wii之后)

附:

Design downloaded from free website templates.