<!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>