Bash em Exemplos, Parte 3

Explorando o sistema ebuild

Daniel Robbins
President and CEO, Gentoo Technologies, Inc.
Abril de 2000

Neste artigo final da série Bash em Exemplos, Daniel Robbins dá uma boa olhada no sistema ebuild do gentoo Linux, um excelente exemplo do poder do bash. Passo a passo, ele mostra como o sistema ebuild foi implementado, e toca em muitas técnicas úteis do bash, e estratégias de projeto. No fim do artigo, você terá uma boa idéia do que está envolvido na produção de uma aplicação completa baseada no bash, bem como terá iniciado a codificação de seu próprio sistema de auto-construção.

Conteúdo


Entra o sistema ebuild

Eu realmente estava olhando para diante para este terceiro e último artigo Bash em Exemplos, por que agora que já cobrimos os fundamentos da programação bash na Parte 1 e Parte 2, podemos nos direcionar para tópicos mais avançados, como desenvolvimento de aplicações bash e projeto de pogramas. Para este artigo, eu vou dar a vocês uma boa dose de experiência de desenvolvimento prática e real de bash, apresentando um projeto em que gastei muitas horas codificando e refinando: o sistema ebuild do Gentoo Linux.

Sou o arquiteto chefe do Gentoo Linux, um Linux OS avançado, atualmente em estágio beta. Uma das minhas responsabilidades primárias é garantir que todos os pacotes binários (similares a pacotes RPM) sejam criados propriamente e funcionem juntos. Como você provavelmente sabe, um sistema padrão Linux não é composto de uma única árvore de fontes unificada (como o BSD), mas é feito de cerca de mais de 25 pacotes principiais que trabalham juntos. Alguns dos pacotes incluem:

Pacote Descrição
linux O kernel
util-linux Uma coleção de programas miscelânea relacionados ao Linux
e2fsprogs Uma coleção de utilitários relacionados ao filesystem ext2
glibc A biblioteca GNU C

Cada pacote está em seu próprio tarball e é mantido por desenvolvedores independentes ou times de desenvolvedores. Para criar uma distribuição, cada pacote tem que ser separadamente baixado, compilado, e empacotado. Cada vez que um pacote deve ser corrigido, atualizado, ou melhorado, os passos de compilação e empacotamento devem ser repetidos (e eles ficam velhos realmente rápido). Para ajudar a eliminar os passos repetitivos envolvidos na criação e atualização de pacotes, eu criei o sistema ebuild, escrito quase que inteiramente em bash. Para melhorar seu conhecimento de bsh, irei mostrar como eu implementei as porções que fazem o desenpacotamento e compilação do sistema ebuild, passo a passo. Conforme eu explico cada passo, também irei discutir pro que certas decisões de projeto foram feitas. No fim do artigo, não somente você terá um excelente entendimento de projetos de programação bash de larga escala, mas você também terá implementado uma boa porção de um sistema de auto criação completa.

Por quê o bash?

O bash é um componente essencial do sistema ebuild do Gentoo Linux. Ele foi escolhido como linguagem primária do ebuild por várias razões. Primeiro, ele possui uma sintaxe simples e familiar que é especialmente apropriada para chamar programas externos. Um sistema auto-build é a "cola' que automatiza a chamada de programas externos, e o bash é bastante apropriado para este tipo de aplicação. Segundo, o suporte do bash para funções permite que o sistema ebuild tenha um código modular e fácil de entender. Terceiro, o sistema ebuild aproveita-se do suporte do bash para variáveis de ambiente, permitindo que mantenedores de pacotes e desenvolvedores o configurem facilmente, enquanto está rodando.

Revisão do processo de construção

antes de olharmos no sistema ebuild, vamos revisar o que está envolvido em fazer com que um pacote esteja compilado e instalado. Para nosso exemplo, iremos olhar o pacote "sed", um utilitário de edição de textos em linha padrão do GNU, que é parte de todas as distribuições do Linux. Primeiro, baixe o tarball do fonte (sed-3.02.tar.gz) (veja Recursos). Iremos armazenar este arquivo em /usr/src/distfiles, um diretório ao qual iremos nos referir usando a variável de ambiente "$DISTDIR". "$DISTDIR" é o diretório em que todos os tarballs de fontes originais residem. É um grande depósito de código fonte.

