Искусство программирования для Unix
Шрифт:
В более широком смысле хорошо спроектированные генераторы процедурного кода никогда не должны требовать от пользователя исправлять вручную или даже просматривать сгенерированный код. Создание корректного кода является непосредственной задачей генератора.
15.3.1.1. Учебный пример: грамматика fetchmailrc
Канонический демонстрационный пример, который, видимо, приводится в каждом учебном пособии по lex и уасс, представляет собой игрушечную программу интерактивного калькулятора, которая анализирует
Вместо этого грамматика анализатора конфигурационных файлов fetchmail предоставляет хороший учебный пример среднего размера по использованию lex и уасс. Здесь имеется несколько интересных моментов.
/ег-спецификация в файле rcfile_l.l— весьма типичная реализация shell-подобного синтаксиса. Обратите внимание на то, как два дополняющих правила поддерживают строки либо с одинарными, либо с двойными кавычками; данная идея хороша в принципе. Правила для принятия (возможно, со знаком) целых литералов и отклонения комментариев также являются достаточно распространенными.
уасс-спецификация в файле rcfile_y.y достаточно длинная, но понятная. Она не осуществляет каких-либо fetchmail-действий, а только устанавливает биты в списке внутренних управляющих блоков. После запуска fetchmail в обычном режиме программа только периодически проходит по данному списку, используя каждую запись для управления сеансом получения почты с удаленного узла.
15.3.2. Учебный пример: Glade
Программа Glade рассматривалась в главе 8 в качестве хорошего примера декларативного мини-языка. Также отмечалось, что в результате работы серверной части Glade генерируется код на одном из нескольких языков.
Glade представляет собой хороший современный пример генератора прикладного кода. Описанные ниже функции, которые отсутствуют в большинстве GUI-построителей (особенно в большинстве коммерческих GUI-построителей), делают Glade. Unix-программой "по духу". (
• Glade GUI и генератор кода Glade не связаны в массивном монолите, а подчиняются правилу разделения (и построены согласно модели "разделения ядра и интерфейса").
• GUI и генератор кода соединяются с помощью текстового формата (основанного на XML), который можно читать и модифицировать с помощью других инструментов.
• Поддерживается несколько целевых языков (а не только С или С++). Существует возможность легко добавлять другие языки.
Конструкция позволяет при необходимости заменить редактор GUI-интерфейса в Glade.
15.4. Утилита make: автоматизация процедур
Сами по себе исходные коды программ не делают приложения. Также важен способ их компоновки и упаковки для распространения. Операционная система Unix предоставляет инструментальное средство для частичной автоматизации данных процессов — make(1). Утилита make описывается в большинстве вводных книг по операционной системе Unix. Более конкретная ссылка приводится в книге "Managing Projects tenth Make" [57].
На сайте FSF доступны версии GNU make для DOS и Windows.
15.4.1. Базовая теория make
При разработке программ на языках С или С++ важной частью для построения приложения является семейство команд компиляции и компоновки, необходимых для получения из файлов исходного кода работающих бинарных файлов. Ввод данных команд — длительная и кропотливая работа, и большинство современных сред разработки включают в себя способ помещения их в командные файлы или базы данных, которые можно автоматически вызывать для сборки приложения.
Unix-программа make(1), родоначальник всех-этих средств, была разработана специально для того, чтобы помочь С-программистам управлять данными инструкциями. Она позволяет описать зависимости между файлами проекта в одном или нескольких "make-файлах". Каждый make-файл состоит из последовательности правил, каждое из которых указывает утилите make, что некоторый заданный целевой файл зависит от некоторого набора исходных файлов и определяет действия в случае, если любой из файлов исходного кода является более новым, чем целевой файл. Фактически программисту не требуется описывать все зависимости, поскольку программа make способна установить большинство очевидных зависимостей по именам файлов и расширениям.
Например, программист может указать в make-файле, что бинарный файл myprog зависит от трех объектных файлов myprog. о, helper. о и stuff . о. Если имеются файлы исходного кода myprog. с, helper. с и stuff. с, то утилита make без специальных указаний определит, что каждый . о-файл зависит от соответствующего . с-файла, и предоставит собственную стандартную инструкцию для сборки .о-файлаиз .с-файла.
Возникновение make связано с визитом ко мне Стива Джонсона (Steve Johnson — автор уасс и других программ). Когда он пришел, он был очень недоволен тем, что ему пришлось потратить впустую утро, занимаясь отладкой корректной программы (ошибка была устранена, файл не был откомпилирован, и, следовательно, ее *. о не работала). А поскольку я потратил часть предыдущего вечера, справляясь с той же проблемой в разрабатываемом мною проекте, у нас появилась идея создания инструмента для решения данной задачи. Все началось с тщательно продуманной идеи анализатора зависимостей, потом свелось к нечто более простому и в те же выходные превратилось в make. Использование инструментов, которые все еше оставались сырыми, было частью культуры. Маке-файлы были текстовыми, а не "волшебно" закодированными бинарными файлами, поскольку это было в духе Unix: печатаемый, отлаживаемый, понятный материал.
Стюарт Фельдман.
После ввода команды make, в каталоге проекта программа make просматривает все правила и временные метки, после чего выполняет минимальный объем работы, необходимый для того, чтобы гарантировать актуальность производных файлов.
Хороший пример шаке-файла умеренной сложности можно взять из исходных кодов программы fetchmail. В дальнейших подразделах он будет рассматриваться снова.