memo

2017-02-09

flatpak を使って python application をパッケージングしてみる試み、その2

前回 は httpie をパッケージングしてみたので、今度は自作のアプリケーションをパッケージングしてみようとしたわけだが。

コケる

このようなビルドスクリプトを用意して:

#!/bin/bash
set -e

REPOSITORY="${HOME}/repos/flatpak/u7fa9.org"

PACKAGE="konnyaku"
URL="git+https://github.com/nakamuray/konnyaku.git"
VERSION="master"
BUILD_FINISH_ARGS=(
  --share=network
  --command=konnyaku
)

_APP="org.u7fa9.${PACKAGE}"
_RUNTIME="org.freedesktop.BasePlatform"
_SDK="org.freedesktop.BaseSdk"
_RUNTIME_VERSION="1.4"

build_location="$(mktemp -d "${_APP}".XXXXXXX)"

flatpak build-init "${build_location}" "${_APP}" "${_SDK}" "${_RUNTIME}" "${_RUNTIME_VERSION}"
flatpak build "${build_location}" python3 -m venv /app
flatpak build --share=network "${build_location}" /app/bin/pip install -U pip
flatpak build --share=network "${build_location}" /app/bin/pip install "${URL}"
flatpak build-finish "${BUILD_FINISH_ARGS[@]}" "${build_location}"
flatpak build-export "${REPOSITORY}" "${build_location}" "${VERSION}"

実行:

$ bash ./build-konnyaku.bash
Collecting pip
... snip ...
Collecting git+https://github.com/nakamuray/konnyaku.git
  Cloning https://github.com/nakamuray/konnyaku.git to /tmp/pip-arprb4sp-build
    /usr/lib/python3.4/distutils/dist.py:244: UserWarning: 'licence' distribution option is deprecated; use 'license'
      warnings.warn(msg)
... snip ...
Collecting lxml (from konnyaku==0.0.1)
  Using cached lxml-3.7.2.tar.gz
    Building lxml version 3.7.2.
    Building without Cython.
    ERROR: b'ERROR: /usr/bin/xslt-config should not be used, use an alternative such as pkg-config\n'
    ** make sure the development packages of libxml2 and libxslt are installed **
    Using build configuration of libxslt --should-not-have-used-/usr/bin/xslt-config
    Traceback (most recent call last):
... snip ...
    ----------------------------------------
    Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-zzfiqm16/lxml

コケた。

どうも xslt-config が意図した値を返してないっぽい。

対応方針

さて、 flatpak の流儀としては、こういう時の正しい対応は「 /app 以下に libxslt をビルド・インストールする」となる (多分)。

が、自分で依存ライブラリを一つずつビルド・インストールしていくとか面倒くさいので、今回は「 alpine ベースでこのアプリケーション専用の runtime/sdk を作成し、使用する」という対応をしてみる。

runtime/sdk の作成

こんな感じのスクリプトを用意して:

#!/bin/bash
set -e

IMAGE_URL="https://nl.alpinelinux.org/alpine/v3.5/releases/x86_64/alpine-minirootfs-3.5.1-x86_64.tar.gz"
REPOSITORY_PATH="${HOME}/repos/flatpak/u7fa9.org"
APP_NAME="org.u7fa9.konnyaku"
RUNTIME_VERSION="3.5-1"
RUNTIME_NAME="${APP_NAME}.Runtime"
SDK_NAME="${APP_NAME}.Sdk"

RUNTIME_PACKAGES=(
  python3
  libxml2
  libxslt
  ca-certificate
)
SDK_PACKAGES=(
  git
  gcc
  musl-dev
  linux-headers
  python3-dev
  libxml2-dev
  libxslt-dev
)


image_filename="${IMAGE_URL##*/}"

if [[ ! -e "${image_filename}" ]]; then
  wget "${IMAGE_URL}"
fi

runtime_dir=$(mktemp -d "${RUNTIME_NAME}-${RUNTIME_VERSION}".XXXXXXXXXX)
sdk_dir=$(mktemp -d "${SDK_NAME}-${RUNTIME_VERSION}".XXXXXXXXXX)

