跳轉到內容

Common Lisp/外部庫/ASDF/Budden 的不常被問到的問題

來自 Wikibooks,開放世界中的開放書籍

關於 ASDF 的 Budden 不常被問到的問題

[編輯 | 編輯原始碼]

此資訊非官方,並非來自 asdf 維護人員,高度主觀,可能重複手冊的某些部分,可能不正確,或用英語表達不佳。

“向上”和“向下”是什麼意思?

[編輯 | 編輯原始碼]

我不知道。根據手冊,“操作向下傳播”意味著 asdf 首先對系統的元件執行操作,然後對系統本身執行操作。“操作向上傳播”意味著操作先在系統上執行,然後在它的元件上執行。但這似乎與系統的依賴無關,而這正是我遇到的問題。

如何在給定目錄中製作帶有自己的 fasl 快取的可移植 Lisp 程式?

[編輯 | 編輯原始碼]

(來自 asdf-devel 郵件列表) https://mailman.common-lisp.net/pipermail/asdf-devel/2015-October/004962.html

;; this is a fragment of my sbclrc (user-level lisp initialization script)
;; note we rely on the fact that we have no :uiop loaded
;; if :uiop is present in the image at the beginning, this answer won't help you
(assert (null (find-package :uiop)))
(require :uiop)
(defconstant *clcon-uiop-user-cache-override* #P"c:/clcon/fasl-cache/asdf/")
(setf uiop:*user-cache* *clcon-uiop-user-cache-override*)
(require 'asdf)
(defun check-output-translations-ok ()
  "Call it now and once again at the end of loading to ensure that fasls are placed to a right place"
  (assert (equalp (asdf:apply-output-translations "c:/aaa.bbb")
    (merge-pathnames "c/aaa.bbb" *clcon-uiop-user-cache-override*))))
(check-output-translations-ok)

#-quicklisp
(let ((quicklisp-init "c:/clcon/quicklisp/setup.lisp"
                                       ))
  (when (probe-file quicklisp-init)
    (load quicklisp-init))) 

; swank is handled separatedly (maybe irrelevant to asdf, but relevant
; to task of creating "portable file releases"
(load (merge-pathnames "swank-loader.lisp" (ql:where-is-system :swank)))
(setq swank-loader::*fasl-directory* "c:/clcon/fasl-cache/swank/")
(swank-loader:init)

;;; Here I load all my systems

; after loading all our code check that no one smashed the variable.
; this is only a limited protection, there is no protection against
; binding
(check-output-translations-ok)

注意:這並非 asdf 維護人員推薦的做法,但對我來說,它確實起到了作用。

:second 系統依賴於 :first 系統,兩者都載入到映象中。我更改了屬於 :first 系統的檔案。 (asdf:load-system :first) 會重新載入 :second 嗎?

[編輯 | 編輯原始碼]

正如我的實驗表明,不會(在 asdf 3.1.5 中)。手冊中指出:“但對於正在積極開發、除錯或修改的程式碼,應該使用 load-system,這樣 ASDF 就會選擇你的修改並遞迴地重建修改後的檔案以及所有依賴它們的程式碼。”但這句話只適用於你傳遞給 load-system 的系統名稱。

我想讓我的檔案被載入,而不是編譯。該怎麼做?

[編輯 | 編輯原始碼]

1. 閱讀asdf FAQ 中的說明

2. 如果你仍然不相信,請嘗試以下操作

;; Contents of s1.asd:
(defsystem :s1
  :components 
  ((:static-file "file-to-load.lisp")
   (:file "loader" :depends-on ("file-to-load.lisp"))))
;; EOF s1.asd 
;; ------------------------------
;; Contents of file-to-load.lisp
(eval-when (:compile-toplevel :load-toplevel) (error "I must be loaded as a source"))
(eval-when (:execute) (print "file-to-load.lisp is loading as a source")) 
;; EOF file-to-load.lisp
;; ------------------------------
;; Contents of loader.lisp
(load (merge-pathnames "file-to-load.lisp" #.*compile-file-pathname*))
;; EOF loader.lisp

3. 另一種方法是我的asdf-load-source-cl-file(在 SBCL 和 asdf 3.1.5 上測試過)。以下是從原始碼中獲取的一個示例系統定義

(asdf:defsystem :asdf-load-source-cl-file-example-system
  :defsystem-depends-on (:asdf-load-source-cl-file)
  :serial t
  :components
  ((:load-source-cl-file "file-to-load")
   ))

我想刪除所有 fasl 檔案。如何找到它們?

[編輯 | 編輯原始碼]

這可能會有所幫助

(asdf:apply-output-translations (asdf:system-source-directory (asdf:find-system :s1)))

當前映象中載入了哪些系統?

[編輯 | 編輯原始碼]

asdf:already-loaded-systems 是一個函式,它返回載入的系統的名稱。手冊中幾乎有關於它的文件。你也可以讀取 asdf::*defined-systems* 的值。注意,這是一個未公開的變數。

如何在 asdf 系統上執行“make clean”?

[編輯 | 編輯原始碼]

無法僅僅執行 make clean。載入系統會對 Lisp 映象產生副作用。例如:

  • 可以建立包
  • 可以在泛型函式上定義方法
  • 可以修改現有的函式
  • 可以修改變數(例如 *features*)

因此,請考慮“解除安裝”而不是“必須清理”。為了實現“解除安裝”,我們必須跟蹤安裝過程中所做的所有更改,但我不知道是否有人嘗試過實現它。

如果你只想刪除所有 fasl,請檢視 arnesi 的開頭。程式碼是 2005 年編寫的,所以可能已過時。我沒有測試過它。來自asdf.lisp 的引用如下

(defclass clean-op (asdf:operation)
  ((for-op :accessor for-op :initarg :for-op :initform 'asdf:compile-op))
  (:documentation "Removes any files generated by an asdf component."))

(defmethod asdf:perform ((op clean-op) (c asdf:component))
  "Delete all the output files generated by the component C."
  (dolist (f (asdf:output-files (make-instance (for-op op)) c))
    (when (probe-file f)
      (delete-file f))))

(defmethod asdf:operation-done-p ((op clean-op) (c asdf:component))
  "Returns T when the output-files of (for-op OP) C don't exist."
  (dolist (f (asdf:output-files (make-instance (for-op op)) c))
    (when (probe-file f) (return-from asdf:operation-done-p nil)))
  t)

關於 asdf:run-program + SBCL + Windows 呢?

[編輯 | 編輯原始碼]

我不知道正確的答案。首先,我在 Windows 上使用 SBCL 執行程式從未成功過。當我在 EMACS 中呼叫 SBCL 時,尤其如此。我在 SBCL launchpad 上提交了一個錯誤報告,但至今未得到理會。也許是我沒有正確閱讀手冊,但我相信實際上存在錯誤。因此我編寫了一個小型 Delphi 程式 CallBatFromGuiDetached.exe,它具有以下功能

  • 它可以執行另一個程式(控制檯或 GUI),並等待其完成或不等待。
  • 它可以在指定目錄中執行程式。
  • 它可以將 stout 和 stderr 重定向到檔案。
  • 可以提取退出程式碼。

你可能仍然會遇到字元編碼問題。請隨意修復它們。執行程式的程式碼如下

(defun call-bat (bat args &key (wait t) redirect-output directory)
  (let ((arglist nil))
    (check-type redirect-output (or null string))
    (when redirect-output
      (setf arglist (append arglist `("-s" ,redirect-output))))
    (setf arglist (append arglist (list bat) args))
    (apply #'sb-ext:run-program
           "c:/clcon/bin/util/CallBatFromGuiDetached.exe"
           arglist
           :wait wait
           (if directory (list :directory directory))
           )))

CallBatFromGuiDetached.exe 的原始碼和二進位制檔案包含在clcon 分發版 中。一些訊息和註釋為俄語。示例

啟動 notepad.exe 而不等待

(call-bat "notepad.exe" () :wait nil)

啟動 notepad.exe 並等待

(call-bat "notepad.exe" ())

啟動一個不存在的命令並提取錯誤程式碼

(sb-impl::process-exit-code (call-bat "nonexisting_command" ()))

在 c:\tmp 中啟動 dir 並將輸出重定向到檔案

(call-bat "cmd" '("/c" "dir" "/b" "/s") :redirect-output "outputs" :directory "c:/tmp")

stdout 和 stderr 將儲存到 c:\tmp\outputs.stdError.txt 和 c:\tmp\outputs.stdOutput.txt 中。

在哪裡可以找到定義自定義元件類的良好示例?

[編輯 | 編輯原始碼]

我不知道哪裡有好的例子,但 asdf 手冊首先推薦使用 CFFI-GROVEL。當您載入包含 :grovel-file 元件的系統時,asdf 會按照以下步驟處理該元件:

1. 在名為 X 的檔案上呼叫特殊的 Lisp 函式 cffi-grovel:process-grovel-file,該檔名作為元件的名稱給出。結果將寫入到一個名為 Y 的 Lisp 檔案中。

2. 檔案 Y 將被編譯並像任何其他正常的 Lisp 檔案一樣載入。

cffi-grovel 的 asdf.lisp 演示了以下內容:

  • 如何建立新的元件類
  • 如何處理 asdf 版本
  • 如何註冊類,以便它可以作為 asdf 元件型別使用
  • 如何定義方法

為了更深入地瞭解,讓我們看一下

(defmethod component-depends-on ((op compile-op) (c process-op-input))
  `((process-op ,c) ,@(call-next-method)))

程式碼指出,要進行 compile-op(編譯原始碼),我們應該先執行 process-op。compile-op 的 input-files 方法被重新定義,以便編譯生成的 Lisp 檔案而不是原始檔案。

(defmethod input-files ((op compile-op) (c process-op-input))
  (list (first (output-files 'process-op c))))

process-op 的方法被定義為呼叫 groveller。

手冊說明,只需定義元件的新類,它就可以在系統中使用。元件類的名稱必須位於哪個包中?

在 grovel 的 asdf.lisp 中,我們看到了以下程式碼:

(in-package :cffi-grovel)
...
;; Allow for naked :grovel-file ... in asdf definitions.
(setf (find-class 'asdf::cffi-grovel-file) (find-class 'grovel-file))

在閱讀 UIOP/UTILITY:COERCE-CLASS 的原始碼後,我得出結論,註釋不正確,應該改為:

;; Allow for naked :cffi-grovel-file ... in asdf definitions.

因此,我們可以將元件指定為

(:cffi-grovel-file "bububub")

或者

(cffi-grovel:grovel-file "bububub")
華夏公益教科書