2009年3月27日星期五

项目管理工具Trac

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://bigwhite.blogbus.com/logs/36729683.html

使用何种工具做Feature或Defect或Task的跟踪一直是挺让我闹心的一件事。用Excel记录,但却不便于共享、统计和直观展示;Jira算是做的好的工具之一了,但无奈它是商业软件,咱没付那份儿钱,所以也就"无福享用";Mingle是著名的Thoughtworks公司的产品,虽说不到5个license是可以免费使用的,但它却是出了名的"内存杀手",无奈我的机器配置太差,运行起来实在太慢,遂没有坚持下去(我"眼冒金星"的渴望着更换一台无所不能的超级计算机^_^);甚至我曾经用过ONENOTE来做跟踪,可是条目多了后,就基本不可用了。寻觅依旧进行中,这不Trac这款软件进入了我的视线中。

网络让我知道了"Trac"。"Trac"这个名字,估计与Track"异曲同工"。至于Trac具体能做什么,你可以到其Demo站点去体验一下。简单的说,Trac
= wiki +
问题处理工作流;Wiki可以用来做知识积累和管理;问题处理工作流恰是我需要的功能。但到底Trac做到啥子程度,那还需要用起来后才能知道。还有一件让我觉得很"幸福"的事,那就是Trac自带一个独立的web
server程序Tracd,并且Trac可使用Python
2.5.x自带的SQLite,这样我可以不用安装和配置庞大的Apache和MySQL了。让用户能快速上手应该是Trac值得其他开源软件学习和参考的一个亮点,要知道一些庞大的开源软件繁复的安装和配置过程让很多使用者产生了"挫败感"而"另辟蹊径"了。

Trac是用Python开发的,可以跨平台使用。这里我以Windows上的Trac为例。Trac官方站上有详尽的文档可以指导你的安装、配置和使用。这里用中文做简单介绍吧,留下一个记录,也便于自己以后参考。安装不是最重要的,但是没有了安装却是万万不能的。

1、安装Python, 设置环境变量
对于Trac而言,其解释执行环境Python是必不可少的,虽然Python发布了最新版是3.0.x,但是在已存的Python代码中,Python
2.x版本还是占据绝大多数,过渡到Python 3.0还需要时间。我这里用的是Python
2.5版本。安装完Python后,别忘了将{Python_INSTALL_DIR}和{Python_INSTALL_DIR}/Scripts加入到你系统的环境变量(path变量)中去(一般Python_INSTALL_DIR为"C:\Python25")。

2、使用Windows installers安装Trac
比起手动安装(Manual
Install),在Windows上使用Installer安装Trac更为简便。依次下载和安装:SetupTools、Genshi和Trac
0.11。注意要下载对应Python
2.5版本的安装文件。如果你要用Trac与Subversion接口的话,建议你下载一份svn-python程序,使用其他svn客户端程序似乎不好用。另外Trac只支持连接本地svn
repository,不支持远程svn
repository。以上安装程序会把相应可执行程序或脚本放到Python相关目录下,所以不需重新设置环境变量。

3、初始化一个工程
完成以上两步,你就可以使用Trac了。Trac运行和管理的基本环境单位是一个工程(Project)。首先你要确定你的工程所在的目录,这里以D:\TracProjects\Foo为例,我们建立一个名为"Foo"的Trac工程。打开一个命令行窗口,执行:
trac-admin.exe D:\TracProjects\Foo initenv
这是个交互执行的过程,你需要填写一些工程的基本信息,比如工程名字、数据库连接字符串等,你大可一路默认下来就可以得到一个默认的工程环境。

4、启动Trac
一切就绪。我们现在就可以启动Trac了。到目前为止,一切都是那么简单,这也充分证明Trac入门简单。在命令行下执行如下命令启动Trac
Web server:
tracd.exe -p 8000 D:\TracProjects\Foo
现在你打开浏览器,访问Url:
http://localhost:8000,Trac的界面就会展现在你面前。界面上你只能看到"Available
Projects"的列表,由于我们只是建立了一个Project,所以你只能看到Foo这一个超链接。点击Foo,进入Foo的工程页面。

