Como resolvo dependências de pacotes para um sistema de destino diferente?

1

(TL; DR: como determino o conjunto mínimo de pacotes a serem instalados, considerando um estado inicial e uma lista de pacotes desejados?)

Estou criando um instalador ISO do Ubuntu para sistemas sem acesso a espelhos de distribuição. Estes são dispositivos embarcados que precisam apenas dos pacotes básicos e um punhado de pacotes compilados localmente, além de todas as suas dependências . Por exemplo, estamos usando o Qt / Xcb, então os vários pacotes libqt5* precisam ser inseridos.

No momento, estou usando uma abordagem de força bruta para determinar o que colocar no diretório pool / extras do instalador. Primeiro, insumo com os pacotes mencionados em d-i pkgsel/include , responda ao debconf e, em seguida, procure as dependências recursivas com o seguinte comando bash :

# 1. Extract the dependencies from packages/*, removing version
#    conditions and changing separator to newline.
# 2. Find the recursive dependencies of all these packages using
#    package database, extracting Depends and PreDepends.
# 3. For each package we require:
#    a. if we find it in our cache, just copy it into the ISO (using
#       hard link for speed and space)
#    b. else, if we find it already in the ISO, do nothing
#    c. else, pass it through to step 4.
# 4. Search for matching packages in repositories that are not virtual
#    or foreign-architecture.
# 5. Download matching package files to the ISO.
(for i in "$TOP/packages"/*.deb; do dpkg-deb -f $i depends; done) | sed 's/([^()]*)//g;s/[|,]/\n/g'                                     \
  | xargs -r apt-cache --recurse --important depends | sed -re 's/^  (Pre)?Depends://;/[<|:]/d;s/^  *//;' | sort -u                     \
  | ( while read p;                                                                                                                     \
    do test $(find "$TOP/extras" -name "${p}_*.deb" -exec mv '{}' "$REMASTER_HOME"/remaster-iso/pool/extras \; -printf "true" -quit)    \
      || test $(find "$REMASTER_HOME"/remaster-iso/pool/{main,extras} -name "${p}_*.deb" -printf "true" -quit)                          \
      || echo $p;                                                                                                                       \
    done; )                                                                                                                             \
  | uniq | sed -r 's/[^ ]+/!~rforeign!~v^&$/' | xargs -r aptitude -q2 -F '%p' search                                                    \
  | (cd "$REMASTER_HOME"/remaster-iso/pool/extras; xargs -r apt-get download)

Aqui, $TOP/packages é o diretório do pacote de sementes, $TOP/extras é um cache de pacotes baixados anteriormente e $REMASTER_HOME"/remaster-iso é a raiz do ISO do instalador descompactado.

Embora o trabalho acima funcione, ele tende a buscar muito mais do que realmente precisamos, porque apt-cache depends informa todas as dependências alternativas de cada pacote. Por exemplo, grub-pc depende de debconf ; cdebconf fornece debconf , então ambos debconf e cdebconf são incluídos, juntamente com suas dependências. Além disso, xserver-xorg depende de xserver-xorg-video-all | xorg-driver-video , então meu script puxa todos os drivers de vídeo - mas eu já declarei uma dependência em xserver-xorg-video-fbdev , então gostaria de satisfazer os requisitos por conta própria.

O que eu gostaria é o equivalente de executar aptitude --download-only install ${MY_PACKAGES} no sistema de destino. Mas, é claro, executar isso no host de compilação perderá a maioria dos pacotes porque eles já estão instalados lá.

Atualmente, estou vendo a biblioteca python-apt , mas não consigo encontrar qualquer exemplo de inicialização para um sistema de destino em vez do host onde está sendo executado. Alguém fez algo semelhante, do qual eu possa copiar?

Alternativamente, existem outras abordagens que podem funcionar? Estou pensando em criar um chroot primitivo e mínimo e executar o comando aptitude nisso, mas estou preocupado que isso seja um fardo de manutenção e difícil para os outros configurarem em suas próprias máquinas.

Espero que a solução possa ser usada (com um novo estado inicial) para encontrar as dependências necessárias quando algum pacote precisar ser atualizado, já que precisarei escrever um conjunto consistente para mídia removível para o sistema de destino ( ainda sem acesso a espelhos de distribuição).

    
por Toby Speight 19.06.2015 / 13:03

2 respostas

1

É um script bash em vez de python-apt , mas eu encontrei dpkg-offline que contém lógica semelhante. Eu olhei como funciona e consegui criar um script próprio para fazer o download necessário - de forma reproduzível e como um usuário comum não-root:

#!/bin/bash
set -e

die() {
    exec >&2
    printf '%s\n' "$@"
    exit 1
}

test "$UID" != "0" || die \
    "Do not run '$0' as root, because it will break your packages."

test -n "$1" || die \
    "Usage: $0 <package-dir> [package] ..." \
    "  package may be followed by _ - + &m etc; like 'aptitude install'" \
    "  local packages must be listed in package-dir/Packages.gz" \
    "  downloaded packages are saved to package-dir/ubuntu"

PACKAGE_DIR="$1"; shift

BASE_DIR=$(mktemp -d)
trap 'rm -r "$BASE_DIR"' EXIT

(
    cd $BASE_DIR

    # Skeleton directory structure
    mkdir -p \
        etc/apt/preferences.d \
        etc/apt/sources.list.d \
        var/cache/apt/archives \
        var/lib/apt/lists \
        var/lib/aptitude \
        var/lib/dpkg \
        var/log

    # Use GPG keys from host, and create empty dpkg status
    cp /etc/apt/trusted.gpg etc/apt
    touch var/lib/dpkg/status

    cat >etc/apt/sources.list <<-EOF
    # Locally-built packages
    deb file://$PACKAGE_DIR /

    # Upstream distribution to pillage
    deb http://gb.archive.ubuntu.com/ubuntu/ trusty main restricted universe
    EOF

    # Don't let user's ~/.aptitude/* influence anything
    export HOME="$BASE_DIR"
    APT_OPTIONS=(-q2 \
        -o "Apt::Architecture=amd64" \
        -o "Apt::Install-Recommends=0" \
        -o "Dir::Etc=$BASE_DIR/etc/apt" \
        -o "Dir::State=$BASE_DIR/var/cache/apt" \
        -o "Dir::Cache=$BASE_DIR/var/cache/apt" \
        -o "Dir::Cache::Archives=$BASE_DIR/var/cache/apt/archives" \
        -o "Dir::Log=$BASE_DIR/var/log" \
        -o "Dir::State::Lists=$BASE_DIR/var/lib/apt/lists" \
        -o "Dir::State::status=$BASE_DIR/var/lib/dpkg/status" \
        -o "DPkg::Options::--admindir=$BASE_DIR/var/lib/dpkg" \
        )

    APTITUDE="aptitude ${APT_OPTIONS[@]}"

    $APTITUDE update
    $APTITUDE --download-only -y --allow-untrusted install "$@"
)

# We now have all required package files download into cache; put them
# into output directory.
ln -t "$PACKAGE_DIR/ubuntu" -vf var/cache/apt/archives/*.deb

# Create updated package index
( cd "$PACKAGE_DIR" && dpkg-scanpackages -m . | gzip -c >Packages.gz )

Eu provavelmente poderia usar isso para criar um programa em Python para fazer o mesmo, mas o script de shell provavelmente será adequado para os meus propósitos.

    
por 24.06.2015 / 12:12
3

debootstrap um novo sistema limpo em um diretório, faça o que você precisa dentro desse diretório usando chroot , e depois pegue tudo o que você encontrar dentro de chrootdir/var/cache/apt/archives/ .

Editar após a segunda resposta: Acabei de verificar o manual e parece que também é possível usar debootstrap para fazer a maioria, senão o que você deseja: pode ser executado em fakeroot e tem --download-only e --print-debs opções, bem como --include para adicionar pacotes adicionais à mistura e resolver suas dependências também.

Não tinha certeza sobre o uso de dois conjuntos diferentes de fontes antes de ler a fonte - parece apoiar isso também:)

Por exemplo:

% fakeroot /usr/sbin/debootstrap --variant=minbase --download-only --include=dnsutils wheezy /tmp/foo "http://ftp.us.debian.org/debian file:///local_packages_dir/"
[...]
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
I: Found additional required dependencies: [...]
I: Found additional base dependencies: [...]
[...]

Existe um pouco de sobrecarga:

% du -sh /tmp/foo/var/cache/apt/archives/
40M     /tmp/foo/var/cache/apt/archives/
% du -sh /tmp/foo/
68M     /tmp/foo/

O download real pode ser otimizado usando algo como apt-mirror, para que arquivos idênticos não sejam baixados de um local remoto a cada vez.

    
por 22.06.2015 / 22:58