3
3
# * makem.sh --- Script to aid building and testing Emacs Lisp packages
4
4
5
5
# URL: https://github.com/alphapapa/makem.sh
6
- # Version: 0.3
6
+ # Version: 0.6-pre
7
7
8
8
# * Commentary:
9
9
10
- # makem.sh is a script helps to build, lint, and test Emacs Lisp
10
+ # makem.sh is a script that helps to build, lint, and test Emacs Lisp
11
11
# packages. It aims to make linting and testing as simple as possible
12
12
# without requiring per-package configuration.
13
13
79
79
Options:
80
80
-d, --debug Print debug info.
81
81
-h, --help I need somebody!
82
- -v, --verbose Increase verbosity, up to -vv .
82
+ -v, --verbose Increase verbosity, up to -vvv .
83
83
--no-color Disable color output.
84
84
85
85
--debug-load-path Print load-path from inside Emacs.
136
136
echo $file
137
137
}
138
138
139
+ function elisp-elint-file {
140
+ local file=$( mktemp)
141
+ cat > $file << EOF
142
+ (require 'cl-lib)
143
+ (require 'elint)
144
+ (defun makem-elint-file (file)
145
+ (let ((errors 0))
146
+ (cl-letf (((symbol-function 'orig-message) (symbol-function 'message))
147
+ ((symbol-function 'message) (symbol-function 'ignore))
148
+ ((symbol-function 'elint-output)
149
+ (lambda (string)
150
+ (cl-incf errors)
151
+ (orig-message "%s" string))))
152
+ (elint-file file)
153
+ ;; NOTE: \` errors' is not actually the number of errors, because
154
+ ;; it's incremented for non-error header strings as well.
155
+ (kill-emacs errors))))
156
+ EOF
157
+ echo " $file "
158
+ }
159
+
139
160
function elisp-checkdoc-file {
140
161
# Since checkdoc doesn't have a batch function that exits non-zero
141
162
# when errors are found, we make one.
@@ -154,7 +175,8 @@ function elisp-checkdoc-file {
154
175
": " text)))
155
176
(message msg)
156
177
(setq makem-checkdoc-errors-p t)
157
- (list text start end unfixable)))))
178
+ ;; Return nil because we *are* generating a buffered list of errors.
179
+ nil))))
158
180
(mapcar #'checkdoc-file files)
159
181
(when makem-checkdoc-errors-p
160
182
(kill-emacs 1))))
165
187
echo $file
166
188
}
167
189
190
+ function elisp-byte-compile-file {
191
+ # This seems to be the only way to make byte-compilation signal
192
+ # errors for warnings AND display all warnings rather than only
193
+ # the first one.
194
+ local file=$( mktemp)
195
+ # TODO: Add file to $paths_temp in other elisp- functions.
196
+ paths_temp+=(" $file " )
197
+
198
+ cat > " $file " << EOF
199
+ (defun makem-batch-byte-compile (&rest args)
200
+ ""
201
+ (let ((num-errors 0)
202
+ (num-warnings 0))
203
+ ;; NOTE: Only accepts files as args, not directories.
204
+ (dolist (file command-line-args-left)
205
+ (pcase-let ((\` (,errors ,warnings) (makem-byte-compile-file file)))
206
+ (cl-incf num-errors errors)
207
+ (cl-incf num-warnings warnings)))
208
+ (zerop num-errors)))
209
+
210
+ (defun makem-byte-compile-file (filename &optional load)
211
+ "Call \` byte-compile-warn', returning the number of errors and the number of warnings."
212
+ (let ((num-warnings 0)
213
+ (num-errors 0))
214
+ (cl-letf (((symbol-function 'byte-compile-warn)
215
+ (lambda (format &rest args)
216
+ ;; Copied from \` byte-compile-warn'.
217
+ (cl-incf num-warnings)
218
+ (setq format (apply #'format-message format args))
219
+ (byte-compile-log-warning format t :warning)))
220
+ ((symbol-function 'byte-compile-report-error)
221
+ (lambda (error-info &optional fill &rest args)
222
+ (cl-incf num-errors)
223
+ ;; Copied from \` byte-compile-report-error'.
224
+ (setq byte-compiler-error-flag t)
225
+ (byte-compile-log-warning
226
+ (if (stringp error-info) error-info
227
+ (error-message-string error-info))
228
+ fill :error))))
229
+ (byte-compile-file filename load))
230
+ (list num-errors num-warnings)))
231
+ EOF
232
+ echo " $file "
233
+ }
234
+
168
235
function elisp-check-declare-file {
169
236
# Since check-declare doesn't have a batch function that exits
170
237
# non-zero when errors are found, we make one.
@@ -200,20 +267,23 @@ Exits non-zero if mis-indented lines are found. Checks files in
200
267
(let ((errors-p))
201
268
(cl-labels ((lint-file (file)
202
269
(find-file file)
203
- (let ((tick (buffer-modified-tick)))
204
- (let ((inhibit-message t))
205
- (indent-region (point-min) (point-max)))
206
- (when (/= tick (buffer-modified-tick))
207
- ;; Indentation changed: warn for each line.
208
- (dolist (line (undo-lines buffer-undo-list))
209
- (message "%s:%s: Indentation mismatch" (buffer-name) line))
210
- (setf errors-p t))))
270
+ (let ((inhibit-message t))
271
+ (indent-region (point-min) (point-max)))
272
+ (when buffer-undo-list
273
+ ;; Indentation changed: warn for each line.
274
+ (dolist (line (undo-lines buffer-undo-list))
275
+ (message "%s:%s: Indentation mismatch" (buffer-name) line))
276
+ (setf errors-p t)))
277
+ (undo-pos (entry)
278
+ (cl-typecase (car entry)
279
+ (number (car entry))
280
+ (string (abs (cdr entry)))))
211
281
(undo-lines (undo-list)
212
282
;; Return list of lines changed in UNDO-LIST.
213
283
(nreverse (cl-loop for elt in undo-list
214
- when (and (consp elt)
215
- (numberp (car elt)))
216
- collect (line-number-at-pos (car elt) )))))
284
+ for pos = (undo-pos elt)
285
+ when pos
286
+ collect (line-number-at-pos pos )))))
217
287
(mapc #'lint-file (mapcar #'expand-file-name command-line-args-left))
218
288
(when errors-p
219
289
(kill-emacs 1)))))
@@ -232,7 +302,6 @@ function elisp-package-initialize-file {
232
302
(cons "melpa-stable" "https://stable.melpa.org/packages/")))
233
303
$elisp_org_package_archive
234
304
(package-initialize)
235
- (setq load-prefer-newer t)
236
305
EOF
237
306
echo $file
238
307
}
@@ -245,6 +314,7 @@ function run_emacs {
245
314
local emacs_command=(
246
315
" ${emacs_command[@]} "
247
316
-Q
317
+ --eval " (setq load-prefer-newer t)"
248
318
" ${args_debug[@]} "
249
319
" ${args_sandbox[@]} "
250
320
-l $package_initialize_file
@@ -286,8 +356,9 @@ function batch-byte-compile {
286
356
[[ $compile_error_on_warn ]] && local error_on_warn=(--eval " (setq byte-compile-error-on-warn t)" )
287
357
288
358
run_emacs \
359
+ --load " $( elisp-byte-compile-file) " \
289
360
" ${error_on_warn[@]} " \
290
- --funcall batch-byte-compile \
361
+ --eval " (unless (makem- batch-byte-compile) (kill-emacs 1)) " \
291
362
" $@ "
292
363
}
293
364
@@ -297,10 +368,13 @@ function byte-compile-file {
297
368
298
369
[[ $compile_error_on_warn ]] && local error_on_warn=(--eval " (setq byte-compile-error-on-warn t)" )
299
370
371
+ # FIXME: Why is the line starting with "&& verbose 3" not indented properly? Emacs insists on indenting it back a level.
300
372
run_emacs \
373
+ --load " $( elisp-byte-compile-file) " \
301
374
" ${error_on_warn[@]} " \
302
- --eval " (byte-compile-file \" $file \" )" \
303
- || error " Compiling file failed: $file "
375
+ --eval " (pcase-let ((\` (,num-errors ,num-warnings) (makem-byte-compile-file \" $file \" ))) (when (or (and byte-compile-error-on-warn (not (zerop num-warnings))) (not (zerop num-errors))) (kill-emacs 1)))" \
376
+ && verbose 3 " Compiling $file finished without errors." \
377
+ || { verbose 3 " Compiling file failed: $file " ; return 1; }
304
378
}
305
379
306
380
# ** Files
@@ -376,7 +450,8 @@ function args-load-files {
376
450
# For file in $@, echo "--load $file".
377
451
for file in " $@ "
378
452
do
379
- printf -- ' --load %q ' " $file "
453
+ sans_extension=${file%% .el}
454
+ printf -- ' --load %q ' " $sans_extension "
380
455
done
381
456
}
382
457
@@ -413,8 +488,7 @@ function ert-tests-p {
413
488
}
414
489
415
490
function package-main-file {
416
- # Echo the package's main file. Helpful for setting package-lint-main-file.
417
-
491
+ # Echo the package's main file.
418
492
file_pkg=$( git ls-files ./* -pkg.el 2> /dev/null)
419
493
420
494
if [[ $file_pkg ]]
@@ -496,6 +570,8 @@ function sandbox {
496
570
args_sandbox=(
497
571
--title " makem.sh: $( basename $( pwd) ) (sandbox: $sandbox_dir )"
498
572
--eval " (setq user-emacs-directory (file-truename \" $sandbox_dir \" ))"
573
+ --load package
574
+ --eval " (setq package-user-dir (expand-file-name \" elpa\" user-emacs-directory))"
499
575
--eval " (setq user-init-file (file-truename \" $init_file \" ))"
500
576
)
501
577
@@ -658,7 +734,8 @@ function verbose {
658
734
if [[ $verbose -ge $1 ]]
659
735
then
660
736
[[ $1 -eq 1 ]] && local color_name=blue
661
- [[ $1 -ge 2 ]] && local color_name=cyan
737
+ [[ $1 -eq 2 ]] && local color_name=cyan
738
+ [[ $1 -ge 3 ]] && local color_name=white
662
739
663
740
shift
664
741
log_color $color_name " $@ " >&2
@@ -706,9 +783,7 @@ function compile-batch {
706
783
verbose 2 " Batch-compiling files..."
707
784
debug " Byte-compile files: ${files_project_byte_compile[@]} "
708
785
709
- batch-byte-compile " ${files_project_byte_compile[@]} " \
710
- && success " Compiling finished without errors." \
711
- || error " Compilation failed."
786
+ batch-byte-compile " ${files_project_byte_compile[@]} "
712
787
}
713
788
714
789
function compile-each {
@@ -726,9 +801,7 @@ function compile-each {
726
801
|| compile_errors=t
727
802
done
728
803
729
- ! [[ $compile_errors ]] \
730
- && success " Compiling finished without errors." \
731
- || error " Compilation failed."
804
+ [[ ! $compile_errors ]]
732
805
}
733
806
734
807
function compile {
@@ -738,6 +811,18 @@ function compile {
738
811
else
739
812
compile-each " $@ "
740
813
fi
814
+ local status=$?
815
+
816
+ if [[ $compile_error_on_warn ]]
817
+ then
818
+ # Linting: just return status code, because lint rule will print messages.
819
+ [[ $status = 0 ]]
820
+ else
821
+ # Not linting: print messages here.
822
+ [[ $status = 0 ]] \
823
+ && success " Compiling finished without errors." \
824
+ || error " Compiling failed."
825
+ fi
741
826
}
742
827
743
828
function batch {
@@ -752,12 +837,15 @@ function batch {
752
837
753
838
function interactive {
754
839
# Run Emacs interactively. Most useful with --sandbox and --install-deps.
840
+ local load_file_args=$( args-load-files " ${files_project_feature[@]} " " ${files_project_test[@]} " )
755
841
verbose 1 " Running Emacs interactively..."
756
- verbose 2 " Loading files:" " ${files_project_feature[@]} " " ${files_project_test[@]} "
842
+ verbose 2 " Loading files: ${load_file_args// --load / } "
843
+
844
+ [[ $compile ]] && compile
757
845
758
846
unset arg_batch
759
847
run_emacs \
760
- $( args-load-files " ${files_project_feature[@]} " " ${files_project_test[@]} " ) \
848
+ $load_file_args \
761
849
--eval " (load user-init-file)" \
762
850
" ${args_batch_interactive[@]} "
763
851
arg_batch=" --batch"
@@ -769,6 +857,9 @@ function lint {
769
857
lint-checkdoc
770
858
lint-compile
771
859
lint-declare
860
+ # NOTE: Elint doesn't seem very useful at the moment. See comment
861
+ # in lint-elint function.
862
+ # lint-elint
772
863
lint-indent
773
864
lint-package
774
865
lint-regexps
@@ -825,6 +916,28 @@ function lint-elsa {
825
916
|| error " Linting with Elsa failed."
826
917
}
827
918
919
+ function lint-elint {
920
+ # NOTE: Elint gives a lot of spurious warnings, apparently because it doesn't load files
921
+ # that are `require'd, so its output isn't very useful. But in case it's improved in
922
+ # the future, and since this wrapper code already works, we might as well leave it in.
923
+ verbose 1 " Linting with Elint..."
924
+
925
+ local errors=0
926
+ for file in " ${files_project_feature[@]} "
927
+ do
928
+ verbose 2 " Linting with Elint: $file ..."
929
+ run_emacs \
930
+ --load " $( elisp-elint-file) " \
931
+ --eval " (makem-elint-file \" $file \" )" \
932
+ && verbose 3 " Linting with Elint found no errors." \
933
+ || { error " Linting with Elint failed: $file " ; (( errors++ )) ; }
934
+ done
935
+
936
+ [[ $errors = 0 ]] \
937
+ && success " Linting with Elint finished without errors." \
938
+ || error " Linting with Elint failed."
939
+ }
940
+
828
941
function lint-indent {
829
942
verbose 1 " Linting indentation..."
830
943
0 commit comments