linkfly (linkfly) wrote,
linkfly
linkfly

Устройство ASDF. Подсистема определения путей. APPLY-OUTPUT-TRANSLATIONS.

(сказаное ниже относится к версии 2.019.7)

Это 18-ая статья цикла.
Первая статья.
Архитектура ASDF.
Предыдущая статья.

APPLY-OUTPUT-TRANSLATIONS
(defun* apply-output-translations (path) ...)

   Ф-ия осуществляет преобразования путей на основе данных в глобальной переменной *OUTPUT-TRANSLATIONS*. Применяется для определения абсолютного пути к файлу, содержащему результат применения операции к компоненту (правда, заменой расширения на .fasl - она не занимается). Как правило это будет скомпилированный файл с расширением .fasl, получившийся после компиляции исходника с лисп-кодом.

Примеры:

(let ((*output-translations*
       '(((T #P"/home/linkfly/.cache/common-lisp/sbcl-1.0.53-linux-x64/**/*.*")))
)
)

  (apply-output-translations "/home/user/project/file.lisp")
)

=> #P"/home/linkfly/.cache/common-lisp/sbcl-1.0.53-linux-x64/home/user/project/file.lisp"

(let ((*output-translations*
             '(((#P"/some/path/**/*.*" T)))
)
)

  (apply-output-translations "/home/user/project/file.lisp")
)

=> #P"/home/user/project/file.lisp"

(let ((*output-translations*
             '(((#P"/some/path/**/*.*" T)
                (#P"/media/COMMON_LISP/lisp-dev-tools/lisp/sbcl/sbcl-1.0.53/lib/sbcl/**/*.*"
                 #P"/tmp/lisp-fasls/sbcl/sbcl-1.0.53/lib/sbcl/**/*.*"
)
)
)
)
)

  (apply-output-translations
   "/media/COMMON_LISP/lisp-dev-tools/lisp/sbcl/sbcl-1.0.53/lib/sbcl/sb-rt/rt.lisp"
)
)

=> #P"/tmp/lisp-fasls/sbcl/sbcl-1.0.53/lib/sbcl/sb-rt/rt.lisp"

Логика работы в следующем:

1. Осуществляется проверка: не является ли переданный аргумент PATH типом LOGICAL-PATHNAME и если это так, то PATH просто возвращается без какой-либо обработки.

