-
Notifications
You must be signed in to change notification settings - Fork 0
/
package-inferred-system.lisp
164 lines (146 loc) · 8.09 KB
/
package-inferred-system.lisp
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
;;;; -------------------------------------------------------------------------
;;;; Package systems in the style of quick-build or faslpath
(uiop:define-package :asdf/package-inferred-system
(:recycle :asdf/package-inferred-system :asdf/package-system :asdf)
(:use :uiop/common-lisp :uiop
:asdf/upgrade :asdf/session
:asdf/component :asdf/system :asdf/system-registry :asdf/lisp-action
:asdf/parse-defsystem)
(:export
#:package-inferred-system #:sysdef-package-inferred-system-search
#:package-system ;; backward compatibility only. To be removed.
#:register-system-packages
#:*defpackage-forms* #:*package-inferred-systems* #:package-inferred-system-missing-package-error))
(in-package :asdf/package-inferred-system)
(with-upgradability ()
;; The names of the recognized defpackage forms.
(defparameter *defpackage-forms* '(defpackage define-package))
(defun initial-package-inferred-systems-table ()
;; Mark all existing packages are preloaded.
(let ((h (make-hash-table :test 'equal)))
(dolist (p (list-all-packages))
(dolist (n (package-names p))
(setf (gethash n h) t)))
h))
;; Mapping from package names to systems that provide them.
(defvar *package-inferred-systems* (initial-package-inferred-systems-table))
(defclass package-inferred-system (system)
()
(:documentation "Class for primary systems for which secondary systems are automatically
in the one-file, one-file, one-system style: system names are mapped to files under the primary
system's system-source-directory, dependencies are inferred from the first defpackage form in
every such file"))
;; DEPRECATED. For backward compatibility only. To be removed in an upcoming release:
(defclass package-system (package-inferred-system) ())
;; Is a given form recognizable as a defpackage form?
(defun defpackage-form-p (form)
(and (consp form)
(member (car form) *defpackage-forms*)))
;; Find the first defpackage form in a stream, if any
(defun stream-defpackage-form (stream)
(loop :for form = (read stream nil nil) :while form
:when (defpackage-form-p form) :return form))
(defun file-defpackage-form (file)
"Return the first DEFPACKAGE form in FILE."
(with-input-file (f file)
(stream-defpackage-form f)))
(define-condition package-inferred-system-missing-package-error (system-definition-error)
((system :initarg :system :reader error-system)
(pathname :initarg :pathname :reader error-pathname))
(:report (lambda (c s)
(format s (compatfmt "~@<No package form found while ~
trying to define package-inferred-system ~A from file ~A~>")
(error-system c) (error-pathname c)))))
(defun package-dependencies (defpackage-form)
"Return a list of packages depended on by the package
defined in DEFPACKAGE-FORM. A package is depended upon if
the DEFPACKAGE-FORM uses it or imports a symbol from it."
(assert (defpackage-form-p defpackage-form))
(remove-duplicates
(while-collecting (dep)
(loop :for (option . arguments) :in (cddr defpackage-form) :do
(ecase option
((:use :mix :reexport :use-reexport :mix-reexport)
(dolist (p arguments) (dep (string p))))
((:import-from :shadowing-import-from)
(dep (string (first arguments))))
#+package-local-nicknames
((:local-nicknames)
(loop :for (nil actual-package-name) :in arguments :do
(dep (string actual-package-name))))
((:nicknames :documentation :shadow :export :intern :unintern :recycle)))))
:from-end t :test 'equal))
(defun package-designator-name (package)
"Normalize a package designator to a string"
(etypecase package
(package (package-name package))
(string package)
(symbol (string package))))
(defun register-system-packages (system packages)
"Register SYSTEM as providing PACKAGES."
(let ((name (or (eq system t) (coerce-name system))))
(dolist (p (ensure-list packages))
(setf (gethash (package-designator-name p) *package-inferred-systems*) name))))
(defun package-name-system (package-name)
"Return the name of the SYSTEM providing PACKAGE-NAME, if such exists,
otherwise return a default system name computed from PACKAGE-NAME."
(check-type package-name string)
(or (gethash package-name *package-inferred-systems*)
(string-downcase package-name)))
;; Given a file in package-inferred-system style, find its dependencies
(defun package-inferred-system-file-dependencies (file &optional system)
(if-let (defpackage-form (file-defpackage-form file))
(remove t (mapcar 'package-name-system (package-dependencies defpackage-form)))
(error 'package-inferred-system-missing-package-error :system system :pathname file)))
;; Given package-inferred-system object, check whether its specification matches
;; the provided parameters
(defun same-package-inferred-system-p (system name directory subpath around-compile dependencies)
(and (eq (type-of system) 'package-inferred-system)
(equal (component-name system) name)
(pathname-equal directory (component-pathname system))
(equal dependencies (component-sideway-dependencies system))
(equal around-compile (around-compile-hook system))
(let ((children (component-children system)))
(and (length=n-p children 1)
(let ((child (first children)))
(and (eq (type-of child) 'cl-source-file)
(equal (component-name child) "lisp")
(and (slot-boundp child 'relative-pathname)
(equal (slot-value child 'relative-pathname) subpath))))))))
;; sysdef search function to push into *system-definition-search-functions*
(defun sysdef-package-inferred-system-search (system-name)
"Takes SYSTEM-NAME and returns an initialized SYSTEM object, or NIL. Made to be added to
*SYSTEM-DEFINITION-SEARCH-FUNCTIONS*."
(let ((primary (primary-system-name system-name)))
;; this function ONLY does something if the primary system name is NOT the same as
;; SYSTEM-NAME. It is used to find the systems with names that are relative to
;; the primary system's name, and that are not explicitly specified in the system
;; definition
(unless (equal primary system-name)
(let ((top (find-system primary nil)))
(when (typep top 'package-inferred-system)
(if-let (dir (component-pathname top))
(let* ((sub (subseq system-name (1+ (length primary))))
(component-type (class-for-type top :file))
(file-type (file-type (make-instance component-type)))
(f (probe-file* (subpathname dir sub :type file-type)
:truename *resolve-symlinks*)))
(when (file-pathname-p f)
(let ((dependencies (package-inferred-system-file-dependencies f system-name))
(previous (registered-system system-name))
(around-compile (around-compile-hook top)))
(if (same-package-inferred-system-p previous system-name dir sub around-compile dependencies)
previous
(eval `(defsystem ,system-name
:class package-inferred-system
:default-component-class ,component-type
:source-file ,(system-source-file top)
:pathname ,dir
:depends-on ,dependencies
:around-compile ,around-compile
:components ((,component-type file-type :pathname ,sub)))))))))))))))
(with-upgradability ()
(pushnew 'sysdef-package-inferred-system-search *system-definition-search-functions*)
(setf *system-definition-search-functions*
(remove (find-symbol* :sysdef-package-system-search :asdf/package-system nil)
*system-definition-search-functions*)))