mirror of
https://github.com/dimitri/el-get.git
synced 2024-09-29 04:58:53 +08:00
ccca97f226
Since Emacs 27 the package cl is deprecated, the replacement is cl-lib, which is available since Emacs 24.3. This patch replaces cl by cl-lib and drops support for Emacs versions less than 24.3. Dropping older Emacsen is required, because cl-lib is a builtin starting from version 24.3 and doesn't need an extra package from ELPA. Testcases for past issues still contain cl. Most of them seem to be broken and need further investigation. This patch is tested with test/run-ert.sh, which outputs: Ran 10 tests, 10 results as expected, 0 unexpected (2021-01-30 13:24:54+0100, 0.672122 sec) 1 expected failures and manually by daily usage for a month now.
226 lines
9.6 KiB
EmacsLisp
226 lines
9.6 KiB
EmacsLisp
;;; el-get-recipes.el --- Manage the external elisp bits and pieces you depend upon
|
|
;;
|
|
;; Copyright (C) 2010-2011 Dimitri Fontaine
|
|
;;
|
|
;; Author: Dimitri Fontaine <dim@tapoueh.org>
|
|
;; URL: http://www.emacswiki.org/emacs/el-get
|
|
;; GIT: https://github.com/dimitri/el-get
|
|
;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/
|
|
;;
|
|
;; This file is NOT part of GNU Emacs.
|
|
;;
|
|
;; Install
|
|
;; Please see the README.md file from the same distribution
|
|
|
|
;;; Commentary:
|
|
;;
|
|
;; el-get-check provides some functions to check for some errors in recipes.
|
|
;;
|
|
|
|
;;; Code:
|
|
|
|
(require 'cl-lib)
|
|
(require 'el-get-recipes)
|
|
(require 'el-get-build)
|
|
|
|
(defvar warning-minimum-log-level)
|
|
(defvar warning-minimum-level)
|
|
(declare-function warning-numeric-level "warnings" (level))
|
|
|
|
(defvar el-get-check--last-file-or-buffer nil
|
|
"The last file-or-buffer checked.")
|
|
|
|
(defun el-get-check-redo ()
|
|
"Rerun `el-get-check-recipe' with last recipe."
|
|
(interactive)
|
|
(when el-get-check--last-file-or-buffer
|
|
(el-get-check-recipe
|
|
el-get-check--last-file-or-buffer)))
|
|
|
|
(defvar el-get-check-mode-map
|
|
(let ((map (make-sparse-keymap)))
|
|
(set-keymap-parent map special-mode-map)
|
|
(define-key map "g" #'el-get-check-redo)
|
|
map)
|
|
"Mode map for `el-get-check-mode'.")
|
|
|
|
(define-derived-mode el-get-check-mode special-mode "El-Get Check"
|
|
"Special mode for `el-get-check-recipe' buffers.
|
|
See Info node `(el-get) Authoring Recipes'.")
|
|
|
|
(defvar el-get-check-suppressed-warnings ()
|
|
"List of `el-get-check-recipe' warnings to suppress.
|
|
|
|
Current possibe elements are:
|
|
`features', `github', `autoloads'")
|
|
|
|
(defun el-get-check-recipe-batch-1 (recipe-file)
|
|
(let ((warning-prefix-function
|
|
(lambda (level entry)
|
|
(list level (format "%s:%s" el-get-check--last-file-or-buffer
|
|
(format (nth 1 entry) ""))))))
|
|
(condition-case err
|
|
(el-get-check-recipe (file-relative-name recipe-file))
|
|
(error (lwarn '(el-get) :emergency "%s" (error-message-string err))
|
|
1))))
|
|
|
|
(defun el-get-check-recipe-batch ()
|
|
"emacs -Q -batch -f el-get-check-recipe-batch [-W<:level>] [-Wno-<warning>...] *.rcp
|
|
|
|
<:level> can be any valid warning level, see `warning-levels'.
|
|
See `el-get-check-suppressed-warnings' for possible <warning> values."
|
|
(cl-assert noninteractive nil
|
|
"`el-get-check-recipe-batch' should only be used with -batch")
|
|
(setq vc-handled-backends nil) ; avoid loading VC during batch mode
|
|
(cl-loop for arg in command-line-args-left
|
|
if (string-match "\\`-Wno-\\(.*\\)" arg)
|
|
do (push (intern (match-string 1 arg)) el-get-check-suppressed-warnings)
|
|
else if (string-match "\\`-W\\(:[-a-z]*\\)" arg)
|
|
do (setq warning-minimum-log-level
|
|
(setq warning-minimum-level (intern (match-string 1 arg))))
|
|
else summing
|
|
(if (file-directory-p arg)
|
|
(cl-reduce #'+ (directory-files arg t "\\.rcp$" t)
|
|
:key #'el-get-check-recipe-batch-1 :initial-value 0)
|
|
(el-get-check-recipe-batch-1 arg))
|
|
into errors
|
|
finally (progn (message "%d warning/error(s) total." errors)
|
|
(kill-emacs (if (zerop errors) 0 1)))))
|
|
|
|
;;;###autoload
|
|
(defun el-get-check-recipe (file-or-buffer)
|
|
"Check the format of the recipe.
|
|
Please run this command before sending a pull request.
|
|
Usage: M-x el-get-check-recipe RET
|
|
|
|
You can run this function from checker script like this:
|
|
test/check-recipe.el PATH/TO/RECIPE.rcp
|
|
|
|
When used as a lisp function, FILE-OR-BUFFER must be a buffer
|
|
object or a file path."
|
|
(interactive (list (current-buffer)))
|
|
(setq el-get-check--last-file-or-buffer file-or-buffer)
|
|
(if (bufferp file-or-buffer)
|
|
(with-current-buffer file-or-buffer
|
|
(el-get-check-recipe-in-current-buffer (buffer-file-name)))
|
|
(with-temp-buffer
|
|
(insert-file-contents file-or-buffer)
|
|
(el-get-check-recipe-in-current-buffer file-or-buffer))))
|
|
|
|
(eval-and-compile
|
|
(unless (fboundp 'file-name-base) ; new in 24.3
|
|
(defun file-name-base (&optional filename)
|
|
"Return the base name of the FILENAME: no directory, no extension.
|
|
FILENAME defaults to `buffer-file-name'."
|
|
(file-name-sans-extension
|
|
(file-name-nondirectory (or filename (buffer-file-name)))))))
|
|
|
|
(defvar el-get-check-warning-buffer)
|
|
(defvar el-get-check-error-count)
|
|
|
|
(defun el-get-check-warning (level message &rest args)
|
|
(declare (indent 1))
|
|
(display-warning '(el-get recipe) (apply #'format message args)
|
|
level el-get-check-warning-buffer)
|
|
(when (>= (warning-numeric-level level)
|
|
(warning-numeric-level warning-minimum-level))
|
|
(cl-incf el-get-check-error-count)))
|
|
|
|
(defun el-get-check-recipe-in-current-buffer (recipe-file-name)
|
|
(let ((inhibit-read-only t)
|
|
(el-get-check-error-count 0)
|
|
(el-get-check-warning-buffer (get-buffer-create "*el-get check recipe*")))
|
|
(display-buffer el-get-check-warning-buffer)
|
|
(with-current-buffer el-get-check-warning-buffer
|
|
(erase-buffer)
|
|
(el-get-check-mode))
|
|
(let* ((recipe (save-excursion
|
|
(goto-char (point-min))
|
|
(prog1 (read (current-buffer))
|
|
(let ((lvl-err (condition-case err
|
|
(progn (read (current-buffer))
|
|
`(:warning . "Extra data following recipe"))
|
|
(end-of-file nil)
|
|
(error `(:error . ,(error-message-string err))))))
|
|
(when lvl-err
|
|
(let ((el-get-check--last-file-or-buffer
|
|
(format "%s:%d:%d" recipe-file-name
|
|
(line-number-at-pos) (current-column))))
|
|
(el-get-check-warning (car lvl-err) (cdr lvl-err))))))))
|
|
(el-get-sources (list recipe))
|
|
(pkg-name (plist-get recipe :name)))
|
|
(when (and recipe-file-name
|
|
(not (string= (file-name-base recipe-file-name) pkg-name)))
|
|
(el-get-check-warning :error
|
|
"File name should match recipe name."))
|
|
;; Check if userspace property is used.
|
|
(cl-loop for key in '(:before :after)
|
|
for alt in '(:prepare :post-init)
|
|
when (plist-get recipe key)
|
|
do (el-get-check-warning :warning
|
|
"Property %S is for user. Use %S instead."
|
|
key alt))
|
|
;; Check for misformatted plists
|
|
(cl-loop for key in recipe by #'cddr
|
|
unless (keywordp key)
|
|
do (el-get-check-warning :warning
|
|
"Property %S is not a keyword!"
|
|
key))
|
|
(cl-destructuring-bind (&key type url autoloads feats builtin
|
|
&allow-other-keys)
|
|
recipe
|
|
;; let-binding `features' causes `provide' to throw error
|
|
(setq feats (plist-get recipe :features))
|
|
;; Is github type used?
|
|
(when (and (not (memq 'github el-get-check-suppressed-warnings))
|
|
(eq type 'git) (string-match "//github.com/" url))
|
|
(el-get-check-warning :warning
|
|
"Use `:type github' for github type recipe"))
|
|
;; Warn when `:autoloads nil' is specified.
|
|
(when (and (not (memq 'autoloads el-get-check-suppressed-warnings))
|
|
(null autoloads) (plist-member recipe :autoloads))
|
|
(el-get-check-warning :warning
|
|
"Are you sure you don't need autoloads?
|
|
This property should be used only when the library takes care of
|
|
the autoload."))
|
|
;; Warn when `:features t' is specified
|
|
(when (and (not (memq 'features el-get-check-suppressed-warnings))
|
|
feats)
|
|
(el-get-check-warning :warning
|
|
"Are you sure you need features?
|
|
If this library has `;;;###autoload' comment (a.k.a autoload cookie),
|
|
you don't need `:features'."))
|
|
;; Check if `:builtin' is used with an integer
|
|
(when (integerp builtin)
|
|
(el-get-check-warning :warning
|
|
"Usage of integers for :builtin is obsolete.
|
|
Use a version string like \"24.3\" instead.")))
|
|
;; Check for shell interpolated :build commands
|
|
(let ((safe-functions '(backquote-list*
|
|
el-get-load-path el-get-package-exists-p
|
|
el-get-package-directory el-get-print-to-string
|
|
el-get-verbose-message
|
|
with-temp-buffer insert-file-contents
|
|
directory-files file-name-as-directory
|
|
expand-file-name shell-quote-argument)))
|
|
(dolist (sys '("" "/darwin" "/berkeley-unix" "/windows-nt"))
|
|
(let ((unsafe (catch 'unsafe-build
|
|
(when (cl-some #'stringp (el-get-build-commands pkg-name 'safe-eval sys))
|
|
(el-get-check-warning :warning
|
|
":build%s should be a *list* of string lists." sys))
|
|
nil)))
|
|
(when unsafe
|
|
(el-get-check-warning :debug ":build%s is unsafep: %s" sys unsafe)))))
|
|
;; Check for required properties.
|
|
(cl-loop for key in '(:description :name)
|
|
unless (plist-get recipe key)
|
|
do (el-get-check-warning
|
|
:error "Required property %S is not defined." key))
|
|
(with-current-buffer el-get-check-warning-buffer
|
|
(insert (format "\n%s: %s error(s) found." recipe-file-name el-get-check-error-count))))
|
|
el-get-check-error-count))
|
|
|
|
(provide 'el-get-check)
|
|
|
|
;;; el-get-check.el ends here
|