#! /usr/bin/env bash set -e show-help() { echo 'usage: evm ' echo '' echo 'Commands:' echo ' help - Show this message' echo ' info - Show evm configuration details.' echo ' list - List installed versions.' echo ' use - Activate specified version.' echo ' install - Download, compile and install Emacs.' echo ' build-deps - Install build dependencies for Emacs.' } main() { local command="$1" shift 1 check-os if [ -z "$command" ]; then show-help 1>&2 exit 1 fi case "$command" in build-deps | deps) install-deps "$@" ;; info) info ;; install) install "$@" ;; use | switch | activate) activate-version "$@" ;; list | ls | versions) versions "$@" ;; *) show-help ;; esac } check-os() { if [[ "$OSTYPE" != "linux"* ]]; then echo "Only Linux-based operating systems are supported" 1>&2 exit 2 fi } check-write-op() { if conf-root-mode && [ "$UID" != "0" ]; then echo "ERROR: evm must be run as root when" \ "EVM_MODE is not empty or \"user\"." 1>&2 exit 1 fi } trim() { local string="$*" string="${string#"${string%%[![:space:]]*}"}" string="${string%"${string##*[![:space:]]}"}" echo -n "$string" } conf-emacs-src-repo() { echo "${EVM_EMACS_SRC_REPO:-emacs-mirror/emacs}" } conf-mode() { if [ "${EVM_MODE:-user}" == "user" ]; then echo "user" else echo "root" fi } conf-root-mode() { if [ "$(conf-mode)" != "root" ]; then return 1 fi } default-root-dir() { if conf-root-mode; then echo "/opt/evm" else echo "$HOME/.evm" fi } default-shims-dir() { if conf-root-mode; then echo "$(conf-root-dir)/bin" else echo "$(conf-root-dir)/shims" fi } conf-root-dir() { echo "${EVM_ROOT_DIR:-$(default-root-dir)}" } conf-shims-dir() { echo "${EVM_VERSIONS_DIR:-$(default-shims-dir)}" } conf-versions-dir() { echo "${EVM_VERSIONS_DIR:-$(conf-root-dir)/versions}" } conf-sources-dir() { echo "${EVM_SOURCES_DIR:-$(conf-root-dir)/sources}" } conf-native-full-aot() { echo "${EVM_NATIVE_FULL_AOT:-1}" } info() { echo "---" echo "# evm information" echo "evm:" echo " mode: $(conf-mode)" echo " paths:" echo " root: $(conf-root-dir)" echo " shims: $(conf-shims-dir)" echo " versions: $(conf-versions-dir)" echo " sources: $(conf-sources-dir)" echo " building:" echo " native_full_aot: $(conf-native-full-aot)" } install() { local ref="$1" local version local workdir local dir local file local prefix check-write-op if [ -z "$ref" ]; then echo "ERROR: git ref to install must be specified" 1>&2 exit 1 fi version="${ref//\//-}_$(date +"%Y-%m-%d")" workdir="$(conf-sources-dir)" prefix="$(conf-versions-dir)/${version}" file="emacs-${version}.tar.gz" dir="emacs-${version}" if [ -d "$prefix" ]; then echo "target $prefix directory exists, remove to force-install" 1>&2 exit 1 fi mkdir -p "$workdir" cd "$workdir" if [ ! -d "$dir" ]; then if [ ! -f "$file" ]; then curl -L "https://github.com/$(conf-emacs-src-repo)/tarball/${ref}" > \ "$file" else echo "source tarball ${workdir}/${file} exists, reusing..." 1>&2 fi mkdir "$dir" tar -C "$dir" --strip-components 1 -zxf "$file" else echo "source directory ${workdir}/${dir} exists, reusing..." 1>&2 fi cd "$dir" if [ -f "autogen/copy_autogen" ]; then ./autogen/copy_autogen fi if [ -f "autogen.sh" ]; then ./autogen.sh fi export CFLAGS="-O2" export CC=/usr/bin/gcc-10 CXX=/usr/bin/gcc-10 ./configure \ --prefix="$prefix" \ --with-json \ --with-modules \ --with-native-compilation \ --with-pgtk \ --with-xinput2 \ --with-xwidgets make -j "$(nproc)" NATIVE_FULL_AOT="$(conf-native-full-aot)" make install } install-deps() { sudo apt-get update sudo apt-get install -y \ build-essential \ g++-10 \ gcc-10 \ libcairo2-dev \ libgccjit-10-dev \ libgccjit0 \ libgconf2-dev \ libgif-dev \ libgnutls28-dev \ libgpm-dev \ libgtk-3-dev \ libharfbuzz-dev \ libjansson-dev \ libjansson4 \ libjpeg-dev \ libjpeg-dev \ libm17n-dev \ libmagick++-dev \ libmagickcore-dev \ libmailutils-dev \ libncurses-dev \ libotf-dev \ libpng-dev \ librsvg2-dev \ libtiff-dev \ libwebp-dev \ libx11-dev \ libxft-dev \ libxml2-dev \ libxpm-dev \ texinfo \ xaw3dg-dev } current-version() { trim "$(cat "$(conf-root-dir)/current" 2> /dev/null)" } activate-version() { local version="$1" local dir local target local base check-write-op if [ -z "$version" ]; then echo "ERROR: version be specified" 1>&2 exit 1 fi dir="$(conf-versions-dir)/${version}" if [ ! -d "$dir" ]; then echo "ERROR: version ${version} is not installed" 1>&2 exit 1 fi mkdir -p "$(conf-shims-dir)" # Remove old symlinks. find "$(conf-shims-dir)" -maxdepth 1 -mindepth 1 -type l -delete # Create new symlinks. find "${dir}/bin" -type f -perm -a=x -exec ln -s "{}" "$(conf-shims-dir)/" \; # Create symlink to `emacs` in case it is a symlink itself. if [ -L "${dir}/bin/emacs" ]; then ln -s "${dir}/bin/emacs" "$(conf-shims-dir)/" fi echo "$(trim "$version")" > "$(conf-root-dir)/current" } versions() { local version_dirs local version_dir local version version_dirs="$(find "$(conf-versions-dir)" -maxdepth 1 -mindepth 1 -type d \ -exec basename "{}" \;)" while read -r version_dir; do version="$(basename "$version_dir")" if [ "$(trim "$version")" == "$(trim "$(current-version)")" ]; then echo "* ${version}" else echo " ${version}" fi done < <(echo "$version_dirs") } print-version() { local version_dir="$1" local version version="$(basename "$version_dir")" if [ "$(trim "$version")" == "$(trim "$(current-version)")" ]; then echo "* ${version}" else echo " ${version}" fi } main "$@"