Created
September 17, 2023 16:51
-
-
Save tammymakesthings/f0429661672cca8624b2006659f8a4d8 to your computer and use it in GitHub Desktop.
lisp-setup.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
############################################################################## | |
# Setup my Common Lisp environment | |
############################################################################## | |
export R="\033[0;31m" | |
export G="\033[0;32m" | |
export Y="\033[0;33m" | |
export B="\033[0;34m" | |
export C="\033[0;36m" | |
export M="\033[0;35m" | |
export NC="\033[0m" | |
export LISPDIR="${HOME}/common-lisp" | |
export ROSDIR="${HOME}/.roswell" | |
export SIMULATE="${SIMULATE:-0}" | |
install_lisp() { | |
packages="sbcl ecl emacs" | |
case "$(uname -s)" in | |
[Dd]arwin) | |
if [ -x "$(which brew | cut -d' ' -f1)" ]; then | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}Darwin${NC} with ${B}brew${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
brew install ${packages} | |
fi | |
elif [ -x "$(which port | cut -d' ' -f1)" ]; then | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}Darwin${NC} with ${B}port${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
port install ${packages} | |
fi | |
else | |
echo "${G}install_lisp()${NC}: ${R}couldn't find a package manager!${NC}" | |
fi | |
;; | |
[Ff]reeBSD) | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}FreeBSD${NC} with ${B}pkg${NC}" | |
if [ "${SIMULATE}" != "1"]; then | |
[ "$EUID" = "0" ] && pkg install ${packages} | |
[ "$EUID" = "0" ] || pkg install ${packages} | |
fi | |
;; | |
[Ll]inux) | |
if [ "${EUID}" = "0" ]; then | |
sudo_cmd="" | |
sudo_msg="" | |
else | |
sudo_cmd="sudo" | |
sudo_msg="/sudo" | |
fi | |
if [ -x "$(which apt | cut -d' ' -f1)" ]; then | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}Linux${NC} with ${B}apt${sudo_msg}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
${sudo_cmd} apt update && ${sudo_cmd} apt install -y ${packages} | |
fi | |
elif [ -x "$(which apk | cut -d' ' -f1)" ]; then | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}Linux${NC} with ${B}apk${sudo_msg}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
${sudo_cmd} apk add -u -U ${packages} | |
fi | |
elif [ -x "$(which pacman | cut -d' ' -f1)" ]; then | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}Linux${NC} with ${B}pacman${sudo_msg}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
${sudo_cmd} pacman -Syu ${packages} | |
fi | |
elif [ -x "$(which yum | cut -d' ' -f1)" ]; then | |
echo "${G}install_lisp()${NC}: installing system Lisp tools for ${C}Linux${NC} with ${B}yum${sudo_msg}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
${sudo_cmd} yum install -y ${packages} | |
fi | |
else | |
echo "${G}install_lisp()${NC}: ${R}couldn't find a package manager!${NC}" | |
fi | |
;; | |
*) | |
echo "${G}install_lisp()${NC}: skipping unknown platform ${C}$(uname -s)${NC}" | |
;; | |
esac | |
} | |
make_eclrc() { | |
eclrc_file="${1:-"${HOME}/.eclrc"}" | |
echo "${G}make_eclrc()${NC}: Creating .eclrc file in ${C}${eclrc_file}${NC}" | |
if [ "${SIMULATE}" = "1" ]; then | |
return | |
fi | |
cat <<'EOF' >"${eclrc_file}" | |
;;;; ECL startup file -*- Lisp; coding: utf-8; lexical-binding: t; -*- | |
;;;; | |
;;;; Tammy Cravit <[email protected]> | |
;;;; Fri Sep 15 08:39:11 MST 2023 | |
#-quicklisp | |
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" | |
(user-homedir-pathname)))) | |
(when (probe-file quicklisp-init) | |
(load quicklisp-init))) | |
#+quicklisp | |
(funcall (find-symbol (symbol-name '#:register-local-projects) | |
(symbol-name '#:ql))) | |
(let ((quicklisp-libraries (merge-pathnames ".libraries.lisp" (user-homedir-pathname)))) | |
(when (probe-file quicklisp-libraries) | |
(load quicklisp-libraries))) | |
(if (fboundp 'tmtlibs:add-global-library) | |
(progn | |
(tmtlibs:add-global-library '("cl-ppcre" "named-readtables" "alexandria" "uiop")) | |
(tmtlibs:add-interactive-library '("cl-project")) | |
(tmtlibs:load-libraries (tmtlibs:interactive-session-p) nil) | |
)) | |
EOF | |
} | |
make_libraries_lisp() { | |
libraries_lisp_file="${1:-"${HOME}/.libraries.lisp"}" | |
echo "${G}make_libraries_lisp()${NC}: Creating .libraries.lisp file in ${C}${libraries_lisp_file}${NC}" | |
if [ "${SIMULATE}" = "1" ]; then | |
return | |
fi | |
cat <<'EOF' >"${libraries_lisp_file}" | |
;;;; Quicklisp Load Helper -*- Lisp; coding: utf-8; lexical-binding: t; -*- | |
;;;; | |
;;;; Tammy Cravit <[email protected]> | |
;;;; Version: 0.1.1, 2023-09-11 09:09 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defpackage :tmtlibs | |
(:use :cl) | |
(:export :add-global-library | |
:add-interactive-library | |
:interactive-session-p | |
:load-libraries) | |
) | |
(in-package :tmtlibs) | |
(defparameter *global-libraries* '() | |
"The list of libraries to load in all SBCL sessions.") | |
(defparameter *interactive-libraries* '() | |
"The list of libraries to load in interactive SBCL sessions.") | |
(defun add-global-library (libs) | |
(setq *global-libraries* (remove-duplicates | |
(append *global-libraries* libs) | |
:test #'equal))) | |
(defun add-interactive-library (libs) | |
(setq *interactive-libraries* (remove-duplicates | |
(append *interactive-libraries* libs) | |
:test #'equal))) | |
(defun interactive-session-p () | |
(interactive-stream-p *terminal-io*)) | |
(defun load-libraries (&optional interactive-p verbose-p) | |
"Load libraries into the sbcl session. If 'interactive-p' is non-nil, the | |
libraries defined by '*global-libraries*' and '*interactive-libraries* will be | |
loaded. Otherwise, only the libraries in *global-libraries* will be loaded." | |
(if (listp *global-libraries*) | |
(progn | |
(if verbose-p | |
(format t "*** load-libraries: loading ~d global libraries~%" (length *global-libraries*))) | |
(dolist (pkg-name *global-libraries*) | |
(ql:quickload pkg-name :prompt nil :verbose verbose-p :silent (not verbose-p))))) | |
(if interactive-p | |
(if (listp *interactive-libraries*) | |
(progn | |
(if verbose-p | |
(format t "*** load-libraries: loading ~d interactive libraries~%" (length *interactive-libraries*))) | |
(dolist (pkg-name *interactive-libraries*) | |
(ql:quickload pkg-name :prompt nil :verbose verbose-p :silent (not verbose-p))))))) | |
EOF | |
} | |
make_sbclrc() { | |
sbclrc_file="${1:-"${HOME}/.sbclrc"}" | |
echo "${G}make_sbclrc()${NC}: Creating sbclrc file in ${C}${sbclrc_file}${NC}" | |
if [ "${SIMULATE}" = "1" ]; then | |
return | |
fi | |
cat <<'EOF' >"${sbclrc_file}" | |
;;;; SBCL startup file -*- Lisp; coding: utf-8; lexical-binding: t; -*- | |
;;;; | |
;;;; Tammy Cravit <[email protected]> | |
;;;; Fri Sep 15 08:39:11 MST 2023 | |
#-quicklisp | |
(let ((quicklisp-init (merge-pathnames | |
"quicklisp/setup.lisp" | |
(user-homedir-pathname)))) | |
(when (probe-file quicklisp-init) | |
(progn | |
(load quicklisp-init) | |
))) | |
(funcall (find-symbol (symbol-name '#:register-local-projects) | |
(symbol-name '#:ql))) | |
(funcall (find-symbol (symbol-name '#:quickload) | |
(symbol-name '#:ql)) | |
'#:repl-utilities :silent t) | |
(use-package '#:repl-utilities) | |
(let ((quicklisp-libraries (merge-pathnames ".libraries.lisp" (user-homedir-pathname)))) | |
(when (probe-file quicklisp-libraries) | |
(load quicklisp-libraries))) | |
(if (member "--no-libraries" sb-ext:*posix-argv* :test 'equal) | |
(setf sb-ext:*posix-argv* | |
(remove "--no-libraries" sb-ext:*posix-argv* :test 'equal)) | |
(if (fboundp 'tmtlibs:add-global-library) | |
(progn | |
(tmtlibs:add-global-library '("cl-ppcre" "named-readtables" "alexandria" "uiop")) | |
(tmtlibs:add-interactive-library '("cl-project")) | |
(tmtlibs:load-libraries (tmtlibs:interactive-session-p) nil) | |
)) | |
) | |
EOF | |
} | |
initial_setup() { | |
lispdir="${1:-${LISPDIR}}" | |
if [ ! -d "${lispdir}" ]; then | |
echo "${G}initial_setup()${NC}: Creating Lisp directory ${C}${lispdir}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
mkdir -p "${lispdir}" | |
fi | |
fi | |
if [ ! -f "${lispdir}/.gitignore" ]; then | |
echo "${G}initial_setup()${NC}: Creating .gitignore file: ${C}${lispdir}/.gitignore${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
curl -o "${lispdir}/.gitignore" \ | |
"https://github.com/github/gitignore/raw/main/CommonLisp.gitignore" | |
fi | |
else | |
echo "${G}initial_setup()${NC}: .gitignore file ${C}${lispdir}/.gitignore${NC} already present; not overwriting" | |
fi | |
if [ ! -f "${HOME}/.sbclrc" ]; then | |
make_sbclrc "${HOME}/.sbclrc" | |
else | |
echo "${G}initial_setup()${NC}: .sbclrc file ${C}${HOME}/.sbclrc${NC} already present; not overwriting" | |
fi | |
if [ ! -f "${HOME}/.libraries.lisp" ]; then | |
make_libraries_lisp "${HOME}/.libraries.lisp" | |
else | |
echo "${G}initial_setup()${NC}: .libraries.lisp file ${C}${HOME}/.libraries.lisp${NC} already present; not overwriting" | |
fi | |
if [ ! -f "${HOME}/.eclrc" ]; then | |
make_eclrc "${HOME}/.eclrc" | |
else | |
echo "${G}initial_setup()${NC}: .eclrc file ${C}${HOME}/.eclrc${NC} already present; not overwriting" | |
fi | |
if [ ! -d "${HOME}/.emacs.d" ]; then | |
echo "${G}initial_setup()${NC}: cloning Emacs configuration to ${C}${HOME}/.emacs.d${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
git clone "[email protected]:tammymakesthings/emacs_d" "${HOME}/.emacs.d" | |
fi | |
else | |
if [ -d "${HOME}/.emacs.d/.git" ]; then | |
echo "${G}initial_setup()${NC}: Updating Emacs configuration in ${C}${HOME}/.emacs.d${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
( | |
cd "${HOME}/.emacs.d" | |
git fetch && git pull | |
) | |
fi | |
fi | |
fi | |
} | |
install_roswell() { | |
install_dir="${1:-${LISPDIR}/roswell}" | |
roswell_dir="${2:-${ROSDIR}/}" | |
echo "${G}install_roswell()${NC}: Ensuring Roswell prerequisites are installed" | |
if [ "${SIMULATE}" != "1" ]; then | |
case "$(uname -s)" in | |
[Dd]arwin) | |
brew install -q curl automake git | |
;; | |
[Ll]inux) | |
apt install -y libcurl4-openssl-dev automake git >/dev/null 2>/dev/null | |
;; | |
esac | |
fi | |
if [ -d "${install_dir}" ]; then | |
echo "${G}install_roswell()${NC}: Updating roswell clone in ${Y}${install_dir}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
( | |
cd "${install_dir}" || return | |
git fetch && git pull | |
) | |
fi | |
else | |
echo "${G}install_roswell()${NC}: Cloning roswell to ${Y}${install_dir}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
git clone -b release "https://github.com/roswell/roswell.git" "${install_dir}" | |
fi | |
fi | |
echo "${G}install_roswell()${NC}: Installing roswell to ${Y}${install_dir}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
( | |
cd "${install_dir}" || return | |
sh bootstrap && | |
./configure --prefix="${roswell_dir}" && | |
make && | |
make install && | |
PATH=${roswell_dir}/bin:${PATH} ros setup && | |
PATH=${roswell_dir}/bin:${PATH} ros install sbcl && | |
PATH=${roswell_dir}/bin:${PATH} ros install ecl && | |
PATH=${roswell_dir}/bin:${PATH} ros use sbcl && | |
PATH=${roswell_dir}/bin:${PATH} ros follow-dependency=t install lem-project/lem | |
) | |
fi | |
} | |
clone_project() { | |
origin="$1" | |
project_name="$(echo "$1" | cut -d'/' -f2 | cut -d'.' -f1)" | |
if [ -d "${LISPDIR}/${project_name}" ]; then | |
echo "${G}clone_project()${NC}: Updating clone of ${C}${project_name}${NC} in ${Y}${LISPDIR}/${project_name}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
( | |
cd "${LISPDIR}/${project_name}" || return | |
git fetch && git pull | |
) | |
fi | |
else | |
echo "${G}clone_project()${NC}: Cloning ${C}${project_name}${NC} from ${Y}${origin}${NC}" | |
if [ "${SIMULATE}" != "1" ]; then | |
git clone "${origin}" "${LISPDIR}/${project_name}" | |
fi | |
fi | |
} | |
clone_projects() { | |
declare -a project_urls=( | |
"[email protected]/asdf/asdf.git" | |
"[email protected]:rpav/c2ffi.git" | |
"[email protected]:tammymakesthings/cl-cookieproject.git" | |
"[email protected]:fukamachi/cl-project.git" | |
"[email protected]:tammymakesthings/cl-provisioning.git" | |
"[email protected]:lem-project/cl-sdl2-image.git" | |
"[email protected]:lem-project/cl-sdl2-ttf.git" | |
"[email protected]:lem-project/cl-sdl2.git" | |
"[email protected]:lispci/fiveam.git" | |
"[email protected]:40ants/lem-pareto" | |
"[email protected]:fukamachi/lem-vi-sexp" | |
"[email protected]:eschulte/lisp-format.git" | |
"[email protected]:gas2serra/mcclim-desktop.git" | |
"[email protected]:lisp-mirror/qbook.git" | |
"[email protected]:dnaeon/clingon" | |
) | |
echo "${G}clone_projects()${NC}: Cloning ${C}${#project_urls[@]}${NC} projects" | |
for url in "${project_urls[@]}"; do | |
clone_project "$url" | |
done | |
} | |
print_banner() { | |
echo "${Y}******************************************************************************${NC}" | |
echo "${Y}* lisp-setup.sh: Install/update Lisp setup *${NC}" | |
echo "${Y}* Tammy Cravit / [email protected] / 2023-09-15 *${NC}" | |
echo "${Y}******************************************************************************${NC}" | |
echo "" | |
echo "${G}==> Script Options${NC}" | |
echo "" | |
echo " ${C}LISPDIR${NC}: ${M}${LISPDIR}${NC}" | |
echo " ${C}ROSDIR${NC}: ${M}${ROSDIR}${NC}" | |
echo " ${C}SIMULATE${NC}: ${M}${SIMULATE}${NC}" | |
echo "" | |
} | |
usage() { | |
return_code=${1:-""} | |
echo "Usage: $(basename "$0") -h" | |
echo " $(basename "$0") [-l <lispdir>] [-r <rosdir>] [-s] [-n]" | |
echo "" | |
echo " -l <lispdir> Specify the lisp directory" | |
echo " Defaults to ${HOME}/common-lisp" | |
echo " -r <rosdir> Specify the Roswell installation directory" | |
echo " Defaults to ${HOME}/.roswell" | |
echo " -s Simulate - print actions but does not take them" | |
echo " -n No color in output" | |
if [ "${return_code}" != "" ]; then | |
exit ${return_code:-0} | |
fi | |
} | |
if [ "${NOCOLOR:-no}" != "no" ]; then | |
export R="" | |
export G="" | |
export Y="" | |
export B="" | |
export C="" | |
export M="" | |
export NC="" | |
fi | |
while getopts ":l:r:snh" opt; do | |
case "${opt}" in | |
h) # -h - help | |
usage | |
exit 0 | |
;; | |
l) # -l <lispdir> - specify LISPDIR | |
export LISPDIR="${OPTARG}" | |
if [ -d "${OPTARG}" ]; then | |
echo "${M}Warning:${NC} ${C}LISPDIR${NC} ${Y}${OPTARG}{$NC} already exists" | |
fi | |
;; | |
r) # -r <rosdir> - specify Roswell install dir | |
export ROSDIR="${OPTARG}" | |
if [ -d "${OPTARG}" ]; then | |
echo "${M}Warning:${NC} ${C}LISPDIR${NC} ${Y}${OPTARG}{$NC} already exists" | |
fi | |
;; | |
s) # -s - simulate mode | |
export SIMULATE=1 | |
echo "${M}Warning:${NC} Simulate mode active; no commands will be run" | |
;; | |
n) # -n - no color | |
export R="" | |
export G="" | |
export Y="" | |
export B="" | |
export C="" | |
export M="" | |
export NC="" | |
;; | |
*) | |
echo "${R}Unrecognized option: ${opt}${NC}" | |
usage 100 | |
;; | |
esac | |
done | |
print_banner "${LISPDIR}" | |
echo "${Y}Beginning installation${NC}" | |
initial_setup "${LISPDIR}" | |
install_lisp | |
clone_projects | |
install_roswell "${LISPDIR}/roswell" "${ROSDIR}" | |
echo "" | |
echo "${Y}Done!${NC} Remember to add ${M}${ROSDIR}/bin${NC} to your shell path." | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment