davypough
davypough

Reputation: 1941

Using ASDF's :around-compile For Individual Files

An SO post containing a response by coredump shows how to apply a compiler policy to an ASDF system's component files:

(defsystem simple-system
  :serial t
  :around-compile (lambda (next)
                    (proclaim '(optimize (debug 3) 
                                         (safety 3)
                                         (debug 3)
                                         (speed 0)))
                    (funcall next))
  :components ((:module "src"
                        :components
                        (...))))

It also mentions that you can "shadow" individual files, but how would this work. It's confusing to me because next in the lambda expression is bound to a closure. Since I only need to apply the optimization to a couple of component files, how do you give those file names to :around-compile?

Upvotes: 4

Views: 293

Answers (1)

coredump
coredump

Reputation: 38799

You can add :around-compile for a system, a module or a file.

More precisely, if you have a :file component like this:

(:file "a")

Then you can add:

(:file "a" :around-compile ...)

If you only want to apply optimizations to a given set of files, group them in a module. You can even set the module's pathanme to "" so that its files are in the same directories are the sibling components:

(:module #:MY-OPTIMIZED-FILES
         :depends-on (...)
         ;; SAME DIRECTORY
         :pathname ""
         :serial t
         :around-compile "my-meta-lib:around-compile"
         :components ((:file "a")
                      (:file "b")
                      (:file "c")
                      (:file "d")))

You cannot refer to a symbol if the system that defines it is not loaded, and in the case of an ASDF system, you can't declare dependencies without reading first the form that defines the system. So you need to use strings to refer to a symbol in another package.

When the system is processed, the string must refer to an existing symbol, so you need to have a different .asd file, for example simple-system.meta.asd, which defines the system "simple-system.meta". You add a dependency with :defsystem-depends-on to make sure the meta system is loaded before simple-system is processed.

That system could be for example:

(defsystem simple-system.meta
  :depends-on ("trivial-cltl2")
  :components ((:file "meta")))

The reason I am using trivial-cltl2 is to be able to introspect the declaration in the global environment and hopefully limit the effects of proclaim:

(defun my-meta-lib:around-compile (next)
  (let ((opt (trivial-cltl2:declaration-information 'optimize)))
    (proclaim '(optimize (debug 3) 
                         (safety 3)
                         (debug 3)
                         (speed 0)))
    (unwind-protect (funcall next)
      (proclaim (list* 'optimize opt)))))

As far as I know, proclaim modifies the global environment and it could affect the compilation of other files, that's why I prefer to restore the environment after compilation finishes.

SBCL has an experimental :policy option for with-compilation-unit that is made for this use case, the policy is modified in the dynamic extent of the macro:

(flet ((debug () (assoc 'debug (sb-cltl2:declaration-information 'optimize))))
  (list (debug)
        (with-compilation-unit (:policy '(optimize (debug 3)))
          (debug))
        (debug)))

 ; => ((DEBUG 1) (DEBUG 3) (DEBUG 1))

Upvotes: 6

Related Questions