You are viewing [info]linkfly's journal

linkfly [entries|archive|friends|userinfo]
linkfly

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Проект LISP-DEV-TOOLS. Новая версия - 0.0.3. [Apr. 19th, 2012|02:14 pm]
[Tags|, , , , , , , , ]

    Можно смело утверждать что новая версия проекта lisp-dev-tools стала гораздо более зрелой чем предыдущая. Особенно хочется отметить появление двух серьёзных возможностей: это появление обобщённого интерфейса использования (параметров командной строки --common-load --common-eval и --common-quit) и появление автоматических тестов (запукаются скриптом tests/run-tests.sh). Если в дистрибутиве, не входящим в число поддерживаемых, все тесты успешно пройдены - проект вполне можно рекомендовать к использованию в этом дистрибутиве! Если же тесты провалились, но вам нужно использовать проект в конкретном дистрибутиве - просто опишите программно-аппаратную среду (процессор, память, архитектуру, версию ядра, название/тип/версию дистрибутива ...) и вышлите мне результаты тестов из папки tests/tests-results/

Новое в версии 0.0.3 (по сравнению с версией 0.0.2):
-----------------------------------------------------
1. Добавлено автоматическое тестирование, запускается скриптом tests/run-tests.sh (рекомендуется запускать перед промышленной эксплуатацией).
2. Сделан общий интерфейс запуска для всех (за исключением XCL и WCL - они не поддерживают параметры
   запуска) лисп-систем. Он представлен тремя ключами:
     --common-load <файл_с_лисп_кодом>
     --common-eval <заключенный_в_кавычки_лисп-код>
     --common-quit
3. Осуществлено разделение на современные (modern), слишком сырые/молодые (young) лисп-системы и
    устаревшие (obsolete). Это отображено в параметрах запуска скриптов ./get-all-lisps и ./provide-all-lisps
4. Налажена работа с символическими ссылками (для управления поддерживающис софтом) теперь изменение в поддерживающих инструментах не влиют на git.
5. Удалены некоторые уже не нужные файлы.
6. Обновлён и изменён TODO: скорректировано состояние выполненных и назначенных задач, а также изменён и скорректирован план развития проекта.
7. Начаты работы по отладке работы проекта в дистрибутиве Arch Linux.
8. Скорректированы некоторые сообщения системы.
9. Удалён баг проявляющийся при постройке XCL на Ubuntu 11.04 x86_64.
10. Исправлена загрузка JRE среды для системы ABCL.
11. Устранен баг в загрузке архивов с лисп-системами.
12. Изменена версия gawk - для устранение ошибки в обеспечении лисп-системы GCL.
13. Сглаживание различий в выводе программы "file", сейчас в проверке на символическую ссылку с помощью "readlink"
14. Устранены также другие ошибки.
------------------------------------------------------------------------------
linkpost comment

Проект LISP-DEV-TOOLS. Новая версия - 0.0.2. [Feb. 28th, 2012|12:27 am]
[Tags|, , , , , , , , ]

   С радостью сообщаю о выходе новой версии проекта lisp-dev-tools анонс которого был сделан в предыдущем посте. Эта версия хоть и имеет не слишком пафосный номер, тем не менее очень серьёзно повзрослела по сравнению с предыдущей версией и неуклонно движется к мажорному релизу проекта. Отловлены неожиданные сюрпризы (что особенно важно для новичков), в смысле исправлены баги и устранены найденные неоднозначности, возникающие при работе с системой. Особенно хочется отметить добавление целых трёх поддерживаемых дистрибутивов Linux и появление совершенно прозрачной (и корректно работающей) проброске параметров к запускаемой лисп-системе, с возможностью посмотреть сформированную командную строку без её выполнения. А также возможность её сохранить для анализа и/или использования в дальнейшем (можно её скопировать и когда необходимо вставить в шелл или скрипт и выполнить, получив аналогичный запуск системы). Ниже перечислены конкретные изменения.

Новое в версии 0.0.2 (по сравнению с версией 0.0.1):
1. Создан и "закреплён" список задач и примерный план добавления возможностей в следующие версии.
2. Исправлена корректировка файла Makefile для постройки XCL.
3. Добавлена поддержка 3-ёх дистрибутивов Linux:
           - Ubuntu Server 10.10/x86-64
           - Debian 6.0.4/x86-64
           - Gentoo (из livedvd-amd64-multilib-2012)
4. Добавлена полноценная поддержка интерпретатора bash (ранее корректная обработка скриптов производилась только интерпретатором dash).
5. Удалена загрузка и компиляция софта от которого зависит поддерживающий софт, если он уже в наличии.
6. Скорректированы зависимости для постройки лисп-систем.
7. Устранена ошибка появляющаяся при перекомпиляции SBCL через ./rebuild-lisp, появляющаяся из-за попыток скрипта сборки лисп-системы - прочитать .git директорию.
8. Устранена ошибка поиска библиотеки libgmp.so.3 для постройки и запуска WCL.
9. Добавлена возможность полностью корректного проброса параметров к текущей лисп-системе, указываемых при запуске ./run-lisp <множество параметров>.
10. Добавлена специальная переменная GET_CMD_P, которая при установке в значение "yes" позволяет получить полностью корректную командную строку с которой запускается лисп-система (эту коммандную строку можно скопировать, вставить в шелл, выполнить и получить тот же результат который был бы если при выполнении "./run-lisp <какие-то параметры>", переменная GET_CMD_P была бы не равна "yes".
11. К значение переменной XDG_CONFIG_DIRS при запуске лисп-системы (через ./run-lisp ...) добавляется полный путь вида "<директория_с_lisp-dev-tools>/conf".
12. Появляющиеся сообщения системы стали более лаконичными и читабельными.
13. Для успешного использования рантайм опций лисп-систем, опции загрузки Quicklisp перенесены в конец формируемой коммандной строки.
14. Добавлена таблица в формате CSV - status.csv. В ней отображено текущее состояние поддержки lisp-dev-tools в различных дистрибутивах (имеется в виду стабильная и ожидаемая работа, без неожиданных сюрпризов).
15. Множество баг-фиксов связанных с обеспечением (загрузкой, постройкой и инсталяцией в lisp-dev-tools, при необходимости) поддерживающего софта.
16. Исправление множества багов, связанных с обеспечением и выполнением лисп-систем (в том числе, связанные с различием в дистрибутивах).

linkpost comment

Проект LISP-DEV-TOOLS. Анонс следующей версии 0.0.2. [Feb. 22nd, 2012|12:56 pm]
[Tags|, , , , , , , , ]

   Ближайшие дни оформлю следующий минорный релиз проекта lisp-dev-tools. Проект был представлен в посте Заповедник для Лисп'ов - проект LISP-DEV-TOOLS. Несмотря на весьма скромный номер версии проект серьёзно развился:
 - оттестирована работа на большем кол-ве дистрибутивов (добавились gentoo, fedora, debian 6.0.4)
 - заведена таблица в формате csv для учёта результатов тестирования работы проекта на разных дистрибутивах.
 - сделано и отлажено совершенно прозрачное "прокидывание" параметров указываемых при запуске скрипта run-lisp а также получение строки запуска текущей лисп-системы (которую можно скопировать, вставить в консоль и запустить, т.е. выводимая строка запуска в точности соотв. реально используемой строке запуска)
- уточнение и расширение инструкции по использованию и добавление дополнительных примечаний (в README, README_ru)
- повышена надёжность системы - выявлены и устранены неожиданные сюрпризы связанные с нюансами работы дистрибутивов.
- устранено множество багов и неоднозначностей в работе системы.
- сообщение системы стали более подробными и единообразными
- оформлен список задач проекта (TODO).
- и другое (подробнее - при выпуске новой версии).
linkpost comment

Гипервизор на голом железе ESXi - следующее поколение виртуализации. [Jan. 23rd, 2012|05:09 pm]
[Tags|, , , , , , , ]

Содержание:

1. Введение.
2. Некоторые возможности ESXi.
3. Управление пользователями и полномочиями.
4. Скриншоты.
5. Заключение.
6. Дополнение.
7. Ссылки.

---------------------------------------

1. Введение.

    Вначале пара слов о гипервизорах вообще и о понятии "на голом железе" в частности.

    Гипервизор это система управления виртуальными машинам. Как ни странно, встречаются до сих пор люди, незнакомые с понятием "виртуализация". Так что поясню, на всякий случай: виртуальная машина это модель реального компьютера с которой вы можете работать как с настоящим компьютером - ставить ОС, на неё программы и соотв. заходить на сайты, смотреть фильмы и даже играть в игры и прочее-прочее. Чувствую, обязательно следует упомянуть о снэпшотах, ибо был прецедент: в одной, казалось бы, серьёзной фирме, занимающейся разработкой банковского софта - использовалась система виртуализации VMware Workstation без задействования технологии снэпшотов вообще. А ведь это основное преимущество, которое предоставляют системы виртуализации! Итак, снэпшоты (snapshots) это сохранённые (причём идеально сохранённые) состояния виртуальной машины. Эти состояния, как не сложно догадаться, можно в любой момент восстановить. В VMware Workstation и тем более в ESXi можно создавать целые иерархии сохранённых состояний и возвращаться к ним в любой момент времени. Этакая машина времени - совершенно незаменимая вещь для разработчика и исследователя.

    Понятие "голое железо" (bare metal). В нашем контексте, это понятие означает что софт, о котором идёт речь, выполняется без какой-либо предустановленной операционной системы. Конкретно ESXi, представляет собой мини-операционную систему, "заточенную" для выполнение виртуальных машин и для управления ими. То есть это тонкий слой между реальными, физическими ресурсами и виртуальными машинами (и вообще виртуальными объектами, например виртуальными сетями). Зачем это нужно? Да так сразу и не расскажешь ... Ну в первую очередь, конечно же для получения преимуществ от упомянутых снэпшотов. Во вторых, для балансировки нагрузки. В третьих для упрощения администрирования ресурсов. В четвёртых для реализации более совершенной информационной безопасности. И это только первое что пришло ну ум. Итак, некоторые "фичи":
    - сохранение/восстановление состояний
    - балансировка нагрузки
    - упрощеное администрирование
    - безопасность
    
2. Некоторые возможности ESXi.

    Вот этот текст, кстати сказать, я набираю в Emacs'e, запущенном на рабочей виртуалке (которая, разумеется, на сервере с ESXi). Главный профит на данный момент лично для меня: в любой момент, не делая никаких лишних телодвижений (вообще), я могу сорваться и поехать куда-либо по делам захватив с собой ноутбук (и Yota'у для интернета). И далее, смогу продолжить работу в любом месте, где есть покрытие связи, предоставляемой Yota'ой (а оно есть практически по всей Москве). Я даже виртуалку в Suspend-режим переводить не буду! Кроме того, мне ведь могут понадобиться в процессе и другие виртуальные машины. Например на каком-либо этапе тестирования разрабатываемого софта, необходимо проверить работу в реализуемой виртуальными машинами тестовое среде (одной из). И самое главное: мне ведь необходимо время от времени корректировать тестовые среды, т.е. создавать/изменять виртуальные машины и их состояния(snapshots). В простых случаях, конечно, можно обойтись и другими решениями вроде использования удалённых рабочих столов и обычных не "bare metal" (не на голом железе) системами виртуализации. Но в случае когда кол-во информационных объектов и ресурсов, с которыми ведётся работа, велико - требуется более мощное решение.

    Итак, некоторые полезные фичи:
1) Пул ресурсов. Есть возможность создавать некие группы ресурсов (пулы ресурсов) и задавать параметры использования физических ресурсов сразу для всей группы. К тому же, эти группы можно вкладывать друг в друга и таким образом строить иерархию из групп и объектов виртуализации.
2) Настройка ограничений. Тонко настраиваемые ограничения на использование физических ресурсов. Любопытно: настройка ресурсов вплоть до указания конкретной тактовой частоты для виртуальных процессоров.
3) Резервирование ресурсов. Резервирование физических ресурсов для виртуальных машин и пулов ресурсов.
4) Управление доступом. Имеется весьма гибкая подсистема управления пользователями, группами, ролями, привилегиями и разрешениями.

    
3. Управление пользователями и полномочиями.

    Вот на управлении доступом остановимся по подробней. Эта подсистема, должен отметить, чрезвычайно гибкая и лаконичная. Вкратце, она состоит из следующих элементов:
        1) Пользователи. Могут входить в несколько групп.
    2) Группы. Содержат несколько пользователей.
    3) Роли. Содержат набор привилегий.
    4) Привилегии. Установленные привилегии, позволяют определённые действия в системе.
    5) Разрешения. Задаются для конкретного объекта (виртуальной машины или пула ресурсов) и состоят из пар пользователь_или_группа/роль.

    В общем схема такая:
        - создаём необходимых пользователей (вероятно, представляющих реальные личности, которых предполагается допустить к взаимодействию с ESXi), расфасовываем их по группам (например: admins, developers, vm-providers и т.д.).
        - безотносительно к пользователям и группам, создаём некоторое кол-во ролей - они в сущности представляют собой набор определённых привелегий (то есть набор допусков к определённым действиям в системе).
        - для конкретных элементов виртуализации, а именно для виртуальных машин и пулов ресурсов (содержащих виртуальные машины и другие пулы) назначаем разрешения. Под назначением разрешения здесь подразумевается добавление пользователей и групп а ассоциирование их (каждого пользователя и каждой группы) с определёнными ролями, причём каждый пользователь или группа может иметь только одну роль (считаю это разумный баланс между гибкостью и простотой использования).

4. Скриншоты.

Иерархия ресурсов и экран виртуальной машины:


Редактирование разрешений:


