Run Multiple functions from an executable? - common-lisp

I build CL executables as advised in the CL cookbook. I use the ASDF method with :entry-point
(asdf:defsystem #:thing
:serial t
:components ((:file "package")
(:file "thing"))
:build-operation "program-op"
:build-pathname "launch"
:entry-point "thing:main")
I added an entry point. Which is the main function. When I open (double click) my executable, the main function runs.
As my app has grown, i have multiple stand alone "main" operations.
Is this a sign for adding a UI or can i create executables that run different operations?

You can dispatch on the binary name, and get the latter with (uiop:raw-command-line-arguments).
Suppose I build a binary. Inside your thing:main function, we study how UIOP gets us the CLI args:
(format t "raw CLI args are: ~S~&" (uiop:raw-command-line-arguments))
(format t "CLI args are: ~S~&" (uiop:command-line-arguments))
If I call ./helloapp me, the values are:
raw CLI args are: ("./helloapp" "me")
CLI args are: ("me")
We can dispatch on the calling binary name from the main function.
(defun hello (args)
(format t "Hello ~a!~&" (first args)))
(defun goodbye (args)
(format t "Hello and bye ~a!~&" (first args)))
(defun main ()
(format t "raw CLI args are: ~S~&" (uiop:raw-command-line-arguments))
(format t "CLI args are: ~S~&" (uiop:command-line-arguments))
(if (search "goodbye" (first (uiop:raw-command-line-arguments))
:test #'equalp)
(goodbye (uiop:command-line-arguments))
(hello (uiop:command-line-arguments)))))
Creating links works:
$ ln -s helloapp goodbyeapp
$ ./goodbyeapp me
raw CLI args are: ("./goodbye-link" "me")
CLI args are: ("me")
var args are: ("me")
Hello and bye me!
ACK: I'm building on Ignis Volens' comment and learned a trick along the way.

Related

Unable to compile file when a defconstant uses a function from the same file

This is example.lisp:
(defun f (x)
(* x 2))
(defconstant +n+ (f 1))
When I compile try to compile the file using (compile-file "example.lisp"), I get an error when compiling the constant: The function COMMON-LISP-USER::F is undefined. How can this be? I have already defined the function f before using it to define the constant. How do I define a constant whose value is the return value of a function?
I am using SBCL 2.0.1.
From DEFCONSTANT:
[...] users must ensure that the initial-value can be evaluated at compile time (regardless of whether or not references to name appear in the file) and that it always evaluates to the same value.
But COMPILE-FILE and 3.2.3 File Compilation only require the DEFUN form to establish that F exists, not that is may be evaluated during compilation.
You need to LOAD the resulting file for the definition to be available in your environment (e.g. Slime compiles a file and loads the result). Likewise, when you evaluate forms one at a time in the REPL, the definition becomes available right away, as-if a small compile-load cycle was done.
You have two main options:
wrap the defun in an eval-when form
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun f (x) (* x 2)))
put the two forms in distinct files, and have the second one depends in the system (in the defsystem sense) on the first one:
(defsystem "foo"
:components ((:file "define-f")
(:file "define-constant"))
:serial t)

sdl2:load-bmp Problem with current working directory, common-lisp