5、为Trac Project添加用户
Ticket是Trac
Project管理和操作的基本元素,但是在通过上面方式以匿名登录方式打开的页面上你只能"View
Tickets",而无法"New Ticket";要想拥有"New
ticket"的权限,你需要以一个Trac用户的身份登录。初始情况下,Trac没有建立任何用户。Trac创建用户是通过建立"Password
file"的方式来完成的。Trac默认的密码文件格式与Apache的相同,都是.htdigest格式的。如果你的系统内没有安装Apache,你可以用Trac
wiki上提供的trac-digest.py脚本来生成密码文件。你可以将trac-digest.py文件放到{Python_INSTALL_DIR}/Scripts下面。

# trac-digest.py
from optparse import OptionParser
# The md5 module is deprecated in Python 2.5
try:
from hashlib import md5
except ImportError:
from md5 import md5
realm = 'trac'

# build the options
usage = "usage: %prog [options]"
parser = OptionParser(usage=usage)
parser.add_option("-u", "--username",action="store", dest="username", type
= "string",
help="the username for whom to generate a password")
parser.add_option("-p", "--password",action="store", dest="password", type
= "string",
help="the password to use")
parser.add_option("-r", "--realm",action="store", dest="realm", type =
"string",
help="the realm in which to create the digest")
(options, args) = parser.parse_args()

# check options
if (options.username is None) or (options.password is None):
parser.error("You must supply both the username and password")
if (options.realm is not None):
realm = options.realm

# Generate the string to enter into the htdigest file
kd = lambda x: md5(':'.join(x)).hexdigest()
print ':'.join((options.username, realm, kd([options.username, realm,
options.password])))

我们使用如下命令生成密码文件:
python trac-digest.py -u "foo" -p "foo123" >>
d:\tracprojects\foo\conf\users.htdigest,
这里我们建立一个用户:用户名为foo,密码为foo123。

我们再次来启动Trac,这次由于带有了用户鉴权,启动命令行与前面略有不同。
tracd --port 8000 --auth=Foo,d:\tracprojects\foo\conf\users.htdigest,trac
d:\trarojects\foo
启动后,我们点击login,Trac会提示我们输入用户名和密码,输入foo/foo123后,你就可以看到界面显示:logged
in as foo,并且"New Ticket"菜单出现在页面上方。

6、Trac.ini
conf目录下的Trac.ini是针对Foo这个Trac
Project的主配置文件。里面各个字段的含义说明在Trac官网都有说明。通过修改Trac.ini你可以很简单的在页面上添加你喜欢的Project
Logo。

7、Ticket
Ticket是Trac的核心,默认情况下,Trac为Ticket设定了诸多属性,并且设定了围绕Ticket的默认工作流。对于Ticket的每个属性字段,我们都可以通过trad-admin工具对字段取值进行增删改,以适合你的需要。诸如:trac-admin
d:\TracProjects\foo component add Webms
foo,这句的含义就是添加一个属于foo工程的名为"Webms"的组件值。Ticket没有类似"Deadline"的时间属性,我们可以用milestone和priority来约束解决Ticket的时间范围。在"View
tickets"页面中,Trac内置了多种"Report",你同样也可以自定义搜索,但是目前用户尚不能在"View
Tickets"中保存自定义搜索为固定的"Report",但是你可以将自定义搜索语句放到一个WIKI页面的链接选项中,这样你就可以方便的直接得到搜索结果了,无需每次都配置搜索条件。

8、WIKI
Trac内置WIKI引擎,你可以通过trac-admin d:\TracProjects\foo wiki
list来查看当前Project的所有WIKI page名称。你也可以通过trac-admin
d:\TracProjects\foo wiki import WIKI_PAGE_NAME new_WIKI_page.txt为Foo
Project导入一个名为"WIKI_PAGE_NAME"新WIKI页,该页内容来自文件new_WIKI_page.txt。和所有其他Wiki一样,你可以任意定制你的Project中的任意Wiki页面。

从上面的8个步骤来看,Trac简单而且实用,我已经根据我自己的需要对Trac
Project进行了初步定制,并导入了我要追踪的需求、任务和问题,更多的高级功能还需要一段时间去发掘,今天的发掘到此为止了。

分布式编译让你的工作更高效

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://bigwhite.blogbus.com/logs/30244208.html

随着工程代码量的增加,往往完整的编译一次Proj消耗的时间可能足够你喝两杯咖啡了,我现在build一次我所在proj的代码需要5分多钟,这是很痛苦的,颇让人懊恼的。为了解决这个工作中的别扭事儿,我在网上搜寻了一番,找到了distcc这个分布式编译工具。