5. Заключение.


    Очевидно, что этой технологии самое место в бизнес-среде как либо связанной с IT (разумеется в случае, если требуется нечто большее чем заполнение данными электронной таблицы и печати документов в Word'e). Особенно это технология проявит себя в области разработки (и эксплуатации) промышленного software, так как позволяет эффективно управлять состояниями вычислительной среды, ресурсами и сократить разрыв между тестовой и продакшен (production) средами выполнения. Считаю, что овладение этой технологией и внедрение туда, где подобное пока ещё не используется - даст мощный технологический рывок вперёд.

6. Дополнение.

    И напоследок напишу ещё пару слов о некоторых "чудесах", предоставляемых платными вариантами гипервизора:

    VMotion - эта технология позволяет осуществлять "горячую миграцию" (Live Migration) виртуальных машин с одного физического хоста на другой, то есть перемещение виртуальных машин без их выключения и потери сетевых соединений.
    
    DRS (Distributed Resource Scheduler) - эта технология в сущности развитие идеи горячей миграции: контролируя работу, так называемых, DRS-кластеров (это неявное объединение реальных аппаратных ресурсов - физические хосты можно объединять в эти самые DRS-кластеры), она осуществляет автоматическое перемещение виртуальных машин на менее загруженных физические хосты, соответственно без остановки и без потери сетевых соеденений.

    HA (High Availability) - технология высокой доступности, в случае выхода из строя физического хоста, обеспечивает автоматический запуск аналогичных виртуальных машин на другом хосте.

    FT (Fault Tolerance) - технология отказоустойчивости. Представляет собой дальнейшее развитие идеи "высокой доступности (High Availability): На физическом хосте отличного от того на котором выполняется виртуальная машина, эта технология поддерживает точную зеркальную копию этой виртульной машины. Всё что происходит в оригинале, выполняется и в копии. Как только, выйдет из строя оригинальная виртульная машина (например из-за поломки физического хоста) - зеркальная копия подхватит её работу, полностью сохранив контекст выполнения и сетевые соединения (вместе с их состояниями).

    VCB (VMware Consolidated Backup) - это набор инструментальных средств и интерфейсов, обеспечивающих резервное копирование.

    vShild Zones - технология управления сетевым траффиком, проходящим через виртуальные коммутаторы. Позволяет применять политики безопасности для групп виртуальных машин. При осуществлении горячей миграции (посредством технологии vMotion и соотв. использующей её DRS) виртульных машин между физическими хостами - эти политики безопасности сохраняются.

    vCenter Orchestrator - это инструментарий автоматизации рабочих процессов, связанных с управлением гипервизорами, виртуальными объектами и перечисленными технологиями (на его основе был создан отдельный продукт - VMware vCenter Lifecycle Manager.
 
7. Ссылки.

1. Зарегистрироваться и скачать ESXi можно здесь.
2. Сайт корпорации разработчика: http://www.vmware.com/ru/
3. VMware ESXi Server - основа для центра данных.
4. vMind.ru - серверная виртуализация.
5. vmgu.ru.
link2 comments|post comment

Устройство ASDF. Оглавление. [Dec. 30th, 2011|01:25 am]
[Tags|, , , , ]


1. Предисловие.
2. Архитектура.
3. Подсистема определения. DEFSYSTEM.
4. Подсистема определения. PARSE-COMPONENT-FORM.
5. Подсистема определения. UNION-OF-DEPENDENCIES.
6. Подсистема загрузки. OPERATE.
7. Подсистема загрузки. Подсистема планирования операций. TRAVERSE.
8. Подсистема загрузки. Подсистема планирования операций. DO-TRAVERSE.
9. Подсистема загрузки. Подсистема планирования операций. DO-DEP.
10. Подсистема загрузки. MAKE-SUB-OPERATION.
11. Подсистема загрузки. PERFORM-PLAN и PERFORM-WITH-RESTARTS
12. Подсистема загрузки. Подсистема выполнения операций. PERFORM.
13. Подсистема загрузки. FIND-COMPONENT.
14. Подсистема поиска. FIND-SYSTEM.
15. Подсистема поиска. COMPUTE-SOURCE-REGISTRY.
16. Подсистема поиска. RESOLVE-LOCATION
17. Подсистема определения путей. INPUT-FILES, OUTPUT-FILES и COMPONENT-PATHNAME.
18. Подсистема определения путей. APPLY-OUTPUT-TRANSLATIONS.
19. Подсистема определения путей. COMPUTE-OUTPUT-TRANSLATIONS.
20. Эпилог.
link5 comments|post comment

Устройство ASDF. Эпилог. [Dec. 30th, 2011|12:52 am]
[Tags|, , , , ]

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

    Вот и подошёл к концу этот, достаточно объёмный, цикл статей. Главное, что я хотел бы подчеркнуть - этот цикл представляет собой ТВОРЧЕСКИЙ ЭКСПЕРИМЕНТ! Причём эксперимент достаточно длительный и доведённый до конца. Один из смыслов этого эксперимента в том, чтобы разобравшись в устройстве фундаментальной для инфраструктуры языка COMMON LISP, библиотеке, с 10-летней (!) историей развития (не считая её предшественицы MK-DEFSYSTEM) - попытаться описать её строение и функционирование в максимально читабельном (и понятном не только законченым гикам) виде. Задача весьма, надо сказать, не простая. Решить её идеально, уверен, просто невозможно. У потенциальных читателей может быть совершенно разный уровень подготовки, а значит для одних подробное "разжёвывание" покажется скучным, а для других и такое изложение покажется слишком трудным для восприятия. В общем, пришлось проявить интуицию и выбрать некий баланс, ориентируясь на потенциального читателя. Этого читателя я представляю себе как либо очень не обычного индивида с совершенно гипертрофированной любознательностью и жгучим желанием знать как всё устроено изнутри, либо редкого высоко-квалифицированного специалиста хорошо знающего ASDF и по каким-либо причинам желающего узнать её внутреннее устройство, например для написания ASDF-расширений (хотя один тип читателя, не исключает другого, конечно же). Я надеюсь что наличие данного труда сильно помогло и ускорило изучение внутреннего устройства этой фундаментальной и чрезвычайно гибкой системы.

    Увлекательных исследований и интересных проектов!

--------------------------------------
Конец цикла.
link1 comment|post comment

Устройство ASDF. Подсистема определения путей. COMPUTE-OUTPUT-TRANSLATIONS. [Dec. 27th, 2011|07:57 am]
[Tags|, , , , ]

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

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

    Ф-ия COMPUTE-OUTPUT-TRANSLATIONS вычисляет значения для инициализации глобальной переменной *OUTPUT-TRANSLATIONS*. Эта ф-ия во многом схожа с ф-ией COMPUTE-SOURCE-REGISTRY, но несколько проще. В ней тоже работает некий конвейр обрабатывающий переменную среды и конфигурационные файлы, если они есть. А используемые ф-ии имеют созвучные с используемыми в COMPUTE-SOURCE-REGISTRY имена.

Логика её функционирования следующая:

COMPUTE-OUTPUT-TRANSLATIONS
(defun* compute-output-translations (&optional parameter) ...)
1. Создаётся контекст для последующего вызова ф-ии INHERIT-OUTPUT-TRANSLATIONS. В этом контексте на самом "верху" находится ф-ия REMOVE-DUPLICATES - она из получившегося результата удалит повторяющиеся элементы. Кроме того, используется макрос WHILE-COLLECTING определяющий локальную ф-ию C для сбора промежуточных результатов:

   (remove-duplicates
     (while-collecting (c)
       (inherit-output-translations ...)
     :test 'equal :from-end t
)


    Список с данными для конвейера состоит из символов именующих ф-ии, которые метод PROCESS-OUTPUT-TRANSLATIONS будет вызывать для получения конфигурационных данных. Во главе списка идёт символ WRAPPING-OUTPUT-TRANSLATIONS. Её вызов приведёт к получению таких конфигурационных данных, которые обеспечат лисп-системам сохранение скомпилированных системных файлов рядом с их исходниками, например для Linux/SBCL результат будет следующим:

       (wrapping-output-translations)
       => (:OUTPUT-TRANSLATIONS
        ((#P"/media/COMMON_LISP/lisp-dev-tools/lisp/sbcl/sbcl-1.0.53/lib/sbcl/"
        #P"**/"
)

         NIL
)

        :INHERIT-CONFIGURATION :ENABLE-USER-CACHE
)


    Далее следует имя ф-ии заданное аргументом PARAMETER (или соответственно NIL если PARAMETER не задан). Все остальные символы именующие функции, берутся из глобальной переменной *DEFAULT-OUTPUT-TRANSLATIONS*, которая по умолчанию равна:

        '(ENVIRONMENT-OUTPUT-TRANSLATIONS
          USER-OUTPUT-TRANSLATIONS-PATHNAME
          USER-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME
          SYSTEM-OUTPUT-TRANSLATIONS-PATHNAME
          SYSTEM-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME)

    Эти ф-ии имеют следующее предназначение:

    ENVIRONMENT-OUTPUT-TRANSLATIONS - возвращает данные, сохранённые в переменной среды "ASDF_OUTPUT_TRANSLATIONS".

    USER-OUTPUT-TRANSLATIONS-PATHNAME - возвращает путь к конфигурационому файлу пользователя если он реально существует. Имя файла определяется глобальной динамической переменной *OUTPUT-TRANSLATIONS-FILE*, которая по умолчанию равна #P"asdf-output-translations.conf". Если выполнение происходит на UNIX-like -системе (например на Linux), то поиск файла производится в следующих директориях:
            - в директории, начальный путь которой, задан через переменную среды "XDG_CONFIG_HOME" а окончание пути - "common-lisp/".
            - в одной из директорий начинающихся с директорий в переменной среды "XDG_CONFIG_DIRS" (директории разделяются символом ":") и заканчивающихся, опять же, на "common-lisp/".
        - в директории начинающейся с домашней директории пользователя и оканчивающейся на ./config/common-lisp/, например: /home/user/common-lisp/.config

    USER-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME - то же самое что и USER-OUTPUT-TRANSLATIONS-PATHNAME, но ищет не конфигурационный файл, а директорию для конфигурационных файлов по тем же путям поиска, с именем *OUTPUT-TRANSLATIONS-DIRECTORY* (по-умолчанию #P"asdf-output-translations.conf.d/").

    SYSTEM-OUTPUT-TRANSLATIONS-PATHNAME - то же самое что и USER-OUTPUT-TRANSLATIONS-PATHNAME, но файл заданный *OUTPUT-TRANSLATIONS-FILE* ищет в директории #P"/etc/common-lisp/".

    SYSTEM-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME - то же самое что и USER-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME, но ищет директорию заданную *OUTPUT-TRANSLATIONS-DIRECTORY* в директории #P"/etc/common-lisp/".

    Итак, вызов COMPUTE-OUTPUT-TRANSLATIONS, допустим с аргументом PARAMETER = NIL, приведёт к вызову INHERIT-OUTPUT-TRANSLATIONS со следующими аргументами:

        (INHERIT-OUTPUT-TRANSLATIONS
            '(WRAPPING-OUTPUT-TRANSLATIONS
             NIL
             ENVIRONMENT-OUTPUT-TRANSLATIONS
             USER-OUTPUT-TRANSLATIONS-PATHNAME
             USER-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME
             SYSTEM-OUTPUT-TRANSLATIONS-PATHNAME
             SYSTEM-OUTPUT-TRANSLATIONS-DIRECTORY-PATHNAME)
     :COLLECT #<CLOSURE (FLET C) {1004A48AAB}>)

        ... на месте не печатаемого объекта "#<CLOSURE (FLET C) {1004A48AAB}>" разумеется будет что-то другое.


INHERIT-OUTPUT-TRANSLATIONS
(defun* inherit-output-translations (inherit &key collect) ...)
2. Конвейрная обработка организуется функцией INHERIT-OUTPUT-TRANSLATIONS и методами PROCESS-OUTPUT-TRANSLATIONS. Они рекурсивно вызывают друг друга (хотя в большинстве случаев PROCESS-OUTPUT-TRANSLATIONS обходится без "неявно-рекурсивного" вызова INHERIT-OUTPUT-TRANSLATIONS), по пути складывая в список промежуточные результаты с помощью локальной функции-коллектора C, определённой как сказано выше макросом WHILE-COLLECTING (в теле ф-ии INHERIT-OUTPUT-TRANSLATIONS она связывается с ключом COLLECT) . Когда аргумент INHERIT не равен NIL, что сигнализирует о необходимости запуска "конвейера", она разбивает его на голову и хвост и передаёт ф-ии PROCESS-OUTPUT-TRANSLATIONS:

    (when inherit
      (process-output-translations (first inherit) :collect collect :inherit (rest inherit)))

    ... хвост (rest inherit) передаётся для того, чтобы метод PROCESS-OUTPUT-TRANSLATIONS мог рекурсивно (через вызов INHERIT-OUTPUT-TRANSLATIONS) продолжить дальнейшую обработку, если в конфигурационном DSL был указан ключ :inherit-configuration.


PROCESS-OUTPUT-TRANSLATIONS
(defgeneric process-output-translations (spec &key inherit collect) ...)
3. Эта обобщёная ф-ия включает 5 методов специализирующихся на следующих типах аргумента SPEC: NULL, SYMBOL, STRING, PATHNAME и CONS. В конечном счёте всё сводится к вызову метода специализирующегося на типе CONS, так как аргумент в этом случае, будет содержать список, представляющий конфигурационные данные (ради получения и обработки которых, собственно всё и затевается).

    3.1 Сначала я рассмотрю ф-ии на которых базируются разные специализации PROCESS-OUTPUT-TRANSLATIONS, после понимания работы этих ф-ий - анализ взаимодействия методов станет тривиальным.

        VALIDATE-CONFIGURATION-FILE
        (defun* validate-configuration-file (file validator &key description) ...)
        3.1.1 Ф-ия считывает с указанного файла FILE список, проверяет что он в файле единственный и вызывает функцию-валидатор VALIDATOR для проверки корректности данных. Ключ DESCRIPTION ей необходим в случае неудачи, для формирования сообщения об ошибке.

        VALIDATE-CONFIGURATION-DIRECTORY
        (defun* validate-configuration-directory (directory tag validator &key invalid-form-reporter) ...)
        3.1.2 А эта ф-ия ищет в указанной директории DIRECTORY файлы с расширением .conf (пропуская скрытые файлы), считывает из них множество форм, представляющих директивы анализируемого DSL (Domain Specific Language - язык предметной области), проверяет каждую их них функией-валидором VALIDATOR и составляет из них правильный формат конфигурационных данных (который, соответственно, возвращается в качестве результата), используя при этом,  переданный ключевой символ TAG (он становится первым элементом результирующего списка).

        VALIDATE-OUTPUT-TRANSLATIONS-FILE
        (defun* validate-output-translations-file (file) ...)
        3.1.3 Ф-ия для считывания и проверки конфигурационных данных обращается за помощью к ф-ии VALIDATE-CONFIGURATION-FILE, которая в качестве валидатора использует функцию VALIDATE-OUTPUT-TRANSLATIONS-FORM. Если проверка корректности данных прошла успешно, то они возвращаются в качестве результата.

        VALIDATE-OUTPUT-TRANSLATIONS-DIRECTORY
        (defun* validate-output-translations-directory (directory) ...)
        3.1.4 Для поиска файлов с конфигурационными данными в директории DIRECTORY, их считывания и проверки вызывается функция VALIDATE-CONFIGURATION-DIRECTORY. В качестве функции-валидатора она использует VALIDATE-OUTPUT-TRANSLATIONS-DIRECTIVE, проверяющей каждую прочитанную форму из конфигурационных файлов (директиву анализируемого DSL).

        PARSE-OUTPUT-TRANSLATIONS-STRING
        (defun* parse-output-translations-string (string &key location) ...)
        3.1.5 Эта ф-ия используется для преобразования данных их переданной строки в правильно сформированные конфигурационные данные. Ф-ия анализирует переданный аргумент и выбирает одно из следующих действий:
            - если STRING не является строкой, то ф-ия сигнализирует об ошибке.
            - если STRING равна пустой строке или NIL то вызвращается список '(:output-translations :inherit-configuration).
            - если STRING содержит вложенную строку (то есть, строку вида "\"...\""), то эта вложенная строка "вынимается" из STRING с помощью вызова (read-from-string string) и повторяется всё заново со вложенной строкой (т.е. ф-ия рекусивно вызывается с получившейся строкой).
            - если первый символ в STRING это "(", то значит в строке содержится список и тогда он читается из строки, с помощью того же вызова (read-from-string string) и также происходит рекурсивный вызов с получившимся результатом.
            - если же не сработал ни один из получившихся вариантов, тогда строка рассматривается как последовательное перечисление директорий, каждая их которых разделяется специфичным для системы симолом-разделителем директорий (для Linux это символ ":"). Причём кол-во директорий должно быть обязательно чётным, иначе сигнализируется ошибка. Отсутствие текста между двумя символами-разделителями (например: "/home/src:/some/compiled::") говорит об использовании ключа :INHERIT-CONFIGURATION (его допускается использовать один раз, при нарушении этого правило сигнализируется ошибка).

        VALIDATE-OUTPUT-TRANSLATIONS-FORM
        (defun* validate-output-translations-form (form &key location) ...)
        3.1.6 Ф-ия проверяет корректность конфигурационных данных с помощью ф-ии VALIDATE-CONFIGURATION-FORM. Для проверки каждой директивы она использует ф-ию VALIDATE-OUTPUT-TRANSLATIONS-DIRECTIVE. Ключ LOCATION используется в случае возникновения ошибки и содержит небольшую часть сообщения.

        VALIDATE-CONFIGURATION-FORM
        (defun* validate-configuration-form (form tag directive-validator
                          &key location invalid-form-reporter) ...)
    3.1.7 Ф-ия проверяет корректность конфигурационных данных, представленных аргументом FORM. В случае если аргумент FORM не является списком или его первый элемент не эквивалентен аргументу TAG, то глобальная переменная *IGNORED-CONFIGURATION-FORM* получает значение T и сигнализируется ошибка. Основная часть ф-ии состоит из итерирования по хвосту списка FORM. Ф-ия проверяет каждую директиву переданым валидатором DIRECTIVE-VALIDATOR, при этом она проверяет что ни один из ключей :INHERIT-CONFIGURATION или :IGNORE-INHERITED-CONFIGURATION не встречается более одного раза. А также она обрабатывает семантику ключа :IGNORE-INVALID-ENTRIES позволяющего игнорировать неправильные директивы, не сигнализируя ошибки.

        VALIDATE-OUTPUT-TRANSLATIONS-DIRECTIVE
        (defun* validate-output-translations-directive (directive) ...)
        3.1.8 Ф-ия отвечает непосредственно за валидацию директивы (обрабатываемого DSL). Если аргумент DIRECTIVE атом, то он обязан иметь только одно из следующих значений: :ENABLE-USER-CACHE, :DISABLE-CACHE, NIL. Если же он является списком, тогда считается корректной одна из следующих ситуаций:

            3.1.8.1 Длина списка = 2 и при этом:
                - либо в качестве первого элемента используется ключ :INCLUDE, а в качестве второго - объект типа STRING, PATHNAME или NIL.
                - либо первый элемент удовлетворяет предикату LOCATION-DESIGNATOR-P, а второй - либо предикату LOCATION-DESIGNATOR-P, либо предикату LOCATION-FUNCTION-P.                    

            3.1.8.2 Длина списка = 1 и его единственный элемент удовлетворяет предикату LOCATION-DESIGNATOR-P.

            Используемые предикаты работают следующим образом:

            LOCATION-DESIGNATOR-P
            Предикат возвращает значение T, в случае если аргумент:
                - атом и имеет тип STRING, PATHNAME или одно из следующих значений:  T, NIL, :ROOT :HOME :HERE :USER-CACHE :SYSTEM-CACHE :DEFAULT-DIRECTORY.
                - список, и тогда в качестве первого элемента в списке должно быть то, что описано выше, а в качестве оставшихся должны быть элементы типа STRING, PATHNAME, или следующие значения: :DEFAULT-DIRECTORY, :*/, :**/, :*.*.*, :IMPLEMENTATION, :IMPLEMENTATION-TYPE.

            LOCATION-FUNCTION-P
            Предикат возвращает T, в случае если аргумент:
                - список из двух элементов, первый из которых это :function, а второй: либо символ, либо список, представляющий лямбда-выражение, принимающее на вход два параметра.
        
        PROCESS-OUTPUT-TRANSLATIONS-DIRECTIVE
        (defun* process-output-translations-directive (directive &key inherit collect) ...)
        3.1.9 Это самая важная ф-ия, она занимается непосредственно обработкой директивы из, предварительно проверенных на корректность, конфигурационных данных. Смысл её в том, чтобы разобрать (распарсить) переданную директиву и сформировать данные для последующего помещения в результирующий список с помощью ф-ии передаваемой ключом COLLECT. Результаты обработки в конце-концов попадут в глобальную переменную *OUTPUT-TRANSLATIONS*, а она в свою очередь, необходима для работы ф-ии APPLY-OUTPUT-TRANSLATIONS (это ф-ия, вычисляющая пути к файлам содержащим результаты применения операций к компонентам - обычно к *.fasl файлам). В процессе работы ф-ия может рекурсивно вызвать сама себя или ф-ию INHERIT-OUTPUT-TRANSLATIONS с параметром INHERIT, если была вызывана с аргументом DIRECTIVE равным ключу :INHERIT-CONFIGURATION. Подробный разбор логики работы этой ф-ии будет дан в конце статьи.

    3.2 Теперь вернёмся к обобщённой ф-ии PROCESS-OUTPUT-TRANSLATIONS, её специализации работают следующим образом:

(DEFMETHOD PROCESS-OUTPUT-TRANSLATIONS NULL)
Осуществляется переход к обработке следующего элемента:

    (inherit-output-translations inherit :collect collect)

(DEFMETHOD PROCESS-OUTPUT-TRANSLATIONS SYMBOL)
Вызывается ф-ия, имя которой представлено символом, для получения конфигурационных данных для дальнейшей обработки:

    (process-output-translations (funcall x) :inherit inherit :collect collect)

(DEFMETHOD PROCESS-OUTPUT-TRANSLATIONS STRING)
Аргумент обрабатывается ф-ией PARSE-OUTPUT-TRANSLATIONS-STRING, возвращающей конфигурационные данные для дальнейшей обработки:

    (process-output-translations (parse-output-translations-string string)
                 :inherit inherit :collect collect)

(DEFMETHOD PROCESS-OUTPUT-TRANSLATIONS PATHNAME)
Если аргумент представляет собой файл, то для чтения конфигурационных данных и их валидации (и возврата в качестве результата) вызывается ф-ия VALIDATE-OUTPUT-TRANSLATIONS-FILE. Если аргумент содержит путь к директории, то файлы в ней рассматриваются как содержащие конфигурационные данные. За сбор этих данных и их валидацию отвечает ф-ия VALIDATE-OUTPUT-TRANSLATIONS-DIRECTORY. Если аргумент представляет собой не существующий файл, то происходит переход к дальнейшей обработки:

    (inherit-output-translations inherit :collect collect)

(DEFMETHOD PROCESS-OUTPUT-TRANSLATIONS CONS)
В этом случае аргумент представляет собой конфигурационные данные. Производится их валидация и каждый элемент этих данных обработывается ф-ией PROCESS-OUTPUT-TRANSLATIONS-DIRECTIVE:

    (dolist (directive (cdr (validate-output-translations-form form)))
      (process-output-translations-directive directive
                         :inherit inherit
                         :collect collect
)
)


--------------------------------------------------------------
        
PROCESS-OUTPUT-TRANSLATIONS-DIRECTIVE
(defun* process-output-translations-directive (directive &key inherit collect) ...)
Это главная ф-ия, используемая в процессе обработки конфигурационных данных, используемых при формирования путей результатов применения операций к компонентам. Эти конфигурационные данные следуют специальным соглашениям ASDF для формирования этих путей, представляющих собой некий мини-DSL.

Логика работы:
    
1. Если аргумент DIRECTIVE - атом, то в зависимости от его значения-ключа происходит следующее:
    - :ENABLE-USER-CACHE - рекурсивный вызов с DIRECTIVE = '(t :user-cache)
    - :DISABLE-CACHE - рекурсивный вызов с DIRECTIVE = '(t t)
    - :INHERIT-CONFIGURATION - вызов INHERIT-OUTPUT-TRANSLATIONS с аргументом INHERIT.
    - для аргументов :IGNORE-INHERITED-CONFIGURATION, :IGNORE-INVALID-ENTRIES, NIL - возвращает NIL.

2. Если же аргумент список определяется локальное окружение с переменными, содержащими элементы списка:

   (let ((src (first directive))
      (dst (second directive))
)

      ...
)


3. Далее, если SRC = :INCLUDE то путь в DST обрабатывается "неявно-рекурсивным" вызовом ф-ии PROCESS-OUTPUT-TRANSLATIONS:

    (if (eq src :include)
        (when dst
     
     (process-output-translations (pathname dst) :inherit nil :collect collect))
    ...
)


4. Иначе, если переменная SRC не равна ни :INCLUDE ни NIL, то происходит следующее:

    4.1 Создаётся локальное окружение с переменной TRUSRC. Она становится равной либо T (если SRC = T):

       (let ((trusrc (or (eql src t)
                           
  ...)))
        ...
)


        ... либо её значение вычисляется из локальной SRC, которая должна представлять собой или путь к файлу непосредственно, или список описывающий путь (оформленный с помощью специальных соглашений ASDF по формированию путей):

       (let ((loc (resolve-location src :directory t :wilden t)))
          (if (absolute-pathname-p loc) (truenamize loc) loc))

        ... как видно используется ф-ия RESOLVE-LOCATION которая воспринимает SRC как директорию и создаёт, так называемый "wildcard" путь (путь использующий групповые символы). Например, вот результат применения RESOLVE-LOCATION с путём "/usr/somedir":

        (resolve-location "/usr/somedir" :directory t :wilden t)
        => #P"/usr/somedir/**/*.*"

        4.2 В созданном локальном окружении, возможные варианты выполнения распадаются на три ветки:
            
            4.2.1 Если переменная DST удовлетворяет предикату LOCATION-FUNCTION-P (т.е это список имеющий первый элемент :FUNCTION, а второй - либо символ, либо список с лямбда-выражением), то в результирующий список (собираемый ф-ией переданной в ключе COLLECT) попадает список из TRUSRC и из функционального объекта представленного вторым элементом DST:

               (cond
                  ((location-function-p dst)
                   (funcall collect
                            (list trusrc
                                  (if (symbolp (second dst))
                                      (fdefinition (second dst))
                                      (eval (second dst))
)
)
)
)

                  ...
)


            4.2.2 Если DST = T, то возвратить список из TRUSRC и T:

               (cond
         
         ...
       
           ((eq dst t)
              
       (funcall collect (list trusrc t)))
        
          ...)

            4.2.3 В остальных случаях в результирующий список попадают 2 списка:
                - список из обработанной (с помощью ф-ии RESOLVE-LOCATION) переменной DST если она не была равна NIL (а если была, то в список попадает значение переменной TRUSRC) и константы T.
                - и список из TRUSRC и обработанной DST (в случае если значение DST было равно NIL, то в списке оказывается значение TRUSRC).

--------------------------------------------------------
Продолжение следует ...
linkpost comment

Устройство ASDF. Подсистема определения путей. APPLY-OUTPUT-TRANSLATIONS. [Dec. 25th, 2011|07:03 am]
[Tags|, , , , ]

(сказаное ниже относится к версии 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 в абсолютный, если он относительный и сглаживание каких-то тонких нюансов в обработке путей.

-------------------------------------------
Продолжение следует ...
linkpost comment

Устройство ASDF. Подсистема поиска. RESOLVE-LOCATION [Dec. 23rd, 2011|11:30 pm]
[Tags|, , , , ]

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

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

 
   Ф-ия resolve-location необходима для того, чтобы получить нормальный для данной системы путь. Причём учитывая некоторые специальные соглашения ASDF для предопределённых директорий и для использования групповых символов (вроде "*.*" или "**"). Ф-ия занимается обработкой этих соглашений и "разруливает" многочисленные различия в обработке путей на разных платформах/лисп-системах. Вот так выглядит её определение:

   (defun* resolve-location (x &key directory wilden)
      ...)

    В теле RESOLVE-LOCATION используются две функции RESOLVE-ABSOLUTE-LOCATION-COMPONENT и RESOLVE-RELATIVE-LOCATION-COMPONENT (чуть ниже будет представлена схема зависимостей ф-ий друг от друга). Причём RESOLVE-LOCATION может быть вызвана рекурсивно, но не явно (в теле определения ф-ии нет рекурсивных вызовов), а опосредовано - RESOLVE-ABSOLUTE-LOCATION-COMPONENT в определённых ситуациях вызывает RESOLVE-LOCATION.

    RESOLVE-ABSOLUTE-LOCATION-COMPONENT использует:
1) саму себя (вызывается рекурсивно).
2) RESOLVE-RELATIVE-LOCATION-COMPONENT.
3) RESOLVE-LOCATION (т.е. может делать опосредованные рекурсивные вызовы).
4) динамические переменные *HERE-DIRECTORY*, *USER-CACHE*, *SYSTEM-CACHE*.
5) общие ф-ии (назовём их так): MERGE-PATHNAMES*, ENSURE-DIRECTORY-PATHNAME, DEFAULT-DIRECTORY, USER-HOMEDIR, ABSOLUTE-PATHNAME-P.
6) ф-ию WILDEN.
   
    RESOLVE-RELATIVE-LOCATION-COMPONENT использует:

