Re: Naive DEFSYSTEM replacement
"Robert Goldman (as rpgoldman at sift dot net)" <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
On 27 Jul 2025, at 20:27, Adam Weaver wrote:
> I still don't really get UIOP though. Sure, there are inconsistencies
> between implementations (vagaries in the standard?) but Peter Seibel
> wrote a 100-odd-line shim FAD for his textbook to paper over the
> PATHNAME differences.
I don't mean to disparage Peter, but CL-FAD is much less ambitious, and
really is not fit for purpose. Consider, for example:
```
(defun directory-pathname-p (pathspec)
"Returns NIL if PATHSPEC \(a pathname designator) does not designate
a directory, PATHSPEC otherwise. It is irrelevant whether file or
directory designated by PATHSPEC does actually exist."
(and
(not (component-present-p (pathname-name pathspec)))
(not (component-present-p (pathname-type pathspec)))
pathspec))
```
Note that this simply looks at the syntactic form of the pathname. Note
also that (as far as I can tell!) there is no part of the spec to tell
us what the form of a directory pathname should be. Peter has to assume
that it is something that has `NIL` or `:unspecific` in its name and
type properties. There is no obligation for a lisp implementation to
abide by Peter's intuitions.
Now compare this with what Fare did to make a reliable, portable version
of `probe-file` that returns the absolute pathname of a file:
```
(defun probe-file* (p &key truename)
"when given a pathname P (designated by a string as per
PARSE-NAMESTRING),
probes the filesystem for a file or directory with given pathname.
If it exists, return its truename if TRUENAME is true,
or the original (parsed) pathname if it is false (the default)."
(values
(ignore-errors
(setf p (funcall 'ensure-pathname p
:namestring :lisp
:ensure-physical t
:ensure-absolute t :defaults
'get-pathname-defaults
:want-non-wild t
:on-error nil))
(when p
#+allegro
(probe-file p :follow-symlinks truename)
#+gcl
(if truename
(truename* p)
(let ((kind (car (si::stat p))))
(when (eq kind :link)
(setf kind (ignore-errors (car (si::stat (truename*
p))))))
(ecase kind
((nil) nil)
((:file :link)
(cond
((file-pathname-p p) p)
((directory-pathname-p p)
(subpathname p (car (last (pathname-directory
p)))))))
(:directory (ensure-directory-pathname p)))))
#+clisp
#.(let* ((fs (or #-os-windows (find-symbol* '#:file-stat :posix
nil)))
(pp (find-symbol* '#:probe-pathname :ext nil)))
`(if truename
,(if pp
`(values (,pp p))
'(or (truename* p)
(truename* (ignore-errors
(ensure-directory-pathname p)))))
,(cond
(fs `(and (,fs p) p))
(pp `(nth-value 1 (,pp p)))
(t '(or (and (truename* p) p)
(if-let (d (ensure-directory-pathname p))
(and (truename* d) d)))))))
#-(or allegro clisp gcl)
(if truename
(probe-file p)
(and
#+(or cmucl scl) (unix:unix-stat (ext:unix-namestring p))
#+(and lispworks os-unix) (system:get-file-stat p)
#+sbcl (sb-unix:unix-stat (sb-ext:native-namestring p))
#-(or cmucl (and lispworks os-unix) sbcl scl)
(file-write-date p)
p))))))
```
If you want to have a portable system definition facility like ASDF, you
simply can't do without this kind of thing. It would be lovely if we
could get the implementation maintainers to agree on a more robust
portable approach to accessing the filesystem and build it themselves,
but that is not the world we live in.
To be honest, I can't imagine anyone but Fare who would have had the
patience and determination to do all this and get it right. There's
some stuff in UIOP that is not strictly speaking critical to ASDF's
function, but much less than you might think. When you need to do
something like figure out what files need loading, you need robust
access to the filesystem. The CL spec does not provide that. CL-FAD
does not either. In a lot of cases Peter made some guesses about the
meaning of pathnames, and they *mostly* work. But "mostly" isn't good
enough for ASDF.