Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 135

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 135

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 187

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 188

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 189

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 194

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 195

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 196

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 197

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 241

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 264

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 269

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 275

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 285

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 286

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 296

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 297

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 298

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 308

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 309

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 310

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 311

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 321

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 322

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 323

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 324

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 325

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 497

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 527

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 540

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 587

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 626

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 668

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 668

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 670

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 673

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 682

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 688

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 693

Deprecated: Array and string offset access syntax with curly braces is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php on line 699

Deprecated: Function get_magic_quotes_gpc() is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 410

Deprecated: Function get_magic_quotes_gpc() is deprecated in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 410

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 272

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 274

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 274

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 274

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/public/lib.urlhandlers.php on line 110

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/public/lib.urlhandlers.php on line 130

Warning: Cannot modify header information - headers already sent by (output started at /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.json.php:309) in /home/didierve/didierverna.net/blog/inc/libs/clearbricks/common/lib.http.php on line 295
Didier Verna's Scientific Blog - Tag - standalone Didier Verna's scientific blog: Lisp, Emacs, LaTeX and random stuff. 2024-01-31T17:45:28+00:00 Didier Verna urn:md5:a22c53786aff986a2da4c770c233a8f9 Dotclear Towards ABCL Standalone Executables urn:md5:a5076791ac7682cc4e3bcda750574100 Saturday, January 22 2011 Saturday, January 22 2011 Didier Verna Lisp ABCLexecutablestandalone <p><strong>UPDATE:</strong> ABCL now supports the POSIX-compliant use of <code>--</code>, so I've modified this blog to reflect that. I've also added a final trick to emulate the existence of a proper <code>argv[0]</code>.</p> <p>Creating standalone executables in Common Lisp is already a portability minefield, as the standard doesn't specify anything in that regard. The situation is even worse with ABCL because it is a Java-based implementation of Common Lisp, and Java has no notion of standalone executable at all. In this article, we will try to figure out how far we can go in that direction nevertheless. This work comes from my wish to port <a href="http://www.lrde.epita.fr/~didier/software/lisp/clon.php" hreflang="en">Clon</a> to ABCL. In order to make it work, ABCL itself needed some changes, so if you want to try out the code provided below, beware that you need at least revision 13156 from the ABCL Subversion trunk (or version 0.24.0). Let's start by writing a great Common Lisp application for ABCL, and call it <code>greet.lisp</code>:</p> <pre class="lisp lisp" style="font-family:inherit"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">defun</span> main <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;Hello,~{ ~A~}!~%&quot;</span> extensions<span style="color: #66cc66;">:</span>*command-line-argument-<span style="color: #b1b100;">list</span>*<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>extensions<span style="color: #66cc66;">:</span><span style="color: #555;">exit</span> <span style="color: #66cc66;">:</span><span style="color: #555;">status</span> <span style="color: #cc66cc;">0</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> &nbsp; <span style="color: #66cc66;">&#40;</span>main<span style="color: #66cc66;">&#41;</span></pre> <p>As you guessed, <code>extensions:*command-line-argument-list*</code> stores the list of command-line options passed to the program. Now, let's try it out:</p> <pre> $ abcl --noinform --noinit --nosystem --load greet.lisp John Doe Hello, John Doe! </pre> <p>So far so good: ABCL-specific options were eaten by ABCL. Not very standalone however. Let's create a wrapper script around that, and call it <code>greet</code>:</p> <pre> #! /bin/sh exec abcl --noinform --noinit --nosystem --load greet.lisp ${1+$@} </pre> <p>In fact, we have a problem:</p> <pre> $ ./greet --help me Parameters: --help displays this help --noinform suppresses the printing of version info --eval &lt;form&gt; evaluates the &lt;form&gt; before initializing REPL --load &lt;file&gt; loads the file &lt;file&gt; before initializing REPL --load-system-file &lt;file&gt; loads the system file &lt;file&gt; before initializing REPL --batch enables batch mode. The --load, --load-system-file and --eval parameters are handled, and abcl exits without entering REPL --noinit suppresses loading a .abclrc startup file --nosystem suppresses loading the system startup file </pre> <p>Woops! We were expecting to get <code>Hello, --help me!</code> weren't we? In other words, our own command-line options may conflict with ABCL's which is not nice. One thing we can do with a recent ABCL version is to use <code>--</code> to separate ABCL's own command-line arguments from ours. Our script becomes:</p> <pre> #! /bin/sh exec abcl --noinform --noinit --nosystem --load greet.lisp -- ${1+$@} </pre> <p>And we now get this:</p> <pre> $ ./greet --help me Hello, --help me! </pre> <p>This is better, although we're not actually very standalone yet: our Lisp file is a separate beast, and the <code>abcl</code> thing is also just a wrapper script (see below). If we want to be more standalone, we will need to go the Java way.</p> <p>The first thing to understand is that there's actually no such thing as an ABCL executable. ABCL is packed into a JAR (Java archive) file, a zip file, really, and what the wrapper does is simply to call <code>java -jar /path/to/abcl/abcl.jar options...</code> The JAR file contains a <code>Main</code> class with a <code>main</code> function the purpose of which is to launch a Lisp interpreter. This is done by calling the method <code>Interpreter.createDefaultInstance</code>, which, amongst other things, handles the command-line options. There is also a method called <code>Interpreter.createInstance</code> which does not handle the command-line at all, so it seems that this is the one we could use, provided that we find a way to set <code>extensions:*command-line-argument-list*</code> manually.</p> <p>The following code does exactly this. Let's call it <code>Greet.java</code>.</p> <pre class="java java" style="font-family:inherit"><span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">org.armedbear.lisp.*</span><span style="color: #339933;">;</span> &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Greet <span style="color: #009900;">&#123;</span> <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main <span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> argv<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #003399;">Runnable</span> r <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Runnable</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> run<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span> LispObject cmdline <span style="color: #339933;">=</span> Lisp.<span style="color: #006633;">NIL</span><span style="color: #339933;">;</span> <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> arg <span style="color: #339933;">:</span> argv<span style="color: #009900;">&#41;</span> cmdline <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Cons <span style="color: #009900;">&#40;</span>arg, cmdline<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> cmdline.<span style="color: #006633;">nreverse</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> Lisp._COMMAND_LINE_ARGUMENT_LIST_.<span style="color: #006633;">setSymbolValue</span> <span style="color: #009900;">&#40;</span>cmdline<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp; Interpreter.<span style="color: #006633;">createInstance</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> Load.<span style="color: #006633;">load</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;greet.lisp&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span>ProcessingTerminated e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #003399;">System</span>.<span style="color: #006633;">exit</span> <span style="color: #009900;">&#40;</span>e.<span style="color: #006633;">getStatus</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span> &nbsp; <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Thread</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">null</span>, r, <span style="color: #0000ff;">&quot;interpreter&quot;</span>, 4194304L<span style="color: #009900;">&#41;</span>.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#125;</span></pre> <p>Our Greet class mimics what ABCL's Main class does, only with some tweaks of our own:</p> <ul> <li>we build the value of <code>extensions:*command-line-argument-list*</code> manually from the actual <code>argv</code> passed to the function <code>main</code>, by consing equivalent Lisp objects and then reversing the list. That way, we completely bypass ABCL's options processing code.</li> <li>as mentioned earlier, we use <code>Interpreter.createInstance</code> instead of <code>Interpreter.createDefaultInstance</code>.</li> <li>we also take the opportunity to load our lisp file directly with the function <code>Load.load</code>.</li> </ul> <p>Finally, the <code>try/catch</code> construct intercepts calls to <code>extensions:exit</code> and effectively leaves the Java environment.</p> <p>Let's compile this beast now:</p> <pre> $ javac -cp /path/to/abcl/abcl.jar Greet.java </pre> <p>The <code>-cp</code> option to <code>javac</code>, the Java compiler, tells it where to find ABCL classes. This command gives us a <code>Greet.class</code> file, but also a second one named <code>Greet$1.class</code>. That's because our code creates an "anonymous inner class", whatever that means, in the Java jargon. Now, how do we bypass ABCL's main function and use or own instead? This is actually very simple. The Java command-line lets you specify (read: override) the name of the class in which to find the <code>main</code> function. So it turns out that we don't even have to modify ABCL itself for this to work. We can simply go:</p> <pre> $ java -cp /path/to/abcl/abcl.jar:. Greet John Doe Hello, John Doe! </pre> <p>and it works. By the way, we can also go:</p> <pre> $ java -cp /path/to/abcl/abcl.jar:. Greet --help me Hello, --help me! </pre> <p>and see that it still works. Since ABCL doesn't process its command-line anymore, there's no question about <code>--</code> or whatever. Note that this time, we need to add the current directory to the class path option in order for Java to find the files <code>Greet.class</code> and <code>Greet$1.class</code>. Note also that with our custom interepreter, there's no more need for all those "standalone" options like <code>--noinit</code>, <code>--noinform</code> etc. Wrapping this in a shell script is left as an exercise, although not a difficult one.</p> <p>Still, we're not quite standalone yet. For example, it's not cool to have these two <code>Greet</code> class files floating around like this. All other class files are in the ABCL JAR file, so we can think of adding ours in too. If we want to do that, we also need to state that the main class in the JAR file should be our <code>Greet</code> class, not ABCL's <code>Main</code> one. This can be done by modifying the so-called JAR <em>manifest</em>.</p> <p>Let's create a manifest file first and call it <code>Greet.txt</code>:</p> <pre> Main-Class: Greet </pre> <p>It says what it says: our application's <code>Main</code> class (where the <code>main</code> function is) is <code>Greet</code>. Now let's make a copy of <code>abcl.jar</code> and add our class file to it:</p> <pre> $ cp /path/to/abcl/abcl.jar greet.jar $ jar umf Greet.txt greet.jar Greet.class Greet$1.class Jan 22, 2011 5:50:32 PM java.util.jar.Attributes read WARNING: Duplicate name in Manifest: Main-Class. Ensure that the manifest does not have duplicate entries, and that blank lines separate individual sections in both your manifest and in the META-INF/MANIFEST.MF entry in the jar file. </pre> <p>The <code>u</code> stands for "update", the <code>m</code> stands for "manifest" and the <code>f</code> stands for target (JAR) "file". The manifest and JAR files must appear in the same order as their corresponding options. Don't worry about the warning. That's because we're changing the <code>Main</code> class (ABCL had one already).</p> <p>Let's try it out:</p> <pre> $ java -jar greet.jar John Doe Hello, John Doe! </pre> <p>Cool. But what about the Lisp file? It's still floating around. As a matter of fact, the JAR file may contain anything you want, not just class files. ABCL itself has a bunch of lisp files in its JAR file already. So we can put it in there just as well:</p> <pre> $ jar uf greet.jar greet.lisp </pre> <p>The last question is: how to have ABCL load a lisp file that's in its own JAR? The answer is: dig into the code and figure out that there is a method called <code>Load.loadSystemFile</code> which does just that. ABCL uses it to load its own <code>boot.lisp</code> for instance. We hence need to modify our <code>main</code> function and replace the line that calls <code>Load.load</code> with this one:</p> <pre> Load.loadSystemFile (&quot;/greet.lisp&quot;, false, false, false); </pre> <p>Note the leading slash. It is needed because we have stored <code>greet.lisp</code> at the root of the JAR file. Let's update our JAR file once again:</p> <pre> $ javac -cp /path/to/abcl/abcl.jar Greet.java $ jar uf greet.jar Greet.class Greet$1.class </pre> <p>And voilĂ ! What you get this time is your application completely packed up in the JAR file. You can move this file around, distribute it... everything's inside.</p> <p>Here's a final additional trick and I think we're done. As you know, ABCL stores the command-line argument list in <code>extensions:*command-line-argument-list*</code>. One potential problem is that there's no notion of a program name (<code>argv[0]</code>) in ABCL (the program is in fact always Java). Since we're going standalone, however, maybe we would like to have that. Something like, say, a variable named <code>extensions:*argv0*</code>. So here's a way to do it (<a href="http://www.lrde.epita.fr/~didier/software/lisp/clon.php" hreflang="en">Clon</a> does this as part of its built-in dump facility): in the <code>Greet</code> class, replace the line</p> <pre class="java java" style="font-family:inherit">Interpreter.<span style="color: #006633;">createInstance</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre> <p>with this:</p> <pre class="java java" style="font-family:inherit">Interpreter interpreter <span style="color: #339933;">=</span> Interpreter.<span style="color: #006633;">createInstance</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> interpreter.<span style="color: #006633;">eval</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;(defvar extensions::*argv0* <span style="color: #000099; font-weight: bold;">\&quot;</span>greet<span style="color: #000099; font-weight: bold;">\&quot;</span>)&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> interpreter.<span style="color: #006633;">eval</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;(export 'extensions::*argv0* 'extensions)&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre> <p>So that's it I guess. It seems to me that this is as far as you can go in the direction of standalone ABCL executables. Of course, this example is overly simple. We didn't try to incorporate full ASDF systems in the archive, and stuff. But you get the idea... I've attached my custom interpreter class below if you want to play with it.</p> <p>Have fun!</p> https://www.didierverna.net/blog/index.php?post/2011/01/22/Towards-ABCL-Standalone-Executables#comment-form https://www.didierverna.net/blog/index.php?feed/navlang:en/atom/comments/67