2019-06-19 23:15:10 +02:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# bat-extras | Copyright (C) 2019 eth-p | MIT License
|
|
|
|
#
|
|
|
|
# Repository: https://github.com/eth-p/bat-extras
|
|
|
|
# Issues: https://github.com/eth-p/bat-extras/issues
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
BIN="$HERE/bin"
|
|
|
|
SRC="$HERE/src"
|
|
|
|
LIB="$HERE/lib"
|
|
|
|
source "${LIB}/print.sh"
|
|
|
|
source "${LIB}/opt.sh"
|
|
|
|
# -----------------------------------------------------------------------------
|
2019-06-21 23:55:43 +02:00
|
|
|
set -eo pipefail
|
2019-06-19 23:15:10 +02:00
|
|
|
|
|
|
|
# Runs the next build step.
|
|
|
|
#
|
|
|
|
# Arguments:
|
|
|
|
# 1 -- The build step function name.
|
|
|
|
# @ -- The function arguments.
|
|
|
|
#
|
|
|
|
# Input:
|
|
|
|
# The unprocessed file data.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The processed file data.
|
|
|
|
next() {
|
2019-10-11 22:03:47 +02:00
|
|
|
"$@"
|
2019-06-19 23:15:10 +02:00
|
|
|
return $?
|
|
|
|
}
|
|
|
|
|
|
|
|
# Prints a build step message.
|
2019-10-22 19:51:56 +02:00
|
|
|
SMSG_CACHE_MSG=()
|
|
|
|
SMSG_CACHE_META=()
|
|
|
|
SMSG_EXPECT=1
|
2019-06-19 23:15:10 +02:00
|
|
|
smsg() {
|
2019-10-22 19:51:56 +02:00
|
|
|
if [[ "$1" != "$SMSG_EXPECT" ]]; then
|
|
|
|
SMSG_CACHE_MSG["$1"]="$2"
|
|
|
|
SMSG_CACHE_META["$1"]="$3"
|
|
|
|
return;
|
|
|
|
fi
|
|
|
|
|
|
|
|
((SMSG_EXPECT++))
|
|
|
|
case "$3" in
|
|
|
|
"SKIP") printc " %{YELLOW} %{DIM}%s [skipped]%{CLEAR}\n" "$2" 1>&2;;
|
|
|
|
*) printc " %{YELLOW} %s...%{CLEAR}\n" "$2" 1>&2;;
|
2019-06-19 23:15:10 +02:00
|
|
|
esac
|
2019-10-22 19:51:56 +02:00
|
|
|
|
|
|
|
# Cached messages.
|
|
|
|
echo "${SMSG_CACHE_MSG[$SMSG_EXPECT]}" 1>&2
|
|
|
|
if [[ -n "${SMSG_CACHE_MSG[$SMSG_EXPECT]}" ]]; then
|
|
|
|
smsg "$SMSG_EXPECT" "${SMSG_CACHE_MSG[$SMSG_EXPECT]}" "${SMSG_CACHE_META[$SMSG_EXPECT]}"
|
|
|
|
fi
|
2019-06-19 23:15:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Build step: read
|
|
|
|
# Reads the file from its source.
|
|
|
|
#
|
|
|
|
# Arguments:
|
|
|
|
# 1 -- The source file.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The file contents.
|
|
|
|
step_read() {
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 1 "Reading"
|
2019-06-19 23:15:10 +02:00
|
|
|
cat "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
# Build step: preprocess
|
|
|
|
# Preprocesses the script.
|
|
|
|
#
|
2019-09-09 02:43:29 +02:00
|
|
|
# This will embed library scripts and replace the BAT variable.
|
2019-06-19 23:15:10 +02:00
|
|
|
#
|
|
|
|
# Input:
|
|
|
|
# The original file contents.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The processed file contents.
|
|
|
|
step_preprocess() {
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 2 "Preprocessing"
|
2019-06-19 23:15:10 +02:00
|
|
|
|
|
|
|
local line
|
|
|
|
while IFS='' read -r line; do
|
|
|
|
# Skip certain lines.
|
|
|
|
[[ "$line" =~ ^LIB=.*$ ]] && continue
|
2019-09-09 02:43:29 +02:00
|
|
|
|
|
|
|
# Replace the BAT variable with the build option.
|
|
|
|
if [[ "$line" =~ ^BAT=.*$ ]]; then
|
|
|
|
printf "BAT=%q\n" "$OPT_BAT"
|
|
|
|
continue
|
|
|
|
fi
|
2019-06-19 23:15:10 +02:00
|
|
|
|
2019-09-25 23:48:52 +02:00
|
|
|
# Replace the DOCS_* variables.
|
|
|
|
if [[ "$line" =~ ^DOCS_[A-Z]+=.*$ ]]; then
|
|
|
|
local docvar="$(cut -d'=' -f1 <<< "$line")"
|
|
|
|
printf "%s=%q\n" "$docvar" "${!docvar}"
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
|
2019-06-19 23:15:10 +02:00
|
|
|
# Embed library scripts.
|
2019-09-27 00:42:52 +02:00
|
|
|
if [[ "$line" =~ ^[[:space:]]*source[[:space:]]+[\"\']\$\{?LIB\}/([a-z_-]+\.sh)[\"\'] ]]; then
|
2019-06-19 23:15:10 +02:00
|
|
|
echo "# --- BEGIN LIBRARY FILE: ${BASH_REMATCH[1]} ---"
|
|
|
|
cat "$LIB/${BASH_REMATCH[1]}" | {
|
|
|
|
if [[ "$OPT_MINIFY" = "lib" ]]; then
|
2019-10-11 21:30:06 +02:00
|
|
|
pp_strip_comments | pp_minify | pp_minify_unsafe
|
2019-06-19 23:15:10 +02:00
|
|
|
else
|
|
|
|
cat
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
echo "# --- END LIBRARY FILE ---"
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Forward data.
|
|
|
|
echo "$line"
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
# Build step: minify
|
|
|
|
# Minifies the output script.
|
|
|
|
#
|
|
|
|
# Input:
|
|
|
|
# The original file contents.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The minified file contents.
|
|
|
|
step_minify() {
|
2019-10-11 21:30:06 +02:00
|
|
|
if [[ "$OPT_MINIFY" =~ ^all($|+.*) ]]; then
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 3 "Minifying" "SKIP"
|
2019-06-19 23:15:10 +02:00
|
|
|
cat
|
|
|
|
return 0
|
|
|
|
fi
|
|
|
|
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 3 "Minifying"
|
2019-06-19 23:15:10 +02:00
|
|
|
printf "#!/usr/bin/env bash\n"
|
2019-10-11 21:30:06 +02:00
|
|
|
pp_minify | pp_minify_unsafe
|
2019-06-19 23:15:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 22:03:47 +02:00
|
|
|
# Build step: compress
|
|
|
|
# Compresses the input into a gzipped self-executable script.
|
|
|
|
#
|
|
|
|
# Input:
|
|
|
|
# The original file contents.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The compressed self-executable script.
|
|
|
|
step_compress() {
|
|
|
|
if ! "$OPT_COMPRESS"; then
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 4 "Compressing" "SKIP"
|
2019-10-11 22:03:47 +02:00
|
|
|
cat
|
|
|
|
return 0
|
|
|
|
fi
|
|
|
|
|
|
|
|
local wrapper="$({
|
|
|
|
printf '#!/usr/bin/env bash\n'
|
|
|
|
printf "(exec -a \"\$0\" bash -c 'eval \"\$(cat <&3)\"' \"\$0\" \"\$@\" 3< <(dd bs=1 if=\"\$0\" skip=::: 2>/dev/null | gunzip)); exit \$?;\n"
|
|
|
|
})"
|
|
|
|
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 4 "Compressing"
|
2019-10-11 22:03:47 +02:00
|
|
|
sed "s/:::/$(wc -c <<< "$wrapper" | bc)/" <<< "$wrapper"
|
|
|
|
gzip
|
|
|
|
}
|
|
|
|
|
2019-06-19 23:15:10 +02:00
|
|
|
# Build step: write
|
|
|
|
# Writes the output script to a file.
|
|
|
|
#
|
|
|
|
# Arguments:
|
|
|
|
# 1 -- The file to write to.
|
|
|
|
#
|
|
|
|
# Input:
|
|
|
|
# The file contents.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The file contents.
|
|
|
|
step_write() {
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 5 "Building"
|
2019-06-19 23:15:10 +02:00
|
|
|
tee "$1"
|
|
|
|
chmod +x "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
# Build step: write
|
|
|
|
# Optionally writes the output script to a file.
|
|
|
|
#
|
|
|
|
# Arguments:
|
|
|
|
# 1 -- The file to write to.
|
|
|
|
#
|
|
|
|
# Input:
|
|
|
|
# The file contents.
|
|
|
|
#
|
|
|
|
# Output:
|
|
|
|
# The file contents.
|
|
|
|
|
|
|
|
step_write_install() {
|
|
|
|
if [[ "$OPT_INSTALL" != true ]]; then
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 6 "Installing" "SKIP"
|
2019-06-19 23:15:10 +02:00
|
|
|
cat
|
|
|
|
return 0
|
|
|
|
fi
|
|
|
|
|
2019-10-22 19:51:56 +02:00
|
|
|
smsg 6 "Installing"
|
2019-06-19 23:15:10 +02:00
|
|
|
tee "$1"
|
|
|
|
chmod +x "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Preprocessor.
|
|
|
|
|
|
|
|
# Strips comments from a Bash source file.
|
|
|
|
pp_strip_comments() {
|
|
|
|
sed '/^[[:space:]]*#.*$/d'
|
|
|
|
}
|
|
|
|
|
|
|
|
# Minify a Bash source file.
|
2019-10-11 21:30:06 +02:00
|
|
|
# https://github.com/mvdan/sh
|
2019-06-19 23:15:10 +02:00
|
|
|
pp_minify() {
|
2019-10-22 19:51:56 +02:00
|
|
|
if [[ "$OPT_MINIFY" = "none" ]]; then
|
|
|
|
cat
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
|
2019-06-22 01:19:31 +02:00
|
|
|
shfmt -mn
|
2019-10-22 19:51:56 +02:00
|
|
|
return $?
|
2019-06-19 23:15:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 21:30:06 +02:00
|
|
|
# Minifies the output script (unsafely).
|
|
|
|
# Right now, this doesn't do anything.
|
|
|
|
# This should be applied after shfmt minification.
|
|
|
|
pp_minify_unsafe() {
|
|
|
|
if ! [[ "$OPT_MINIFY" =~ ^.*+unsafe(+.*)*$ ]]; then
|
|
|
|
cat
|
|
|
|
return 0
|
|
|
|
fi
|
|
|
|
|
|
|
|
cat
|
|
|
|
}
|
|
|
|
|
2019-06-19 23:15:10 +02:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Options.
|
|
|
|
OPT_INSTALL=false
|
2019-10-11 22:03:47 +02:00
|
|
|
OPT_COMPRESS=false
|
2019-09-27 00:49:36 +02:00
|
|
|
OPT_VERIFY=true
|
2019-06-19 23:15:10 +02:00
|
|
|
OPT_MINIFY="lib"
|
|
|
|
OPT_PREFIX="/usr/local"
|
2019-09-09 02:43:29 +02:00
|
|
|
OPT_BAT="bat"
|
2019-06-19 23:15:10 +02:00
|
|
|
|
2019-09-25 23:48:52 +02:00
|
|
|
DOCS_URL="https://github.com/eth-p/bat-extras/blob/master/doc"
|
|
|
|
DOCS_MAINTAINER="eth-p <eth-p@hidden.email>"
|
|
|
|
|
2019-06-19 23:15:10 +02:00
|
|
|
while shiftopt; do
|
|
|
|
case "$OPT" in
|
2019-09-09 02:43:29 +02:00
|
|
|
--install) OPT_INSTALL=true;;
|
2019-10-11 22:03:47 +02:00
|
|
|
--compress) OPT_COMPRESS=true;;
|
2019-09-09 02:43:29 +02:00
|
|
|
--prefix) shiftval; OPT_PREFIX="$OPT_VAL";;
|
|
|
|
--alternate-executable) shiftval; OPT_BAT="$OPT_VAL";;
|
|
|
|
--minify) shiftval; OPT_MINIFY="$OPT_VAL";;
|
2019-09-27 00:49:36 +02:00
|
|
|
--no-verify) shiftval; OPT_VERIFY=false;;
|
2019-09-25 23:48:52 +02:00
|
|
|
--docs:url) shiftval; DOCS_URL="$OPT_VAL";;
|
|
|
|
--docs:maintainer) shiftval; DOCS_MAINTAINER="$OPT_VAL";;
|
2019-06-19 23:15:10 +02:00
|
|
|
|
|
|
|
*) printc "%{RED}%s: unknown option '%s'%{CLEAR}" "$PROGRAM" "$OPT";
|
|
|
|
exit 1;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
2019-09-09 02:43:29 +02:00
|
|
|
if [[ "$OPT_BAT" != "bat" ]]; then
|
|
|
|
printc "%{YELLOW}Building executable scripts with an alternate bat executable at %{CLEAR}%s%{YELLOW}.%{CLEAR}\n" "$OPT_BAT" 1>&2
|
|
|
|
if ! command -v "$OPT_BAT"; then
|
|
|
|
printc "%{YELLOW}WARNING: Bash cannot execute the specified file.\n" 1>&2
|
|
|
|
printc "%{YELLOW} The finished scripts may not run properly.%{CLEAR}\n" 1>&2
|
|
|
|
fi
|
|
|
|
printc "\n" 1>&2
|
|
|
|
fi
|
|
|
|
|
2019-06-19 23:15:10 +02:00
|
|
|
if [[ "$OPT_INSTALL" = true ]]; then
|
|
|
|
printc "%{YELLOW}Installing to %{MAGENTA}%s%{YELLOW}.%{CLEAR}\n" "$OPT_PREFIX" 1>&2
|
|
|
|
else
|
|
|
|
printc "%{YELLOW}This will not install the script.%{CLEAR}\n" 1>&2
|
|
|
|
printc "%{YELLOW}Use %{BLUE}--install%{YELLOW} for a global install.%{CLEAR}\n\n" 1>&2
|
|
|
|
fi
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
2019-06-22 01:19:31 +02:00
|
|
|
# Check for resources.
|
2019-06-19 23:15:10 +02:00
|
|
|
|
|
|
|
[[ -d "$BIN" ]] || mkdir "$BIN"
|
|
|
|
|
2019-06-22 01:19:31 +02:00
|
|
|
if [[ "$OPT_MINIFY" != "none" ]] && ! command -v shfmt &>/dev/null; then
|
|
|
|
printc "%{RED}Warning: cannot find shfmt. Unable to minify scripts.%{CLEAR}\n"
|
|
|
|
OPT_MINIFY=none
|
2019-06-19 23:15:10 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Find files.
|
|
|
|
|
|
|
|
SOURCES=()
|
|
|
|
|
|
|
|
printc "%{YELLOW}Preparing scripts...%{CLEAR}\n" 1>&2
|
|
|
|
for file in "$SRC"/*.sh; do
|
|
|
|
SOURCES+=("$file")
|
|
|
|
done
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Build files.
|
|
|
|
|
|
|
|
printc "%{YELLOW}Building scripts...%{CLEAR}\n" 1>&2
|
|
|
|
file_i=0
|
|
|
|
file_n="${#SOURCES[@]}"
|
|
|
|
for file in "${SOURCES[@]}"; do
|
2019-06-22 00:00:40 +02:00
|
|
|
((file_i++)) || true;
|
2019-06-19 23:15:10 +02:00
|
|
|
|
|
|
|
filename="$(basename "$file" .sh)"
|
|
|
|
|
|
|
|
printc " %{YELLOW}[%s/%s] %{MAGENTA}%s%{CLEAR}\n" "$file_i" "$file_n" "$file" 1>&2
|
|
|
|
step_read "$file" |\
|
|
|
|
next step_preprocess |\
|
|
|
|
next step_minify |\
|
2019-10-11 22:03:47 +02:00
|
|
|
next step_compress |\
|
2019-06-19 23:15:10 +02:00
|
|
|
next step_write "${BIN}/${filename}" |\
|
|
|
|
next step_write_install "${OPT_PREFIX}/bin/${filename}" |\
|
|
|
|
cat >/dev/null
|
|
|
|
done
|
|
|
|
|
2019-09-27 00:49:36 +02:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Verify files by running the tests.
|
|
|
|
|
|
|
|
if "$OPT_VERIFY"; then
|
|
|
|
printc "\n%{YELLOW}Verifying scripts...%{CLEAR}\n" 1>&2
|
2019-09-27 00:57:17 +02:00
|
|
|
TEST_QUIET=true "$HERE/test/run.sh" consistency-test
|
2019-09-27 00:49:36 +02:00
|
|
|
exit $?
|
|
|
|
fi
|
2019-06-19 23:15:10 +02:00
|
|
|
|