How to build Common Lisp project? - common-lisp

I'm new to CL and I can't figure out how to build binaries from my simple project.
I've created app.asd file with following contents:
(asdf:defsystem game
:version "0.0.1"
:components ((:file "package")
(:file "main")))
package.lisp contents
(defpackage :app
(:use :common-lisp :asdf)
(:export :start))
and main.lisp
(in-package :app)
(defun start (args)
(format t "Hello"))
I've also symlinked app.asd to ~/quicklisp/quicklisp/app.asd, and when I execute
(require 'asdf)
(asdf:operate 'asdf:load-op :app)
it looks like something compiles, but I can't find binaries/object files anywhere.
How do I build my project so I can copy it to another machine without CL installed and run?
I'm using sbcl 1.1.13 and asdf 3.0.2 on osx.

Don't put .asd files in ~/quicklisp/quicklisp/. Use ~/quicklisp/local-projects/ instead.
The syntax for your start defun is wrong. It needs a lambda list.
Common Lisp is often used by starting the environment, loading the application, and then interactively calling functions defined by your application. The load process compiles the source code to machine code and the functions you call interactively are compiled functions.
When I want to make a binary file I can run from the command line, I use buildapp. In your case, you could do something like this:
sbcl --no-userinit --no-sysinit --non-interactive --load ~/quicklisp/setup.lisp \
--eval '(ql:write-asdf-manifest-file "systems.txt")'
buildapp --manifest-file systems.txt \
--manifest-file ~/quicklisp/local-projects/system-index.txt \
--load-system game \
--entry app::start \
--output game
In this scheme, you would have to modify app::start to accept one argument, a list containing all the command line argument strings passed to game.
In general, most Common Lisps have some way to produce a binary program to run independently of the normal runtime. The terminology varies, but it's often called "delivery". If you use a different Common Lisp in the future, you may get more information if you search the documentation for information about delivery.

Related

Why do I need to evaluate defpackage for a new REPL?

I use ADSF to organise my projects.
(asdf:defsystem :sender
:version "0.1.4"
:serial t
:depends-on (:cl-json :dexador :clsql :clsql-sqlite3)
:components ((:file "packages")
(:file "validation")
(:file "sender")))
But when I open a new repl (slime, emacs), I have to go to the packages.lisp file and evaluate the form before I can change the repl to that package with (in-package :sender).
Is there a way to make the slime repl remember my packages?
My current thinking is that I need to "run" adsf to load all my files. If so, how?
UPDATE:
Actually, I need to compile all the files manually before I can actively use them in the repl. I believe I am using asdf incorrectly.
From a .asd system definition, the steps would be:
compile the .asd file, so that you Lisp image knows about the system
C-c C-k or programmatically: (asdf:load-asd "system").
LOAD it somehow: (ql:quickload "system") to install dependencies
or (asdf:load-system "system"), but it will fail if your project has unknown dependencies, Quicklisp would download them.
avoid using cl:load for asdf systems, load-asd / load-system will do more complex stuff.
use it on the REPL. The package that was defined in a packages.lisp file should be available, because this file was declared in the .asd.
You can add stuff to your ~/.sbclrc (or similar for other implementations). Here I tell ASDF where a project lives, so I can quickload it without manually compiling the .asd before:
(pushnew "/home/vince/projets/ciel/" asdf:*central-registry* :test #'equal)
This is an "old" style not encouraged anymore by the ASDF documentation.
Or simply create a symlink for your project to ~/quicklisp/local-projects/ or ~/common-lisp/.
I wouldn't "load-system" my project in the init file, because that's a side effect that would show sometimes when I don't want it (like building an image: if that involves reading my init file, I'd have an unwanted system in it).
Saving a core image with a ton of dependencies is cool. Use it with sbcl --core …. The advantage is that it starts up instantly in the REPL, when loading all projects and dependencies would take a few seconds.
Pointers
ASDF
ASDF cookbook
ASDF manual
ASDF best practices
ASDF Build System explained
Overview
A system is an object which describes a library (or an application) which consists of: a name, a version, a list of files and subsystems, and a list of other systems it depends on. There is more.
If you want to have Lisp know a certain system, then we need to execute its defsystem form either manually or find it automatically. That ONLY makes the system definition known.
If you want to load the system's files, then you need to LOAD the SYSTEM. If you want to compile the system's files files, the you need to COMPILE the system.
So, if you want to use a package (which is a namespace), then you need to run the DEFPACKAGE form. If that form is describe in a file which is component of a system, then you need to load that system.
ASDF : a popular build system for Common Lisp
system -> library, application. It is a bunch of files and dependencies.
system definition -> not the system itself, but a description of it. Use DEFSYSTEM to describe a system
system operations -> like load-system, compile-system and others.
system registry -> Typically we want ASDF to find the systems by name. See the manual or a tutorial for ASDF.
lisp file -> some file we can compile or load
fasl file -> some compiled file we can load
package -> a namespace for symbols, that's actually a built-in feature for Common Lisp
Quicklisp -> a library manager with a selection of curated Common Lisp libraries/applications. The libraries can be loaded over the Internet. Be aware that there is little to no security when loading foreign code from the Internet.
Use a system, its components and dependencies
If you want to use a system you need to load it first. Manually or automatically.
That loading can be done automatically:
For example in an init-file for your Lisp you can load all systems you want/need. Alternative write a function which loads all systems you want.
saving an image. Some Lisps support saving an image. Thus one can load all interesting systems and then save an image. If one starts that image later, all those systems are already in memory. (side problem: getting newer versions loaded)

How to determine file type in Lisp

Is there anything like file in shell for lisp to determine file type? If not, how do you determine file tpye?
In most case pathname-type is good enough, but sometimes you may run into some files doesn't have been correct named.
The functionality of file is available in libmagic:
As of version 4.00 of the Ian Darwin/Christos Zoulas version of file, the functionality of file is incorporated into a libmagic library that is accessible via C (and C-compatible) linking;[7][8] file is implemented using that library.
Wikipedia
And there is a binding for Common Lisp.
I had to install libmagic-dev on my system, and then:
CL-USER> (ql:quickload :magicffi)
...
(:MAGICFFI)
CL-USER> (magicffi:pathname-mime #P"/bin/ls")
"application/x-sharedlib; charset=binary"
See MAGICFFI.

Compile With Static Library Using GNAT

To learn the processes behind compilation of Ada programs (without resorting to using gpr), I've decided to compile one of my projects manually. To compile a program, I run the following command on each .adb file therein (not manually, however):
$ gcc -c src/<file>.adb -o obj/<file>.o
That compiles all the files and puts them in the obj directory.
Or rather, it would. There is a slight problem with this. I have an archive (static library) that I've generated from another project, called libapples.a, containing package Apples. The Apples package is used by the files you see me compiling above.
Since libapples.a doesn't have source files anymore (given its archive format), it's not viable (not even possible) to provide the sources of that library to the command above using -I switches; on the other hand, if I don't have the sources included, the command above comes back to me saying:
<file>.adb:<y>:<x>: file "apples.ads" not found
gnatmake: "src/<file>.adb" compilation error
I've attempted to include the library in the compilation process by using flags -L and l (which I believe is how you'd do it in C; feel free to correct me if I'm wrong). In another attempt I placed the archive right in the source directory.
How does one go about including a library in the compilation process?
apples.ads is somewhat like a header in C and you definitely need it for your file to compile. You should use -I to point gcc to the directory where apples.ads is located in.
Be aware that compiling an Ada source not only yields an object file, but also an Ada Library Information (.ali) file. You'll need that for the next step. You should not use the -o flag because it may lead to your .o and .ali files having different names.
On the other hand, since you only want to produce an object file, you should use -c to tell gcc to only compile. In other languages like C, gcc compiles and links by default; in Ada, the process is more complex.
After compilation, you need to call gnatbind on the .ali file of your main program. This generates a package that will take care of proper initialization of everything. To do that, gnatbind needs the .ali file of the Apples package! You can specify the .ali search directory with -aO.
Finally, gnatlink called on the main .ali file will link everything together. It figures out itself which object files and libraries it needs. It will append the necessary options to the linker, including the reference to libapples.a. It also compiles the package generated by gnatlink.
I don't know exactly what gnatlink will do under the hood; you could probably compile the package generated by gnatbind manually and then call the linker if you can figure out the proper arguments.

Loading quicklisp package for use in another lisp file

I have downloaded a large software project FriCAS which I have compiled from source and using SBCL. This was just a matter of using GNU .configure - I am a complete Lisp newbie.
However, in order to add some further functionality I have - by very carefully following directions - installed quicklisp and a few extra packages. So far so good.
Here's my issue: I am trying to compile an external lisp file for use in the system. I need to make quicklisp and its packages visible to the compiler. So I've copied my .sbclrc file to the top of my lisp file:
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
I have followed this with
(eval-when (:compile-toplevel)
(ql:quickload "f2cl-lib"))
I have already installed f2cl-lib with quicklisp. However, my compiler (within FriCAS) immediately aborts with
; caught ERROR:
; READ error during COMPILE-FILE:
;
; Package QL does not exist.
I'm a bit stumped here - there's probably something trivial and obvious which is missing, but as I say from a newbie perspective I don't know what it is.
Looks like a FAQ. See also the documentation for eval-when.
The File Compiler
Remember, a file compiler compiles forms and does not execute them. It just generates code for later execution. Generally. Though, in Common Lisp there are some exceptions to this rule: for example a macro, used in the source code, will be run by the file compiler. But Common Lisp also allows us to tell the file compiler to execute code at compile time, that's one of the use cases of eval-when.
Your example simplified
If you have a file:
(load "file-which-creates-package-foo")
(eval-when (:compile-toplevel)
(foo:bar))
Then the file compiler will generate code for loading the file, but it will not load the file.
Next, the file compiler sees a second form, where the file compiler is instructed to execute a statement at compile time (and only at compile time), so (foo:bar) will be executed at compile time. But it is worse: Since the loading code has not been executed yet, the package FOO is unknown, and the form (foo:bar) will be read at compile time, the reader already complains at compile time that the package FOO does not exist.
The use of EVAL-WHEN
What you need to do, is to tell the file compiler to actually load the stuff it needs during compilation. The file already contains an eval-when form, so that should give you a hint: use eval-when. It has three situations:
:compile-toplevel -> the file compiler executes the enclosed forms
:load-toplevel -> the file compiler generates code so that the code will be executed when loading the code
:execute
Make sure that those situations are mentioned, when the code should be executed.
Possible solutions
So there are possible solutions:
as above, use EVAL-WHEN to be able to execute forms by the file compiler
write the loading code into a separate file and compile/load this file before you use the package. One could also add this file to a system such that it will be loaded by the compiling the system, before the using code will be compiled.

The logic of the ocaml compile process

I wrote a small project in OCaml.
I have two folders:
./myUnionFind. inside there is a file myUnionFind.ml.
./percolation. inside there are two files: myPercolation.ml and percolation_stats.ml.
myUnionFind.ml works as a module.
myPercolation.ml works as a module too, but it uses MyUnionFind module defined in myUnionFind.ml.
percolation_stats.ml uses myPercolation.ml.
together with above all, I also use Batteries.
I want to compile them all to work and get a executable file run.
But how?
I have tried the following:
inside folder ./percolation, I did ocamlfind ocamlc -package batteries -linkpkg ../myUnionFind/myUnionFind.ml myPercolation.ml percolation_stats.ml -o run
It failed, and said File "myPercolation.ml", line 1, characters 0-16:
Error: Unbound module MyUnionFind, but I did include that folder ../myUnionFind, right?
Also, if I just want to compile ./myUnionFind/myUnionFind.ml once, how can I do so that not every time, myUnionFind.ml gets compiled again and again?
Inside myUnionFind, you should compile myUnionfind.ml to a compiled unit:
cd myUnionFind
ocamlc -c myUnionFind.ml
This will generate myUnionFind.cmo, which stores the compiled implementation, and myUnionFind.cmi, which stores the compiled interface. Other modules using MyUnionFind will need to access the cmi at type-checking type.
Inside percolation, you can compile myPercolation.ml to a module by doing
cd percolation
ocamlc -I ../myUnionFind -c myPercolation.ml
Again, you get both a .cmo and a .cmi. Note that the compiler has looked up myUnionFind.cmi automatically in the search path, and found it because of the -I option.
You can then compile percolation_stats (relying on both previous compilation units)
ocamlc -I ../myUnionFind -c percolation_stats.ml
You finally link the three resulting .cmo together to build an executable:
ocamlc ../myUnionFind.cmo myPercolation.cmo percolation_stats.cmo -o run
(If you use batteries, wrap each command with ocamlfind ocamlc -package batteries, but only the linking command with linkpkg).
To make this process simpler:
ocamlbuild is good at finding and compiling all the files of your current project to produce an executable. In the percolation directory, ocamlbuild percolation_stats.byte can produce an executable with all the stuff present
but if you want to use myUnionFind as an external library, the best thing to do would be to install it with findlib, to make it easy to find and specify from percolation; for information on how to create a findlib package (it's very simple), please see this older answer
once myUnionFind is a findlib package, the single command ocamlbuild -use-ocamlfind -pkgs batteries,my-union-find percolation_stats.byte (or .native) should be enough to get an executable

Resources