While trying to work through cl-sdl2-tutorial, I'm having trouble loading a bitmap due to a wrong current working directory.
I'd like to get a proper solution to the problem using relative path names.
A minimal working example:
Having modified the code of example two from above mentioned tutorial.
(defpackage #:sdl2-tutorial-2
(:use :common-lisp)
(:export :main))
(in-package :sdl2-tutorial-2)
(defparameter *screen-width* 640)
(defparameter *screen-height* 480)
(defmacro with-window-surface ((window surface) &body body)
`(sdl2:with-init (:video)
(sdl2:with-window (,window
:title "SDL2 Tutorial"
:w *screen-width*
:h *screen-height*
:flags '(:shown))
(let ((,surface (sdl2:get-window-surface ,window)))
,#body))))
(defun main(&key (delay 2000))
(format t " cwd: ~a, ~% dpd: ~a, ~& e-p: ~a, ~% pf: ~a, ~& load: ~a"
(sb-posix:getcwd)
*default-pathname-defaults*
(uiop:file-exists-p "hello_world.bmp")
(probe-file "hello_world.bmp")
(sdl2:load-bmp "hello_world.bmp"))
(with-window-surface (window screen-surface)
(let ((image (sdl2:load-bmp "hello_world.bmp")))
(break "1 here with ~a~%" image)
(setf image (sdl2:load-bmp "hello_world.bmp"))
(break "2 here with ~a~%" image)
(break "3 error: ~a~%" (sdl2-ffi.functions:sdl-get-error))
(sdl2:blit-surface image
nil
screen-surface
nil)
(sdl2:update-window window)
(sdl2:with-event-loop (:method :poll)
(:quit () t)
(:idle ()
(sdl2:delay delay))))))
Before compiling above code and running (main), I changed working directory in the REPL, via:
(sb-posix:chdir (truename "/test/cl-sdl2-tutorial/2/"))
(setf *default-pathname-defaults* (truename "/test/cl-sdl2-tutorial/2/"))
The above code prints, as expected, when running (main) in the REPL:
SDL2-TUTORIAL-2> (sdl2-tutorial-2:main)
0: (SDL2-TUTORIAL-2:MAIN)
cwd: /test/cl-sdl2-tutorial/2,
dpd: /test/cl-sdl2-tutorial/2/,
e-p: /test/cl-sdl2-tutorial/2/hello_world.bmp,
pf: /test/cl-sdl2-tutorial/2/hello_world.bmp,
load: #<SDL-SURFACE {#X7F5CBC018DD0}>
Problem:
The bitmap can not be found and therefore not loaded.
Calls to (sdl2:load-bmp "hello_world.bmp") always return a a zero pointer (#<SDL-SURFACE {#X00000000}>) and breakpoint 3 states:
3 error: Couldn't open /home/jue/hello_world.bmp
but evaling (sdl2:load-bmp "hello_world.bmp") during a break from breakpoints 1 or 2 or 3, is successful and continuing (main) displays the picture.
Questions:
Why is sdl2:load-bmp using the "wrong" working directory and why is it using the "correct" working directory during breakpoints?
How to make sdl2:load-bmp use the wanted working directory (instead of "/home/jue/") when using relative paths?
Remarks
(I'm using current released versions of sbcl, Emacs, sly on a Linux machine, if that matters. I'm only intermediate experienced with Common Lisp and its development environment, but advanced at elisp)
I suspect but don't know that the problem is that the sdl2 library is doing fanciness with threads, with the result that the working directory isn't what you think.
The solution to this in my experience is never to let the implementation second-guess you like that. In any case where there's some interface which says "do something to a file" give it an absolute pathname so it has no chance to do any thinking of its own. Do something like.
(defparameter *where-my-bitmaps-live* (merge-pathnames
(pathname "lib/bitmaps/")
(user-homedir-pathname)))
...
(defun load-a-bitmap (name)
(load-bmp (merge-pathnames (pathname name) *where-my-bitmaps-live*)))
And now it really has no excuse.
(You want to check that the above pathname-mergery is actually right: it seems to be for me but I forget the details of pathname rules every time I look away for more than a few minutes).

Error: Unbound variable: *AJAX-PROCESSOR* using HT-SIMPLE-AJAX

I'm using HT-SIMPLE-AJAX to provide a simple JSON structure over AJAX. It works beautifully if the function defined by defun-ajaxis compiled after the lisp image and the server is started.
If I load the lisp program (with ccl --load) with the function defined, I get this error:
Error: Unbound variable: *AJAX-PROCESSOR*
While executing: #, in process listener(1).
Type :GO to continue, :POP to abort, :R for a list of available restarts.
If continued: Skip loading "/home/hunchentoot/quicklisp/local-projects/gac-man/run.lisp"
Type :? for other options.
The function is as follows:
(defun-ajax machine-info (serial) (*ajax-processor*)
(let* ((serialn (remove #\" serial)))
(concatenate 'string
"Lots of boring stuff" "here")))
The ajax processor is created in another function, called at the start of the program:
(defun start ()
(setup)
(connect-to-database)
(defvar *web-server* (start (make-instance 'hunchentoot:easy-acceptor :port 8080
:document-root #p"~/www/")))
(defvar *ajax-processor*
(make-instance 'ajax-processor :server-uri "/ajax"))
(print "Starting web server...")
(setf *show-lisp-errors-p* t
*show-lisp-backtraces-p* t)
(define-easy-handler (docroot :uri "/") () (docroot)
....
....
(setq *dispatch-table* (list 'dispatch-easy-handlers
(create-ajax-dispatcher *ajax-processor*)))))
And yet if I start everything and then compile in the function through slime later, it works just fine. Why is this error occurring?
I'm using Clozure Common Lisp on 64-bit Linux.
It seems that your defun-ajax form is loaded before the start function has run. That is not surprising. Usually, all code is loaded, and only then the entry point is called.
You should always be very suspicious of defvar, defun, defparameter etc. forms appearing in a function body. They don't belong there. Put them as toplevel forms, so they are loaded as part of the program. Most of the things defined during the run of the start function shown should really be toplevel forms.

Function invisible from macrolet? [duplicate]

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.

ESS (R) Auto-complete

I try to get a practical development environment for R in Emacs, hoping to get auto-completion working as shown in http://www.emacswiki.org/emacs/ESSAuto-complete.
However, even in a minimal configuration, I can't get it working.
See what I get on http://screencast.com/t/qcyVwkECX. In fact, while AC does work (see completion menu appearing), it's like if there was no info from the R language itself, while ac-source-R is WELL added to ac-sources.
Do you understand what's happening?
Best regards.
PS- Here is my minimal Emacs configuration file for the demo:
;; Auto Completion
(add-to-list 'load-path "~/.emacs.d/elpa/auto-complete-20140824.1658/")
(add-to-list 'load-path "~/.emacs.d/elpa/popup-20140815.629/")
(when (require 'auto-complete-config)
(ac-config-default)
;; use `C-n/C-p' to select candidates
(setq ac-use-menu-map t)
(define-key ac-menu-map (kbd "C-n") 'ac-next)
(define-key ac-menu-map (kbd "C-p") 'ac-previous)
;; unbind some keys (inconvenient in iESS buffers)
(define-key ac-completing-map (kbd "M-n") nil)
(define-key ac-completing-map (kbd "M-p") nil)
;; set default sources
(setq ac-sources
(append '(ac-source-features
ac-source-functions
ac-source-yasnippet
ac-source-variables
ac-source-symbols)
ac-sources))
(setq ac-delay 0) ; faster than default 0.1
(setq ac-auto-show-menu 0.2)
(setq ac-quick-help-delay 0.5)
(setq ac-quick-help-height 10)
(setq ac-candidate-limit 100)
;; completion by TAB
(define-key ac-completing-map
(kbd "<tab>") 'ac-complete)
;; avoid Flyspell processes when auto completion is being started
(ac-flyspell-workaround))
;; ESS: Emacs Speaks Statistics
(add-to-list 'load-path "~/.emacs.d/elpa/ess-20140824.1452/lisp/")
(setq shell-file-name "zsh.exe")
(add-to-list 'auto-mode-alist '("\\.[rR]\\'" . R-mode))
(autoload 'R "ess-site" "Call 'R', the 'GNU S' system from the R Foundation." t)
(autoload 'R-mode "ess-site" "Major mode for editing R source." t)
(setq ess-ask-for-ess-directory nil)
(setq inferior-ess-same-window nil)
(setq ess-default-style 'DEFAULT)
(with-eval-after-load "ess-site"
;; use eldoc to report R function names
(require 'ess-eldoc)
(add-hook 'inferior-ess-mode-hook 'ess-use-eldoc))
PPS- In fact, ElDoc does not seem to work either!
UPDATE
I just discovered it almost works when the iESS buffer gets created, not when just editing R code in its own buffer. See http://screencast.com/t/fKRjLmIC6K0.
What would explain that iESS must be run first before it finally works?
Still, something that does not work is the completion on function arguments (like with the cat function on the page http://www.emacswiki.org/emacs/ESSAuto-complete).
Why does that not work?
After opening a R file with emacs, if you have this mode described into your emacs status bar:
(ESS[S] [none] ElDoc AS)
You can run this shortcut C-c C-s to attach a R session to you ESS[S] editor mode.
If you already have one or some R session open, emacs will ask you to choose the R session you want to use. Otherwise if you have not already open a R session then emacs will open a new one for you.
Next, you should have this information inside your status bar:
(ESS[S] [R db -] ElDoc AS)
and the completion should work.

Resources