Last active
August 29, 2015 14:16
-
-
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.
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 | |
# 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