1) саму себя (вызывается рекурсивно).
2) дин. переменные: *WILD-DIRECTORY*, *WILD-INFERIORS* *WILD-FILE*.
3) общие ф-ии: RELATIVIZE-PATHNAME-DIRECTORY, MERGE-PATHNAMES*, ENSURE-DIRECTORY-PATHNAME, ABSOLUTE-PATHNAME-P.
4) ф-ии для получения текущей реализации: IMPLEMENTATION-IDENTIFIER, IMPLEMENTATION-TYPE.
5) ф-ию WILDEN.


На "общих ф-иях" не будем останавливаться, их названия говорят сами за себя. Ф-ии для получения текущей
реализации, в случае SBCL/Linux работают так:

(implementation-identifier) => "sbcl-1.0.48-linux-x86"
(implementation-type) => :SBCL

Ф-ия WILDEN добавляет путь из "wildcard" символов в конец пути, это говорит о том что это: "любой файл или директория имеющие началом указанную директорию", обычно это путь #P"**/*.*".

Примеры:

(resolve-location :home)
= > #P"/home/someuser/"

(resolve-location '(:root :implementation "main-source"))
=> #P"sbcl-1.0.48-linux-x86/main-source"

(resolve-location '(:home (:implementation-type "my-source") "main-source"))
=> #P"/home/lispuser/sbcl/my-source/main-source"

                    Логика работы RESOLVE-LOCATION.

1. Ф-ия передаёт управление (вызывает) ф-ию RESOLVE-ABSOLUTE-LOCATION-COMPONENT если X - атом.

   (if (atom x)
      (resolve-absolute-location-component x :directory directory :wilden wilden)

2. Если X не атом, а список, то организуется итерация по списку, при этом:

    2.1 Определяется сразу базовый абсолютный путь PATH (который дальше будет аргументом SUPER в вызовах RESOLVE-RELATIVE-LOCATION-COMPONENT). Для его определения берётся первый элемент из списка X:

   (loop :with path = (resolve-absolute-location-component
                                (car x) :directory (and (or directory (cdr x)) t)
                                :wilden (and wilden (null (cdr x))))
       ...)

    ... как видно ключ :DIRECTORY для RESOLVE-ABSOLUTE-LOCATION-COMPONENT равен T если либо задан соотв. ключ в вызове RESOLVE-LOCATION либо в списке X больше одного аргумента, а :WILDEN если задан соотв. ключ и в списке X нет больше аргументов.

    2.2 Собственно, декларирование прохода по списку X, начиная с его хвоста. Локальная переменная COMPONENT будет получать текущий элемент, а переменная MOREP будет нам говорить о том, остались ли в списке ещё элементы:

   (loop
       ...
       :for (component . morep) :on (cdr x)
       ...)

    2.3 Определяется локальная переменная DIR - она будет вычисляться на каждой итерации и будет равна T, если значение ключа DIRECTORY равно T либо если в списке ещё есть элементы (MOREP не равна NIL):

   (loop
       ...
       :for dir = (and (or morep directory) t)
       ...)
   
    2.4 Определяется локальная переменная WILD - она также вычисляется на каждой итерации и равна истине, только в случае если значение WILDEN = T и в списке нет больше элементов (то есть это последняя итерация):

   (loop
       ...
       :for wild = (and wilden (not morep))
       ...)

    2.5 В главном операторе итерации вычисляется новый PATH с помощью старого PATH и вычисленных локальных переменных. Старый PATH становится началом для вычисляемого пути (значением аргумента SUPER для RESOLVE-RELATIVE-LOCATION-COMPONENT):

   (loop
       ...
       :do (setf path (resolve-relative-location-component
               path component :directory dir :wilden wild))
       ...)

    2.6 После прохода по списку возвращается переменная PATH:

         (loop ... :finally (return path))


                RESOLVE-ABSOLUTE-LOCATION-COMPONENT.
                (defun* resolve-absolute-location-component (x &key directory wilden) ...)

    Ф-ия занимается тем, что формирует путь используя строки, имена путей (pathnames), предопределённые ключи :root :home :here :user-cache :system-cache :default-directory , а также списки. Если передаётся список, то для его головы происходит рекурсивный вызов и результат становится началом вычисляемого пути, а его окончанием - результат предварительной обработки хвоста ф-ей RESOLVE-RELATIVE-LOCATION-COMPONENT.

Примеры:

(resolve-absolute-location-component :root)
=> #P""

(resolve-absolute-location-component "/my/path")
=> #P"/my/path"

(resolve-absolute-location-component '(:home ("my" "path")))
=> #P"/home/lispuser/my/path"

                Логика работы RESOLVE-ABSOLUTE-LOCATION-COMPONENT.
1. Создаёт локальный контекст и устанавливает значение для лексической переменной R в соответствии с типом аргумента X:

   (let* ((r (etypecase x ...))
       ...)
      ...)

    LET -> ETYPECASE
    1.1 Если X путь (pathname) то она просто возвращается. Если строка, то  используется либо ENSURE-DIRECTORY-PATHNAME (которая выбрасывает ошибку в случае если данный pathspec не получается рассматривать как путь к директории) либо PARSE-NAMESTRING (что по крайней мере для SBCL чревато проблемами в обработке путей и лучше для этой системы было бы использовать SB-EXT:PARSE-NATIVE-NAMESTRING) в зависимости от переданного аргумента DIRECTORY:

     (etypecase x
            (pathname x)
            (string (if directory (ensure-directory-pathname x) (parse-namestring x)))
        ...)

    LET -> ETYPECASE
    1.2 Если X список (cons), то осуществляется выход из ф-ии с некоторым значением:
   
     (etypecase x
        ...
        (cons
             (return-from resolve-absolute-location-component (if ...) ...))
         ...)
        
         ... которое формируется следующим образом:

            LET -> ETYPECASE -> RETURN-FROM
            1.2.1 Если список состоит из одного элемента, то ф-ия просто рекурсивно вызывается для этого элемента:

               (if (null (cdr x))
                  (resolve-absolute-location-component   
                     (car x) :directory directory :wilden wilden)
              ...)

            LET -> ETYPECASE -> RETURN-FROM
            1.2.2 Иначе вычисляется путь, в котором началом станет результат вызова RESOLVE-ABSOLUTE-LOCATION-COMPONENT с первым элементом списка:

                (resolve-absolute-location-component (car x) :directory t :wilden nil)
                             
             ... а его окончанием, результат вызова RESOLVE-RELATIVE-LOCATION-COMPONENT с хвостом списка:

                (resolve-relative-location-component (cdr x) :directory directory :wilden wilden)   
                                                         
        LET -> ETYPECASE
        1.3 Также может быть задан один из предопределённых ключей:

            :root :home :here :user-cache :system-cache :default-directory
       
:root - просто некоторый относительный путь, возможно (если :wilden = t) с "wildcard" символами.

:home - путь становится равным домашней директории текущего пользователя (на Linux это #P"/home/someuser/").

:here - если была задана некоторая текущая директория для процесса обработки, через динамическую переменную *HERE-DIRECTORY*, то вызывается resolve-location для этой директории иначе она вызывается с ключом :default-directory :

   (resolve-location (or *here-directory*
              ;; give semantics in the case of use interactively
             :default-directory)
              :directory t :wilden nil)

   ... динамическая переменная *HERE-DIRECTORY* обычно задаётся при обработке файла конфигурации имя которого в *SOURCE-REGISTRY-FILE* (по умолчанию "source-registry.conf") и будет равна директории в которой этот файл находится, либо при обработке директории, имя которой в *SOURCE-REGISTRY-DIRECTORY* (по умолчанию "source-registry.conf.d/"), с конфигурационными файлами и будет равна ей же.

:user-cache - здесь вызывается RESOLVE-LOCATION со значением *USER-CACHE*, которая по умолчанию равна:

    '(:HOME ".cache" "common-lisp" :IMPLEMENTATION)

    ... результат вызова на Linux будет таким:

    (resolve-location *user-cache* :directory t :wilden nil)

    => #P"/home/someuser/.cache/common-lisp/sbcl-1.0.48-linux-x86/"

:system-cache - при использовании этого ключа сигнализируется ошибка с сообщением о том, что этот ключ устарел.

:default-directory - вычисляется директория из *DEFAULT-PATHNAME-DEFAULTS*.

    LET
    2. Далее в локальном контексте определяется переменная S. Её значение вычисляется с использованием определённой ранее R. Если был задан ключ :WILDEN и тип X не является типом PATHNAME то к значению R прибавляется путь #P"**/*.*" (для обозначения любого файла и директории с началом пути равным R), иначе просто берётся значение R:

   (let (...
           (s (if (and wilden (not (pathnamep x)))
                  (wilden r)
                  r)))
     ...)

    LET
    3. Проверяется что переменная S обозначает абсолютный путь, если это не так выбрасывается ошибка. Иначе S возвращается из ф-ии RESOLVE-ABSOLUTE-LOCATION-COMPONENT.


        RESOLVE-RELATIVE-LOCATION-COMPONENT.
        (defun* resolve-relative-location-component (super x &key directory wilden) ...)
       
    Зачем нужна RESOLVE-RELATIVE-LOCATION-COMPONENT ? Она необходима для обработки ещё нескольких соглашений ASDF по формированию пути. Они нужны для повышения гибкости в задании пути, с помощью так называемых "wildcard" символов (символов для обозначения группы файлов/директорий, например: "*"), предопределённых имён директорий и единообразного использования ASDF на разных платформах. В соглашении применяются следующие ключи:
   
          :default-directory :*/ :**/ :*.*.* :implementation :implementation-type :uid (только для unix-систем)
          
     Работа этой ф-ии напоминает работу ф-ии resolve-absolute-location-component.

Примеры:

(resolve-relative-location-component "/home/user/" :**/)
=> #P"/home/user/**/"

(resolve-relative-location-component nil :**/)
=> #P"**/"

(resolve-relative-location-component "/tmp/" '(:implementation :uid :**/))
=> #P"/tmp/sbcl-1.0.48-linux-x86/1000/**/"

                Логика работы RESOLVE-RELATIVE-LOCATION-COMPONENT.

1. Формируется локальный контекст в котором определяется переменная R, её значение вычисляется в зависимости от типа переданной переменной X:

(let* ((r (etypecase x   
              ...
)
)
          ...)
    ...)

    LET -> ETYPECASE
    1.1 Если X имеет тип PATHNAME или STRING то переменная R приравнивается к X:

   (etypecase x
      (pathname x)
      (string x)
      ...)

    LET -> ETYPECASE
    1.2 Если X список (cons), то осуществляется выход из ф-ии с некоторым значением:

   (etypecase x
      ...
      (cons
       (return-from resolve-relative-location-component ...))
      ...)
               
    ... которое вычисляется следующим образом:
           
        LET -> ETYPECASE -> RETURN-FROM   
        1.2.1 Если список из одного элемента ты происходит рекурсивный вызов с этим аргументом:

               (if (null (cdr x))
                    (resolve-relative-location-component
                           super (car x) :directory directory :wilden wilden)
                    ...)

        LET -> ETYPECASE -> RETURN-FROM
        1.2.2 Если же нет, то всё происходит почти также как и в RESOLVE-ABSOLUTE-LOCATION-COMPONENT:

            LET -> ETYPECASE -> RETURN-FROM -> IF
            1.2.2.1 Формируется локальный контекст с переменной CAR, в котором происходит рекурсивный вызов:
                     
                   (let* ((car (resolve-relative-location-component
                                                  super (car x) :directory t :wilden nil
)
)
                           ...)
                      ...)

        LET -> ETYPECASE -> RETURN-FROM -> IF
        1.2.2.2 Дальше значение CAR используется для задания начала вычисляемого пути, а его окончание формируется с помощью рекурсивного вызова RESOLVE-RELATIVE-LOCATION-COMPONENT:

                (merge-pathnames*
                     (resolve-relative-location-component
                  
        (cdr x) :directory directory :wilden wilden)
                     car
)


    LET -> ETYPECASE
    1.3 Если же X является одним из следующих ключей:

    :default-directory :*/ :**/ :*.*.* :implementation :implementation-type :uid (только для unix-систем)        

    ... то возвратить некоторое отображенное на этот ключ значение. Ниже представлены типичные значения на которые отображаются ключи для конфигурации SBCL/Linux:

:default-directory = <значение *default-pathname-defaults* превращенное в относительный путь>
:*/ = *wild-directory* = #P"*/"
:**/ = *wild-directory* = #P"**/"
:*.*.* = *wild-file* = #P"*.*"
:implementation = (implementation-identifier) = "sbcl-1.0.48-linux-x86"
:implementation-type = (string-downcase (implementation-type)) = "sbcl"
:uid = (princ-to-string (get-uid)) = "1000"

2. Проверка на относительность пути - выбрасывается ошибка в случае, если R представляет собой абсолютный путь:

   (when (absolute-pathname-p r)
      (error (compatfmt "~@<pathname ~S is not relative~@:>") x)
)


3. Пост-обработка окончательного результата. Если ключ WILDEN был задан и значение X не имеет тип PATHNAME , то значит надо добавить в конец пути относительный "wildcard" путь:

      (if (or (pathnamep x) (not wilden)) r (wilden r)))
                 
    ... например: (wilden "/home/someuser/") => #P"/home/someuser/**/*.*" - и это будет означать: "любые файлы и директории начинающиеся с /home/someuser/". Иначе R возвращается "как есть".

-------------------------------------------------------------
Продолжение следует ...
linkpost comment

Устройство ASDF. Подсистема определения путей. INPUT-FILES, OUTPUT-FILES и COMPONENT-PATHNAME. [Dec. 23rd, 2011|10:02 pm]
[Tags|, , , , ]

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

Это 17-ая статья цикла.
Первая статья.
Архитектура ASDF.
Предыдущая статья.
 
INPUT-FILES
Ф-ия используется для получения абсолютного пути к файлу, представляющему компонент. Этим файлом будет в большинстве случаев либо файл *.lisp с лисп-исходником, либо файл *.fasl с результатом компиляции исходника. Для комбинаций аргументов (COMPILE-OP STATIC-FILE) и (OPERATION MODULE) метод ничего не делает. При вызове с аргументами (OPERATION COMPONENT) происходит следующее:

1. В локальной PARENT сохраняется предок компонента.
2. В локальной SELF-DEPS, сохранются пары всех операций, в которых вторым значением является сам этот компонент.
3. Если нет пар (нет операций с этим компонентом) в SELF-DEPS, просто возвращается абсолютный путь к компоненту.
4. Если SELF-DEPS не NIL, создаётся список из результатов применения анонимной ф-ии к каждой паре в SELF-DEPS. Эта ф-ия разбивает пару на операцию и имя компонента, а затем вычисляет путь к файлу, который связан как-либо с результатом применения операции. В случае с операцией COMPILE-OP этим результатом будет абсолютный путь к скомпилированному файлу (обычно *.fasl). Отвечает за вычисление пути к выходному файлу ф-ия OUTPUT-FILES:

  (output-files (make-instance op)
          (find-component parent name)
)


          ... ф-ия FIND-COMPONENT относится к подсистеме загрузки, поэтому я здесь её рассматривать не буду. Смысл её тривиален - возвратить компонент с именем NAME найденный в PARENT.

OUTPUT-FILES
Эта ф-ия возвращает абсолютный путь к результату компиляции исходника (обычно это файл с расширением .fasl) Это обобщённая ф-ия включающая 5 методов, 3 из которых:

  (defmethod output-files load-source-op component)
  (defmethod output-files operation component)
  (defmethod output-files compile-op static-file)

   ... ничего не делают, а просто возвращают NIL.

Другой метод является декоратором "обёртывающим" все последующие методы. А последний специализируется на (COMPILE-OP CL-SOURCE-FILE) аргументах:

1 Сначала выполняется декорирующий метод со стандартным комбинатором :AROUND :

   (defmethod output-files :around (operation component) ...)

   Смысл его в том, чтобы список файлов, возвращенных следующим методом (по умолчанию, если не было определено дополнительных методов, это будет список из одного файла) обработать ф-ией APPLY-OUTPUT-TRANSLATIONS:

   (multiple-value-bind (files ...) (call-next-method)
       ...
       (mapcar #'apply-output-translations files)
)


   Ф-ия APPLY-OUTPUT-TRANSLATIONS, конструирует абсолютные пути файлов ориентируясь на глобальную переменную *OUTPUT-TRANSLATIONS*. Эта переменная содержит wildcard пути (шаблоны путей, например: #P"/home/someuser/.cache/common-lisp/sbcl-1.0.48-linux-x86/**/*.*"). Она трансформирует путь компонента, таким образом, чтобы заменить начало пути по которому содержится компонент на специальный путь, формируемый на основе имени пользователя, версии лисп-системы и возможно других правил, которые достаточно гибко настраиваются. Логика работы этой ф-ии и принцип настройки wildcard путей во многом схожи с логикой работы поиска *.asd файлов и настройкой путей для их поиска.

2. И самый главный метод позволяющий получить путь для скомпилированного файла:

   (defmethod output-files ((operation compile-op) (c cl-source-file)) ...)

   Метод работает достаточно просто: вызывает (component-pathname с) для получения абсолютного пути компонента, превращает его в путь к лисп-файлу (добавляя расширение *.lisp) и выясняет с помощью стандартной ф-ии COMPILE-FILE-PATHNAME путь до скомпилированного файла:

   (let ((p (lispize-pathname (component-pathname c))))
      -broken-fasl-loader (list (compile-file-pathname p))
      +broken-fasl-loader (list p)
)


    ... как мы видим, учитывается некая ситуация, в которой загрузчик fasl-файлов может не работать.

COMPONENT-PATHNAME
Ф-ия возвращает значение слота ABSOLUTE-PATHNAME если он установлен. Иначе вычисляется абсолютный путь, установливается вычисленное значение в этот слот и возвращается. Принцип вычисления в том, что путь формируется из имён предков компонента и/или путей, сохраненных в слоте relative-pathname каких-либо предков этого компонента. Начало же абсолютного пути берётся из самого верхнего в иерархии компонента, который имеет тип SYSTEM и содержит абсолютный путь, вычисленный ещё при выполнении формы (defsystem ...).

Подробная логина работы состоит в следующем:

1. Проверка: связан ли слот компонента ABSOLUTE-PATHNAME, если да, то возвращается его значение:

     (if (slot-boundp component 'absolute-pathname)
          (slot-value component 'absolute-pathname)
          ...
)


2. Если нет, то вычисляется абсолютный путь из относительного пути компонента и абсолютного пути его предка:

   (merge-pathnames*
     (component-relative-pathname component)
     (pathname-directory-pathname (component-parent-pathname component))
)


   COMPONENT-RELATIVE-PATHNAME
   (defmethod component-relative-pathname ((component component)) ...)
   2.1 Относительный путь конструируется с помощью ф-ии COERCE-PATHNAME. Она вызывается с именем компонента (или его специфическим, относительным путём заданным опцией :path в описании компонента), ключом :type, ассоциированном со значением, возвращаемым ф-ией SOURCE-FILE-TYPE (для компонента класса CL-SOURCE-FILE это будет конечно же "lisp") и относительным путём его предка:

       (coerce-pathname
         (or (slot-value component 'relative-pathname)
             (component-name component)
)

         :type (source-file-type component (component-system component))
         :defaults (component-parent-pathname component)
)


   COMPONENT-PARENT-PATHNAME
   2.2 Ф-ия для вычислеиня пути обращается к ф-ии COMPONENT-PATHNAME. Таким образом производятся неявные рекурсивные вызовы, вплоть до получения пути из самого "верхнего" объекта в иерархии компонентов, которым должен быть объект класса/подкласса SYSTEM.
    
3. Если путь вычислен и он не абсолютный, то сигнализируется ошибка с сообщением "Invalid relative pathname ...".

4. В слот ABSOLUTE-PATHNAME компонта устанавливается вычисленный абсолютный путь и он же возвращается в качестве результата ф-ии.

--------------------------------------
Продолжение следует ...
linkpost comment

navigation
[ viewing | most recent entries ]
[ go | earlier ]