Nosso próximo passo é criar um diretório temporário chamado "work", que irá abrigar os fontes descompactados. Iremos nos referir a este diretório mais tarde usando a variável de ambiente "$WORKDIR". Para isto, iremos trocar de diretório para um em que tenhamos permissão de escrita, e iremos escrever o seguinte:

Descompactando o sed em um diretório temporário

$ mkdir work
$ cd work
$ tar xzf /usr/src/distfiles/sed-3.02.tar.gz

O tarball é então descompactado, criando um diretório chamado sed-3.02 que contém todo o código fonte. Iremos nos referir ao diretório sed-3.02 mais tarde usando a variável de ambiente "$SRCDIR". Para compilar o programa, usamos o seguintes comandos:

Compilando o sed

$ cd sed-3.02
$ ./configure --prefix=/usr
(autoconf gera os arquivos makefile apropriados, pode demorar um pouco)

$ make
(o pacote é compilado a partir dos fontes, também pode demorar um pouco)

Vamos omitir o passo final, o "make install", já que estamos apenas cobrindo a descompactação e compilação neste artigo. Se quisermos escrever um scrpt bash para executar todos estes passos para nós, ele poderia se parecer com o seguinet:

Script bash de exemplo para executar o processo de descompactar/compilar

#!/usr/bin/env bash

if [ -d work ]
then
# remove old directory if it exists
	rm -rf work
fi
mkdir work
cd work
tar xzf /usr/src/distfiles/sed-3.02.tar.gz
cd sed-3.02
./configure --prefix=/usr
make

Generalizando o código

Apesar deste script de autocompilação funcionar, ele não é muito flexível. Basicamente, o script só contém a listagem de todos os comandos que escrevemos na linha de comando. Apesar desta solução funcionar, seria legal fazer um script mais genérico que possa ser configurado rapidamente para descompactar e compilar qualquer pacote pela alteração de algumas linhas. Desta forma, é menos trabalho para o administrador do pacote acrescentar novos pacotes à distribuição. Vamos dar o primeiro passo nesta direção usando bastante variáveis de ambiente, tornando nosso script mais genérico:

Um script novo, mais genérico

#!/usr/bin/env bash

# P is the package name

P=sed-3.02

# A is the archive name

A=${P}.tar.gz

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work
export SRCDIR=${WORKDIR}/${P}

if [ -z "$DISTDIR" ]
then
	# set DISTDIR to /usr/src/distfiles if not already set
	DISTDIR=/usr/src/distfiles
fi
export DISTDIR

if [ -d ${WORKDIR} ]
then
	# remove old work directory if it exists
	rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}

tar xzf ${DISTDIR}/${A}
cd ${SRCDIR}
./configure --prefix=/usr
make

Acrescentamos muitas variáveis de ambiente ao código, mas ele ainda faz basicamente a mesma coisa. Entretanto, agora, para compilar qualquer tarball de fonte padrão GNU baseado no autoconf, podemos simplesmente copiar este arquivo para um novo arquivo (com um nome apropriado para refletir o nome do novo pacote que ele compila), e então trocar os valores de "$A" e "$P" para os novos valores. Todas as outras variáveis de ambiente automaticamente são ajustadas para as configurações corretas, e o script irá funcionar como esperado. Enquanto isto é útil, existe ainda alguns melhoramentos que podem ser feitos ao código. Este códito em particular é muito maior que o script de "transcrição" que criamos. Como um dos objetivos de qualquer projeto de programação deve ser a redução da complexidade para o usuário, seria legal diminuir dramaticamente o código, ou, pelo menos, organizá-lo melhor. Podemos fazer isto com um truque legal -- dividir o código em dois arquivos separados. Salve este arquivo como "sed-3.02.ebuild":

sed-3.02.ebuild

#the sed ebuild file -- very simple!
P=sed-3.02
A=${P}.tar.gz

Nosso primeiro arquivo é trivial, e contém somente as variáveis de ambiente que devem ser configuradas por pacote. Aqui está o seguindo script, que contém o cérebro da operação. Salve este como "ebuild" e torne-o executável:

O script ebuild

#!/usr/bin/env bash

