#!/usr/bin/env bash this_dir="$( cd "$( dirname "$0" )" && pwd )" CPU_ARCH="$(uname --m)" # ----------------------------------------------------------------------------- # Command-line Arguments # ----------------------------------------------------------------------------- . "${this_dir}/etc/shflags" DEFINE_string 'venv' "${this_dir}/.venv" 'Path to create virtual environment' DEFINE_string 'download-dir' "${this_dir}/download" 'Directory to cache downloaded files' DEFINE_string 'build-dir' "${this_dir}/build_${CPU_ARCH}" 'Directory to build dependencies in' DEFINE_boolean 'system' true 'Install system dependencies' DEFINE_boolean 'flair' false 'Install flair' DEFINE_boolean 'precise' false 'Install Mycroft Precise' DEFINE_boolean 'adapt' false 'Install Mycroft Adapt' DEFINE_boolean 'google' false 'Install Google Text to Speech' DEFINE_boolean 'kaldi' false 'Install Kaldi' DEFINE_boolean 'offline' false "Don't download anything" DEFINE_boolean 'web' true "Build Vue web interface with yarn" DEFINE_boolean 'sudo' true "Use sudo for apt" DEFINE_integer 'make-threads' 4 'Number of threads to use with make' 'j' DEFINE_string 'python' 'python3' 'Path to Python executable' FLAGS "$@" || exit $? eval set -- "${FLAGS_ARGV}" # ----------------------------------------------------------------------------- # Default Settings # ----------------------------------------------------------------------------- set -e python="${FLAGS_python}" venv="${FLAGS_venv}" download_dir="${FLAGS_download_dir}" mkdir -p "${download_dir}" echo "Download directory: ${download_dir}" build_dir="${FLAGS_build_dir}" mkdir -p "${build_dir}" echo "Build directory: ${build_dir}" if [[ "${FLAGS_system}" -eq "${FLAGS_FALSE}" ]]; then no_system='true' fi if [[ "${FLAGS_flair}" -eq "${FLAGS_FALSE}" ]]; then no_flair='true' fi if [[ "${FLAGS_precise}" -eq "${FLAGS_FALSE}" ]]; then no_precise='true' fi if [[ "${FLAGS_adapt}" -eq "${FLAGS_FALSE}" ]]; then no_adapt='true' fi if [[ "${FLAGS_kaldi}" -eq "${FLAGS_FALSE}" ]]; then no_kaldi='true' fi if [[ "${FLAGS_google}" -eq "${FLAGS_FALSE}" ]]; then no_google='true' fi if [[ "${FLAGS_offline}" -eq "${FLAGS_TRUE}" ]]; then offline='true' fi if [[ "${FLAGS_web}" -eq "${FLAGS_FALSE}" ]]; then no_web='true' fi if [[ "${FLAGS_sudo}" -eq "${FLAGS_TRUE}" ]]; then function run_sudo { sudo "$@" } else function run_sudo { "$@" } fi make_threads="${FLAGS_make_threads}" # ----------------------------------------------------------------------------- # Create a temporary directory for building stuff temp_dir="$(mktemp -d)" function cleanup { rm -rf "${temp_dir}" } trap cleanup EXIT function maybe_download { if [[ ! -s "$2" ]]; then if [[ -n "${offline}" ]]; then echo "Need to download $1 but offline." exit 1 fi mkdir -p "$(dirname "$2")" curl -sSfL -o "$2" "$1" || { echo "Can't download $1"; exit 1; } echo "$1 => $2" fi } # ----------------------------------------------------------------------------- echo "Checking required programs" if [[ -z "${no_web}" ]]; then if [[ ! -n "$(command -v yarn)" ]]; then echo "Please install yarn to continue (https://yarnpkg.com)" exit 1 fi fi # ----------------------------------------------------------------------------- if [[ -z "${no_system}" ]]; then echo "Installing system dependencies" run_sudo apt-get update run_sudo apt-get install --no-install-recommends \ python3 python3-pip python3-venv python3-dev \ python \ build-essential autoconf autoconf-archive libtool automake bison \ sox espeak flite swig portaudio19-dev \ libatlas-base-dev \ gfortran \ sphinxbase-utils sphinxtrain pocketsphinx \ jq checkinstall unzip xz-utils \ curl \ lame fi # ----------------------------------------------------------------------------- echo "Downloading dependencies" # Python-Pocketsphinx pocketsphinx_file="${download_dir}/pocketsphinx-python.tar.gz" if [[ ! -s "${pocketsphinx_file}" ]]; then pocketsphinx_url='https://github.com/synesthesiam/pocketsphinx-python/releases/download/v1.0/pocketsphinx-python.tar.gz' echo "Downloading pocketsphinx (${pocketsphinx_url})" maybe_download "${pocketsphinx_url}" "${pocketsphinx_file}" fi # OpenFST openfst_dir="${build_dir}/openfst-1.6.9" if [[ ! -d "${openfst_dir}/build" ]]; then openfst_file="${download_dir}/openfst-1.6.9.tar.gz" if [[ ! -s "${openfst_file}" ]]; then openfst_url='http://openfst.org/twiki/pub/FST/FstDownload/openfst-1.6.9.tar.gz' echo "Downloading openfst (${openfst_url})" maybe_download "${openfst_url}" "${openfst_file}" fi fi # Opengrm opengrm_dir="${build_dir}/opengrm-ngram-1.3.4" if [[ ! -d "${opengrm_dir}/build" ]]; then opengrm_file="${download_dir}/opengrm-ngram-1.3.4.tar.gz" if [[ ! -s "${opengrm_file}" ]]; then opengrm_url='http://www.opengrm.org/twiki/pub/GRM/NGramDownload/opengrm-ngram-1.3.4.tar.gz' echo "Downloading opengrm (${opengrm_url})" maybe_download "${opengrm_url}" "${opengrm_file}" fi fi # Phonetisaurus phonetisaurus_dir="${build_dir}/phonetisaurus" if [[ ! -d "${phonetisaurus_dir}/build" ]]; then phonetisaurus_file="${download_dir}/phonetisaurus-2019.tar.gz" if [[ ! -s "${phonetisaurus_file}" ]]; then phonetisaurus_url='https://github.com/synesthesiam/docker-phonetisaurus/raw/master/download/phonetisaurus-2019.tar.gz' echo "Downloading phonetisaurus (${phonetisaurus_url})" maybe_download "${phonetisaurus_url}" "${phonetisaurus_file}" fi fi # Kaldi kaldi_dir="${this_dir}/opt/kaldi" if [[ -z "${no_kaldi}" && ! -d "${kaldi_dir}" ]]; then install libatlas-base-dev libatlas3-base gfortran run_sudo ldconfig kaldi_file="${download_dir}/kaldi-2019.tar.gz" if [[ ! -s "${kaldi_file}" ]]; then kaldi_url='https://github.com/kaldi-asr/kaldi/archive/master.tar.gz' echo "Downloading kaldi (${kaldi_url})" maybe_download "${kaldi_url}" "${kaldi_file}" fi fi # ----------------------------------------------------------------------------- # Re-create virtual environment echo "Creating virtual environment" rm -rf "${venv}" "${python}" -m venv "${venv}" source "${venv}/bin/activate" pip3 install wheel setuptools # ----------------------------------------------------------------------------- # openfst # http://www.openfst.org # # Required to build languag models and do intent recognition. # ----------------------------------------------------------------------------- if [[ ! -d "${openfst_dir}/build" ]]; then echo "Building openfst (${openfst_file})" tar -C "${build_dir}" -xf "${openfst_file}" && \ cd "${openfst_dir}" && \ ./configure "--prefix=${openfst_dir}/build" \ --enable-far \ --disable-static \ --enable-shared \ --enable-ngram-fsts && \ make -j "${make_threads}" && \ make install fi # Copy build artifacts into virtual environment cp -R "${openfst_dir}"/build/include/* "${venv}/include/" cp -R "${openfst_dir}"/build/lib/*.so* "${venv}/lib/" cp -R "${openfst_dir}"/build/bin/* "${venv}/bin/" # ----------------------------------------------------------------------------- # opengrm # http://www.opengrm.org/twiki/bin/view/GRM/NGramLibrary # # Required to build language models. # ----------------------------------------------------------------------------- # opengrm if [[ ! -d "${opengrm_dir}/build" ]]; then echo "Building opengrm (${opengrm_file})" export CXXFLAGS="-I${venv}/include" export LDFLAGS="-L${venv}/lib" tar -C "${build_dir}" -xf "${opengrm_file}" && \ cd "${opengrm_dir}" && \ ./configure "--prefix=${opengrm_dir}/build" && \ make -j "${make_threads}" && \ make install fi # Copy build artifacts into virtual environment cp -R "${opengrm_dir}"/build/bin/* "${venv}/bin/" cp -R "${opengrm_dir}"/build/include/* "${venv}/include/" cp -R "${opengrm_dir}"/build/lib/*.so* "${venv}/lib/" # ----------------------------------------------------------------------------- # phonetisaurus # https://github.com/AdolfVonKleist/Phonetisaurus # # Required to guess word pronunciations. # ----------------------------------------------------------------------------- if [[ ! -d "${phonetisaurus_dir}/build" ]]; then echo "Installing phonetisaurus (${phonetisaurus_file})" tar -C "${build_dir}" -xf "${phonetisaurus_file}" && \ cd "${phonetisaurus_dir}" && \ ./configure "--prefix=${phonetisaurus_dir}/build" \ --with-openfst-includes="${venv}/include" \ --with-openfst-libs="${venv}/lib" && \ make -j "${make_threads}" && \ make install fi # Copy build artifacts into virtual environment cp -R "${phonetisaurus_dir}"/build/bin/* "${venv}/bin/" # ----------------------------------------------------------------------------- # kaldi # https://kaldi-asr.org # # Required for speech recognition with Kaldi-based profiles. # ----------------------------------------------------------------------------- if [[ -z "${no_kaldi}" && ! -f "${kaldi_dir}/src/online2bin/online2-wav-nnet3-latgen-faster" ]]; then echo "Installing kaldi (${kaldi_file})" # armhf if [[ -f '/usr/lib/arm-linux-gnueabihf/libatlas.so' ]]; then # Kaldi install doesn't check here, despite in being in ldconfig export ATLASLIBDIR='/usr/lib/arm-linux-gnueabihf' fi # aarch64 if [[ -f '/usr/lib/aarch64-linux-gnu/libatlas.so' ]]; then # Kaldi install doesn't check here, despite in being in ldconfig export ATLASLIBDIR='/usr/lib/aarch64-linux-gnu' fi tar -C "${build_dir}" -xf "${kaldi_file}" && \ cp "${this_dir}/etc/linux_atlas_aarch64.mk" "${kaldi_dir}/src/makefiles/" && \ cd "${kaldi_dir}/tools" && \ make -j "${make_threads}" && \ cd "${kaldi_dir}/src" && \ ./configure --shared --mathlib=ATLAS --use-cuda=no && \ make depend -j "${make_threads}" && \ make -j "${make_threads}" fi # ----------------------------------------------------------------------------- # Python requirements # ----------------------------------------------------------------------------- echo "Installing Python requirements" "${python}" -m pip install requests # pytorch is not available on ARM case "${CPU_ARCH}" in armv7l|arm64v8) no_flair="true" ;; esac requirements_file="${temp_dir}/requirements.txt" temp_requirements_file="${temp_dir}/temp_requirements.txt" cp "${this_dir}/requirements.txt" "${requirements_file}" # Exclude requirements if [[ -n "${no_flair}" ]]; then echo "Excluding flair from virtual environment" sed '/^flair/d' "${requirements_file}" > "${temp_requirements_file}" && mv "${temp_requirements_file}" "${requirements_file}" fi if [[ -n "${no_precise}" ]]; then echo "Excluding Mycroft Precise from virtual environment" sed '/^precise-runner/d' "${requirements_file}" > "${temp_requirements_file}" && mv "${temp_requirements_file}" "${requirements_file}" fi if [[ -n "${no_adapt}" ]]; then echo "Excluding Mycroft Adapt from virtual environment" sed '/^adapt-parser/d' "${requirements_file}" > "${temp_requirements_file}" && mv "${temp_requirements_file}" "${requirements_file}" fi if [[ -n "${no_google}" ]]; then echo "Excluding Google Text to Speech from virtual environment" sed '/^google-cloud-texttospeech/d' "${requirements_file}" > "${temp_requirements_file}" && mv "${temp_requirements_file}" "${requirements_file}" fi # Install everything except openfst first sed '/^openfst/d' "${requirements_file}" > "${temp_requirements_file}" && mv "${temp_requirements_file}" "${requirements_file}" "${python}" -m pip install -r "${requirements_file}" echo "Installing Python openfst wrapper" "${python}" -m pip install \ --global-option=build_ext \ --global-option="-I${venv}/include" \ --global-option="-L${venv}/lib" \ -r <(grep '^openfst' "${this_dir}/requirements.txt") # ----------------------------------------------------------------------------- # Pocketsphinx for Python # https://github.com/cmusphinx/pocketsphinx # # Speech to text for most profiles. # ----------------------------------------------------------------------------- pocketsphinx_file="${download_dir}/pocketsphinx-python.tar.gz" echo "Installing Python pocketsphinx (${pocketsphinx_file})" "${python}" -m pip install "${pocketsphinx_file}" # ----------------------------------------------------------------------------- # Snowboy # https://snowboy.kitt.ai # # Wake word system # ----------------------------------------------------------------------------- case "${CPU_ARCH}" in x86_64|armv7l) snowboy_file="${download_dir}/snowboy-1.3.0.tar.gz" echo "Installing snowboy (${snowboy_file})" "${python}" -m pip install "${snowboy_file}" ;; *) echo "Not installing snowboy (${CPU_ARCH} not supported)" esac # ----------------------------------------------------------------------------- if [[ -z "${no_web}" ]]; then echo "Building web interface" cd "${this_dir}" && yarn install && yarn build fi