Skip to content

Instantly share code, notes, and snippets.

@danclien
Last active August 29, 2015 14:16
Show Gist options
  • Save danclien/17b0e82324a0efc1d46e to your computer and use it in GitHub Desktop.
Save danclien/17b0e82324a0efc1d46e to your computer and use it in GitHub Desktop.
Compiles a binary in a temporary sandbox and moves the binaries to \$HOME/.cabal/bin or to the path set by SANDBOX_INSTALL_DIR.
#!/usr/bin/env bash
# Utility functions
## Logging
log_debug() {
if [ "$OPT_VERBOSE" = true ]; then
echo "$(tput setaf 5)Debug: $(tput sgr 0)" "$@"
fi
}
log_info() { echo "$(tput setaf 6)Info: $(tput sgr 0)" "$@"; }
log_warning() { echo "$(tput setaf 3)Warning:$(tput sgr 0)" "$@"; }
log_success() { echo "$(tput setaf 2)Success:$(tput sgr 0)" "$@"; }
log_error() { echo "$(tput setaf 1)Error: $(tput sgr 0)" "$@" 1>&2; }
## Assertions
check_path() {
if [[ ! ":$PATH:" == *":$1:"* ]]; then
log_warning "'$1' is not in your PATH"
log_warning "You should add '$1' to your PATH and restart your terminal"
fi
}
check_installed() {
command -v "$1" >/dev/null
return $?
}
assert_installed() {
log_debug "Checking for '$1'"
check_installed "$1"
if [ $? -ne 0 ]; then
log_error "$1" "not installed."
exit 1
fi
}
update_cabal() {
log_info "Updating Cabal's list of packages"
cabal update
if [ $? -ne 0 ]; then
log_error "Failed updating Cabal's list of packages"
exit 1
fi
}
warn_if_exists() {
if [ -f "$1" ] && [ "$OPT_FORCE" = false ]; then
log_warning "$1 already exists. This may cause the copying binaries to fail. Use --force to overwrite."
fi
}
assert_home() {
if [ -z "$HOME" ]; then
log_error "HOME environment variable is not set."
exit 4
fi
}
## Trap management
### Appends `trap` calls instead of overwriting
trap_add() {
# By Richard Hansen, from http://stackoverflow.com/a/7287873
trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
for trap_add_name in "$@"; do
trap -- "$(
# helper fn to get existing trap command from output
# of trap -p
extract_trap_cmd() { printf '%s\n' "$3"; }
# print existing trap command with newline
eval "extract_trap_cmd $(trap -p "${trap_add_name}")"
# print the new trap command
printf '%s\n' "${trap_add_cmd}"
)" "${trap_add_name}" \
|| fatal "unable to add to trap ${trap_add_name}"
done
}
declare -f -t trap_add
## Temporary directory that automatically cleans up
make_temp_dir() {
TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
trap_add "log_debug Removing temporary directory \'$TEMP_DIR\'; rm -rf $TEMP_DIR" EXIT
log_debug "Using temporary directory '$TEMP_DIR'"
}
## Change to directory and return on script exit
run_from() {
trap_add 'popd &>/dev/null' EXIT
pushd . &>/dev/null
cd "$1"
}
# sandbox-install
get_install_dir() {
if [ -z "$SANDBOX_INSTALL_DIR" ]; then
log_debug "SANDBOX_INSTALL_DIR not set. Using default install location."
assert_home
fi
SANDBOX_INSTALL_DIR=${SANDBOX_INSTALL_DIR:-$HOME/.cabal/bin}
log_info "Installing to '${SANDBOX_INSTALL_DIR}'"
}
init_sandbox() {
log_info "Initializing Cabal sandbox"
cabal sandbox init
if [ $? -ne 0 ]; then
log_error "Failed to initialize sandbox"
exit 1
fi
}
install() {
log_info "Installing '$1'"
cabal install "$1"
if [ $? -ne 0 ]; then
log_error "Failed to install '$1' into sandbox"
exit 1
fi
}
create_install_dir() {
if [ ! -d "$SANDBOX_INSTALL_DIR" ]; then
log_warning "Installation directory '${SANDBOX_INSTALL_DIR}'' does not exist"
log_info "Creating '${SANDBOX_INSTALL_DIR}'"
mkdir -p "$SANDBOX_INSTALL_DIR"
if [ $? -ne 0 ]; then
log_error "Failed to create '$SANDBOX_INSTALL_DIR'"
exit 1
fi
fi
}
copy_binaries() {
log_info "Copying binaries from '$TEMP_DIR/.cabal-sandbox/bin'"
log_info " to '$SANDBOX_INSTALL_DIR'"
for f in $TEMP_DIR/.cabal-sandbox/bin/*
do
log_info "Copying '${f##*/}'"
if [ "$OPT_FORCE" = true ]; then
cp "$f" "$SANDBOX_INSTALL_DIR/"
else
cp -n "$f" "$SANDBOX_INSTALL_DIR/"
fi
if [ $? -ne 0 ]; then
log_error "Failed to copy '${f##*/}'"
exit 1
fi
done
}
usage() {
cat << EOF
Compiles a binary in a temporary sandbox and moves the binaries to
\$HOME/.cabal/bin or to the path set by SANDBOX_INSTALL_DIR.
Usage: $(basename "$0") [options] [package-name]
-f, --force Overwrites existing files on install
-h, --help Print this message
-v, --verbose Turn on verbose output
Current install location: $SANDBOX_INSTALL_DIR
EOF
exit 0;
}
get_install_dir
# Parse options
OPT_FORCE=false
OPT_SPEC=":fhv-:"
while getopts "$OPT_SPEC" optchar; do
case "${optchar}" in
-) case "${OPTARG}" in
force) OPT_FORCE=true;;
help) usage;;
verbose) OPT_VERBOSE=true;;
esac;;
f) OPT_FORCE=true;;
h) usage;;
v) OPT_VERBOSE=true;;
esac
done
shift $(expr $OPTIND - 1 )
# Default run options
if [ -z "$1" ]; then usage; fi
log_info "Attempting to install" \'"$1"\'
create_install_dir
warn_if_exists "$SANDBOX_INSTALL_DIR/$1"
make_temp_dir
assert_installed cabal
run_from "$TEMP_DIR"
init_sandbox
update_cabal
install "$1"
copy_binaries
log_success "Installed '$1'"
check_path "$SANDBOX_INSTALL_DIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment