<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Functions COMPILED-FILE-P and ABI-VERSION</title>
<meta name="author" value="Sam Steingold"/>
</head>
<body>
<h1>Functions <code>COMPILED-FILE-P</code>
and <code>ABI-VERSION</code></h1>
<h2>Author</h2>
<p>Sam Steingold</p>
<h2>Related</h2>
<p>ANSI Common Lisp standard function <code>compile-file</code>.</p>
<h2>Abstract</h2>
<p>A facility to determine whether a file is a valid compiled file for
the specific implementation and to identify the current compiled file
format.</p>
<h2>Rationale</h2>
<p>Build tools, like <code>defsystem</code> or <code>asdf</code>,
have to determine whether a file needs to be recompiled.</p>
<p>Obviously, when the compiled file is older than the
source file, recompilation is in order.</p>
<p>Alas, there are other situations when this might be necessary, e.g.,
when the implementation changes the compiled file format or when two
implementations use the same name for their compiled files
(<code>.fasl</code> is used by both <code>SBCL</code> and <code>ACL</code>).</p>
<p>Traditionally, system definition facilities have taken the route of
creating a separate directory for each combination of implementation
type, version, operating system, and architecture. This is wasteful
because the the compiled file format does not necessarily change between
versions and does not even have to depend on OS and architecture.</p>
<p>The proposed functions will simplify the build directory tree
structure and reduce the number of binary distribution bundles.</p>
<h3>Current Practice</h3>
<p>Implementation-dependent.</p>
<h3>Cost of adoption</h3>
<p>For <code>COMPILED-FILE-P</code>, probably tiny: an implementation
must be able to check for compiled file validity, so all it takes is to
export the necessary functionality, e.g.:</p>
<pre id="compiled-file-p-clisp">
#+clisp
(defun compiled-file-p (file-name)
(with-open-file (in file-name :direction :input :if-does-not-exist nil)
(and in (char= #\( (peek-char nil in))
(let ((form (ignore-errors (read in nil nil))))
(and (consp form)
(eq (car form) 'SYSTEM::VERSION)
(null (nth-value 1 (ignore-errors (eval form)))))))))
</pre>
<p>For <code>ABI-VERSION</code>, it probably depends on the
implementation; for some it might be trivial:</p>
<pre id="abi-version-clisp">
#+clisp
(defun abi-version ()
(car (system::version)))
</pre>
<p>and for others it might not.</p>
<h3>Cost of non-adoption</h3>
<p>Users will suffer random errors when trying to load invalid binary
files.</p>
<h2>Specification</h2>
<h3>Function <code>COMPILED-FILE-P</code></h3>
<p>Function</p><pre>
(compiled-file-p file-name) ==> valid-p
</pre>
<p>Returns</p><dl>
<dt><code>true</code></dt><dd>if the file appears to be a valid compiled file
(i.e., exists, is readable, and the contents appears to be
valid for this implementation),</dd>
<dt><code>false</code></dt><dd>otherwise.</dd></dl>
<p>Implementations are required to inspect the contents
(e.g., checking just the pathname type is not sufficient).
Although the completeness of the inspection is not required,
this function should be able to detect,
e.g., file format changes between versions.</p>
<h4>Exceptional situations</h4> <ul>
<li>Signals an error of type <code>type-error</code>
when the argument is not a <em>pathname designator</em>.</li>
</ul>
<h4>Examples</h4>
<pre>
(compiled-file-p "foo.lisp") ==> NIL
(compiled-file-p (compile-file "foo.lisp")) ==> T
</pre>
<h3>Function <code>ABI-VERSION</code></h3>
<p>Function</p><pre>
(abi-version &optional object) ==> object
</pre>
<p>When called without arguments, returns an implementation-defined
object which uniquely identifies the compiled file
format <em>produced</em> by the implementation.</p>
<p>The return <em>value</em> must satisfy two conditions:</p><ol>
<li><code>(prin1-to-string value)</code> must be a valid logical
pathname component</li>
<li><code>(equalp value (read-from-string (prin1-to-string
value)))</code> must be true</li>
</ol>
<p>which guarantee that it can be used to name directories where the
compiled files are stored.</p>
<p>When called with an argument, returns a generalized boolean:</p><dl>
<dt><code>true</code></dt><dd>if the argument identifies an ABI version
which can be <em>consumed</em> by the implementation,</dd>
<dt><code>false</code></dt><dd>otherwise.</dd></dl>
<h4>Exceptional situations</h4> <ul>
<li>May signal an error of type <code>type-error</code>
when the argument is not a valid ABI version name for this implementation.</li>
</ul>
<h4>Examples</h4>
<pre>
(abi-version (abi-version)) ==> T
</pre>
<h2>Reference Implementation</h2>
<p>See <a href="#abi-version-clisp">above</a>.</p>
<h2>History</h2>
<p>The <code>compiled-file-p</code> spec was accepted
as <strong>CLRFI-2</strong> (in 2004).</p>
<h2>Notes</h2>
<p>The trivial implementation:</p>
<pre>
(defun compiled-file-p (file-name)
(not (nth-value 1 (ignore-errors (load file-name)))))
</pre>
<p>is wrong because,</p>
<ol>
<li><code>load</code> may fail even though the file is valid:
even when <code>foo.lisp</code> contains calls to <code>error</code>,<pre>
(compiled-file-p (compile-file "foo.lisp"))
</pre>should still return <code>T</code>.</li>
<li>this is not side-effect-free, i.e., this may define new functions and
macros (or, worse yet, redefine some existing functions and macros or
execute some malicious code).</li></ol>
<p>If we could require <code>(abi-version file)</code> to return either
the abi-version of the implementation which produced the compiled
file, or nil if the <em>file</em> is not a compiled file for this
implementation, then we could define</p><pre>
(defun compiled-file-p (file) (equalp (abi-version) (abi-version file)))
</pre><p>however, it is not obvious that all implementation can
actually do this without unwelcome invasive changes.</p>
</body>
</html>