'(ASDF 3) How do you flag a file|component as "dirty" or in need of rebuilding?

I would like to tell ASDF that the status of a particular component has changed and it should be recompiled the next time the enclosing system gets loaded. Motivation: I envision having configuration variables which trigger this process upon being modified.

The necessary tools appear to be in the asdf/plan package, if I have to guess.


3 March 2022: My use of the word 'variables' in my example may have misled @coredump, whose reply deals with environment variables. For clarification, what I had in mind was a special variable acting as a compile-time constant for everything downstream of it in the build process. Modifying it would involve a setf-expansion side effect to set the theoretical "dirty" state for the appropriate component tree.

Despite the miscommunication, I'm fairly certain his technique of subclassing asdf:cl-source-file and specializing asdf:operation-done-p--something I hadn't tried--could still do it.



Solution 1:[1]

I tested the following by first adding a new component type that depends on the current value of an environment variable. The package and the definitions are as follows, based on the Object Model of ASDF section of the manual (this depends on osicat):

(defpackage env-var-asdf
  (:use :cl)
  (:export #:cl-source-file-env-var
           #:depends-on-env-var))

(in-package env-var-asdf)

;; mixin
;;
;; this component depends on a posix environment variable (`varname`) and
;; stores the last known value (`last-value`). AFAIK objects are rebuilt when the 
;; asd file is reloaded, so you can preserve some state in the components if
;; the system definition does not change.
;;
(defclass depends-on-env-var ()
  ((varname :initarg :varname :reader env-var-varname)
   (last-value :accessor env-var-last-value)))

;; this method defines if the operation is already done, we return NIL if
;; the file needs to be recompiled; here, if no last known value exist for the
;; environment variable, or if the current value differs from the last one.
;;
;; I made it an `:around` method but this might be wrong (it never 
;; calls the next method; the behavior depends only on the 
;; environment variable).
;;
(defmethod asdf:operation-done-p :around (operation (component depends-on-env-var))
  (let ((value (osicat-posix:getenv (env-var-varname component))))
    (prog1 (and (slot-boundp component 'last-value)
                (string= value (env-var-last-value component)))
      (setf (env-var-last-value component) value))))

;; a cl-source-file with that mixin being the first superclass,
;; again to prioritize the modified behaviour.
(defclass cl-source-file-env-var (depends-on-env-var asdf:cl-source-file) ())

In order to test this, I created test-asdf in quicklisp/local-projects, with the following test-asdf.asd file:

(defsystem test-asdf
  :depends-on ()
  :components ((env-var-asdf:cl-source-file-env-var "a"
                :varname "LANG")))

Here, component a is recompiled/reloaded when the LANG environment variable is modified. Here is a.lisp:

(defpackage :test-asdf (:use :cl))
(in-package :test-asdf)

(print "loaded")

In the REPL, when I quickload the system the first time, "loaded" is printed to standard output. If I touch the file a.lisp, it is reloaded, thought I am not exactly sure why (ASDF is complex), I mean the input-files of the components are newer so it might force the recompilation even if operation-done-p returns NIL. Finally, if I don't touch the file but changes the LANG environment variable from within the process, then the file is loaded again.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 coredump