2012年10月23日星期二

git-buildpackage示例(二)

《git-buildpackage示例(一)》中,我介绍了如何利用git-buildpackage为Ubuntu已有包做一个补丁包的办法。

当时,我的补丁是基于tolua 5.1.3版本。一段时间后,tolua的作者释放了5.1.4版本。问题出现了,如何将我的补丁合并到5.1.4版本中呢? 

下面我将继续使用git-buildpackage来解决合并上游新版本的问题:
  1. 下载tolua 5.1.4源码包(假设放在git工作目录上层):
    wget http://www.tecgraf.puc-rio.br/~celes/tolua/tolua-5.1.4.tar.gz
    cd tolua
    git-import-orig -u 5.1.4 ../tolua-5.1.4.tar.gz
    

  2. 手动解决遇到的冲突(如src/bin/Makefile)并提交更新:
    git commit
    

  3. 这时,运行
    git log --format=%d:%s
    
    输出:
     (HEAD, master):Merge commit 'upstream/5.1.4'
     (upstream/5.1.4, upstream):Imported Upstream version 5.1.4
     (debian/5.1.3-2):Fix relocation R_X86_64_32 against '.rodata' can not be used when making a shared object
     (debian/5.1.3-1):Imported Debian patch 5.1.3-1
     (upstream/5.1.3):Imported Upstream version 5.1.3
    
    upstream/5.1.4分支被创建,且将其合并到master分支中。如此,master分支合并完毕,接下来将合并debian的patches。

  4. 重整patch-queue:
    gbp-pq rebase
    
    手动解决遇到的冲突:
    git rm -f src/bin/toluabind.c
    git rebase --continue
    

  5. 导出patch-queue(至master分支)
    git clean -df
    gbp-pq export

  6. 指定版本号5.1.4-1自动生成snapshot的debian/changelog:
    git-dch -a -S -N 5.1.4-1
    git add debian/changelog
    git add debian/patches/0001-mkdir-for-tolua-lib-archive-and-remove-temp-files.patch
    git commit -m "Update patches from debian/5.1.3-1"
    测试构建新的deb包。为了避免污染当前环境,这里指定git首先导出源码至../tolua-build目录:
    git-buildpackage --git-export-dir=../tolua-build --git-ignore-new
    

  7. 生成release的版本信息,并构建release的deb包:
    git-dch -a -R
    git ci --amend
    git tag debian/5.1.4-1
    git-buildpackage --git-export-dir=../tolua-build --git-ignore-new
    
  8. 到此为止,我已经演示了git-buildpackage合并上游版本的过程。不难发现,git-buildpackage充分利用了git的特点,在很大程度上简化了补丁开发和维护的过程。

2012年2月19日星期日

git-buildpackage示例(一)

《为tolua的deb包作一个补丁》中,我介绍了如何利用quilt为Ubuntu已有包做一个补丁包的办法。可以看出quilt具有一定的版本管理能力,然而与流行版本管理系统相比,功能较弱也不灵活。

Debian New Maintainers' Guide中,了解到deb包的制作和维护管理已经与现有流行版本管理系统结合在了一块,其中一款工具为git-buildpackage,它将包制作和维护,特别是第三方补丁包维护,与git紧密的结合了起来。

