-
Notifications
You must be signed in to change notification settings - Fork 456
/
el-get-byte-compile.el
199 lines (183 loc) · 8.72 KB
/
el-get-byte-compile.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
;;; el-get --- Manage the external elisp bits and pieces you depend upon
;;
;; Copyright (C) 2010-2011 Dimitri Fontaine
;;
;; Author: Dimitri Fontaine <[email protected]>
;; 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
(require 'cl-lib) ; yes I like loop
(require 'bytecomp)
(declare-function el-get-build-commands "el-get-build" (package))
(declare-function el-get-read-package-with-status "el-get-status" (action &rest statuses))
;; byte-recompile-file:
;;
;; - in Emacs23 will not recompile a file when the source is newer than the
;; bytecode (.elc)
;;
;; - in Emacs24 has another different and unhelpful behavior:
;;
;; If the `.elc' file does not exist, normally this function *does not*
;; compile FILENAME. If ARG is 0, that means compile the file even if it
;; has never been compiled before.
;;
;; so we just define our own
(defun el-get-byte-compile-file (el &optional warnings)
"Byte compile the EL file, and skips unnecessary compilation.
Specifically, if the compiled elc file already exists and is
newer, then compilation is skipped."
(let ((elc (concat (file-name-sans-extension el) ".elc"))
(byte-compile-warnings warnings)
;; Byte-compile runs emacs-lisp-mode-hook; disable it
emacs-lisp-mode-hook)
(when (or (not (file-exists-p elc))
(not (file-newer-than-file-p elc el)))
(when (file-exists-p elc)
;; Delete the old elc to make sure that if the compilation fails to
;; generate a new one, there will be no discrepancy between them.
(delete-file elc))
(condition-case err
(byte-compile-file el)
((debug error) ;; catch-all, allow for debugging
(message "%S" (error-message-string err)))))))
(defun el-get-byte-compile-file-or-directory (file)
"Byte-compile FILE or all files within it if it is a directory."
(let ((byte-compile-warnings nil)
;; Byte-compile runs emacs-lisp-mode-hook; disable it
emacs-lisp-mode-hook)
(if (file-directory-p file)
(byte-recompile-directory file 0)
(el-get-byte-compile-file file))))
(defun el-get-assemble-files-for-byte-compilation (package)
"Assemble a list of *absolute* paths to byte-compile for PACKAGE."
(when el-get-byte-compile
(let* ((source (el-get-package-def package))
(comp-prop (plist-get source :compile))
(compile (el-get-as-list comp-prop))
;; nocomp is true only if :compile is explicitly set to nil.
(explicit-nocomp (and (plist-member source :compile)
(not comp-prop)))
(method (el-get-package-method source))
(pdir (el-get-package-directory package))
(el-path (el-get-load-path package))
(files '()))
(cond
(compile
;; only byte-compile what's in the :compile property of the recipe
(dolist (path compile)
(let ((fullpath (expand-file-name path pdir)))
(if (file-exists-p fullpath)
;; path is a file/dir, so add it literally
(add-to-list 'files fullpath)
;; path is a regexp, so add matching file names in package dir
(mapc (apply-partially 'add-to-list 'files)
(directory-files pdir nil path))))))
;; If package has (:compile nil), or package has its own build
;; instructions, or package is already pre-compiled by the
;; installation method, then don't compile anything.
((or explicit-nocomp
(el-get-build-commands package)
(member method '(apt-get fink pacman)))
nil)
;; Default: compile the package's entire load-path
(t
(mapc (apply-partially 'add-to-list 'files) el-path)))
files)))
(defun el-get-clean-stale-compiled-files (dir &optional recursive)
"In DIR, delete all elc files older than their corresponding el files.
With optional arg RECURSIVE, do so in all subdirectories as well."
;; Process elc files in this dir
(let ((elc-files (directory-files dir 'full "\\.elc$")))
(cl-loop for elc in elc-files
for el = (concat (file-name-sans-extension elc) ".el")
if (and (file-exists-p elc)
(not (file-directory-p elc))
(not (file-newer-than-file-p elc el)))
do (progn
(message "el-get-byte-compile: Cleaning stale compiled file %s" elc)
(delete-file elc)))
;; Process subdirectories recursively
(when recursive
(cl-loop for dir in (directory-files dir 'full)
for localdir = (file-name-nondirectory dir)
if (file-directory-p dir)
unless (member localdir '("." ".."
;; This list of dirs to ignore courtesy of ack
;; http://betterthangrep.com/
"autom4te.cache" "blib" "_build"
".bzr" ".cdv" "cover_db" "CVS" "_darcs"
"~.dep" "~.dot" ".git" ".hg" "_MTN"
"~.nib" ".pc" "~.plst" "RCS" "SCCS"
"_sgbak" ".svn"))
do (el-get-clean-stale-compiled-files dir recursive)))))
(defun el-get-byte-compile-from-stdin ()
"byte compile files from stdin.
Standard input must be a property list with properties
`:load-path' and `:compile-files', each of which should have a
value that is a list of strings. The variable `load-path' will be
set from the `:load-path' property, and then all the files listed
in `:compile-files' will be byte-compiled.
Standard input can also contain a `:clean-directory' property,
whose value is a directory to be cleared of stale elc files."
(cl-assert noninteractive nil
"`el-get-byte-compile-from-stdin' is to be used only with -batch")
(let* ((input-data (read-minibuffer ""))
(files (plist-get input-data :compile-files))
(input-load-path (plist-get input-data :load-path))
(dir-to-clean (plist-get input-data :clean-directory)))
;; Use setq so that `load-path' stays updated until
;; shutdown. Certain packages (eg w3m) might install shutdown
;; hooks during compilation.
(setq load-path (append input-load-path load-path))
(unless (or dir-to-clean files)
(warn "Did not get a list of files to byte-compile or a directory to clean. The input may have been corrupted."))
(when dir-to-clean
(cl-assert (stringp dir-to-clean) nil
"The value of `:clean-directory' must be a string.")
(message "el-get-byte-compile: Cleaning stale compiled files in %s" dir-to-clean)
(el-get-clean-stale-compiled-files dir-to-clean 'recursive))
(cl-loop for f in files
do (progn
(message "el-get-byte-compile: %s" f)
(el-get-byte-compile-file-or-directory f)))))
(defun el-get-byte-compile-process (package buffer working-dir sync files)
"return the `el-get-start-process-list' entry to byte compile PACKAGE"
(let* ((input-data
(list :load-path (cons "." load-path)
:compile-files files
:clean-directory (el-get-package-directory package)))
(subprocess-function 'el-get-byte-compile-from-stdin)
(bytecomp-command
`(,el-get-emacs
"-Q" "-batch" "-f" "toggle-debug-on-error"
"-l" ,(file-name-sans-extension
(symbol-file subprocess-function 'defun))
"-f" ,(symbol-name subprocess-function))))
`(:command-name "byte-compile"
:buffer-name ,buffer
:default-directory ,working-dir
:shell t
:stdin ,input-data
:sync ,sync
:program ,(car bytecomp-command)
:args ,(cdr bytecomp-command)
:message ,(format "el-get-build %s: byte-compile ok." package)
:error ,(format
"el-get could not byte-compile %s" package))))
(defun el-get-byte-compile (package)
"byte compile files for given package"
(interactive
(list (el-get-read-package-with-status "Byte compile" "installed")))
(let ((pdir (el-get-package-directory package))
(buf "*el-get-byte-compile*")
(files (el-get-assemble-files-for-byte-compilation package)))
(el-get-start-process-list
package
(list (el-get-byte-compile-process package buf pdir t files))
nil)))
(provide 'el-get-byte-compile)