branch: elpa-admin commit 7e5b75b0b0807f914ccecb8265d6e3d005dd4dc2 Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Stefan Monnier <monn...@iro.umontreal.ca>
* elpa-admin.el: Add support for checking copyright notices (elpaa--copyright-file): New variable. (elpaa--copyright-check): New function. (elpaa--make-one-tarball): Use it. (elpaa--copyright-filter, elpaa--copyright-collect) (elpaa--copyright-files, elpaa-batch-copyright-check): New functions. --- GNUmakefile | 47 +++++------------------------- README | 6 +++- elpa-admin.el | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 43 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 3f02588..d9fe616 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -3,50 +3,16 @@ EMACS=emacs --batch -.PHONY: archive-tmp changelogs process-archive archive-full org-fetch clean all do-it - +.PHONY: all all: all-in-place -CR_EXCEPTIONS=copyright_exceptions -.PHONY: check_copyrights -check_copyrights: - @echo "Compute exceptions >$(CR_EXCEPTIONS)~" - @export LC_ALL=C; \ - (cd packages && \ - find . -name '.git' -prune -o \ - -name 'test' -prune -o \ - -name '*.el' -print0 | \ - xargs -0 grep -L 'Free Software Foundation, Inc' | \ - grep -v '\(\.dir-locals\|.-\(pkg\|autoloads\)\)\.el$$'; \ - find . -name '.git' -prune -o -name '*.el' -type f -print | \ - while read f; do \ - fquoted="$$(echo $$f|tr '|' '_')"; \ - sed -n -e '/[Cc]opyright.*, *[1-9][-0-9]*,\?$$/N' \ - -e '/Free Software Foundation/d' \ - -e "s|^\\(.*;.*[Cc]opyright\\)|$$fquoted:\\1|p" \ - "$$f"; \ - done) | sort >$(CR_EXCEPTIONS)~ - diff -u "$(CR_EXCEPTIONS)" "$(CR_EXCEPTIONS)~" - -.PHONY: check/% +.PHONY: check/% check-all +check-all: check/- check/%: - @export LC_ALL=C; \ - (cd packages && \ - find ./$* -name '.git' -prune -o \ - -name 'test' -prune -o \ - -name '*.el' -print0 | \ - xargs -0 grep -L 'Free Software Foundation, Inc' | \ - grep -v '\(\.dir-locals\|.-\(pkg\|autoloads\)\)\.el$$'; \ - find ./$* -name '.git' -prune -o -name '*.el' -type f -print | \ - while read f; do \ - fquoted="$$(echo $$f|tr '|' '_')"; \ - sed -n -e '/[Cc]opyright.*, *[1-9][-0-9]*,\?$$/N' \ - -e '/Free Software Foundation/d' \ - -e "s|^\\(.*;.*[Cc]opyright\\)|$$fquoted:\\1|p" \ - "$$f"; \ - done; \ - cat ../$(CR_EXCEPTIONS) ../$(CR_EXCEPTIONS)) | sort | uniq -u + $(EMACS) -l $(CURDIR)/admin/elpa-admin.el \ + -f elpaa-batch-copyright-check $* +.PHONY: build/% build-all build/%: $(EMACS) -l $(CURDIR)/admin/elpa-admin.el \ -f elpaa-batch-make-one-package $* @@ -55,6 +21,7 @@ build-all: $(EMACS) -l $(CURDIR)/admin/elpa-admin.el \ -f elpaa-batch-make-all-packages +.PHONY: clean clean: # rm -rf archive $(ARCHIVE_TMP) rm -f packages/*/*-autoloads.el diff --git a/README b/README index 6dcf846..0cc8fd9 100644 --- a/README +++ b/README @@ -19,7 +19,11 @@ This code expects to be used in a directory that has the following layout: - =admin/= -- Directory containing a copy of the here files - =GNUmakefile= -- A copy of or symlink to =admin/GNUmakefile= - =externals-list= -- The specifications of the packages - +- =copyright_exceptions= -- List of exceptions for the copyright-notices checks + +The =copyright_exceptions= file can be absent, in which case copyright notices +will simply not be checked. + Additionally to the above, this code will then add to that directory the following elements: diff --git a/elpa-admin.el b/elpa-admin.el index e8918ee..9e99ae0 100644 --- a/elpa-admin.el +++ b/elpa-admin.el @@ -22,7 +22,6 @@ ;;;; TODO ;; Missing from GNU ELPA script: -;; - check_copyrights ;; - Support for :core (seems to be partly working, actually, tho it likely ;; doesn't select the right release revision). ;; - Support for Org's package @@ -37,7 +36,7 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) +(require 'cl-lib) (require 'lisp-mnt) (require 'package) @@ -54,6 +53,7 @@ (defconst elpaa--release-branch-prefix "externals-release/") (defconst elpaa--specs-file "externals-list") +(defconst elpaa--copyright-file "copyright_exceptions") (defvar elpaa--debug t) (defun elpaa--message (&rest args) @@ -256,6 +256,7 @@ Return non-nil if a new tarball was created." (delete-file (expand-file-name (format "%s-pkg.el" pkgname) dir)) (when revision-function (elpaa--select-revision dir pkg-spec (funcall revision-function))) + (elpaa--copyright-check) ;; FIXME: Build Info files and corresponding `dir' file. (elpaa--write-pkg-file dir pkgname metadata) ;; FIXME: Allow renaming files or selecting a subset of the files! @@ -1121,6 +1122,94 @@ If WITH-CORE is non-nil, it means we manage :core packages as well." (message "Unknown package kind `%S' for %s" kind pkg) (message "Unknown package %s" pkg)))))))) +;;; Check copyrights + +(defun elpaa--copyright-files (pkg-spec) + "Return the list of ELisp files in the package PKG-SPEC." + (let* ((pkgname (car pkg-spec)) + (default-directory (elpaa--dirname pkgname "packages")) + (ignores (elpaa--spec-get pkg-spec :ignored-files)) + (all-ignores '("." ".." ".git" "test" ".dir-locals.el")) + (dir-files (lambda (d) + (cl-set-difference (directory-files d) + all-ignores :test #'equal))) + (pending (cl-set-difference + (funcall dir-files ".") + (list (concat pkgname "-pkg.el") + (concat pkgname "-autoloads.el")) + :test #'equal)) + (files '())) + (while pending + (pcase (pop pending) + ((pred (lambda (f) (member f ignores)))) + ((pred file-symlink-p)) + ((and (pred file-directory-p) d) + (setq pending (nconc (mapcar (lambda (f) (concat d "/" f)) + (funcall dir-files d)) + pending))) + ((and (pred (string-match "\\.el\\'")) f) + (push f files)))) + files)) + +(defun elpaa--copyright-collect (pkg-spec) + ;; This is crude but is only meant to catch the all too common mistakes where + ;; we forget to update the copyright information after transferring the + ;; copyright to the FSF. + (with-temp-buffer + (let* ((pkgname (car pkg-spec)) + (files (mapcar (lambda (f) (concat pkgname "/" f)) + (elpaa--copyright-files pkg-spec))) + (default-directory (elpaa--dirname "packages"))) + ;; Look for ELisp files which omit a copyright line for the FSF. + (apply #'elpaa--call t "grep" "-L" "Free Software Foundation, Inc" files) + ;; Look for *other* lines attributing copyright to someone else. + (dolist (file files) + (elpaa--call t "sed" "-n" + "-e" "/[Cc]opyright.*, *[1-9][-0-9]*,\\?$/N" + "-e" "/Free Software Foundation/d" + ;; FIXME: This tends to suffer from misc false positives. + "-e" (format "s|^\\(.*;.*[Cc]opyright\\)|%s:\\1|p" + (replace-regexp-in-string "|" "_" file)) + file))) + (sort-lines nil (point-min) (point-max)) + (buffer-string))) + +(defun elpaa--copyright-filter (collected) + (let ((res '())) + (with-current-buffer (find-file-noselect elpaa--copyright-file) + (dolist (line (split-string collected "\n" t)) + (goto-char (point-min)) + (unless (re-search-forward (concat "^" (regexp-quote line) "$") nil t) + (push line res)))) + res)) + +(defun elpaa--copyright-check (pkg-name) + "Check the copyright notices, if applicable." + (when (file-readable-p elpaa--copyright-file) + (let* ((collected (elpaa--copyright-collect pkg-name)) + (filtered (elpaa--copyright-filter collected))) + (when filtered + (message "Problem with copyright notices:\n%s" + (mapconcat (lambda (line) + (if (string-match ":" line) line + (concat "Missing copyright notice in " line))) + filtered "\n")) + (error "Abort"))))) + +(defun elpaa-batch-copyright-check (&rest _) + (let ((specs (elpaa--get-specs)) + (pkgs command-line-args-left)) + (setq command-line-args-left nil) + (when (equal pkgs '("-")) + (setq pkgs (delq nil (mapcar (lambda (spec) + (when (file-directory-p + (elpaa--dirname (car spec) + "packages")) + (car spec))) + specs)))) + (dolist (pkg pkgs) + (elpaa--copyright-check (assoc pkg specs))))) + ;;; Fetch updates from upstream (defun elpaa--branch (pkg-spec)