Skip to content

Commit 85bfd98

Browse files
committed
check package version constraints when building packages
Ensure dependencies' minimum versions are satisfied when building E. If not, the order is failed. This is only checked when building packages to prevent slower init times. elpaca-info--buttons: replace with elpaca-info--button elpaca-info--print: add dependency versions to info section, improve formatting elpaca-build-steps: add elpaca--check-version elpaca--queue-dependencies: remove Emacs version check elpaca--latest-tag: elpaca--tag-regexp elpaca--date-version, elpaca--core-date, elpaca--declared-version, elpaca--check-version: add to check versions
1 parent 7506c06 commit 85bfd98

File tree

2 files changed

+91
-27
lines changed

2 files changed

+91
-27
lines changed

elpaca-info.el

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,9 @@
121121
when vals concat (string-join vals "\n"))
122122
")"))))))
123123

124-
(defun elpaca-info--buttons (ids)
125-
"Return list of buttons from IDS."
126-
(mapcar (lambda (id) (elpaca-ui--buttonize (symbol-name id)
127-
(lambda (id) (elpaca-info--print id))
128-
id))
129-
ids))
124+
(defun elpaca-info--button (id)
125+
"Return info button for package associated with ID."
126+
(elpaca-ui--buttonize (symbol-name id) (lambda (id) (elpaca-info--print id)) id))
130127

