bat-extras/build.sh

335 lines
7.1 KiB
Bash
Raw Normal View History

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.
smsg() {
2019-10-27 08:14:55 +01:00
case "$2" in
2020-03-31 23:51:00 +02:00
"SKIP") printc " %{YELLOW} %{DIM}%s [skipped]%{CLEAR}\n" "$1" 1>&2 ;;
*) printc " %{YELLOW} %s...%{CLEAR}\n" "$1" 1>&2 ;;
2019-06-19 23:15:10 +02:00
esac
}
# Build step: read
# Reads the file from its source.
#
# Arguments:
# 1 -- The source file.
#
# Output:
# The file contents.
step_read() {
cat "$1"
2019-10-27 08:14:55 +01:00
smsg "Reading"
2019-06-19 23:15:10 +02:00
}
# Build step: preprocess
# Preprocesses the script.
#
# This will embed library scripts and replace the BAT variable.
2019-06-19 23:15:10 +02:00
#
# Input:
# The original file contents.
2020-03-31 23:51:00 +02:00
#
2019-06-19 23:15:10 +02:00
# Output:
# The processed file contents.
step_preprocess() {
local line
while IFS='' read -r line; do
# Skip certain lines.
[[ "$line" =~ ^LIB=.*$ ]] && continue
2020-03-31 23:51:00 +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
# Replace the DOCS_* variables.
if [[ "$line" =~ ^DOCS_[A-Z]+=.*$ ]]; then
2020-03-31 23:51:00 +02:00
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.
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
2020-03-31 23:51:00 +02:00
2019-10-27 08:14:55 +01:00
smsg "Preprocessing"
2019-06-19 23:15:10 +02:00
}
# 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-06-19 23:15:10 +02:00
cat
2019-10-27 08:14:55 +01:00
smsg "Minifying" "SKIP"
2019-06-19 23:15:10 +02:00
return 0
fi
printf "#!/usr/bin/env bash\n"
2019-10-11 21:30:06 +02:00
pp_minify | pp_minify_unsafe
2019-10-27 08:14:55 +01:00
smsg "Minifying"
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
cat
2019-10-27 08:14:55 +01:00
smsg "Compressing" "SKIP"
2019-10-11 22:03:47 +02:00
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"
})"
2020-03-31 23:51:00 +02:00
sed "s/:::/$(wc -c <<<"$wrapper" | bc)/" <<<"$wrapper"
2019-10-11 22:03:47 +02:00
gzip
2019-10-27 08:14:55 +01:00
smsg "Compressing"
2019-10-11 22:03:47 +02:00
}
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() {
tee "$1"
chmod +x "$1"
2019-10-27 08:14:55 +01:00
smsg "Building"
2019-06-19 23:15:10 +02:00
}
# 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
cat
2019-10-27 08:14:55 +01:00
smsg "Installing" "SKIP"
2019-06-19 23:15:10 +02:00
return 0
fi
tee "$1"
chmod +x "$1"
2019-10-27 08:14:55 +01:00
smsg "Installing"
2019-06-19 23:15:10 +02:00
}
# -----------------------------------------------------------------------------
# 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
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"
OPT_BAT="bat"
2019-06-19 23:15:10 +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
2020-03-31 23:51:00 +02:00
--install) OPT_INSTALL=true ;;
--compress) OPT_COMPRESS=true ;;
--prefix)
shiftval
OPT_PREFIX="$OPT_VAL"
;;
--alternate-executable)
shiftval
OPT_BAT="$OPT_VAL"
;;
--minify)
shiftval
OPT_MINIFY="$OPT_VAL"
;;
--no-verify)
shiftval
OPT_VERIFY=false
;;
--docs:url)
shiftval
DOCS_URL="$OPT_VAL"
;;
--docs:maintainer)
shiftval
DOCS_MAINTAINER="$OPT_VAL"
;;
*)
printc "%{RED}%s: unknown option '%s'%{CLEAR}" "$PROGRAM" "$OPT"
exit 1
;;
2019-06-19 23:15:10 +02:00
esac
done
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
# -----------------------------------------------------------------------------
# Check for resources.
2019-06-19 23:15:10 +02:00
[[ -d "$BIN" ]] || mkdir "$BIN"
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
2020-03-31 23:51:00 +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
2020-03-31 23:51:00 +02:00
step_read "$file" |
next step_preprocess |
next step_minify |
next step_compress |
next step_write "${BIN}/${filename}" |
next step_write_install "${OPT_PREFIX}/bin/${filename}" |
2019-06-19 23:15:10 +02:00
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
2020-02-20 05:49:20 +01:00
"${HERE}/test.sh" --compiled
2019-09-27 00:49:36 +02:00
exit $?
fi