Existem os seguintes tipos de pacote Debian:
Neste pacote, o principal conteúdo é o próprio código do upstream, onde esses fontes são mantidos sem nenhuma alteração quando comparado ao que foi desenvolvido pelos autores do software. O que diferencia o pacote fonte do código do upstream é a existência do diretório debian/ na raiz do projeto. Esse diretório contém metadados, Makefiles, scripts, etc. utilizados para construção, instalação, teste, upgrade, remoção desse software.
Todo o trabalho de empacotamento do software será realizado dentro do diretório debian/. Lembre-se de nunca alterar os fontes do upstream diretamente! Caso seja necessária alguma alteração nos fontes do upstream, a mesma deve ser feita através de patches.
Dentro do diretório debian/ pode haver vários arquivos (para uma lista completa verifique essa documentação), mas aqui abordaremos apenas os arquivos obrigatórios, são eles:
Neste arquivo, temos informações tanto do pacote fonte quanto do(s) pacote(s) binário(s) gerado(s). O primeiro parágrafo é sempre referente ao pacote fonte. Um exemplo hipotético a seguir:
Source: foo
Section: misc
Priority: optional
Maintainer: Lucas Kanashiro <kanashiro@debian.org>
Build-Depends: debhelper-compat (= 13)
Standards-Version: 4.7.0
Vcs-Git: <insert the git repo url>
Vcs-Browser: <insert the git repo url to access via browser>
Homepage: <insert the upstream URL, if relevant>
Existem mais campos que podem estar presentes nesse parágrafo, para lista completa verifique a documentação oficial.
Vamos destrinchar o conteúdo de cada um dos campos apresentados:
git clone
e obter o repositório com o pacote fonte. Esse campo não é obrigatório, porém é recomendado.Do segundo parágrafo em diante (pode haver N parágrafos), as informações dizem respeito aos pacotes binários que serão gerados a partir da construção do pacote fonte. Vejamos um exemplo hipotético a seguir:
Package: bar
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: <inserir descrição curta de até 60 caracteres>
<inserir descrição longa, indentada com espaços>
.
<segundo parágrafo da descrição longa>
Novamente, existem mais campos que podem estar presentes nesse tipo de parágrafo. Para a lista completa, verifique a documentação oficial.
Agora, vamos detalhar o conteúdo de cada um dos campos apresentados:
Neste arquivo serão listados a licença de software e copyright de todos os arquivos presentes no pacote fonte. A forma recomendada para escrever esse arquivo é seguindo a DEP-5 (Debian Enhancement Proposal), que determina um formato de arquivo que é machine readable. Você pode encontrar alguns pacotes (no geral mais antigos) que não segue esse formato, se possível, tente convertê-lo para usar a DEP-5.
Quando você for debianizar o seu software, ferramentas como o dh-make já irão gerar um template desse arquivo para você, basta preenchê-lo de forma correta. A seguir um exemplo hipotético do arquivo:
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: foo
Upstream-Contact: John Doe <john@doe.org>
Source: http://github.com/john/foo
Files: *
Copyright: 2020-2024 John Doe <john@doe.org>
License: GPL-2+
Files: icons/*
Copyright: 2019 Jane Doe <jane@doe.org>
License: GPL-2+
Files: debian/*
Copyright: 2021-2024 Lucas Kanashiro <kanashiro@debian.org>
License: GPL-2+
License: GPL-2+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
'/usr/share/common-licenses/GPL-2'.
No primeiro parágrafo, temos informações do formato do arquivo e também sobre o projeto upstream. O campo Format será o mesmo para praticamente todos os pacotes, e os demais (Upstream-Name, Upstream-Contact, e Source) vão variar dependendo do software que está sendo empacotado.
Os demais parágrafos que se iniciam com Files, estarão listando arquivos ou diretórios e suas respectivas licenças de software e copyright. Todos os arquivos presentes no pacote fonte devem estar listados aqui. Para facilitar a vida do mantenedor e não necessitar listar arquivo por arquivo, usamos o asterisco (*) para representar todos os arquivos e diretórios ali presentes de maneira recursiva. Por exemplo, na linha 6 do exemplo acima temos "Files: *", que representa que todos os arquivos e diretórios (recursivamente) na raiz do pacote fonte. As duas linhas a seguir indicam o copyright e a licença de todos esses arquivos. Nos demais parágrafos com Files, são listadas algumas exceções ao primeiro parágrafo, onde tudo dentro do diretório icons/ possui um copyright diferente do restante dos fontes, e o diretório debian/ também.
Para atingir o grau de detalhamento desejado, deve-se inspecionar todos os arquivos do pacote fonte. Para auxiliar essa tarefa que pode ser nada fácil, você pode utilizar ferramentas como o licensecheck, ou simplesmente usar ferramentas como o grep e procurar por referências as palavras copyright e license.
Os demais parágrafos iniciados com License, possuem o texto completo das licenças de software utilizados nos parágrafos anteriores. Importante notar que o nome da licença que vem depois do "License:" deve ser o mesmo utilizado no mesmo campo "License:" nos parágrafos anteriores, pois serve como uma referência ao que já foi adicionado anteriormente. Você deve colar o texto completo da licença aqui, indentado por um espaço e com um ponto final (.) nas linhas em branco. No diretório /usr/share/common-licenses/ você pode encontrar o texto das licenças mais utilizadas, e como foi feito no exemplo acima, esses arquivos podem ser referenciados (linhas 33-35) e evitar a adição do texto por completo.
Uma ferramenta que pode ser bem útil quando estiver com alguma dúvida, é pesquisar como isso está sendo feito em outros pacotes no codesearch.debian.net.
Neste arquivo vamos ter várias entradas apresentando várias informações como alterações realizadas, versão e revisão do pacote, distribuição alvo entre outras. Este arquivo tem um formato especial que é apresentado na Debian Policy. Um exemplo hipotético de uma entrada de changelog a seguir:
foo (0.0.1-1) unstable; urgency=medium
* Initial release. (Closes: #nnnn) <nnnn é o número do bug ITP>
-- Lucas Kanashiro <kanashiro@debian.org> Mon, 22 Mar 2021 10:37:31 -0300
O exemplo acima representa a primeira entrada de changelog de um pacote, onde mencionamos apenas que é uma lançamento inicial (Initial release). Lembrando que a língua inglesa sempre deve ser utilizada no seu changelog. A primeira linha segue o seguinte formato:
<nome_do_pacote_fonte> (<versão_do_software>-<revisão_do_pacote>) <distribuição_alvo>; urgency=<urgência_do_pacote>
O primeiro campo é o nome do pacote, que deve ser o mesmo listado no campo Source do debian/control.
Entre parênteses, temos a versão do software (mesma do upstream) e a revisão do pacote fonte. Para a mesma versão do software upstream podemos ter várias revisões, então poderíamos ter um entrada de changelog subsequente com 0.0.1-2, onde o código do upstream é o mesmo (versão 0.0.1), mas com alterações apenas no empacotamento (diretório debian/).
Na sequência temos a distribuição alvo, que diz basicamente para onde iremos fazer o upload dessa versão do pacote fonte. No geral, vamos sempre usar o unstable como alvo devido ao ciclo de desenvolvimento do projeto. Em alguns casos específicos, como correção de bugs de segurança, backports, correções de bugs em releases estáveis, usaremos distribuições alvo diferentes.
E finalmente temos o campo urgency, que é uma descrição do quão importante é atualizar das versões anteriores para esta. Consiste em uma única palavra-chave com um dos valores low, medium, high, emergency ou critical. Por padrão usamos medium.
Após a primeira linha do changelog, temos a lista de modificações realizadas naquela revisão do pacote fonte. As linhas devem ser indentadas com 2 espaços e iniciaadas com um asterisco (*). Pode haver subtópicos aninhados, neste caso, utilizamos mais espaços na indentação para dar essa conotação (múltiplos de 2), e outros caracteres podem ser utilizados, como (- e +). Um exemplo a seguir:
* Fix X (Closes: #nnnn).
- Add patch Y.
- Add dependency on Z.
+ Remove dependency on A, not needed anymore.
Na descrição das alterações realizadas também podemos fechar bugs no BTS do Projeto Debian (Bug Tracking System), basta adicionar a palavra-chave Closes seguida de dois pontos e cerquilha (#) com o número do bug, como pode ser visto na linha 1 acima.
Na última linha de uma entrada de changelog temos dados de quem está preparando essa nova revisão, e essa linha segue o seguinte formato:
-- <nome_uploader> <<email_do_uploader>> <timestamp>
O nome e email do uploader, no geral, são adicionados pelas ferramentas de empacotamento baseado no conteúdo das variáves de ambiente DEBFULLNAME e DEBEMAIL. Então, não esqueça de configurar essas variáveis antes de gerar o seu changelog, adicionar as linhas a seguir no seu ~/.bashrc talvez seja uma boa solução:
export DEBFULLNAME="John Doe"
export DEBEMAIL="john@doe.org"
Após as informações do uploader temos um timestamp que contem: dia da semana (3 primeiras letras em inglês), vírgula, dia do mês (2 dígitos), nome do mês (3 primeiras letras em inglês), ano, horário, e fuso horário.
Este arquivo é um Makefile com diretrizes para construir os pacotes binários que foram listados no arquivo debian/control. No processo de contrução desses pacotes existem alguns passos a serem seguidos como, configurar o ambiente para construção do binário, compilação do código (se necessário), execução de testes do upstream, instalação dos arquivos nos diretórios apropriados (seguindo a FHS - Filesystem Hierarchy Standard), dentre outros detalhes. Para nos auxiliar nessa tarefa de escrever esse Makefile, utilizamos uma ferramenta chamada debhelper (que deve estar listada como Build-Depends no debian/control) que já tem implementada todos esses passos comumente necessários para construir um pacote binário (para entender melhor quais passos são esses, verifique a manpage do debhelper). Com isso, o nosso Makefile pode ser tão simples quanto:
#!/usr/bin/make -f
%:
dh $@
Caso o comportamento padrão do debhelper não satisfaça a construção daquele software específico, você pode sobrescrever esse comportamento de uma sequência específica do debhelper. Um exemplo hipotético a seguir:
#!/usr/bin/make -f
%:
dh $@
override_dh_installdocs:
# Faço algo aqui para instalar a documentação do pacote
Caso o comportamento padrão do debhelper seja próximo ao que você precisa, mas, antes ou depois da chamada daquele passo, algum comando precisa ser executado, você pode seguir a seguinte abordagem (se o nível de compatibilidade do debhelper for >= 12):
#!/usr/bin/make -f
%:
dh $@
execute_before_dh_installdocs:
# Faço algo aqui antes da chamada do dh_installdocs
execute_after_dh_installman:
# Faço algo aqui depois da chamada do dh_installman
Para mais detalhes, verifique a manpage do debhelper. E, em caso de dúvida de implementação, o codeserach.debian.net sempre será uma boa ferramenta para te auxiliar. Nele vocẽ consegue encontrar exemplos similares ao que deseja e adaptar para o seu pacote.
Neste pacote, temos os arquivos que serão instalados no sistema alvo e possivelmente scripts de pré- e pós-instalação do mesmo. Este pacote é comumente conhecido como .deb. Esse arquivo .deb nada mais é que um arquivo ar que agrega vários outros arquivos, similar a um tarball (.tar.gz or .zip), contendo tudo o que foi mencionado anteriormente. Vamos investigar o conteúdo de um .deb:
$ ar -x ruby-byebug_11.1.3-1_amd64.deb
$ ls -l
total 144
-rw-r--r-- 1 lucas lucas 4164 abr 14 12:21 control.tar.xz
-rw-r--r-- 1 lucas lucas 61544 abr 14 12:21 data.tar.xz
-rw-r--r-- 1 lucas lucas 4 abr 14 12:21 debian-binary
-rw-rw-r-- 1 lucas lucas 65900 abr 14 12:20 ruby-byebug_11.1.3-1_amd64.deb
Para descompactar um arquivo .deb você pode usar o comando ar -x (precisa ter o pacote binutils instalado). Dentro dele, é possivel ver que temos 3 arquivos: control.tar.xz, data.tar.xz e debian-binary.
Vamos checar cada um desse arquivos no exemplo anterior:
$ cat debian-binary
2.0
$ mkdir control
$ tar xf control.tar.xz -C control
$ ls -l control
total 24
-rw-r--r-- 1 lucas lucas 841 jun 6 2020 control
-rw-r--r-- 1 lucas lucas 16759 jun 6 2020 md5sums
$ cat control/control
Package: ruby-byebug
Version: 11.1.3-1
Architecture: amd64
Maintainer: Debian Ruby Team <pkg-ruby-extras-maintainers@lists.alioth.debian.org>
Installed-Size: 290
Depends: ruby (>= 1:2.7~0), ruby-columnize, libc6 (>= 2.4), libruby2.7 (>= 2.7.0), ruby (<< 1:2.8~)
Breaks: ruby-pry-byebug (<< 3.7.0-1~)
Section: ruby
Priority: optional
Multi-Arch: same
Homepage: https://github.com/deivid-rodriguez/byebug
Description: Ruby fast debugger - base + CLI
Byebug is a Ruby 2 debugger. It's implemented using the
Ruby 2 TracePoint C API for execution control and the Debug Inspector C API
for call stack navigation. The core component provides support that
front-ends can build on. It provides breakpoint handling and bindings for
stack frames among other things and it comes with an easy to use command
line interface.
Ruby-Versions: ruby2.7
$ head control/md5sums
150c95275d51bf09d834ff56dabd59f8 usr/bin/byebug
d675a0b655307a6d55bc706d5ac19fc2 usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/byebug/byebug.so
d41d8cd98f00b204e9800998ecf8427e usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/extensions/x86_64-linux/2.7.0/byebug-11.1.3/gem.build_complete
fd941652ee823775562a26b1364f1cd5 usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/exe/byebug
4914f106301d7a1d4ee1319b6f5d2fc3 usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/lib/byebug.rb
2f7e507480510ae7e077c45bfbc80af6 usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/lib/byebug/attacher.rb
176764b04e70a6678c1301c9cec3f561 usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/lib/byebug/breakpoint.rb
e4ece523b0a34c5ca13a2be56ffa9bfa usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/lib/byebug/command.rb
c441dfd8a8937a4a861be40891c61a5d usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/lib/byebug/command_list.rb
91a3d0a9840d2c07c9df4031d30504b7 usr/lib/x86_64-linux-gnu/rubygems-integration/2.7.0/gems/byebug-11.1.3/lib/byebug/commands.rb
Como se pode ver acima, o control.tar.xz possui apenas dois arquivos nesse caso (não possui scripts do mantenedor). Um arquivo chamado control com os metadados do pacote binários, e um arquivo md5sums que contém uma hash md5 para cada arquivo que será instalado no sistem alvo. Com isso, as ferramentas de gerenciamento de pacotes conseguem identificar se existe algum arquivo corrompido durante o processo.
$ mkdir data
$ tar xf data.tar.xz -C data
$ ls -l data
total 4
drwxr-xr-x 5 lucas lucas 4096 jun 6 2020 usr
$ tree data
data
└── usr
├── bin
│ └── byebug
├── lib
│ └── x86_64-linux-gnu
│ └── rubygems-integration
│ └── 2.7.0
│ ├── extensions
│ │ └── x86_64-linux
│ │ └── 2.7.0
│ │ └── byebug-11.1.3
│ │ ├── byebug
│ │ │ └── byebug.so
│ │ └── gem.build_complete
│ ├── gems
│ │ └── byebug-11.1.3
│ │ ├── exe
│ │ │ └── byebug
│ │ └── lib
│ │ ├── byebug
│ │ │ ├── attacher.rb
│ │ │ ├── breakpoint.rb
│ │ │ ├── command_list.rb
│ │ │ ├── command.rb
│ │ │ ├── commands
│ │ │ │ ├── break.rb
│ │ │ │ ├── catch.rb
│ │ │ │ ├── condition.rb
│ │ │ │ ├── continue.rb
│ │ │ │ ├── debug.rb
│ │ │ │ ├── delete.rb
│ │ │ │ ├── disable
│ │ │ │ │ ├── breakpoints.rb
│ │ │ │ │ └── display.rb
│ │ │ │ ├── disable.rb
│ │ │ │ ├── display.rb
│ │ │ │ ├── down.rb
│ │ │ │ ├── edit.rb
│ │ │ │ ├── enable
│ │ │ │ │ ├── breakpoints.rb
│ │ │ │ │ └── display.rb
│ │ │ │ ├── enable.rb
│ │ │ │ ├── finish.rb
│ │ │ │ ├── frame.rb
│ │ │ │ ├── help.rb
│ │ │ │ ├── history.rb
│ │ │ │ ├── info
│ │ │ │ │ ├── breakpoints.rb
│ │ │ │ │ ├── display.rb
│ │ │ │ │ ├── file.rb
│ │ │ │ │ ├── line.rb
│ │ │ │ │ └── program.rb
│ │ │ │ ├── info.rb
│ │ │ │ ├── interrupt.rb
│ │ │ │ ├── irb.rb
│ │ │ │ ├── kill.rb
│ │ │ │ ├── list.rb
│ │ │ │ ├── method.rb
│ │ │ │ ├── next.rb
│ │ │ │ ├── pry.rb
│ │ │ │ ├── quit.rb
│ │ │ │ ├── restart.rb
│ │ │ │ ├── save.rb
│ │ │ │ ├── set.rb
│ │ │ │ ├── show.rb
│ │ │ │ ├── skip.rb
│ │ │ │ ├── source.rb
│ │ │ │ ├── step.rb
│ │ │ │ ├── thread
│ │ │ │ │ ├── current.rb
│ │ │ │ │ ├── list.rb
│ │ │ │ │ ├── resume.rb
│ │ │ │ │ ├── stop.rb
│ │ │ │ │ └── switch.rb
│ │ │ │ ├── thread.rb
│ │ │ │ ├── tracevar.rb
│ │ │ │ ├── undisplay.rb
│ │ │ │ ├── untracevar.rb
│ │ │ │ ├── up.rb
│ │ │ │ ├── var
│ │ │ │ │ ├── all.rb
│ │ │ │ │ ├── args.rb
│ │ │ │ │ ├── const.rb
│ │ │ │ │ ├── global.rb
│ │ │ │ │ ├── instance.rb
│ │ │ │ │ └── local.rb
│ │ │ │ ├── var.rb
│ │ │ │ └── where.rb
│ │ │ ├── commands.rb
│ │ │ ├── context.rb
│ │ │ ├── core.rb
│ │ │ ├── errors.rb
│ │ │ ├── frame.rb
│ │ │ ├── helpers
│ │ │ │ ├── bin.rb
│ │ │ │ ├── eval.rb
│ │ │ │ ├── file.rb
│ │ │ │ ├── frame.rb
│ │ │ │ ├── parse.rb
│ │ │ │ ├── path.rb
│ │ │ │ ├── reflection.rb
│ │ │ │ ├── string.rb
│ │ │ │ ├── thread.rb
│ │ │ │ ├── toggle.rb
│ │ │ │ └── var.rb
│ │ │ ├── history.rb
│ │ │ ├── interface.rb
│ │ │ ├── interfaces
│ │ │ │ ├── local_interface.rb
│ │ │ │ ├── remote_interface.rb
│ │ │ │ ├── script_interface.rb
│ │ │ │ └── test_interface.rb
│ │ │ ├── option_setter.rb
│ │ │ ├── printers
│ │ │ │ ├── base.rb
│ │ │ │ ├── plain.rb
│ │ │ │ └── texts
│ │ │ │ ├── base.yml
│ │ │ │ └── plain.yml
│ │ │ ├── processors
│ │ │ │ ├── command_processor.rb
│ │ │ │ ├── control_processor.rb
│ │ │ │ ├── post_mortem_processor.rb
│ │ │ │ └── script_processor.rb
│ │ │ ├── remote
│ │ │ │ ├── client.rb
│ │ │ │ └── server.rb
│ │ │ ├── remote.rb
│ │ │ ├── runner.rb
│ │ │ ├── setting.rb
│ │ │ ├── settings
│ │ │ │ ├── autoirb.rb
│ │ │ │ ├── autolist.rb
│ │ │ │ ├── autopry.rb
│ │ │ │ ├── autosave.rb
│ │ │ │ ├── basename.rb
│ │ │ │ ├── callstyle.rb
│ │ │ │ ├── fullpath.rb
│ │ │ │ ├── histfile.rb
│ │ │ │ ├── histsize.rb
│ │ │ │ ├── linetrace.rb
│ │ │ │ ├── listsize.rb
│ │ │ │ ├── post_mortem.rb
│ │ │ │ ├── savefile.rb
│ │ │ │ ├── stack_on_error.rb
│ │ │ │ └── width.rb
│ │ │ ├── source_file_formatter.rb
│ │ │ ├── subcommands.rb
│ │ │ └── version.rb
│ │ └── byebug.rb
│ └── specifications
│ └── byebug-11.1.3.gemspec
└── share
└── doc
└── ruby-byebug
├── changelog.Debian.gz
├── changelog.gz
├── copyright
└── README.md.gz
33 directories, 126 files
E o data.tar.xz, como pode ser visto acima, contém exatamente os arquivos e diretórios que serão instalados por esse pacote binário. Outra forma de obter apenas o conteúdo do data.tar.xz é executando os seguintes comandos:
$ # Lista o conteúdo do pacote binário
$ dpkg -c <arquivo.deb>
$ # Extrai o conteúdo do pacote binário
$ dpkg-deb -x <arquivo.deb> <diretório_de_output>