Packaging: criando .deb, .rpm e AppImage

1. Introdução ao Packaging de Aplicações em C

Empacotar uma aplicação escrita em C é o passo final para transformar código-fonte em software distribuível. Usuários finais não compilam programas — eles instalam pacotes. Sem packaging, você entrega um tarball e deixa o usuário lidar com dependências, paths e permissões. Com pacotes .deb, .rpm ou AppImage, você oferece instalação limpa, remoção segura e gerenciamento automático de bibliotecas.

Cada formato atende a um ecossistema: .deb para Debian, Ubuntu e derivados; .rpm para Fedora, RHEL, CentOS e openSUSE; AppImage para qualquer distribuição Linux, sem necessidade de instalação. Este artigo mostra como gerar os três a partir de um mesmo projeto em C.

Pré-requisitos: gcc, make, cmake (opcional), dpkg-deb, rpmbuild, appimagetool e linuxdeploy. Tenha também um projeto C mínimo com estrutura src/ e include/.

2. Estrutura Base do Projeto e Compilação

Organize seu projeto assim:

meu_app/
├── src/
│   └── main.c
├── include/
│   └── utils.h
├── Makefile
└── packaging/
    ├── deb/
    ├── rpm/
    └── appimage/

Exemplo de Makefile com linkedição dinâmica:

CC = gcc
CFLAGS = -O2 -Wall -Iinclude
LDFLAGS = -lm
TARGET = meu_app

all: $(TARGET)

$(TARGET): src/main.o
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

