Skip to content
This repository has been archived by the owner on Aug 30, 2020. It is now read-only.

Commit

Permalink
Added debian package
Browse files Browse the repository at this point in the history
  • Loading branch information
synesthesiam committed Dec 7, 2019
1 parent dac5fbe commit 5834c4f
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 1 deletion.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@ etc/test/pt/original/
etc/test/vi/2*

# Misc
workbench.xmi
workbench.xmi

# Debian
site/
debian/rhasspy-server*
8 changes: 8 additions & 0 deletions debian/DEBIAN/control
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Package: rhasspy-server
Version: 2.4.8
Section: utils
Priority: optional
Depends: sox,alsa-utils,espeak,libstdc++6,jq,xz-utils,unzip,curl,sphinxbase-utils,sphinxtrain,flite,libatlas-base-dev,gfortran
Architecture: ${architecture}
Maintainer: Michael Hansen
Description: Offline voice assistant
11 changes: 11 additions & 0 deletions debian/bin/rhasspy-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

if [[ -z "${RHASSPY_BASE_DIR}" ]]; then
export RHASSPY_BASE_DIR="/usr/lib/rhasspy"
fi

if [[ -z "${KALDI_PREFIX}" ]]; then
export KALDI_PREFIX="${RHASSPY_BASE_DIR}"
fi

cd "${RHASSPY_BASE_DIR}" && rhasspy/rhasspy "$@"
177 changes: 177 additions & 0 deletions debianize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#!/usr/bin/env bash
rhasspy_version="2.4.8"

this_dir="$( cd "$( dirname "$0" )" && pwd )"

# -----------------------------------------------------------------------------
# Command-line Arguments
# -----------------------------------------------------------------------------

. "${this_dir}/etc/shflags"

DEFINE_string 'architecture' '' 'Debian architecture'
DEFINE_string 'version' "${rhasspy_version}" 'Package version'
DEFINE_boolean 'package' true 'Create debian package (.deb)'

FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"

# -----------------------------------------------------------------------------
# Settings
# -----------------------------------------------------------------------------

export architecture="${FLAGS_architecture}"
version="${FLAGS_version}"
debian_dir="${this_dir}/debian"

set -e

if [[ -z "${architecture}" ]]; then
# Guess architecture
architecture="$(dpkg-architecture | grep 'DEB_BUILD_ARCH=' | sed 's/^[^=]\+=//')"
fi

# -----------------------------------------------------------------------------
# Activate virtual environment
# -----------------------------------------------------------------------------

venv="${this_dir}/.venv"

if [[ ! -d "${venv}" ]]; then
echo "Missing virtual environment at ${venv}"
echo "Did you run create-venv.sh?"
exit 1
fi

cd "${this_dir}"
source "${venv}/bin/activate"

if [[ -z "$(which pyinstaller)" ]]; then
echo "Missing PyInstaller"
exit 1
fi

# -----------------------------------------------------------------------------
# Run PyInstaller
# -----------------------------------------------------------------------------

echo "Running PyInstaller"
package_name="rhasspy-server_${version}_${architecture}"
package_dir="${debian_dir}/${package_name}"
output_dir="${package_dir}/usr/lib/rhasspy"
share_dir="${package_dir}/usr/share/rhasspy"

pyinstaller\
-y \
--workpath "pyinstaller/build" \
--distpath "${output_dir}" \
"${this_dir}/rhasspy.spec"