if [ $# -ne 1 ]
then
	echo "one argument expected."
	exit 1
fi

if [ -e "$1" ]
then
	soruce $1
else
	echo "ebuild file $1 not found."
	exit 1
fi

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work
export SRCDIR=${WORKDIR}/${P}

if [ -z "$DISTDIR" ]
then
	# set DISTDIR to /usr/src/distfiles if not already set
	DISTDIR=/usr/src/distfiles
fi
export DISTDIR

if [ -d ${WORKDIR} ]
then
	# remove old work directory if it exists
	rm -f ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}
tar xzf ${DISTDIR}/${A}
cd ${SRCDIR}
./configure --prefix=/usr
make

Agora que dividimos nosso sistema em dois arquivos, eu aposto que você está tentando imaginar como ele funciona. Basicamente, para compilar o sed, escreva:

$ ./ebuild sed-3.02.ebuild

Quando o "ebuild" é executado, ele primeiro tenta fazer um "source" de "$1". O que isto significa? Do meu artigo anterior, lembre-se que "$1" é o primeiro argumento da linha de comando -- neste caso, "sed-3.02.ebuild". No bash, o comando "source" lê declarações bash de um arquivo, e executa elas como se elas estivessem no lugar em que o comando "source" está. Assim, "source ${1}" faz com que o script "ebuild" execute os comandos em "sed-3.02.ebuild", que faz com que "$P" e "$A" sejam definidas. Esta alteração de projeto é realmente útil, por que se queremos compilar outro programa ao invés do sed, simplesmente criamos um novo arquivo .ebuild e passamos o mesmo como um argumento para nosso script "ebuild". Desta forma, os arquivos .ebuild são realmente simples, enquanto o cérebro complicado do sistema ebuild fica armazenado em um lugar -- nosso script "ebuild". Desta forma, podemos atualizar ou melhorar o sistema ebuild simplesmente editando o script "ebuild", mantendo os detalhes de implementação fora dos arquivos ebuild. Segue um arquivo ebuild exemplo para o gzip:

gzip-1.2.4a.ebuild

#another really simple ebuild script!
P=gzip-1.2.4a
A=${P}.tar.gz

Acrescentando funcionalidades

OK, estamos fazendo algum progresso. Mas existe uma funcionalidade adicional que eu gostaria de acrescentar. Eu gostaria que o script ebuild aceitasse um segundo argumento de linha de comando, que será "compile", "unpack", ou "all". Este segundo argumento de linha de comando informa ao script ebuild qual passo em particular do script eu quero executar. Desta forma, eu posso informar ao ebuild para descompactar o arquivo, mas não compilar ele (caso eu queira inspecionar o código fonte antes que a compilação inicie). Para isto, irei acrescentar uma declaração case que irá testar a variável "$2", e fazer coisas diferentes baseado em seu valor. Aqui está o nosso novo código:

ebuild, revisão 2

#!/usr/bin/env bash

if [ $# -ne 2 ]
then
	echo "Please specify two args - .ebuild file and unpack, compile or all"
	exit 1
fi

if [ -z "$DISTDIR" ]
then
	# set DISTDIR to /usr/src/distfiles if not already set
	DISTDIR=/usr/src/distfiles
fi
export DISTDIR

ebuild_unpack() {
	#make sure we're in the right directory
	cd ${ORIGDIR}

	if [ -d ${WORKDIR} ]
	then
		rm -rf ${WORKDIR}
	fi

	mkdir ${WORKDIR}
	cd ${WORKDIR}
	if [ ! -e ${DISTDIR}/${A} ]
	then
		echo "${DISTDIR/${A} does not exist. Please download first."
		exit 1
	fi
	tar xzf ${DISTDIR/${A}
	echo "Unpacked ${DISTDIR}/${A}."
	#source is now correctly unpacked
}

ebuild_compile() {
	#make sure we're in the right directory
	cd ${SRCDIR}
	if [ ! -d "${SRCDIR}" ]
	then
		echo "${SRCDIR} does not exist -- please unpack first."
		exit 1
	fi
	./configure --prefix=/usr
	make
}

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work

if [ -e "$1" ]
then
	source $1
else
	echo "Ebuild file $1 not found."
	exit 1
fi

export SRCDIR=${WORKDIR}/${P}

case "${2}" in
	unpack)
		ebuild_unpack
		;;
	compile)
		ebuild_compile
		;;
	all)
		ebuild_unpack
		ebuild_compile
		;;
	*)
		echo "Please specify unpack, compile or all as the second arg"
		exit 1
		;;