src/main.o: src/main.c include/utils.h
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -f $(TARGET) src/*.o

Compile com make e obtenha o binário meu_app. Para distribuição dinâmica, use -lm para linkedição com a math library. Se quiser estática, adicione -static, mas lembre-se: pacotes .deb e .rpm geralmente preferem linkedição dinâmica para economia de espaço e updates via sistema.

3. Criando Pacotes .deb (Debian/Ubuntu)

Crie a estrutura do pacote em packaging/deb/meu_app_1.0-1_amd64/:

DEBIAN/
├── control
├── postinst
└── prerm
usr/
└── local/
    └── bin/
        └── meu_app

Arquivo control:

Package: meu-app
Version: 1.0-1
Section: utils
Priority: optional
Architecture: amd64
Depends: libc6 (>= 2.31), libm6
Maintainer: Seu Nome <email@exemplo.com>
Description: Aplicação exemplo em C
 Empacotada como .deb para demonstração.

postinst (script pós-instalação):

#!/bin/bash
set -e
ldconfig
echo "meu-app instalado com sucesso!"

prerm (script pré-remoção):

#!/bin/bash
set -e
echo "Removendo meu-app..."

Dê permissão de execução aos scripts:

chmod +x packaging/deb/meu_app_1.0-1_amd64/DEBIAN/postinst
chmod +x packaging/deb/meu_app_1.0-1_amd64/DEBIAN/prerm

Copie o binário:

cp meu_app packaging/deb/meu_app_1.0-1_amd64/usr/local/bin/

Gere o .deb:

dpkg-deb --build packaging/deb/meu_app_1.0-1_amd64

Valide com:

lintian meu_app_1.0-1_amd64.deb

O resultado é um pacote instalável com sudo dpkg -i meu_app_1.0-1_amd64.deb.

4. Criando Pacotes .rpm (Fedora/CentOS)

No diretório packaging/rpm/, crie o arquivo .spec:

Name:           meu-app
Version:        1.0
Release:        1%{?dist}
Summary:        Aplicação exemplo em C

License:        MIT
URL:            https://exemplo.com
Source0:        %{name}-%{version}.tar.gz

BuildRequires:  gcc, make
Requires:       glibc >= 2.31

%description
Aplicação exemplo em C empacotada como .rpm para demonstração.

%prep
%setup -q

%build
make

%install
mkdir -p %{buildroot}%{_bindir}
install -m 755 meu_app %{buildroot}%{_bindir}/

%files
%{_bindir}/meu_app

%changelog
* Tue Jan 14 2025 Seu Nome <email@exemplo.com> - 1.0-1
- Versão inicial

Crie o tarball do código-fonte:

tar czf ~/rpmbuild/SOURCES/meu-app-1.0.tar.gz .

Construa o pacote:

rpmbuild -ba packaging/rpm/meu-app.spec

O .rpm será gerado em ~/rpmbuild/RPMS/x86_64/. Verifique com:

rpmlint ~/rpmbuild/RPMS/x86_64/meu-app-1.0-1.x86_64.rpm

Instale com sudo rpm -ivh meu-app-1.0-1.x86_64.rpm.

5. Criando AppImage (Portátil e Autossuficiente)

AppImage é um formato autossuficiente: contém binário e todas as dependências. Comece criando um AppDir em packaging/appimage/meu-app.AppDir/:

meu-app.AppDir/
├── AppRun
├── meu-app.desktop
├── meu-app.png
└── usr/
    └── bin/
        └── meu_app

AppRun (script de entrada):

#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH="${HERE}/usr/bin:${PATH}"
export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
exec meu_app "$@"

meu-app.desktop:

[Desktop Entry]
Name=Meu App
Exec=meu_app
Icon=meu-app
Type=Application
Categories=Utility;

Copie o binário:

mkdir -p packaging/appimage/meu-app.AppDir/usr/bin
cp meu_app packaging/appimage/meu-app.AppDir/usr/bin/

Use linuxdeploy para incluir bibliotecas automaticamente:

cd packaging/appimage
linuxdeploy-x86_64.AppImage --appdir meu-app.AppDir --output appimage

Ou, manualmente, copie as bibliotecas detectadas por ldd:

ldd meu_app | grep "=> /" | awk '{print $3}' | xargs -I {} cp {} meu-app.AppDir/usr/lib/

Gere o AppImage final com appimagetool:

appimagetool-x86_64.AppImage meu-app.AppDir

O arquivo Meu_App-x86_64.AppImage é portátil: baixe, dê permissão de execução e rode em qualquer distribuição.

6. Gerenciamento de Dependências e Compatibilidade

Aplicações em C dependem da libc e de outras bibliotecas compartilhadas. No .deb, declare Depends: libc6 (>= 2.31). No .rpm, use Requires: glibc >= 2.31. Para bibliotecas de terceiros (ex.: libcurl), adicione Depends: libcurl4 ou Requires: libcurl.

Use pkg-config no Makefile para localizar flags de compilação:

CFLAGS += $(shell pkg-config --cflags libcurl)
LDFLAGS += $(shell pkg-config --libs libcurl)

No pós-instalação, scripts postinst (.deb) ou %post (.rpm) podem executar ldconfig para atualizar o cache de bibliotecas.

Para compatibilidade de ABI, evite linkedição estática da libc. Prefira linkedição dinâmica e especifique versões mínimas no pacote.

7. Automação com Scripts e CI/CD

Crie um script package.sh que gera os três formatos:

#!/bin/bash
set -e

make clean
make

# .deb
dpkg-deb --build packaging/deb/meu_app_1.0-1_amd64

# .rpm
rpmbuild -ba packaging/rpm/meu-app.spec

# AppImage
cd packaging/appimage
linuxdeploy-x86_64.AppImage --appdir meu-app.AppDir --output appimage
cd ../..

No GitHub Actions, use uma matriz para testar em múltiplas distribuições:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: sudo apt install dpkg-dev rpm lintian rpmlint
      - run: make
      - run: dpkg-deb --build packaging/deb/meu_app_1.0-1_amd64
      - uses: actions/upload-artifact@v4
        with:
          name: meu-app-deb
          path: packaging/deb/*.deb

Para AppImage, use appimagetool disponível via GitHub Releases. Publique os artefatos em GitHub Releases ou em PPAs (Launchpad) e COPR (Fedora).

8. Boas Práticas e Considerações Finais

Teste a instalação e remoção em containers Docker de diferentes distribuições:

docker run --rm -v $(pwd):/pkg ubuntu:22.04 bash -c "dpkg -i /pkg/*.deb && meu_app"

Assine pacotes .deb com GPG:

dpkg-sig --sign builder meu_app_1.0-1_amd64.deb

Para .rpm, use rpm --addsign com chave GPG.

Mantenha um changelog no formato padrão de cada sistema e documente o processo de packaging no README do projeto. Isso facilita contribuições e manutenção futura.

Empacotar uma aplicação C corretamente é sinal de profissionalismo. Com .deb, .rpm e AppImage, você cobre a maioria dos usuários Linux com o mínimo de esforço de manutenção.

Referências