131128
(defun elpaca-info--files (files)
132129
"Return list of formatted FILES strings."
@@ -174,20 +171,31 @@
174171
(elpaca-info--format-recipe (format "%S" (plist-get item :recipe)))))
175172
(elpaca-info--section "%s\n%s" "full recipe:" (elpaca-info--recipe item))
176173
(elpaca-info--section "%s %s" "dependencies:"
177-
(if-let ((ds (remq 'emacs (ignore-errors (elpaca-dependencies id)))))
178-
(concat i (string-join (elpaca-info--buttons (cl-sort ds #'string<)) i))
174+
(if-let ((deps (ignore-errors (elpaca--dependencies (elpaca-get id) t))))
175+
(concat "\n"
176+
(substring
177+
(cl-loop
178+
with max = (cl-loop for (id . _) in deps maximize (length (symbol-name id)))
179+
for (id . min) in deps concat
180+
(format (concat " %" (number-to-string (- max)) "s >= %s\n")
181+
(if (eq id 'emacs) id (elpaca-info--button id))
182+
(car min)))
183+
0 -1))
179184
(if on-disk-p "nil"
180185
(if (memq item (cl-set-difference elpaca-ignored-dependencies '(emacs elpaca)))
181186
"built-in" "?"))))
182187
(elpaca-info--section "%s %s" "dependents:"
183188
(if-let ((ds (remq 'emacs (elpaca--dependents id 'noerror))))
184-
(concat i (string-join (elpaca-info--buttons (cl-sort ds #'string<)) i))
189+
(concat i (mapconcat #'elpaca-info--button (cl-sort ds #'string<) i))
185190
(if on-disk-p "nil" "?")))
186-
(when e (elpaca-info--section "%s %s" "commit: "
187-
(let ((default-directory (elpaca<-repo-dir e)))
188-
(string-trim (or (ignore-errors (elpaca-process-output
189-
"git" "rev-parse" "--short" "HEAD"))
190-
"")))))
191+
(when e (elpaca-info--section "%s %s" "version:"
192+
(concat (when-let ((default-directory (elpaca<-repo-dir e))
193+
(version (elpaca--declared-version e)))
194+
(string-trim version))
195+
" "
196+
(when-let ((commit (ignore-errors (elpaca-process-output
197+
"git" "rev-parse" "--short" "HEAD"))))
198+
(string-trim commit)))))
191199
(when-let ((e) (statuses (elpaca<-statuses e)))
192200
(elpaca-info--section "%s\n %S" "statuses:" statuses))
193201
(when-let ((e) (files (ignore-errors (elpaca--files e))))

elpaca.el

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Setting it too high causes prints fewer status updates."
118118
elpaca--checkout-ref
119119
elpaca--run-pre-build-commands
120120
elpaca--queue-dependencies
121+
elpaca--check-version
121122
elpaca--link-build-files
122123
elpaca--generate-autoloads-async
123124
elpaca--byte-compile
@@ -1090,6 +1091,67 @@ The keyword's value is expected to be one of the following:
10901091
(elpaca--directory-files-recursively file regexp))
10911092
(when (string-match-p regexp file) (expand-file-name file)))))))
10921093

1094+
(defvar elpaca--tag-regexp "v\\(.*\\)")
1095+
(defun elpaca--latest-tag (e)
1096+
"Return E's latest merged tag matching recipe tag regexp or `elpaca--tag-regexp'."
1097+
(when-let ((default-directory (elpaca<-repo-dir e))
1098+
(recipe (elpaca<-recipe e))
1099+
(regexp (or (plist-get recipe :version-regexp) elpaca--tag-regexp))
1100+
(tags (elpaca-with-process
1101+
(elpaca-process-call "git" "tag" "--sort=-taggerdate" "--merged")
1102+
(when (and success stdout) (split-string stdout "\n" 'omit-nulls)))))
1103+
(cl-loop for tag in tags when (string-match regexp tag) do
1104+
(cl-return (or (match-string 1 tag) (match-string 0 tag))))))
1105+
1106+
(defun elpaca--date-version (e)
1107+
"Return date of E's checked out commit."
1108+
(let ((default-directory (elpaca<-repo-dir e)))
1109+
(elpaca-with-process
1110+
(elpaca-process-call "git" "log" "-n" "1" "--format=%cd" "--date=format:%Y%m%d.%s")
1111+
(if (not success) (elpaca--fail e stderr) (version-to-list (string-trim stdout))))))
1112+
1113+
(defvar elpaca--core-date
1114+
(list (or (and emacs-build-time (string-to-number (format-time-string "%Y%m%d" emacs-build-time)))
1115+
(alist-get emacs-version '(("27.1" . 20200804) ("27.2" . 20210319) ("28.1" . 20220403)
1116+
("28.2" . 20220912) ("29.1" . 20230730))
1117+
nil nil #'equal)
1118+
(warn "Unable to determine elpaca--core-date"))))
1119+
1120+
(defun elpaca--declared-version (e)
1121+
"Return E's version as listed in main file's metadata."
1122+
(when-let ((repo (elpaca<-repo-dir e))
1123+
(main (elpaca--main-file e)))
1124+
(with-current-buffer (get-buffer-create " *elpaca--dependencies*")
1125+
(setq default-directory repo)
1126+
(insert-file-contents-literally main nil nil nil 'replace)
1127+
(goto-char (point-min))
1128+
(if (string-suffix-p "-pkg.el" main)
1129+
(nth 2 (read (current-buffer)))
1130+
(when-let ((case-fold-search t)
1131+
(regexp "^;+[ ]+\\(Package-\\)?\\(Version\\)[ ]*:[ ]*")
1132+
((re-search-forward regexp nil 'noerror)))
1133+
(buffer-substring-no-properties (point) (line-end-position)))))))
1134+
1135+
(defun elpaca--check-version (e)
1136+
"Ensure E's dependency versions are met."
1137+
(cl-loop
1138+
initially (elpaca--signal e "Checking dependency versions")
1139+
with queued = (elpaca--queued)
1140+
for (id declared) in (elpaca--dependencies e)
1141+
for min = (version-to-list declared)
1142+
for datep = (> (car min) 10000) ;; Handle YYYYMMDD date version schema.
1143+
for dep = (elpaca-alist-get id queued)
1144+
for core = (unless dep (elpaca-alist-get id package--builtin-versions))
1145+
when (or (and core (version-list-< (if datep elpaca--core-date core) min))
1146+
(unless (memq id elpaca-ignored-dependencies)
1147+
(let ((version (if datep (elpaca--date-version dep)
1148+
(version-to-list (or (elpaca--declared-version dep) "0")))))
1149+
(and (version-list-< version min)
1150+
(let ((tag (elpaca--latest-tag dep)))
1151+
(or (null tag) (version-list-< (version-to-list tag) min)))))))
1152+
do (cl-return (elpaca--fail e (format "Requires %s >= %s" id declared))))
1153+
(elpaca--continue-build e))
1154+
10931155
(defun elpaca--main-file (e &optional recache)
10941156
"Return E's main file name. Recompute when RECACHE non-nil."
10951157
(or (and (not recache) (elpaca<-main e))
@@ -1152,20 +1214,15 @@ If RECACHE is non-nil, do not use cached dependencies."
11521214
(unless (memq 'continued-dep (elpaca<-statuses e))
11531215
(elpaca--continue-build e nil 'continued-dep)))
11541216

1155-
;;@MAYBE: Package major version checks.
11561217
(defun elpaca--queue-dependencies (e)
11571218
"Queue E's dependencies."
11581219
(cl-loop
1159-
named out
11601220
initially (elpaca--signal e "Queueing Dependencies" 'blocked nil 1)
1161-
with externals =
1162-
(cl-loop for (id version) in (elpaca--dependencies e)
1163-
when (and (eq id 'emacs)
1164-
(< emacs-major-version (truncate (string-to-number version))))
1165-
do (cl-return-from out (elpaca--fail e (concat "Requires Emacs " version)))
1166-
unless (memq id elpaca-ignored-dependencies) collect id)
1167-
with q = (and externals (elpaca--q e))
1168-
with qd = (and externals (elpaca--queued))
1221+
with externals = (or (cl-loop for (id . _) in (elpaca--dependencies e)
1222+
unless (memq id elpaca-ignored-dependencies) collect id)
1223+
(cl-return (elpaca--continue-build e "No external dependencies" 'unblocked)))
1224+
with q = (elpaca--q e)
1225+
with qd = (elpaca--queued)
11691226
with finished = 0
11701227
with q-id = (elpaca<-queue-id e)
11711228
with e-id = (elpaca<-id e)
@@ -1176,7 +1233,7 @@ If RECACHE is non-nil, do not use cached dependencies."
11761233
for d-id = (elpaca<-id d)
11771234
for d-status = (elpaca--status d)
11781235
do (and queued (> (elpaca<-queue-id d) q-id)
1179-
(cl-return-from out (elpaca--fail d (format "dependent %S in past queue" e-id))))
1236+
(cl-return (elpaca--fail d (format "dependent %S in past queue" e-id))))
11801237
(cl-pushnew e-id (elpaca<-dependents d))
11811238
(when (or (eq d-status 'queued)
11821239
(and (elpaca--throttled-p d) (= elpaca-queue-limit 1) ;; Dependency must be continued.
@@ -1193,8 +1250,7 @@ If RECACHE is non-nil, do not use cached dependencies."
11931250
(cl-pushnew e-id (elpaca<-blocking d))
11941251
(cl-pushnew d-id (elpaca<-blockers e)))
11951252
finally do (if (= (length externals) finished)
1196-
(elpaca--continue-build
1197-
e (when (zerop finished) "No external dependencies") 'unblocked)
1253+
(elpaca--continue-build e nil 'unblocked)
11981254
(mapc #'elpaca--continue-dependency pending))))
11991255

12001256
(defun elpaca--remote-default-branch (remote)

0 commit comments

Comments
 (0)