This compiles fine on ccl, but fails with a circular ref. error on SBCL:
kp.asd:
(in-package :asdf)
(defsystem kp
:components
((:module "utils"
:components
((:file "utils")
))
))
(load-system :kp)
------------------------
utils.lisp:
(defpackage :utils)
(in-package :utils)
(defvar *kp-version-utime* (get-universal-time))
------------------------
Error reported by SBCL:
debugger invoked on a LOAD-SYSTEM-DEFINITION-ERROR in thread #<THREAD "main thread" RUNNING {10005E85B3}>: Error while trying to load definition for \
system kp from pathname /var/www/ai/insights/kp.asd: Circular dependency: ((#<DEFINE-OP > . #<SYSTEM "kp">) (#<LOAD-OP > . #<SYSTEM "kp">) (#<LOAD-OP\
> . #<MODULE "kp" "utils">) (#<LOAD-OP > . #<CL-SOURCE-FILE "kp" "utils" "utils">) (#<PREPARE-OP > . #<CL-SOURCE-FILE "kp" "utils" "utils">) (#<PREP\
ARE-OP > . #<MODULE "kp" "utils">) (#<PREPARE-OP > . #<SYSTEM "kp">))
(It looks like your post is mostly code; please add some more details.)
(load-system :kp)
You should not have this in an ASDF declaration. Better not consider a system definition as a Lisp program, only as a declarative way of stating your dependencies.
If, during the declaration of the system, you also need to load another system, then that system becomes a dependency. What happens most likely is that SBCL consider the file as a whole when evaluating it, and while loading kp, you ask it to load kp, which is indeed a circular dependency. Maybe CCL silently consider the file already loaded, I don't know.
If you remove it, entering (ql:quickload :kp) in the REPL works perfectly fine. Also, the (in-package :asdf) is useless (and if provided, it should be asdf-user)
Your .asd is invalid and deserves to lose.
Do you have an old CCL with ASDF 3.2 or earlier? Then it fails to detect the bug in your .asd. ASDF 3.3 correctly detects circular dependencies in the loading of .asd files.
Related
I've built a library that wraps custom C code and thinking about the best way to build the shared library as part of the ASDF load. The makefile is conditionalised for various OSs, so it could be as simple as uiop:run-program ..., but I thought I'd ask here if there were a more standard idiom for this.
Because the C code is specific to this application, it won't be available through a package manager and must be built specifically for each users machine. I'm fine with documenting a manual build, but if I can smooth things for the user I will. I notice that Python seems to have some kind of automated way of building libs for their CFFI and wonder if there's something for CL.
For an answer to my own question: there seems to be neither a de-facto way of doing this (based on a search of github asd files), nor a definitive method in the ASDF best practices, though there are some ideas to be gleaned from that document.
I'll put my implementation out as a suggested idiom for this use case, along with some possible alternatives. Hopefully some of the ASDF experts here will correct any misunderstandings.
;; Define a makefile as a type of source file for the system
(defclass makefile (source-file) ((type :initform "m")))
;; tell ASDF how to compile it
(defmethod perform ((o load-op) (c makefile)) t)
(defmethod perform ((o compile-op) (c makefile))
(let* ((lib-dir (system-relative-pathname "cephes" "scipy-cephes"))
(lib (make-pathname :directory `(:relative ,(namestring lib-dir))
:name "libmd"
:type #+unix "so" #+(or windows win32) "dll"))
(built (probe-file (namestring lib))))
(if built
(format *error-output* "Library ~S exists, skipping build" lib)
(format *error-output* "Building ~S~%" lib))
(unless built
(run-program (format nil "cd ~S && make" (namestring lib-dir)) :output t))))
(defsystem "cephes"
:description "Wrapper for the Cephes Mathematical Library"
:version (:read-file-form "version.sexp")
:license "MS-PL"
:depends-on ("cffi")
:serial t
:components ((:module "libmd"
:components ((:makefile "makefile")))
(:file "package")
(:file "init")
(:file "cephes")))
This works fine, on both MS Windows and UNIX. Adding a method to perform seems to be the most common method on github.
An alternative might be to use a build-op, as described in building a system. The description
Some systems offer operations that are neither loading in the current
image, nor testing. Whichever operation a system is meant to be used
with, you may use it with:
(asdf:make :foobar)
This will invoke build-op, which in turn will depend on the
build-operation for the system, if defined, or load-op if not.
Therefore, for usual Lisp systems that want you to load them, the
above will be equivalent to (asdf:load-system :foobar), but for other
Lisp systems, e.g. one that creates a shell command-line executable,
(asdf:make ...) will do the Right Thing™, whatever that Right Thing™
is.
suggest to me that this is rather close to the idea of building a C library, and it would map nicely to the mental model of using a makefile and the asdf:make command. I didn't find too many examples in the wild of this being used though and technically we are loading the C lib into the existing image.
Another point that could be reconsidered is the detection of an existing shared library to avoid the rebuild. make will avoid recompiling if the shared library exists, but will still call the linker again. This causes errors because it can't write to the shared library when it's in use, at least on MS Windows. The ASDF example used Lisp code to detect the existence of the library and avoiding recompilation, but an alternative might be to use output-files.
The ASDF docs are a bit muddled on the purpose of output-files and there are no examples that make their intentions clear, but in the manual section on creating new operations we have:
output-files If your perform method has any output, you must define a
method for this function. for ASDF to determine where the outputs of
performing operation lie.
which suggests that defining the shared library (libmd.so or libmd.dll) is the recommended way to avoid a recompilation if the output-files already exists.
Finally, the C library could be considered a secondary system, cephes/libmd in this case, and added to the :depends-on clause in the main system. The section on other secondary systems demonstrates building an executable this way, with build-op. Except for the fact that this is building an executable and hard-codes ".exe" it seems to map well onto the use case:
To build an executable, define a system as follows (in this case, it's
a secondary system, but it could also be a primary system). You will
be able to create an executable file foobar-command by evaluating
(asdf:make :foobar/executable):
(defsystem "foobar/executable"
:build-operation program-op
:build-pathname "foobar-command" ;; shell name
:entry-point "foobar::start-foobar" ;; thunk
:depends-on ("foobar")
:components ((:file "main")))
The build-pathname gives the name of the executable; a .exe type will
be automatically added on Windows.
I didn't use this method because the secondary system would look almost exactly like the primary one does now, but would be slightly less understandable.
I put failing.asd
(in-package :asdf-user)
(defsystem "failing"
:description "some code destined to fail"
:version "0.1"
:author "me"
:components ((:file "package")))
and package.lisp
(defpackage :failing
(:export :foo :bar))
(in-package :failing)
(defun foo () 42)
(defmacro bar ()
(let ((x (foo)))
`(print ,x)))
(bar)
into ~/quicklisp/local-projects/failing. Using Clozure CL with Quicklisp installed, I run
(ql:quickload :failing)
which gives me
To load "failing":
Load 1 ASDF system:
failing
; Loading "failing"
[package failing]
> Error: Undefined function FOO called with arguments () .
> While executing: BAR, in process listener(1).
> Type :GO to continue, :POP to abort, :R for a list of available restarts.
> If continued: Retry applying FOO to NIL.
> Type :? for other options.
It seems I can't call a function from a macro inside the package. Why not?
That can only happen when the file gets compiled before loaded. Generally it has nothing to do with ASDF (which is a tool to manage file dependencies and to compile/load code) or packages (which are namespaces and are not in any way related to ASDF).
It has to do with how file compilation works in Common Lisp:
The file compiler sees the function foo and compiles it -> the code for it gets written to a file. It does not (!) load the code into the compile-time environment.
The file compiler then sees the macro bar and compiles it -> the code gets written to a file. It does (!) load the code into the compile-time environment.
The file compiler then sees the macro form (bar) and wants to expand it. It calls the macro function bar. Which calls foo, which is undefined, since it is not in the compile-time environment.
Solutions:
put the function definition in a separate file in the ASDF system and compile/load it earlier.
put the function inside the macro as a local function
put (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) around the function definition. It causes the definition to be executed at compile-time.
Remember: the file compiler needs to know macro functions -> otherwise it would not be able to macroexpand code. Ordinary functions just get compiled, but not loaded at compile time, during compilation of a file.
I am having trouble using the asdf build tools with common lisp. Here is my fractals.asd file:
(defpackage :fractals
(:use :cl :asdf :cl-opengl :cl-glu :cl-glut)
(:export :frac-tree :draw-tree))
(in-package :fractals)
;----------------------------------------------------------
(defsystem fractals
:name "fractals"
:version "0.0.0"
:serial t
:components ((:file "frac-tree")
(:file "fractal-lab")))
Both the ffiles fract-tree.lisp and fractal-lab.lisp have the statement (in-package :fractals) at the very beginning of the file. However, I am automatically getting an error saying The name "CL-OPENGL" does not designate any package. I don't understand why this is wrong. Furthermore, if I don't include these libraries, then I have to manually require them myself... I created a directory called: ~/.config/common-lisp/source-registry.conf.d/ where I placed a file called fractals2.conf which contains the following:
(:directory "~/lisp_proj/fractals2/")
This is the directory of my fractals.asd file as shown above. Apparently, this is supposed to tell asdf where my user-defined systems are located. I followed this tutorial.
In summary, how can I get asdf to find my user-defined systems so that I do not have to manually load them?
Thanks for all the help!
You need to declare your dependencies in the defsystem with a :depends-on clause. The :use clause of the defpackage only makes names of already loaded packages available, but won't load them.
BTW, your .asd file should not include the defpackage of the main package. While it makes sense to declare a special package to run the defsystems in there, regular packages should be declared in .lisp files.
EDIT: This may also solve your second problem. If not, it's probably because ASDF doesn't automatically expand the ~ character in paths. In that case, replace it with the actual path of your home directory.
1- Looks like you were using the undefined fractals:defsystem instead of asdf:defsystem
2- the ASDF DSL accepts the directive (:directory (:home "lisp_proj/fractals_2"))
3- Starting with 3.1.2, you can put everything under ~/common-lisp/
4- For God's Sake, RTFM!
Here's a strange situation. I have this code:
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload "cffi-grovel")
(setf cffi-grovel::*cc* "mpicc")) ; <--- this is the line it complains about.
Which I belive has to load cffi-grovel package before setting cffi-grovel::*cc* variable. When this form is executed from SLIME it works, but it doesn't work when loaded directly by SBCL, here's the output:
$ sbcl --noinfo
* (ql:quickload "cl-mpi")
debugger invoked on a LOAD-SYSTEM-DEFINITION-ERROR in thread
#<THREAD "main thread" RUNNING {10029C0E43}>:
Error while trying to load definition for system cl-mpi from pathname
/home/wvxvw/quicklisp/local-projects/cl-mpi/cl-mpi.asd:
READ error during COMPILE-FILE:
Package CFFI-GROVEL does not exist.
Line: 6, Column: 25, File-Position: 264
< restarts ... >
* (ql:quickload "cffi-grovel")
To load "cffi-grovel":
Load 1 ASDF system:
cffi-grovel
; Loading "cffi-grovel"
..
("cffi-grovel")
* (ql:quickload "cl-mpi")
To load "cffi-grovel":
Load 1 ASDF system:
cffi-grovel
; Loading "cffi-grovel"
To load "cl-mpi":
Load 1 ASDF system:
cl-mpi
; Loading "cl-mpi"
; mpicc -m64 ...
; ...
.
("cl-mpi")
Why does it fail the first time?
PS. I also tried #.cffi-grovel::*cc* instead - same result.
It fails because Lisp reads every form before executing it. And when it reads it, package cffi-grovel indeed does not exist yet, because cffi-grovel is loaded at execution time (whatever it means for form wrapped with eval-when).
Try spliting the eval-when form into two eval-whens: ql:quickload and setf. Or write something like this:
(setf (symbol-value (find-symbol "*CC*" "CFFI-GROVEL"))
"mpicc")
monoid gives a good answer. Here is a slightly shorter form.
Find the symbol at runtime, when the package actually exists. Note the use of SET, not SETF here:
(set (find-symbol "*CC*" "CFFI-GROVEL") "mpicc")
To make it more robust, one would need to check that FIND-SYMBOL actually finds the symbol.
The following function also gives you meaningful error messages and restarts:
(defun set-runtime-symbol (name package value)
(assert (find-package package) (package))
(assert (find-symbol name package) (name))
(set (find-symbol name package) value))
Alternatively use two EVAL-WHEN statements, instead of one.
OK, finally figured it I can do it like so:
(setf #.(intern "cffi-grovel::*cc*") "mpicc")
But I'm not sure how much this is fail-safe.
Doing a SWIG tutorial, and using the example.c, example.i as they provided there. I generated lisp file with swig -cffi example.i.
But when I run test.lisp with SBCL, i get a complaint about undefined alien function, as well as complaints when compiling the example.lisp itself. I'm pretty sure I still have to compile my example.c into a library and then somehow tell SBCL to load it! But the docs are very scant on this, except for this.
Can somebody tell me how to do this or is there a better way than SWIG to automatically generate CFFI bindings from C/C++ sources??
sbcl output:
...
;
; caught STYLE-WARNING:
; Undefined alien: "fact"
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
;
; caught STYLE-WARNING:
; Undefined alien: "my_mod"
...
test.lisp
;call C functions defined in example.c
(require :cffi)
;;;(require :example "example.lisp")
(load "example.lisp")
(fact 2)
(quit)
First, you need to compile the C library. Do something like:
gcc -shared example.c -o libexample.so
Of course, for a complex existing library compilation could be considerably more complex -- if you're wrapping an existing library, it probably comes with some sort of Makefile to help you build it.
Then, in Lisp, use CFFI to define and load the library. This seems to be the main part that you're missing.
(cffi:define-foreign-library libexample
(t (:default "libexample"))) ;; note no .so suffix here
(cffi:use-foreign-library libexample)
This part:
(t (:default "libexample"))
is a conditional which you can use to give different loading instructions for different platforms. (t ...) is the catchall option, much like with COND. You can find the exact syntax in the documentation for define-foreign-library.
You would now normally use cffi:defcfun and so on to define the functions in the library. This is what the SWIG-generated file does for you, so load it:
(load "example.lisp")
You can now call the functions as normal Lisp functions:
(fact 5)
=> 120