2. Если PATH имеет тип PATHNAME или STRING, то вызывается ф-ия ENSURE-OUTPUT-TRANSLATIONS для инициализации глобальной динамической переменной *OUTPUT-TRANSLATIONS* (если этого ещё не было сделано) и осуществляется обработка составляющих её элементов:

   ENSURE-OUTPUT-TRANSLATIONS
   (defun* ensure-output-translations () ...)
   2.1 Ф-ия проверяет была ли проинициализирована *OUTPUT-TRANSLATIONS* (содержит значение отличное от NIL). И если была, то возвращает её первый элемент с помощью ф-ии OUTPUT-TRANSLATIONS, а если нет, то производит инициализацию с помощью ф-и INITIALIZE-OUTPUT-TRANSLATIONS:

       (defun* ensure-output-translations ()
          (if (output-translations-initialized-p)
              (output-translations)
              (initialize-output-translations)
)
)


       INITIALIZE-OUTPUT-TRANSLATIONS
       (defun* initialize-output-translations
            (&optional (parameter *output-translations-parameter*)) ...
)

       2.1.1 Предварительно ф-ия изменяет глобальную переменную *OUTPUT-TRANSLATIONS-PARAMETER* на значение переданного аргумента PARAMETER:

             (setf *output-translations-parameter* parameter ...)

             Вообще, аргумент PARAMETER используется для реализации двух возможностей:
                  - для включения поддержки старой системы ASDF-BINARY-LOCATIONS (которая предназначена в сущности для того же, для чего применяется и ASDF-OUTPUT-TRANSLATIONS - сортирует скомпилированные файлы в зависимости от пути к исходнику, а также типа и версии лисп-системы).
                  - для отключения сохранения скомпилированных файлов отдельно от исходников с помощью ф-ии DISABLE-OUTPUT-TRANSLATIONS (после её вызова скомпилированные файлы будут лежать рядом с исходниками).

            Затем INITIALIZE-OUTPUT-TRANSLATIONS выполняет главное своё предназначение - инициализацию *OUTPUT-TRANSLATIONS*:

           (setf ...
                  (output-translations) (compute-output-translations parameter)
)


           ... как видно, осуществляется вызов ф-ии (setf output-translations), её определение декларировано следующим образом:

           (defun* (setf output-translations) (new-value) ...)

           ... в которой уже, переменной *OUTPUT-TRANSLATIONS* присваивается предварительно обработанный список NEW-VALUE, который был возвращён вызовом (compute-source-registry parameter). Этот список состоит из элементов вида (<T_или_PATHNAME> <T_или_PATHNAME>). Суть предварительной его обработки заключается в сортировке этого списка - все элементы-подсписки с первым элементом T выталкиваются в конец списка, а в начало попадают те элементы, в которых кол-во директорий в пути (которое представляется первым элементом) наибольшее:

           (stable-sort (copy-list new-value) #'>
                      
      :key #'(lambda (x)
                            
            (etypecase (car x)
                               
             ((eql t) -1)
                               
             (pathname
                                  
               (let ((directory (pathname-directory (car x))))
                                    
                (if (listp directory) (length directory) 0))))))

            Логика работы ф-ии COMPUTE-SOURCE-REGISTRY тема для отдельной статьи.

   2.2 Преобразование пути, заданного аргументом PATH ф-ия осуществляет руководствуясь парами (<T_или_PATHNAME> <T_или_PATHNAME>), содержащимся в *OUTPUT-TRANSLATIONS*. Для этого организуется цикл, в котором локальная P получает обработанное ф-ией TRUENAMIZE значение. Эта ф-ия, кроме сглаживания некоторых межсистемные различий в обработке путей, занимается "абсолютизацией" пути, используя для этого опциональную переменную DEFAULTS, равную по умолчанию глобальной переменной *DEFAULT-PATHNAME-DEFAULTS*. На каждом шаге итерации берётся упомянутая пара и разбирается на элементы:

       (loop :with p = (truenamize path)
           :for (source destination) :in (car *output-translations*)
           ...
)


       2.2.1 На каждой итерации локальная переменная ROOT будет получать значение #P"/" если SOURCE либо равна T, либо равна НЕ-абсолютному пути, либо и то и другое:

           (loop
               ...
               :for root = (when (or (eq source t)
                                   
         (and (pathnamep source)
                                       
            (not (absolute-pathname-p source))))
                       
            (pathname-root p))
               ...
)


           ... иначе, как видно, ROOT получит значение NIL.

       2.2.2 Локальная переменная ABSOLUTE-SOURCE будет получать следующие значение в зависимости от следующих условий:

           - если SOURCE = T, тогда значение #P"/**/*.*"
           - если ROOT была определена, то значение SOURCE "абсолютизируется" (для этого добавляется "/" в начало пути).
           - иначе SOURCE это абсолютный путь и он никак не обрабатывается.

           (loop
               ...
               :for absolute-source = (cond
                                     
             ((eq source t) (wilden root))
                                      
            (root (merge-pathnames* source root))
                                     
             (t source))                     
               ...
)


           ... таким образом ABSOLUTE-SOURCE получает в любом случае абсолютный путь, а если SOURCE = T, то он будет не только абсолютным, но ещё и "wildcard" путём: #P"/**/*.*".

       2.2.3 Если SOURCE = T или путь соответствует шаблону ABSOLUTE-SOURCE, то правило для пути PATH считается найденым и вызывается ф-ия TRANSLATE-PATHNAME* для преобразования пути, а иначе P возвращается как есть:

           (loop
               ...
               :when (or (eq source t) (pathname-match-p p absolute-source))
               :return (translate-pathname* p absolute-source destination root source)
               :finally (return p)
)


           TRANSLATE-PATHNAME*
           (defun* translate-pathname* (path absolute-source destination &optional root source)
           Ф-ия оборачивает стандартную ф-ию TRANSLATE-PATHNAME некоторым дополнительным функционалом. Сама стандартная ф-ия получает на вход путь, который надо преобразовать, "wildcard" путь для отделения от пути необходимого окончания и "wildcard" путь, который задаст начало результирующего пути, например:

                (translate-pathname "/home/user/file.lisp" "/home/**/*.*" "/elsepath/**/*.*")
                => #P"/elsepath/user/file.lisp"            

            Дополнительный функционал, который несёт в себе TRANSLATE-PATHNAME*, состоит в:

                - вызове DESTINATION, если это ф-ия (с аргументами PATH и ABSOLUTE-SOURCE)
                - возвращении пути PATH как есть, если DESTINATION = T
                - и преобразовании пути с помощью TRANSLATE-PATHNAME (для подстановки пути DESTINATION в начало PATH) - в остальных случаях.

            Описанное выше, осуществляется кодом:

                (cond
                  ((functionp destination)
                   (funcall destination path absolute-source)
)

                  ((eq destination t)
                   path
)

                  ...
                  (t
                   (translate-pathname path absolute-source destination)
)
)


                ... пропущенные варианты не принципиальны и представляют собой превращение пути DESTINATION в абсолютный, если он относительный и сглаживание каких-то тонких нюансов в обработке путей.

-------------------------------------------
Продолжение следует ...
Tags: asdf, lisp, programming, лисп, программирование
Subscribe

  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 0 comments