先看看distcc能帮助我节省多少时间吧。我在公司的一台Sun SPARC
Solaris9主机下对整个项目源代码按照以前的编译方式进行了一次build,这次build用了5分多钟;同样我使用distcc编译(安装了两个节点,都是Sun
SPARC
Solaris9主机),居然只用了1分多钟,试想如何有更多的服务器作为distcc的守候进程主机节点的话,势必编译性能还会有提升。

有了"惊人"结果后,我们来看看distcc的原理,distcc本身是gcc的一个wrapper,也可用作本地编译,但是更多的是其分布式编译的强大功能,简单来说:就是将gcc的编译任务分布到各个其他主机上去,然后再传回来整合。它提高的是gcc
-c阶段的速度,链接阶段的速度由于肯定要在本地实施,所以distcc无能为力。另外distcc推荐分布的不同主机上安装的编译器版本最好要一致,否则可能会有意想不到的错误。

distcc的安装和使用方法甚是简单,我安装的是distcc-2.13-sol9-sparc-local,直接在root下pkgadd即可。然后在各个distcc节点启动后台守候进程:distccd
--daemon --allow x.x.x.x/16,以普通用户启动即可。

客户端使用方法:
1、在自己的用户下,添加环境变量(如果你用的是C shell):setenv DISTCC_HOSTS
'localhost x.x.x.x',代表本机和x.x.x.x上安装并启动了distccd
2、将你的makefile 中的CC=gcc改为CC=distcc gcc
3、make即可 。同样你还可以在make的-j参数选项,如make -j
12,这样在单机上进行多任务并行编译,使速度更快。
4、如果你想观察各节点上distcc的工作状态,可使用distccmon-text 2
命令查看distcc在各台主机上的任务快照。参数2代表:每隔2秒刷新一次。

Distcc理论上是可以部署在不同平台上辅助进行分布式编译的,但是在异构平台上分布需要一段时间设置和调试,我推荐还是尽量部署在同一类型平台上吧。有了distcc,我们的服务器的计算能力得到了充分的发挥,个人工作效率也会有所提高的,不知道长此下去喝咖啡的机会会不会被剥夺了:)

通用makefile文件(适合小型程序)

通常在linux下写c/c++程序时都需要makefile文件,makefile文件的产生通常有三种方式
1. 通过ide帮你生成
2. 使用automake等gnu工具自动生成,这是linux下发布源码的标准方式。
3. 自己手写makefile
三种方式无一例外的都需要随时编辑修改makefile,典型的过程是
编辑源文件.c/.cpp—编辑makefile(通过以上三种方式)—执行make生成可执行程序
本文介绍的这种makefile可以在一定程度上省去第二个过程,因为它利用了makefile的自动扩展和若干默认规则,能较智能的进行编译链接。
makefile全文如下
######################################
#
# Generic makefile
#
# by George Foot
# email: george.foot@merton.ox.ac.uk
#
# Copyright (c) 1997 George Foot
# All rights reserved.
#
# No warranty, no liability;
# you use this at your own risk.#
# You are free to modify and
# distribute this without giving
# credit to the original author.
#
######################################### Customising
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
#
# 修改生成可执行文件的名称
EXECUTABLE := foo
# 修改LIBS,添加所需要链接的库,以空格分隔,例如 LIBS := pthread rt ssl
LIBS :=# Now alter any implicit rules' variables if you like, e.g.:
#
# 修改CC,改变编译器
CC:=g++
# 修改编译选项
CFLAGS := -g -D_DEBUG -Wall
CXXFLAGS := $(CFLAGS)RM-F := rm -f# You shouldn't need to change anything
below this point.
#
#匹配的文件,默认仅匹配当前目录,可做如下修改
#可添加所需要包含的目录,例如$(wildcard test/*.cpp)
#可添加所支持的后缀文件,例如$(wildcard *.cc)
SOURCE := $(wildcard *.c) $(wildcard *.cpp) OBJS := $(patsubst
%.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS))
$(patsubst %.d,%.cpp,$(MISSING_DEPS)))
CPPFLAGS += -MD.PHONY : everything deps objs clean veryclean
rebuildeverything : $(EXECUTABLE)deps : $(DEPS)

objs : $(OBJS)

clean :
@$(RM-F) *.o
@$(RM-F) *.d

veryclean: clean
@$(RM-F) $(EXECUTABLE)

rebuild: veryclean everything

ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM-F) $(patsubst %.d,%.o,$@)
endif

-include $(DEPS)

$(EXECUTABLE) : $(OBJS)
$(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))