rootfs_dir="${runtime_dir}"/rootfs
mkdir -p "${rootfs_dir}"
tar xpf "${image_filename}" -C "${rootfs_dir}" --exclude="./dev/*"

run_in_rootfs() {
  /usr/libexec/flatpak-bwrap \
    --unshare-ipc \
    --unshare-pid \
    --unshare-uts \
    --setenv PATH "/bin:/usr/bin:/sbin:/usr/sbin" \
    --bind "${rootfs_dir}" / \
    --bind /etc/resolv.conf /etc/resolv.conf \
    --proc /proc --dev /dev --tmpfs /tmp \
    "$@"
}
run_in_rootfs apk update
run_in_rootfs apk add "${RUNTIME_PACKAGES[@]}" || true

# create sdk based on runtime
cp -a "${rootfs_dir}" "${sdk_dir}"/

# as flatpak link (mount) runtime's files as a "/usr", copy all important files to /usr and use it as a "files"
pushd "${rootfs_dir}"
cp -a ./etc ./usr/
cp -a ./bin/* ./usr/bin
cp -a ./sbin/* ./usr/sbin
# to overwrite symlinks which look back to /lib it self, remove it before
yes | cp -a --remove-destination ./lib/* ./usr/lib
popd

cat > "${runtime_dir}"/metadata <<EOF
[Runtime]
name=${RUNTIME_NAME}
runtime=${RUNTIME_NAME}/x86_64/${RUNTIME_VERSION}
sdk=${SDK_NAME}/x86_64/${RUNTIME_VERSION}
EOF

flatpak build-export --runtime --files=rootfs/usr "${REPOSITORY_PATH}" "${runtime_dir}" "${RUNTIME_VERSION}"

# setup sdk, same as runtime
rootfs_dir="${sdk_dir}"/rootfs

run_in_rootfs apk add "${SDK_PACKAGES[@]}" || true

pushd "${rootfs_dir}"
cp -a ./etc ./usr/
cp -a ./bin/* ./usr/bin
cp -a ./sbin/* ./usr/sbin
# to overwrite symlinks which look back to /lib it self, remove it before
yes | cp -a --remove-destination ./lib/* ./usr/lib
popd

cat > "${sdk_dir}"/metadata <<EOF
[Runtime]
name=${SDK_NAME}
runtime=${SDK_NAME}/x86_64/${RUNTIME_VERSION}
EOF

flatpak build-export --runtime --files=rootfs/usr "${REPOSITORY_PATH}" "${sdk_dir}" "${RUNTIME_VERSION}"

実行。(一般ユーザーで apk を実行すると色々動かないので、 sudo で。) (コンソール出力記録し忘れた。)

改めてアプリケーションのパッケージング

runtime/sdk を先程作成したものに切り替えて

diff -r d1d5803c4e99 flatpak/build-konnyaku.bash
--- a/flatpak/build-konnyaku.bash       Thu Feb 09 21:22:13 2017 +0900
+++ b/flatpak/build-konnyaku.bash       Thu Feb 09 21:22:20 2017 +0900
@@ -12,9 +12,9 @@
 )

 _APP="org.u7fa9.${PACKAGE}"
-_RUNTIME="org.freedesktop.BasePlatform"
-_SDK="org.freedesktop.BaseSdk"
-_RUNTIME_VERSION="1.4"
+_RUNTIME="${_APP}.Runtime"
+_SDK="${_APP}.Sdk"
+_RUNTIME_VERSION="3.5-1"

 build_location="$(mktemp -d "${_APP}".XXXXXXX)"

再度パッケージング実行。

$ flatpak --user install u7fa9 org.u7fa9.konnyaku.Sdk
Installing: org.u7fa9.konnyaku.Sdk/x86_64/3.5-1 from u7fa9

Scanning metadata: 290
$ bash build-konnyaku.bash
Collecting pip
  Using cached pip-9.0.1-py2.py3-none-any.whl
Installing collected packages: pip
  Found existing installation: pip 8.1.1
    Uninstalling pip-8.1.1:
      Successfully uninstalled pip-8.1.1
Successfully installed pip-9.0.1
Collecting git+https://github.com/nakamuray/konnyaku.git
  Cloning https://github.com/nakamuray/konnyaku.git to /tmp/pip-orfzyndp-build
Collecting aiohttp (from konnyaku==0.0.1)
  Using cached aiohttp-1.3.0.tar.gz
Collecting click (from konnyaku==0.0.1)
  Using cached click-6.7-py2.py3-none-any.whl
Collecting cssselect (from konnyaku==0.0.1)
  Using cached cssselect-1.0.1-py2.py3-none-any.whl
Collecting lxml (from konnyaku==0.0.1)
  Using cached lxml-3.7.2.tar.gz
Collecting pyxdg (from konnyaku==0.0.1)
Collecting sqlalchemy (from konnyaku==0.0.1)
Collecting chardet (from aiohttp->konnyaku==0.0.1)
Collecting multidict>=2.1.4 (from aiohttp->konnyaku==0.0.1)
  Using cached multidict-2.1.4.tar.gz
Collecting async_timeout>=1.1.0 (from aiohttp->konnyaku==0.0.1)
  Using cached async_timeout-1.1.0-py3-none-any.whl
Collecting yarl>=0.8.1 (from aiohttp->konnyaku==0.0.1)
  Downloading yarl-0.9.2.tar.gz (125kB)
    100% |████████████████████████████████| 133kB 1.0MB/s
Installing collected packages: chardet, multidict, async-timeout, yarl, aiohttp, click, cssselect, lxml, pyxdg, sqlalchemy, konnyaku
  Running setup.py install for multidict ... done
  Running setup.py install for yarl ... done
  Running setup.py install for aiohttp ... done
  Running setup.py install for lxml ... done
  Running setup.py install for konnyaku ... done
Successfully installed aiohttp-1.3.0 async-timeout-1.1.0 chardet-2.3.0 click-6.7 cssselect-1.0.1 konnyaku-0.0.1 lxml-3.7.2 multidict-2.1.4 pyxdg-0.25 sqlalchemy-1.1.5 yarl-0.9.2
Please review the exported files and the metadata
Commit: 82aac3014529a0400b080f7092fff457fb7900c13bc57fa09d76b7ef72778eb7
Metadata Total: 349
Metadata Written: 173
Content Total: 1420
Content Written: 1156
Content Bytes Written: 27112616 (27.1 MB)

今度はうまくいった。

インストールして実行

$ flatpak --user install u7fa9 org.u7fa9.konnyaku
Required runtime for org.u7fa9.konnyaku/x86_64/master (org.u7fa9.konnyaku.Runtime/x86_64/3.5-1) is not installed, searching...
Found in remote u7fa9, do you want to install it? [y/n]: y
Installing: org.u7fa9.konnyaku.Runtime/x86_64/3.5-1 from u7fa9

252 metadata, 3929 content objects fetched; 18779 KiB transferred in 24 seconds

Installing: org.u7fa9.konnyaku/x86_64/master from u7fa9

175 metadata, 1145 content objects fetched; 8243 KiB transferred in 7 seconds

$ flatpak run org.u7fa9.konnyaku
Usage: konnyaku [OPTIONS] COMMAND [ARGS]...

Options:
  --debug / --no-debug
  --help                Show this message and exit.

Commands:
  add      add site
  check    check site updates
  links    list links
  list     list sites
  modify   modify site
  oneshot  add & check but not save
  remove   remove site
  show     show site information

今後の方針について

今後の application/runtime/sdk を更新していく方針についても考える。

  • OS パッケージ側 (alpine 側) に更新があったら、 runtime/sdk のみを作成し直す

  • アプリケーション側を更新したら、 application 側のみを作成し直す

  • アプリケーション側を更新し、新たに OS 側に依存パッケージが増えたら、 runtime/sdk を作り直してバージョンを上げ、 application はその新しいバージョンを使用するように作り直す

という感じかなぁ。