esac

Fizemos muitas alterações, então vamos primeiro revisá-las. Primeiro, colocamos os passos de compilação e desarquivamento em suas próprias funções, chamadas ebuild_compile() e ebuild_unpack(), respectivamente. Este foi um movimento inteligenes, uma vez que o código vai ficando mais complicado, e as novas funções dão uma modularidade, que ajuda a manter as coisas organizadas. Na primeira linha de cada função, eu explicitamente fiz um "cd" para o diretório que queria estar por que, conforme nosso código está ficando mais modular que linear, é mais provável que cometamos um deslize e executemos uma função no diretório de trabalho errado. O comando "cd" explicitamente coloca-nos no lugar certo, e evita que cometamos um erro mais tarde -- um passo importante -- especialmente se você irá excluir arquivos em funções.

Além disto, eu acrescentei uma checagem útil no início da função ebuild_compile(). Agora, ela checa para certificar-se que "$SRCDIR" exista, e, caso não exista, imprime uma mensagem de erro informando o usuário para primeiro desempacotar o arquivo, e então sai. Se você quiser, pode alterar este comportamento, de forma que se "$SRCDIR" não existir, nosso script ebuild irá descompactar o arquivo fonte automaticamente. Você pdoe fazer isto substituindo o código do ebuild_compile() pela seguinte versão:

Uma nova alteração em ebuild_compile()

ebuild_compile() {
	#make sure we're in the right directory
	if [ ! -d "${SRCDIR}" ]
	then
		ebuild_unpack
	fi
	cd ${SRCDIR}
	./configure --prefix=/usr
	make
}

Uma das alterações mais óbvias em nossa segunda versão do script ebuild é a nova declaração case no fim do código. Esta declaração case simplesmente checa o segundo argumento da linha de comando, e executa a ação correta, dependendo de seu valor. Se agora escrevermos:

$ ebuild sed-3.02.ebuild

iremos obter uma mensagem de erro. O ebuild agora quer que digamos a ele o que fazer, como abaixo:

ebuild sed-3.02.ebuild unpack

ou

ebuild sed-3.02.ebuild compile

ou

ebuild sed-3.02.ebuild all

Se você fornecer um segundo argumento diferente de qualquer destas opções listadas, receberá uma mensagem de erro (a cláusula *), e o programa terminará.

Modularizando o código

Agora que o código está bastante avançado e funcional, você pode ficar tentado a criar vários scripts ebuild para descompactar e compilar seus programas favoritos. Se você o fizer, cedo ou tarde irá cruzar com alguns fontes que não usam o autoconf ("./configure") ou possivelmente outros que possuam processos de compilação não-padrão. Precisamos nos certificar de fazer mais algumas alterações para o sistema ebuild para acomodar estes programas. Mas, antes disto, é uma boa idéia pensar um pouco sobre como fazer isto.

Uma das coisas boas de ter escrito explicitamente "./configure --prefix=/usr; make' em nosso estágio de compilação é que, na maior parte do tempo, isto funciona. Mas precisamos que o sistema ebuild acomode fontes que não usem o autoconf ou Makefiles normais. Para resolver este problema, eu proponho que nosso script ebuil deve, por padrão, fazer o seguinte:

  1. Se existir um script configure em $"{SRCDIR}", executar o mesmo como segue:
    ./configure --prefix=/usr
    
    Caso contrário, não executar este passo
  2. Executar o seguinte comando:
    make
    

Uma vez que o ebuild somente executa o ebuild se ele realmente existir, podemos acomodar automaticamente os programas que não usam o autoconf e possuem makefiles padrão. Mas se um simples "make" não fizer o mesmo truque para alguns fontes? Precisamos uma forma de contornar nossos valores defaults com algum código específico para tratar estas situações. Para isto, iremos transformar nossa função ebuild_compile() em duas funções. A primeira função, que pode ser vista como uma função "pai", ainda será chamada de ebuild_compile(). Entretanto, teremos uma nova função, chamada user_compile(), que contém somente nossas ações de compilação razoáveis:

ebuild_compile() dividido em duas funções

user_compile() {
	#we're already in ${SRCDIR}
	if [ -e configure ]
	then
		#run configure script if it exists
		./configure --prefix=/usr
	fi
	#run make
	make
}