使用时,
1.将所有代码放在一个单独的目录下,例如/code
2.将makefile文件copy一份在/code下
3.执行make,即可生成目标文件foo
其中有几点可自行改变,已在文中用红色文字标出对于小型项目或者平时写写测试代码,用这个makefile实在是方便又快捷

《UNIX环境高级编程》例子的通用makefile(推荐)

//方法一:普通方法

H_PATH = $(HOME)/unix_c/include #H_PATH是包含头文件apue.h的目录
LIB_PATH = $(HOME)/unix_c/lib
#LIB_PATH是包含书中附带的一些函数库的.c文件的目录
INCLUDE = -I$(H_PATH) -I$(LIB_PATH)

CFLAGS = -g -w -c $(INCLUDE)

FILE = vfork.c error.c

OBJ = $(FILE:.c=.o)
#这里要添加所用到的函数的.o,下面要把他编译出来

all : $(OBJ)
cc -o $@ $(OBJ)
rm -f $(OBJ)
$(FILE).o :
cc $(CFLAGS) $(FILE).c

error.o :
cc $(CFLAGS) $(LIB_PATH)/error.c

clean:
rm -f all $(OBJ)


//方法二:考虑把附带的那些函数编译成.so或.a库包


//先看动态库的例子:多个常用函数编译成一个.so动态库,或者.a静态库

CFLAGS= -fpic -I$(MY_INCL)
OBJS = open_read.o open_write.o sort.o del_repeat.o
FUNC = open_read.c open_write.c sort.c del_repeat.c

#all : libfunc.so
//动态库,如果内容有改动,调用它的程序无须重新编译(如果接口没有被改的话)

#libfunc.so : $(OBJS)
# cc -o $@ -shared $(OBJS) -lc
# cp $@ $(MY_LIB)


all :libfunc.a
//静态库,如果内容有改动,调用它的程序需要重新编译

libfunc.a : $(OBJS)
ar ur $@ $(OBJS)
cp $@ $(MY_LIB)


$(OBJS) : $(F)*.c
cc $(CFLAGS) -c $*.c

clean:
rm -f $(OBJS) libfunc.so libfunc.a


//调用库包里的函数

OBJ=info.o

edit : $(OBJ)
cc -o $@ $(OBJ) -L$(MY_LIB) -lfunc
//如果.so和.a都存在,默认链接.so文件
rm -f $(OBJ)

info.o :
cc -c -I$(MY_INCL) info.c

clean:
rm -f edit $(OBJ)

//方法二:使用动态库

//*************** $(HOME)/unix_c/setenv *********** 记得导入

export LD_LIBRARY_PATH=$APUE_LIB:$LD_LIBRARY_PATH

//**************** ${HOME}/unix_c/public/make.def.public

APUE_LIB = $(HOME)/unix_c/lib #// 准备存放生成的库包
APUE_INCL = $(HOME)/unix_c/include #// apue.h的目录

//**************** 存放函数定义文件(.c文件)目录下的 Makefile

include ${HOME}/unix_c/public/make.def.public

CFLAGS = -c -I$(APUE_INCL)

OBJS = bufargs.o cliconn.o clrfl.o \
daemonize.o error.o lockreg.o locktest.o \
openmax.o pathalloc.o popen.o prexit.o prmask.o \
ptyfork.o linux_ptyopen.o readn.o recvfd.o \
senderr.o sendfd.o servaccept.o servlisten.o \
setfl.o signal.o signalintr.o \
sleep.o sleepus.o spipe.o \
tellwait.o ttymodes.o writen.o # pathconf.o sysconf.o
strerror.o errorlog.o

APUESO = libapue.so

all : $(APUESO)

$(APUESO) : $(OBJS)
cc -o $@ -shared -fpic $(OBJS) -lc
mv -f $(APUESO) $(APUE_LIB)

#all :libapue.a

#libapue.a : $(OBJS)
# ar ur $@ $(OBJS)
# mv -f $@ $(APUE_LIB)


$(OBJS) : $(F)*.c
cc $(CFLAGS) $*.c

clean:
rm -f $(OBJS)

//****************** main主函数文件目录下的 Makefile

include ${HOME}/unix_c/public/make.def.public

CFLAGS = -g -c -I$(APUE_INCL)

FILE = mainfile.c func.c //只要在这里修改添加文件名即可

OBJ = $(FILE:.c=.o)

all : $(OBJ)
cc -o $@ $(OBJ) -L$(APUE_LIB) -lapue

