-
Notifications
You must be signed in to change notification settings - Fork 2
/
keg-command.el
381 lines (324 loc) · 13.3 KB
/
keg-command.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
;;; keg-command.el --- CLI subcommand definition -*- lexical-binding: t; -*-
;; Copyright (C) 2020 Naoya Yamashita
;; Author: Naoya Yamashita <conao3@gmail.com>
;; URL: https://github.com/conao3/keg.el
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; CLI subcommand definition
;;; Code:
(provide 'keg-command)
(require 'keg)
(defvar keg-directory)
(defvar keg-version)
(defun keg--argument-count-check (num-min num-max subcommand args)
"Check number of ARGS range NUM-MIN to NUM-MAX in SUBCOMMAND.
Meaning of -1 is no restriction."
(let ((num (length args)))
(unless (and (or (= -1 num-min)
(<= num-min num))
(or (= -1 num-max)
(<= num num-max)))
(let ((usage (keg-argument-usage subcommand)))
(error (concat
"USAGE: keg"
(format " %s" (if usage usage subcommand))
"\n\n"
(format "The `%s' subcommand expects %s to %s arguments
but currently %s arguments have been specified"
subcommand
(if (not (= -1 num-min)) num-min 0)
(if (not (= -1 num-max)) num-max 'inf)
num)))))))
(defun keg--argument-package-check (package &optional allow-nil)
"Check PACKAGE is one of defined packages.
Return package symbol if package defined.
PACKAGE as string is also acceptable.
If ALLOW-NIL is non-nil, it don't warn if package is nil."
(let ((packages (keg-packages))
(pkg (if (not (stringp package)) package (intern package))))
(if (not (memq pkg (keg-packages)))
(unless allow-nil
(warn "Package %s is not defined. Package should one of %s" pkg packages))
pkg)))
(defun keg-command-help (&rest args)
"Show this help.
ARGS is CLI arguments.
USAGE: keg help"
(keg--argument-count-check 0 0 'help args)
(keg--princ
"USAGE: keg [SUBCOMMAND] [OPTIONS...]
Modern Elisp package development system.
SUBCOMMANDS:")
(dolist (elm (keg-subcommands))
(let ((usage (keg-usage elm))
(argument-usage (keg-argument-usage elm)))
(keg--princ (format " %s" (if argument-usage argument-usage elm)))
(keg--princ (keg--indent 5 usage)))))
(defun keg-command-version (&rest args)
"Show `keg' version.
ARGS is CLI arguments
USAGE: keg version"
(keg--argument-count-check 0 0 'version args)
(keg--princ
(format "Keg %s running on Emacs %s"
keg-version
emacs-version)))
(defun keg-command-init (&rest args)
"Create Keg template file.
ARGS is CLI argument.
USAGE: keg init"
(keg--argument-count-check 0 0 'install args)
(when (file-exists-p "Keg")
(error "Keg file already exists. Do nothing"))
(with-temp-file "Keg"
(insert "\
(source gnu)
(source melpa)
(depends-on \"keg\")
(depends-on \"leaf\")
"))
(keg--princ "Successful creating Keg file"))
(defun keg-command-install (&rest args)
"Install PACKAGES dependencies in .keg sandbox folder.
ARGS is CLI argument.
USAGE: keg install [PACKAGES...]"
(keg--argument-count-check -1 -1 'install args)
(keg--princ "Install dependencies")
(let ((reqinfo (keg-build--get-dependency-from-keg-file))
(packages (cond
(args (mapcar #'keg--argument-package-check args))
((getenv "KEGINSTALLPACKAGES")
(mapcar #'keg--argument-package-check (split-string (getenv "KEGINSTALLPACKAGES"))))
(t (keg-packages)))))
(dolist (info (keg-file-read-section 'packages))
(let* ((name (car info))
(_alist (cdr info))
(reqs (keg--alist-get name reqinfo)))
(when (memq name packages)
(keg--princ (format " Package: %s" name))
(keg--princ (format " Dependency: %s"
(mapcar
(lambda (elm)
(let ((pkg (car elm))
(ver (cadr elm)))
`(,pkg ,(package-version-join ver))))
reqs))))))
(keg--princ (format " DevDependency: %s"
(mapcar
(lambda (elm)
(let ((pkg (car elm))
(ver (cadr elm)))
`(,pkg ,(package-version-join ver))))
(keg--alist-get 'keg--devs reqinfo))))
(keg-around-script install
(keg-build--resolve-dependency packages))))
(defun keg-command-exec (&rest args)
"Exec COMMAND with appropriate environment variables.
ARGS is list of string.
USAGE: keg exec COMMAND [ARGS...]"
(keg--argument-count-check 1 -1 'exec args)
(keg-around-script exec
(let ((proc (apply #'keg-start-process args)))
(set-process-sentinel
proc
(lambda (proc _event)
(kill-emacs (process-exit-status proc))))
(while t ; wait acync process
(accept-process-output proc 0 100)))))
(defun keg-command-emacs (&rest args)
"Exec Emacs with appropriate environment variables.
Exec Emacs with ARGS.
USAGE: keg Emacs [ARGS...]"
(keg--argument-count-check -1 -1 'emacs args)
(keg-around-script emacs
(apply #'keg-command-exec "emacs" args)))
(defun keg-command-eval (&rest args)
"Eval SEXP via batch Emacs with appropriate environment variables.
ARGS are (separated) SEXP.
USAGE: keg eval [SEXP]"
(keg--argument-count-check -1 -1 'eval args) ; sexp is separated
(when args
(keg-around-script eval
(keg-command-exec "emacs" "--batch"
(format "--eval=\"%s\"" (keg--string-join args " "))))))
(defun keg-command-lint (&rest args)
"Exec linters for PACKAGE.
ARGS first value is specified package.
USAGE: keg lint [PACKAGE]"
(keg--argument-count-check -1 1 'lint args)
(keg-around-script lint
(let ((pkg (keg--argument-package-check (car args) 'allow)))
(kill-emacs (keg-lint pkg)))))
(defun keg-command-build (&rest args)
"Byte compile for PACKAGE.
ARGS first value is specified package.
USAGE: keg build [PACKAGE]"
(keg--argument-count-check -1 1 'build args)
(keg-around-script build
(dolist (file (keg-elisp-files (car args)))
(if (fboundp 'byte-recompile-file)
(byte-recompile-file file 'force 0)
(byte-compile-file file)))))
(defun keg-command-clean-elc (&rest args)
"Clean `.elc' files.
ARGS first value is specified package.
USAGE: keg clean-elc [PACKAGE]"
(keg--argument-count-check -1 1 'clean-elc args)
(keg-around-script clean-elc
(let ((pkg (keg--argument-package-check (car args) 'allow)))
(dolist (file (keg-elisp-files pkg))
(let ((elc (concat file "c")))
(keg--princ (format "Removing %s..." elc))
(ignore-errors (delete-file elc)))))))
(defun keg-command-clean (&rest args)
"Clean `.elc' files and `.keg' sandbox.
ARGS is CLI argument.
USAGE: keg clean"
(keg--argument-count-check 0 0 'clean args)
(keg-around-script clean
(keg--princ "Removing .keg...")
(delete-directory ".keg" 'force)
(keg-command-clean-elc)))
(defun keg-command-info (&rest args)
"Show PACKAGE information.
ARGS first value is specified package.
USAGE: keg info [PACKAGE]"
(keg--argument-count-check -1 1 'info args)
(let ((reqinfo (keg-build--get-dependency-from-keg-file))
(section (keg-file-read-section 'packages))
(pkg (keg--argument-package-check (car args) 'allow)))
(when (and pkg (not (keg--alist-get pkg section)))
(error "%s is not defined. PACKAGE should one of %s" pkg (keg-packages)))
(dolist (info (if (not pkg)
section
(list (assoc pkg section))))
(let* ((name (car info))
(alist (cdr info))
(reqs (keg--alist-get name reqinfo)))
(keg--princ (format " Package: %s" name))
(keg--princ (format " Recipe: %s" (keg--alist-get 'recipe alist)))
(keg--princ (format " Dependency: %s"
(mapcar
(lambda (elm)
(let ((pkg (car elm))
(ver (cadr elm)))
`(,pkg ,(package-version-join ver))))
reqs)))))
(keg--princ (format " DevDependency: %s"
(mapcar
(lambda (elm)
(let ((pkg (car elm))
(ver (cadr elm)))
`(,pkg ,(package-version-join ver))))
(keg--alist-get 'keg--devs reqinfo))))))
(defun keg-command-load-path (&rest args)
"Show Emacs appropriate `load-path' same format as PATH.
ARGS is CLI argument.
USAGE: keg `load-path'"
(keg--argument-count-check 0 0 'load-path args)
(keg--princ (keg-load-path)))
(defun keg-command-files (&rest args)
"Show files associated with PACKAGE.
ARGS is specified package.
USAGE: keg files [PACKAGE]"
(keg--argument-count-check -1 1 'files args)
(let ((pkg (keg--argument-package-check (car args) 'allow)))
(dolist (elm (keg-files pkg))
(keg--princ elm))))
(defun keg-command-elisp-files (&rest args)
"Show Elisp files associated with PACKAGE.
ARGS is specified package.
USAGE: keg files [PACKAGE]"
(keg--argument-count-check -1 1 'files args)
(let ((pkg (keg--argument-package-check (car args) 'allow)))
(dolist (elm (keg-elisp-files pkg))
(keg--princ elm))))
(defun keg-command-debug (&rest args)
"Show debug information.
ARGS is CLI argument.
USAGE: keg debug"
(keg--argument-count-check 0 0 'debug args)
(keg--princ "Keg debug information")
(let ((reqinfo (keg-build--get-dependency-from-keg-file)))
(dolist (info (keg-file-read-section 'packages))
(let* ((name (car info))
(alist (cdr info))
(reqs (keg--alist-get name reqinfo)))
(keg--princ (format " Package: %s" name))
(keg--princ (format " Recipe: %s" (keg--alist-get 'recipe alist)))
(keg--princ (format " Dependency: %s"
(mapcar
(lambda (elm)
(let ((pkg (car elm))
(ver (cadr elm)))
`(,pkg ,(package-version-join ver))))
reqs)))))
(keg--princ (format " DevDependency: %s"
(mapcar
(lambda (elm)
(let ((pkg (car elm))
(ver (cadr elm)))
`(,pkg ,(package-version-join ver))))
(keg--alist-get 'keg--devs reqinfo)))))
(keg--princ " Keg file: %s" (keg-file-path))
(keg--princ " Keg file parsed")
(keg--princ (keg--indent 5 (pp-to-string (keg-file-read)))))
(defun keg-command-run (&rest args)
"Run script named SRCIPT defined in Keg file.
ARGS is name of script.
USAGE: keg run [SCRIPT]"
(keg--argument-count-check 1 1 'run args)
(kill-emacs (keg-run-script (car args))))
(defvar keg-global-commands '(init help version debug)
"List of commands that don't require a Keg file.")
(defvar keg-no-install-commands `(,@keg-global-commands
install clean clean-elc info run)
"List of commands that don't require dependency installation.")
(defun keg-command ()
"Init `keg' and exec subcommand."
(unless noninteractive
(error "`keg-command' is to be used only with --batch"))
(let* ((opraw (car command-line-args-left))
(op (if opraw
(intern opraw)
(if (file-exists-p "Keg")
'install 'help)))
(args (cdr command-line-args-left))
(no-install (keg--no-install-p op keg-no-install-commands)))
(when (not (memq op keg-global-commands))
(keg-initialize))
(cond
((and
(not (memq op keg-global-commands))
(not (file-exists-p "Keg")))
(keg--princ "Missing Keg file in current directory")
(keg--princ "Keg file is required to exec `%s' command" op)
(keg--princ "Exec `keg init' to create Keg file")
(kill-emacs 1))
((and
(not no-install)
(not (file-directory-p user-emacs-directory)))
(keg--princ "As missing .keg sandbox, install dependencies")
(make-directory user-emacs-directory 'parent)
(keg-command-install))
((not no-install)
(make-directory user-emacs-directory 'parent)
(keg-command-install)))
(cond
((null op)) ;Do nothing because `keg-command-install' is already run.
((memq op (keg-subcommands))
(apply (intern (format "keg-command-%s" (symbol-name op))) args))
(t
(keg-command-help)
(keg--princ)
(error (format "Subcommand `%s' is not defined" op))))))
;;; keg-command.el ends here