下面仍然以tolua的补丁制作为例,一步一步展示git-buildpackage的基本操作。

  1. 安装必要的工具:
    sudo apt-get install git-buildpackage build-essential debhelper quilt

  2. 下载libtolua-dev的源码(建立upstream目录单独存放Ubuntu的deb源码包是为了保证清洁和正确):
    mkdir upstream
    apt-get source libtolua-dev
    

  3. 导入upsteam的dsc文件(将生成与目录upstream同级的目录tolua):
    cd ..
    git-import-dsc upstream/tolua_5.1.3-1.dsc
    cd tolua
    
    这时,运行
    git log --format=%d:%s
    输出:
     (HEAD, debian/5.1.3-1, master):Imported Debian patch 5.1.3-1
     (upstream/5.1.3, upstream):Imported Upstream version 5.1.3
    
    从下至上,首条提交导入了tolua 5.1.3的源码,次条提交导入了deb包维护者的deb包文件(debian/*);并且建立了upstream和master两个分支,标签upstream/5.1.3位于upstream分支上,标签debian/5.1.3-1位于master分支头部。

    此外,upstream分支用于维护源码作者的发布版本更新情况,master分支用于维护deb包描述文件及其补丁文件。git-buildpackage工具集的正确运行将依赖于标签upstream/5.1.3和debian/5.1.3-1,不能随意删改。

  4. 导入quilt patches到patch queue中——创建patch-queue/master分支,并将debian/patches/*逐一变成该分支的提交,并自动切换到该分支上:
    gbp-pq import

  5. 执行make后发现构建目标libtolua.a的生成目录lib不存在,这是git只针对文件做版本,所以upstream导入git时,该目录被忽略了。为此,我将src/lib/Makefile中
    $T: $(OBJS)
        $(AR) $@ $(OBJS)
        $(RANLIB) $@
    
    修改为:
    $T: $(OBJS)
        mkdir -p $(@D)
        $(AR) $@ $(OBJS)
        $(RANLIB) $@
    
    这样,它将在每次构建该目标时,创建该目标所在目录。更进一步不难发现,src/bin/tolua_lua.o和src/bin/toluabind.c为受版本控制的中间文件,将影响构建的正确运行。为此,删除这两个文件并提交日志。
    git rm -f src/bin/tolua_lua.o src/bin/toluabind.c
    git commit -a -m "mkdir for tolua lib archive and remove temp files"
    
    此时,可以正确make该工程了。

  6. 修复x86_64链接问题,将config文件中,如下内容
    CFLAGS= -g $(WARN) $(INC)
    CPPFLAGS= -g $(WARN) $(INC)
    替换为
    CFLAGS= -fPIC -O2 -pipe -g $(WARN) $(INC)
    CPPFLAGS= -fPIC -O2 -pipe  -g $(WARN) $(INC)
    并提交日志:
    git commit -a -m "Fix relocation R_X86_64_32 against '.rodata' can not be used when making a shared object"

  7. 导出patch-queue——将其分支提交逐一转化为debian/patches目录下的补丁文件(为保证正确运行,清理掉中间文件):
    git clean -df
    gbp-pq export

  8. 指定版本号5.1.3-2自动生成snapshot的debian/changelog:
    git-dch -S -a -N 5.1.3-2
    debian/change的新增内容如下:
    tolua (5.1.3-2~1.gbp896bed) UNRELEASED; urgency=low
    
      ** SNAPSHOT build @896bede5a4eb6f3967cdfe94ea2ef419235e7183 **
    
      * UNRELEASED
    
     -- Like Ma   Sun, 19 Feb 2012 01:07:06 +0800
    
    提交相关修改:
    git add debian/changelog debian/patches/series debian/patches/0001-mkdir-for-tolua-lib-archive-and-remove-temp-files.patch debian/patches/0002-Fix-relocation-R_X86_64_32-against-.rodata-can-not-b.patch
    git commit -m "Fix relocation R_X86_64_32 against '.rodata' can not be used when making a shared object"
    
    测试构建新的deb包。为了避免污染当前环境,这里指定git首先导出源码至../tolua-build目录:
    git-buildpackage --git-export-dir=../tolua-build --git-ignore-new
    
    可以看出,上述debian/changelog新增信息,除版本号5.1.3-2外(新包的版本信息),并无实在意义,仅用于测试deb包的构建。
  9. 生成release的版本信息,并构建release的deb包:
    git checkout src/bin/tolua_lua.o src/bin/toluabind.c
    git-dch -R -a
    git commit -a --amend
    git-buildpackage --git-export-dir=../tolua-build --git-ignore-new
    git tag debian/5.1.3-2
    
    注意,前面仅仅在patch-queue分支上删除的两个中间文件,并未在master分支上删除它们,所以重新checkout它们以保证后续构建的正确运行。

    这里的关键命令git-dch -R -a自动生成了release的版本信息,当然我们也可以根据需要再修改它们。最后一条命令,给master分支的HEAD加上标签debian/5.1.3-2,git-buildpackage将依赖于它才能继续正确工作。

到此为止,我已经演示了git-buildpackage的补丁制作过程,可以观察../tolua-build目录中生成的文件,它们就是我们可以用于发布的deb包源码文件,形式上与ubuntu/debian中apt-get source得到的一样。

2011年12月20日星期二

Ubuntu 11.10 VirtualBox的Host-only网卡上外网和DHCP永久地址

VirtualBox支持各种虚拟网络:NAT, Bridge Adapter, Internal Network和Host-only Adapter等。其中Bridged Adapter最为简单和常用,它几乎是0配置,直接桥接有线或无线物理网卡就可以与互联网通信。

然而,我工作场所内部网和家里内部网的网段不相同,DHCP存在一定租赁时间,如果使用Bridged Adapter并DHCP获取IP地址的时候,虚拟机地址经常会改变。为此,我将笔记本电脑的VirtualBox虚拟机都修改为Host-only Adapter模式。


一个问题是Host-only Adapter(网段为192.168.56.0/24)默认不能与互联网通信。google之后发现网上早有人遇到类似问题,他们给出的解决办法是在/etc/rc.local中加入:

iptables -t nat -I POSTROUTING -s 192.168.56.0/24 -j MASQUERADE


另一个问题是VirtualBox内置DHCP的IP租赁时间设置,也无法将MAC地址与IP地址静态绑定,这造成虚拟机IP地址每隔一段时间改变一次,给使用带来诸多不方便。另一方面,我也不想静态设置IP地址,因为如果这样做,我必须每安装一次虚拟机都要重新设置IP地址。

以前就听说过dnsmasq,不仅集成DNS、DHCP和TFTP功能,而且占用资源很少,设置也相对简单。
  1. 安装dnsmasq
    sudo apt-get install dnsmasq
    
  2. 打开/etc/dnsmasq.conf,针对vboxnet0配置DHCP。
    interface=vboxnet0
    
    # 192.168.56.1是默认网关(host机器的vboxnet0地址)
    # 208.67.222.222和208.67.220.220是DNS地址(这里使用了OpenDNS)
    dhcp-option=vboxnet0,option:dns-server,192.168.56.1,208.67.222.222,208.67.220.220
    
    # 192.168.56.2和192.168.56.254为分配地址范围
    # infinite表示IP永远不过期
    dhcp-range=vboxnet0,192.168.56.2,192.168.56.254,infinite
    
  3. 重启动dnsmasq
    sudo service dnsmasq restart
    

当然,dnsmasq也支持MAC地址与IP地址静态绑定。比如,在/etc/dnsmasq.conf中针对MAC地址08:00:27:81:51:85,分配机器名vbox-xp,分配IP地址192.168.56.2
dhcp-host=vbox-xp,08:00:27:81:51:85,192.168.56.2
最后,不要忘了重启动dnsmasq。

2011年10月31日星期一

为tolua的deb包作一个补丁

近日在学习tolua时发现在Kubuntu 11.10 amd64平台下将其链接到so时报告如下错误:
... relocation R_X86_64_32 against '.rodata' can not be used when making a shared object; recompile with -fPIC

为此,我决定在其原deb基础上加一个补丁,这样生成的新包可以安装到其他开发机器上,省去了每次重编译tolua的重复劳动。


  1. 为构建和修改deb安装必要的工具(配置quilt):
    sudo apt-get install build-essential debhelper quilt


  2. 下载libtolua-dev的源码,创建补丁add-fpic-O2-for-amd64.patch,并将config文件加入其中:
    apt-get source libtolua-dev
    cd tolua-5.1.3
    mkdir -p debian/patches
    quilt new add-fpic-O2-for-amd64.patch
    quilt add config


  3. 将config文件中,如下内容
    CFLAGS= -g $(WARN) $(INC)
    CPPFLAGS= -g $(WARN) $(INC)
    替换为
    CFLAGS= -fPIC -O2 -pipe -g $(WARN) $(INC)
    CPPFLAGS= -fPIC -O2 -pipe  -g $(WARN) $(INC)


  4. 生成补丁add-fpic-O2-for-amd64.patch
    quilt refresh

    为补丁增加描述信息
    quilt header -e

    其具体内容如下:
    Description: Fix relocation R_X86_64_32 against '.rodata' can not be used when making a shared object
    Author: Like Ma <likemartinma@gmail.com>
    
      * config: add -fPIC -O2 -pipe to CFLAGS and CPPFLAGS


  5. 在debian/changelog顶部增加日志信息:
    tolua (5.1.3-2) unstable; urgency=low
    
      * Fix relocation R_X86_64_32 against '.rodata' can not be used when making a shared object
    
    -- Like Ma <likemartinma@gmail.com> Mon, 31 Oct 2011 15:23:09 +0800
    


  6. 构建libtolua-dev_5.1.3-2_amd64.deb:
    dpkg-buildpackage -d -uc -us -rfakeroot

blogger的代码加亮

作为一个程序员,没有代码加亮的世界是多么苍白。在上一篇博客中,我利用vim的代码html转换功能来美化代码部分。这个办法实在太笨了,它花了我不少时间来编辑上一篇博客,以至于让我失去了写博客的兴趣。

最近,我无意中发现了一篇如何利用google-code-prettify来加亮代码的文章——Code highlighting on blogger。该文章并没有讲述不同语言的加亮方法,官方网站的README描述了相关解决办法。

我马上将其应用在前一篇博客中,然而默认效果并不令我满意。由于我长期使用vim的黑底系列的颜色方案,我需要一种类似的代码颜色方案来美化博客。

让人欣喜的是官方网站的themes gallery正好提供了我需要的颜色方案sunburst,这只需要简单将上文提及的
http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css
替换为
http://google-code-prettify.googlecode.com/svn/trunk/styles/sunburst.css
另外,我还专门为我的博客换了主题,每切换一次主题需要我重新将代码加亮的相关css和js重新手动加上。为此我用sed一个脚本来简化这个过程,具体内容如下:
#!/bin/sed -f

/<\/head>/i \
<link href='http://google-code-prettify.googlecode.com/svn/trunk/styles/sunburst.css' rel='stylesheet' type='text/css'/> \
<script src='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js' type='text/javascript'/>

/<body[[:blank:]]/ {
s/>/ onload='prettyPrint()'>/
}
具体的使用办法为:
  1. 在Design页面中,下载当前的template
  2. 利用上述脚本处理下载的template,从而生成新的template
  3. 在Design页面上传新的template

2008年10月5日星期日

MakeProjectCreator (MPC) 简介

Makefile, Project, and Workspace Creator (MPC)原始OCI为网络通信库ACE开发的跨平台工程文件生成工具,它可以生成很多种工程文件,例如Windows平台的Borland Developer Studio, Borland C++ Build 2007, Borland Makefile,Microsoft eMbedded VC3, Microsoft Visual C++ 6/7/71/8/9/10, nmake;Linux平台的automake, make等。

下面将以Windows平台的nmake和vc9为例介绍MPC的使用方法。首先,我们需要下载
  1. ActivePerl,双击执行安装;
  2. MPC,以解压到C:盘为例,设置系统环境变量MPC_ROOT为C:\MPC,以及将%MPC_ROOT%追加系统环境变量PATH。当然你也可以通过双击执行C:\MPC\registry.pl将生成工程文件的功能注册到右键菜单中。

一、一个Hello World工程
首先,建立目录helloworld,并且在这个目录下且创建helloworld.cpp,内容如下:
int main () 
{
    std::cout << "Hello World!" << std::endl;
}
然后,创建MPC工程文件helloworld.mpc,内容如下:
project {
    exename = helloworld
}
最后,打开Visual Studio 2008 Command Prompt(也可以用VS2003或2005的),进入helloworld的目录, 然后输入
mwc.pl -type nmake
就生成了格式为nmake的Makefile,它默认包含两个配置Debug和Release。让我们编译一下Debug版本:
nmake CFG="Win32 Debug"
你会发现helloworld.exe就生成在当前目录。

或许你希望debug和release生成的exe分别放在debug和release目录,修改一下MPC文件
project {
    exename = helloworld
    specific (nmake, vc6, vc7, vc71, vc8, vc9, vc10) {
        Debug::install = debug
        Release::install = release
    }
}
或许你更喜欢用IDE,让我们生成VS2008的工程文件吧:
mwc.pl -type vc9

二、一个简单dll工程
首先,建立目录hellodll,并且在这个目录下且创建hellodll.h,内容如下:
#ifndef HELLO_DLL_H
#define HELLO_DLL_H

#ifdef HELLO_BUILD_DLL
#   define HELLO_Export __declspec(dllexport)
#else
#   define HELLO_Export __declspec(dllimport)
#endif /* HELLO_BUILD_DLL */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

HELLO_Export void print_hello ();

#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */

#endif /* HELLO_DLL_H */
然后,创建文件hellodll.cpp,内容如下:
#include "hellodll.h"
#include 

void print_hello ()
{
    std::cout << "Hello World!" << std::endl;
}
最后,创建文件hellodll.mpc,内容如下
project {
    sharedname = hello
    dynamicflags += HELLO_BUILD_DLL
    specific(nmake, vc6, vc7, vc71, vc8, vc9, vc10) {
        Debug::dllout = debug
        Release::dllout = release
    }
}
之后,执行如下命令,可以生成Debug和Release的dll。注意Debug的dll是hellod.dll。
mwc.pl -type nmake
nmake
nmake CFG="Win32 Release"
接下来,我们做一个工程来驱动这个dll

三、hello dll的测试工程
首先,在原来的目录hellodll下建立文件hellowtest.cpp,内容如下:
#include "hellodll.h"

int main ()
{
    print_hello ();
    return 0;
}
然后,创建文件hellotest.mpc,内容如下:
project {
    after += hellodll
    exename = hellotest
    libs += hello

    Source_Files {
        hellotest.cpp
    }

    specific(nmake, vc6, vc7, vc71, vc8, vc9, vc10) {
        Debug::libpaths = debug
        Debug::install = debug
        Release::libpaths = Release
        Release::install = Release
    }
}
注意到helloworld.mpc和hellotest.mpc的差异吗?后者的after语句表示hellotest.mpc依赖于hellodll.mpc,必须在其后编译。另外,我在后者增加了Source_Files块,顾名思义,其作用是指定工程源文件(cpp和c)列表。如果没有它,MPC默认会将hellotest.mpc所在目录的所有源文件作为这个工程的源文件,这显然不是我们希望看到的;同理,需要在hellodll.mpc增加Source_Files块,修改后的内容如下:
project {
    sharedname = hello
    dynamicflags += HELLO_BUILD_DLL

    Source_Files {
        hellodll.cpp
    }

    specific (nmake, vc6, vc7, vc71, vc8, vc9, vc10) {
        Debug::dllout = debug
        Release::dllout = Release
    }
}
类似Source_Files块,还有Header_Files, Inline_Files和Resoure_Files等,具体可以查看MPC官方文档。
最后,重新执行一下
mwc.pl -type nmake
nmake
nmake CFG="Win32 Release"

关于MPC更多的内容和细节可以下载OCI的《TAO开发指南》的MPC样章