ebuild_compile() {
	if [ -d "${SRCDIR}" ]
	then
		echo "${SRCDIR} does not existe -- please unpack first."
		exit 1
	fi
	#make sure we're in the right directory
	cd ${SRCDIR}
	user_compile
}

Pode não parecer óbvio o que eu estou fazendo agora, mas confie em mim. Enquanto o código funciona de forma quase idêntica a nossa versão anterior do ebuild, podemos fazer agora algo que não poderíamos fazer antes -- podemos sobrescrever o user_compile() no sed-3.02.ebuild. Assim, se o user_compile() padrão não atende nossas necessidades, podemos definir um novo em nosso arquivo .ebuild que contém os comandos necessários para compilar o pacote. Por exemplo, aqui temos um ebuild para o e2fsprogs-1.18, que requer uma linha "./configure" um pouco diferente:

e2fsprogs-1.18.ebuild

#este arquivo ebuild sobrescreve o user_compile() padrão
P=e2fsprogs-1.18
A=${P}.tar.gz

user_compile() {
	./configure --enable-elf-shlibs
	make
}

Agora, o e2fsprogs será compilado exatamente da forma que queremos. Mas, na maioria dos pacotes, poderemos omitir qualquer função user_compile() no arquivo .ebuild, e a função default user_compile() será usada.

Como exatamente o script ebuild sabe qual função user_compile() usar? Isto é, na verdade, bastante simples. No script ebuild, a função padrão user_compile() é definida antes que o arquivo e2fsprogs-1.18.ebuild seja "sourced". Se houver uma função user_compile() no e2fsprogs-1.18.ebuild, ela sobrescreve a versão default definida anteriormente. Se não, a função default user_compile() é usada.

Isto é muito legal, acrescentamos bastante flexibilidade sem precisar de nenhum código complexo desnecessário. Não iremos cobrir isto aqui, mas você pode fazer modificações similares ao ebuild_unpack() de forma que os usuários consigam contornar o processo padrão de desempacotamento. Isto pode ser útil se qualquer patch deve ser aplicado, ou se os arquivos estão contidos em múltiplos arquivos. Também é uma boa idéia modificar nosso código de desempacotamento de forma que ele reconheça tarballs compactados pelo bzip2 por default.

Arquivos de configuração

Já cobrimos bastantes técnicas do bash, e agora é hora de cobrirmos mais uma. Geralmente, é útil para um programa ter um arquivo de configurações globais que resida no /etc. Felizmente, isto é fácil de fazer usando o bash. Simplesmente crie o seguinte arquivo e salve-o como /etc/ebuild.conf:

/etc/ebuild.conf

# /etc/ebuild.conf: set system-wide ebuild options in this file

# MAKEOPTS are options passed to make
MAKEOPTS="-j2"

Neste exemplo, incluímos apenas uma opção de configuração, mas você poderia ter incluído muitas mais. Uma das coisas bonitas sobre o bash é que este arquivo pode ser executado via o "source", de forma muito simples. Este é um truque de projeto que funciona com a maioria das linguagens interpretadas. Depois que /etc/ebuild.conf é "sourced", "$MAKEOPTS" está definida dentro de seu script ebuild. Iremos usar ele para permitir que o usuário passe opções para o make. Normalmente, esta opção pode ser utilizada para permitir ao usuário informar ao ebuild para fazer um make em paralelo.

O que é um make em paralelo?

Para aumentar a velocidade de compilação em máquinas multiprocessadas, o make suporta a compilação em paralelo de programas. Isto significa que, em vez de somente compilar um código fonte por vez, o make compila um número especificado pelo usuário de arquivos fonte simultaneamente (assim aqueles processadores extras em máquinas multiprocessadas são usadas). O make paralelo é habilitado passando a opção -j # para o make, conforme abaixo:

make -j4 MAKE="make -j4"

Este código instrui o make a compilar quatro programas simultaneamente. O argumento MAKE="make -j4" diz ao make para passar a opção -j4 para qualquer processo-filho que o make venha a lançar.

Aqui está a versão final do nosso programa ebuild:

ebuild, versão final

#!/usr/bin/env bash