# Remove all symbols (Liantian warning)
strip --strip-all "${output_dir}/rhasspy"/*.so* || true

# Remove executable bit from shared libs (Lintian warning)
chmod -x "${output_dir}/rhasspy"/*.so* || true

# -----------------------------------------------------------------------------
# Copy Rhasspy
# -----------------------------------------------------------------------------

# Profiles
mkdir -p "${output_dir}/profiles"
rsync -av \
--delete \
--exclude 'acoustic_model' \
--exclude 'download' \
--exclude 'flair' \
--exclude 'base_dictionary.txt' \
--exclude 'base_language_model.txt' \
--exclude 'g2p.fst' \
--exclude 'HCLG.fst' \
--exclude 'final.mdl' \
--exclude '*.umdl' \
"${this_dir}/profiles/" \
"${output_dir}/profiles/"

# Sounds
mkdir -p "${output_dir}/etc/wav"
rsync -av \
--delete \
"${this_dir}/etc/wav/" \
"${output_dir}/etc/wav/"

# Web
mkdir -p "${output_dir}/dist"
rsync -av \
--delete \
"${this_dir}/dist/" \
"${output_dir}/dist/"

# Documentation
mkdocs build
mkdir -p "${share_dir}/docs"
rsync -av \
--delete \
"${this_dir}/site/" \
"${share_dir}/docs/"

# Source code
mkdir -p "${share_dir}/src"
rsync -av \
--delete \
--exclude '.mypy_cache' \
--exclude '__pycache__' \
"${this_dir}/rhasspy/" \
"${share_dir}/src/rhasspy/"

cp "${this_dir}/app.py" "${share_dir}/src/"

# -----------------------------------------------------------------------------
# Copy Kaldi
# -----------------------------------------------------------------------------

echo "Copying Kaldi"
kaldi_src="${venv}/kaldi"
if [[ ! -d "${kaldi_src}" ]]; then
echo "Missing Kaldi at ${kaldi_src}"
exit 1
fi

kaldi_dest="${output_dir}/kaldi"
mkdir -p "${kaldi_dest}"
rsync -av --delete "${kaldi_src}/" "${kaldi_dest}/"

# Avoid link recursion
rm -f "${kaldi_dest}/egs/wsj/s5/utils/utils"

# Turn duplicate .so files into symbolic links
function fix_library_links() {
lib_dir="$1"

for lib in "${lib_dir}"/*.so; do
lib_base="$(basename ${lib})"
for lib_link in "${lib_dir}/${lib_base}".*; do
rm -f "${lib_link}"
ln -s "${lib_base}" "${lib_link}"
done
done
}

fix_library_links "${kaldi_dest}/tools/openfst/lib"

# -----------------------------------------------------------------------------
# Create Debian package
# -----------------------------------------------------------------------------

echo "Creating Debian package"
mkdir -p "${package_dir}/DEBIAN"
cat "${debian_dir}/DEBIAN/control" | \
envsubst > "${package_dir}/DEBIAN/control"

mkdir -p "${package_dir}/usr/bin"
cp "${debian_dir}/bin/rhasspy-server" "${package_dir}/usr/bin/"

if [[ "${FLAGS_package}" -eq "${FLAGS_TRUE}" ]]; then
# Actually build the package
cd 'debian' && fakeroot dpkg --build "${package_name}"
fi
84 changes: 84 additions & 0 deletions rhasspy.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# -*- mode: python ; coding: utf-8 -*-
import os
import platform
from pathlib import Path

from PyInstaller.utils.hooks import copy_metadata

block_cipher = None

# Use either virtual environment or lib/bin dirs from environment variables
venv = Path.cwd() / ".venv"
bin_dir = Path(os.environ.get("spec_bin_dir", venv / "bin"))
lib_dir = Path(os.environ.get("spec_lib_dir", venv / "lib"))

site_dir = os.environ.get("spec_site_dir", None)
if site_dir is None:
venv_lib = venv / "lib"
for dir_path in venv_lib.glob("python*"):
if dir_path.is_dir() and (dir_path / "site-packages").exists():
site_dir = dir_path / "site-packages"
break

assert site_dir is not None, "Missing site-packages directory"
site_dir = Path(site_dir)

# Need to specially handle these snowflakes
pywrapfst_path = list(site_dir.glob("pywrapfst.*.so"))[0]
webrtcvad_path = list(site_dir.glob("_webrtcvad.*.so"))[0]

a = Analysis(
["app.py"],
pathex=["."],
binaries=[
(pywrapfst_path, "."),
(webrtcvad_path, "."),
(lib_dir / "libfstfarscript.so.13", "."),
(lib_dir / "libfstscript.so.13", "."),
(lib_dir / "libfstfar.so.13", "."),
(lib_dir / "libfst.so.13", "."),
(lib_dir / "libngram.so.134", "."),
(bin_dir / "ngramread", "."),
(bin_dir / "ngramcount", "."),
(bin_dir / "ngrammake", "."),
(bin_dir / "ngrammerge", "."),
(bin_dir / "ngramprint", "."),
(bin_dir / "ngramsymbols", "."),
(bin_dir / "ngramperplexity", "."),
(bin_dir / "farcompilestrings", "."),
(bin_dir / "phonetisaurus-apply", "."),
(bin_dir / "phonetisaurus-g2pfst", "."),
],
datas=copy_metadata("webrtcvad"),
hiddenimports=["doit", "dbm.gnu", "networkx", "numbers"],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="rhasspy",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name="rhasspy",
)
9 changes: 9 additions & 0 deletions rhasspy/train/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,15 @@ def runtime_error(self, msg):

DOIT_CONFIG = {"action_string_formatting": "old", "reporter": MyReporter}

# Monkey patch inspect to make doit work inside Pyinstaller.
# It grabs the line numbers of functions probably for debugging reasons, but
# PyInstaller doesn't seem to keep that information around.
#
# This better thing to do would be to create a custom TaskLoader.
import inspect

inspect.getsourcelines = lambda obj: [0, 0]

# Run doit main
result = DoitMain(ModuleTaskLoader(locals())).run(sys.argv[1:])
return (result, errors)
Expand Down

0 comments on commit 5834c4f

Please sign in to comment.