#!/bin/bash # * makem.sh --- Script to aid building and testing Emacs Lisp packages # * Commentary: # * License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # * Safety # NOTE: These are disabled by default in this template but should be # enabled when feasible. Documentation is from the Bash man page. # ** errexit # Exit immediately if a pipeline (which may consist of a single simple # command), a list, or a compound command (see SHELL GRAMMAR above), # exits with a non-zero status. The shell does not exit if the # command that fails is part of the command list immediately following # a while or until keyword, part of the test following the if or elif # reserved words, part of any command executed in a && or || list # except the command follow‐ ing the final && or ||, any command in a # pipeline but the last, or if the command's return value is being # inverted with !. If a compound command other than a subshell # returns a non-zero status because a command failed while -e was # being ignored, the shell does not exit. A trap on ERR, if set, is # executed before the shell exits. This option applies to the shell # environment and each subshell environment separately (see COMMAND # EXECUTION ENVIRONMENT above), and may cause subshells to exit before # executing all the commands in the subshell. # If a compound command or shell function executes in a context where # -e is being ignored, none of the commands executed within the # compound command or function body will be affected by the -e # setting, even if -e is set and a command returns a failure status. # If a compound command or shell function sets -e while executing in a # context where -e is ignored, that setting will not have any effect # until the compound command or the command containing the function # call completes. # set -o errexit # ** nounset # Treat unset variables and parameters other than the special # parameters "@" and "*" as an error when performing parameter # expansion. If expansion is attempted on an unset variable or # parameter, the shell prints an error message, and, if not # interactive, exits with a non-zero status. # NOTE: When this is not enabled, individual variables can be required # to be set by using "${var:?}" parameter expansion syntax. # set -o nounset # ** pipefail # If set, the return value of a pipeline is the value of the last # (rightmost) command to exit with a non-zero status, or zero if all # commands in the pipeline exit successfully. This option is disabled # by default. # set -o pipefail # * Elisp # These functions return a path to an elisp file which can be loaded # by Emacs on the command line with -l or --load. function elisp-buttercup-file { # The function buttercup-run, which is called by buttercup-run-discover, # signals an error if it can't find any Buttercup test suites. We don't # want that to be an error, so we define advice which ignores that error. local file=$(mktemp) cat >$file <$file <$file <&1) fi output_file=$(mktemp) emacs -Q --batch \ --load=$package_initialize_file \ -L "$load_path" \ "$@" \ &>$output_file exit=$? [[ $exit != 0 ]] && debug "Emacs exited non-zero: $exit" if [[ $verbose -gt 1 || $exit != 0 ]] then cat $output_file fi rm -f $output_file return $exit } # ** Compilation function batch-byte-compile { debug "batch-byte-compile: ERROR-ON-WARN:$compile_error_on_warn FILES:$@" [[ $compile_error_on_warn ]] && local error_on_warn=(--eval "(setq byte-compile-error-on-warn t)") run_emacs \ "${error_on_warn[@]}" \ --funcall batch-byte-compile \ "$@" } # ** Files function project-elisp-files { # Echo list of Elisp files in project. git ls-files 2>/dev/null | egrep "\.el$" | exclude-files } function project-source-files { # Echo list of Elisp files that are not tests. project-elisp-files | egrep -v '^tests?/test-?' } function project-test-files { # Echo list of Elisp test files. project-elisp-files | egrep '^tests?/test-?' } function exclude-files { # Filter out paths (STDIN) which should be excluded by default. egrep -v "(/\.cask/|-autoloads.el)" } function load-files-args { # For file in $@, echo "--load $file". for file in "$@" do printf -- '--load %q ' "$file" done } function files_args { # For file in STDIN, echo "$file". while read file do printf -- '%q ' "$file" done } # ** Utility function cleanup { # Remove temporary paths (${temp_paths[@]}). for path in "${temp_paths[@]}" do if [[ $debug ]] then debug "Debugging enabled: not deleting temporary path: $path" elif [[ -r $path ]] then rm -rf "$path" else debug "Temporary path doesn't exist, not deleting: $path" fi done } function echo_color { # This allows bold, italic, etc. without needing a function for # each variation. local color_code="COLOR_$1" shift if [[ $color ]] then echo -e "${!color_code}${@}${COLOR_off}" else echo "$@" fi } function debug { if [[ $debug ]] then function debug { echo_color yellow "DEBUG ($(ts)): $@" >&2 } debug "$@" else function debug { true } fi } function error { echo_color red "ERROR ($(ts)): $@" >&2 ((errors++)) return 1 } function die { error "$@" exit $errors } function log { echo "LOG ($(ts)): $@" >&2 } function log_color { local color=$1 shift echo_color $color "LOG ($(ts)): $@" >&2 } function success { if [[ $verbose -ge 2 ]] then log_color green "$@" >&2 fi } function verbose { # $1 is the verbosity level, rest are echoed when appropriate. if [[ $verbose -ge $1 ]] then [[ $1 -eq 1 ]] && local color=blue [[ $1 -ge 2 ]] && local color=cyan shift log_color $color "$@" >&2 fi } function ts { date "+%Y-%m-%d %H:%M:%S" } function usage { cat </dev/null | grep "$rule is a function" &>/dev/null then $rule elif [[ $rule = test ]] then # Allow the "tests" rule to be called as "test". Since "test" # is a shell builtin, this workaround is required. tests else error "Invalid rule: $rule" fi done if [[ $errors -gt 0 ]] then log_color red "Finished with $errors errors." else success "Finished without errors." fi exit $errors