diff --git a/Makefile b/Makefile index 9be2568..2c1de7a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,16 @@ -# * Verbosity +# * Arguments + +# For consistency, we use only var=val options, not hyphen-prefixed options. + +ifdef autoinstall + AUTOINSTALL = "--auto-install" +endif + +ifdef sandbox + SANDBOX = "--sandbox" +endif + +# ** Verbosity # Since the "-v" in "make -v" gets intercepted by Make itself, we have # to use a variable. @@ -14,4 +26,4 @@ endif # * Rules %: - @./makem.sh $(VERBOSE) $(@) + @./makem.sh $(VERBOSE) $(SANDBOX) $(AUTOINSTALL) $(@) diff --git a/makem.sh b/makem.sh index 67a92db..d4ea826 100755 --- a/makem.sh +++ b/makem.sh @@ -2,8 +2,32 @@ # * makem.sh --- Script to aid building and testing Emacs Lisp packages +# https://github.com/alphapapa/makem.sh + # * Commentary: +# makem.sh is a script helps to build, lint, and test Emacs Lisp +# packages. It aims to make linting and testing as simple as possible +# without requiring per-package configuration. + +# It works similarly to a Makefile in that "rules" are called to +# perform actions such as byte-compiling, linting, testing, etc. + +# Source and test files are discovered automatically from the +# project's Git repo, and package dependencies within them are parsed +# automatically. + +# Output is simple: by default, there is no output unless errors +# occur. With increasing verbosity levels, more detail gives positive +# feedback. Output is colored by default to make reading easy. + +# When desired, emacs-sandbox.sh can be used as a backend, which +# allows package dependencies to be installed automatically into a +# clean Emacs "sandbox" configuration without affecting the +# developer's personal configuration. This is especially helpful when +# upstream dependencies may have released new versions that differ +# from those installed in the developer's personal configuration. See +# . # * License: @@ -145,21 +169,21 @@ EOF # ** Emacs function run_emacs { - debug "run_emacs: emacs -Q --batch --load=$package_initialize_file -L \"$load_path\" $@" + debug "run_emacs: $emacs_command -Q --batch --load=$package_initialize_file -L \"$load_path\" $@" if [[ $debug_load_path ]] then - debug $(emacs -Q --batch \ - --load=$package_initialize_file \ - -L "$load_path" \ - --eval "(message \"LOAD-PATH: %s\" load-path)" \ - 2>&1) + debug $($emacs_command -Q --batch \ + --load=$package_initialize_file \ + -L "$load_path" \ + --eval "(message \"LOAD-PATH: %s\" load-path)" \ + 2>&1) fi output_file=$(mktemp) - emacs -Q --batch \ - --load=$package_initialize_file \ - -L "$load_path" \ - "$@" \ + $emacs_command -Q --batch \ + --load=$package_initialize_file \ + -L "$load_path" \ + "$@" \ &>$output_file exit=$? @@ -224,6 +248,15 @@ function files_args { done } +function dependencies { + # Echo list of package dependencies. + egrep '^;; Package-Requires: ' $(project-source-files) $(project-test-files) \ + | egrep -o '\([^([:space:]][^)]*\)' \ + | egrep -o '^[^[:space:])]+' \ + | sed -r 's/\(//g' \ + | egrep -v '^emacs$' # Ignore Emacs version requirement. +} + # ** Utility function cleanup { @@ -329,7 +362,6 @@ Options: -d, --debug Print debug info. -h, --help I need somebody! -v, --verbose Increase verbosity, up to -vv. - --debug-load-path Print load-path. -f FILE, --file FILE Check FILE in addition to discovered files. @@ -337,8 +369,19 @@ Options: --no-color Disable color output. -C, --no-compile Don't compile files automatically. +Sandbox options: + These require emacs-sandbox.sh to be on your PATH. Find it at + . + + --sandbox Run Emacs with emacs-sandbox.sh. + --auto-install Automatically install package dependencies. + -i, --install PACKAGE Install PACKAGE before running rules. + Source files are automatically discovered from git, or may be specified with options. + +Package dependencies are discovered from "Package-Requires" headers in +source files. EOF } @@ -444,6 +487,8 @@ function test-ert { # * Defaults +emacs_command="emacs" + # TODO: Disable color if not outputting to a terminal. color=true errors=0 @@ -474,12 +519,15 @@ COLOR_white='\e[0;37m' # * Args -args=$(getopt -n "$0" -o dhvf:C -l debug,debug-load-path,help,verbose,file:,no-color,no-compile -- "$@") || { usage; exit 1; } +args=$(getopt -n "$0" -o dhi:svf:C -l auto-install,debug,debug-load-path,help,install:,verbose,file:,no-color,no-compile,sandbox -- "$@") || { usage; exit 1; } eval set -- "$args" while true do case "$1" in + --auto-install) + auto_install=true + ;; -d|--debug) debug=true verbose=2 @@ -491,6 +539,13 @@ do usage exit ;; + -i|--install) + shift + sandbox_install_packages_args+=(--install "$1") + ;; + -s|--sandbox) + sandbox=true + ;; -v|--verbose) ((verbose++)) ;; @@ -523,6 +578,43 @@ debug "Remaining args: ${rest[@]}" trap cleanup EXIT INT TERM +if [[ $sandbox ]] +then + # Setup sandbox. + config_dir=$(mktemp -d) || die "Unable to make temp dir." + temp_paths+=("$config_dir") + + sandbox_basic_args=( + -d "$config_dir" + ) + [[ $debug ]] && sandbox_basic_args+=(--debug) + + if [[ $auto_install ]] + then + deps=($(dependencies)) + debug "Installing dependencies: ${deps[@]}" + + for package in "${deps[@]}" + do + sandbox_install_packages_args+=(--install $package) + done + fi + + # Initialize the sandbox (installs packages once rather than for + # every rule. + emacs_command="emacs-sandbox.sh ${sandbox_basic_args[@]} ${sandbox_install_packages_args[@]} -- " + debug "Initializing sandbox..." + + run_emacs || die "Unable to initialize sandbox." + + # After the sandbox is initialized and packages are installed, set + # the command to prevent the package lists from being refreshed on + # each invocation. + emacs_command="emacs-sandbox.sh ${sandbox_basic_args[@]} --no-refresh-packages -- " + + debug "Sandbox initialized." +fi + if ! [[ ${project_source_files[@]} ]] then error "No files specified and not in a git repo."