#$(OBJ) : $(F)*.c
# cc $(CFLAGS) $*.c

%.o:%.c
cc $(CFLAGS) $<

clean:
rm -f all $(OBJ)

通用Makefile及部分解释

######################################
# Copyright (c) 1997 George Foot (george.foot@merton.ox.ac.uk)
# All rights reserved.
######################################
#目标(可执行文档)名称,库(譬如stdcx,iostr,mysql等),头文件路径
DESTINATION := test
LIBS :=
INCLUDES := .


RM := rm -f
#C,CC或CPP文件的后缀
PS=cpp
# GNU Make的隐含变量定义
CC=g++
CPPFLAGS = -g -Wall -O3 -march=i486
CPPFLAGS += $(addprefix -I,$(INCLUDES))
CPPFLAGS += -MMD

#以下部分无需修改
SOURCE := $(wildcard *.$(PS))
OBJS := $(patsubst %.$(PS),%.o,$(SOURCE))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.$(PS),$(MISSING_DEPS)))

.PHONY : all deps objs clean rebuild

all : $(DESTINATION)

deps : $(DEPS)
$(CC) -MM -MMD $(SOURCE)


objs : $(OBJS)

clean :
@$(RM) *.o
@$(RM) *.d
@$(RM) $(DESTINATION)

rebuild: clean all

ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM) $(patsubst %.d,%.o,$@)
endif

-include $(DEPS)

$(DESTINATION) : $(OBJS)
$(CC) -o $(DESTINATION) $(OBJS) $(addprefix -l,$(LIBS))
#结束
原作者是Gorge Foot,写这个Makefile的时候还是一个学生
":="赋值,和"="不同的是,":="在赋值的同时,会将赋值语句中所有的变量就地展开,也就是说,A:=$(B)后,B的值的改变不再影响A
隐含规则。GUN Make在不特别指定的情况下会使用诸如以下编译命令:$(CC)
$(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o
$@,这也是为什么这个Makefile最后一个命令没有添加$(CPPFLAGS)的原因,因为缺省是包含这个变量的
函数和变量很相似:"$ (函数名,空格,一列由逗号分隔的参数)"
SOURCES = $(wildcard *.cpp)
列出工作目录下文件名满足"*.cpp"条件的文件,以空格分隔,并将列表赋给SOURCE变量
patsubst函数:3个参数。功能是将第三个参数中的每一项(由空格分隔)符合第一个参数描述的部分替换成第二个参数制定的值
addprefix函数:2个参数。将源串(第2个参数,由空格分隔)中的每一项添加前缀(第1个参数)
filter-out函数:2个参数。从第二串中过滤掉包含在第一个串中的项
$(CC) -MM -MMD $(SOURCE) :
对每个源文件生成依赖(dependence,Make通过依赖规则来判断是否需要重新编译某个文件),"D"生成".d"文件,-MM表示去掉
depends里面的系统的头文件(使用<>包含的头文件)(若使用-M则全部包含,事实上,系统头文件被修改的可能性极小,不需要执行依赖检查)
.PHONY,不检查后面制定各项是否存在同名文件
ifneg...else...endif,Makefile中的条件语句
-include $(DEPS) : 将DEPS中的文件包含进来,"-"表示忽略文件不存在的错误
@$(RM) *.o : 开头的"@"表示在Make的时候,不显示这条命令(GNU
Make缺省是显示的)
all :
作为第一个出现的目标项目,Make会将它作为主要和缺省项目("make"就表示"make
all")
deps : 只生成依赖文件(.d文件)
objs : 为每一个源码程序生成或更新 '.d' 文件和'.o'文件
clean : 删除所有'.d','.o'和可执行文件
rebuild : clean然后重建
内部变量$@, $< $^ :
分别表示目标名(:前面的部分,比如all),依靠列表(:后面的部分)中的第一个依靠文件,所有依靠文件

Linux平台Makefile文件的编写基础篇

目的:
基本掌握了 make 的用法,能在Linux系统上编程。
环境:
Linux系统,或者有一台Linux服务器,通过终端连接。一句话:有Linux编译环境。
准备:
准备三个文件:file1.c, file2.c, file2.h
file1.c:
#include <stdio.h>
#include "file2.h"
int main()
{
printf("print file1$$$$$$$$$$$$$$$$$$$$$$$$\n");
File2Print();
return 0;
}
file2.h:
#ifndef FILE2_H_
#define FILE2_H_
#ifdef __cplusplus
extern "C" {