if [ $# -ne 2 ]
then
	echo "Please specify ebuild file and unpack, compile or all"
	exit 1
fi

source /etc/ebuild.conf

if [ -z "$DISTDIR" ]
then
	# set DISTDIR to /usr/src/distfiles if not already set
	DISTDIR=/usr/src/distfiles
fi
export DISTDIR

ebuild_unpack() {
	#make sure we're in the right directory
	cd ${ORIGDIR}

	if [ -d ${WORKDIR} ]
	then
		rm -rf ${WORKDIR}
	fi

	mkdir ${WORKDIR}
	cd ${WORKDIR}
	if [ ! -e ${DISTDIR}/${A} ]
	then
		echo "${DISTDIR}/${A} does not exist. Please download first."
		exit 1
	fi
	tar xzf ${DISTDIR}/${A}
	echo "Unpacked ${DISTDIR}/${A}."
	#ource is now correctly unpacked
}

user_compile() {
	#we're already in ${SRCDIR}
	if [ -e configure ]
	then
		#run configure script if it exists
		./configure --prefix=/usr
	fi
	#run make
	make $MAKEOPTS MAKE="make $MAKEOPTS"
}

ebuild_compile() {
	if [ ! -d "${SRCDIR}" ]
	then
		echo "${SRCDIR} does not exist -- please unpack first."
		exit 1
	fi
	#make sure we're in the right directory
	cd ${SRCDIR}
	user_compile
}

export ORIGDIR=`pwd`
export WORKDIR=${ORIGDIR}/work

if [ -e "$1" ]
then
	source $1
else
	echo Ebuild file $1 not found."
	exit 1
fi

export SRCDIR=${WORKDIR}/${P}

case "${2}" in
	unpack)
		ebuild_unpack
		;;
	compile)
		ebuild_compile
		;;
	all)
		ebuild_unpack
		ebuild_compile
		;;
	*)
		echo "Please specify unpack, compile or all as the second arg"
		exit 1
		;;
esac

Note que o "sourcing" de /etc/ebuild.conf é feito perto do início do arquivo. Note também que usamos "$MAKEOPTS" em nossa função user_compile() default.Você pode estar se perguntando como isto irá funcionar -- afinal de contas, nós estamos nos referindo a "$MAKEOPTS" antes de fazer o "source" em /etc/ebuild.conf, que é o que define "$MAKEOPTS" em princípio. Para nossa sorte, isto estáOK, por que a expansão de variáveis somente acontece quando user_compile() é executado. Na hora que user_compile() é executado, /etc/ebuild.conf já foi "sourced", e "$MAKEOPTS" está configurada com o valor correto.

Empacotando tudo

Nós cobrimos muitas técnicas de programação bash neste artigo, mas somente tocamos na superfície do poder do bash. Por exemplo, o sistema ebuild em produção no Gentoo Linux não somente desempacota e compila cada pacote, mas ele também:

Além disto, o sistema ebuild em produção possui várias outras opções de configuração global, permitindo que o usuário especifique opções como quais as flags de otimização utilizadas durante a compilação, e se o suporte opcional a pacotes como o GNOME e o slang devem ser habilitados por padrão nos pacotes que suportam esta opção.

Está claro que o bash pode realizar muito mais do que o que foi tocado nesta série de artigos. Espero que você tenha aprendido bastante sobre esta incrível ferramenta, e está excitado sobre o uso do bash para tornar mais rápido e melhorar seus projetos de desenvolvimento.

Recursos

Sobre o autor

Residindo em Albuquerque, Novo México, Daniel Robbins é o Chief Architect do Gentoo Project, CEO da Gentoo Technologies, Inc., o mentor do Linux Advanced Multimedia Project (LAMP), e um autor-contribuinte dos livros da Macmillan Caldera OpenLinux Unleashed, SuSE Linux Unleashed, e Samba Unleashed. Daniel está envolvido com computadores de alguma forma desde o segundo grau, quando ele foi exposto pela primeira vez à linguagem de programação Logo, bem como a uma dose podencialmente perigosa de Pac Man. Isto provavelmente explica por que ele tem servido desde então como Lead Graphic Artist na SONY Eletronic Publishing/Psygnosis. Daniel gosta de passar o tempo com sua esposa, Mary, que está esperando uma criança para esta primavera. Ele pode ser encontrado no email drobbins@gentoo.org.

1