Производственно-внедренческий кооператив
                    "И Н Т Е Р Ф Е Й С"
                Диалоговая Единая Мобильная
                    Операционная Система
                        Демос/P 2.1
                       Интерпретатор
                            make
                           Москва
                            1988
                        АННОТАЦИЯ
     Описан  интерпретатор  make,  используемый  для  сборки
пакетов программ, минимизирующий число вызовов транслятора.
1.  Введение
     Интерпретатор make  [1],  наиболее  часто  используемый
программистами  ДЕМОС,  предоставляет уникальные возможности
по управлению любыми видами работ  в  операционной  системе.
Всюду,  где имеется необходимость учитывать зависимости фай-
лов и времена их создания  (модификации),  make  оказывается
незаменимым  инструментом.  Интерпретатор реализует непроце-
дурный язык, который позволяет управлять группами  командных
строк  системы.  В основу такого управления положены зависи-
мости между файлами с исходными данными и файлами, в которых
содержатся результаты. При этом предполагается любой возмож-
ный список действий над исходными файлами: компиляция,  мак-
рообработка, редактирование, печать, упаковка или шифрование
и т.д.  Исходной  информацией  для  интерпретатора  является
Make-программа,  представлящая список определений макропере-
менных и список правил. Каждое правило включает формулировку
цели  и список действий для интерпретатора shell. При выпол-
нении Make-программы интерпретатор make использует  информа-
цию  о  связях  между  целями  и  результатами и передает на
выполнение shell списки действий, которые  в  данный  момент
необходимо  выполнить  для  получения  заданного результата.
Таким образом, интерпретатор make позволяет записывать любой
набор  действий  над  исходными  данными,  благодаря чему он
широко используется при решении прикладных  и  общесистемных
задач.   Очень  важно  и  то,  что Make-программа становится
общесистемным стандартным описанием структуры задачи,  алго-
ритма  сборки  и установки программного комплекса.  Програм-
мист, владеющий средствами интерпретатора  make,  использует
следующую   технологию  разработки  программного  комплекса,
независимо от его сложности:
    редактор -> make -> проверка -> редактор
При такой технологии существенно  повышается  производитель-
ность  труда программиста,так как он освобождается от ручной
сборки программ, сокращается загрузка ЭВМ - make "следит" за
тем,  чтобы при многократных компиляциях и отладках программ
"не делалолсь то, что можно не делать".
     Важно отметить, что make является средством автоматиза-
ции процедур установки компонент ДЕМОС. Например, компонента
системы cat может включать следующие файлы:
выполняемый код  - файл   /bin/cat
текст программы  - файл  ./src/cmd/cat/cat.c
документацию     - файл  ./man/man1/cat.1
программу сборки - файл  ./src/cmd/cat/Makefile
Файл ./src/cmd/cat/Makefile  содержит  всю  необходимую  для
правильной компиляции и установки в ОС компоненты cat инфор-
мацию.  Особенно эффективен  make  для  выполнения  работ  в
программных   проектах  малой  и  средней  (до  200  файлов)
                           - 3 -
величин.
2.  Принципы выполнения Make-программы
     Интерпретатор make выполняет программу,  которую  будем
называть Make-программой.  Make-программа содержит структуру
зависимостей файлов и действий над ними, оформленных в  виде
списка правил.  Выполнение действий приводит к созданию тре-
буемых файлов. Допустим, имеются файлы
    a  b  c  d  e  f
из которых определенным образом необходимо получить файл  E.
Пусть далее известно, что над различными комбинациями исход-
ных файлов  выполняются  некоторые  действия,  а  результаты
будут  размещены  в промежуточных файлах A, B, C и D.  Расс-
мотрим граф, узлы которого - имена файлов. Дуги графа  отра-
жают  зависимости файлов, стрелка указывает направление пре-
образований от исходных файлов к файлам, которые  необходимо
получить.
     В Make-программе каждой паре  (x,y)  инцидентных  узлов
этого  графа ставится в соответствие список действий, выпол-
нение которых приведет к созданию x. Когда  файл  x  сущест-
вует,  список  действий не выполняется, но только в том слу-
чае, если файл y создан (модифицирован) раньше  по  времени,
чем файл x.  Каждой дуге графа можно поставить в соответсвие
значение функции t(x,y). Функция t(x,y) возвращает результат
в виде
                                        /\
                      цель E           /  \
                           |          /----\
                           |            ||
                 t(E,D )   |    t(E,C)  ||
                ----------------------- ||
               |                      | ||
          цель D                 цель C ||
        t(D,A) | t(D,B)               | ||
       ----------------               | ||
       |              |               | ||
  цель A         цель B               | ||
       |              |         t(C,e)| ||
 t(A,a)| t(A,b) t(B,c)| t(B,d)  t(C,d)| ||
  ------------   ------------   ------- ||
 |            | |            | |      | ||
 |            | |            | |      | ||
 a            b c            d e      f ||
                           - 4 -
    x МОЛОЖЕ y - список действий не выполняется;
    x СТАРЕЕ y - список действий выполняется.
Множество значений функции t(x,y) образует структуру динами-
ческих  (зависящих от времени) связей между файлами. На этой
основе  интерпретатор  make  выделяет   те   разделы   Make-
программы, которые можно не выполнять.
     Выше предполагалось, что каждый узел графа - это  файл.
Существует  возможность  записать  в  Make-программe  список
действий, выполнение которых не связано с  созданием  файла.
Поэтому в общем случае узел графа правильнее называть целью.
Пара инцидентных узлов графа образует цель и подцель. В при-
мере узел E - цель, узлы D и C  - подцели цели E. Аналогично
узел D - цель, узлы A и B - подцели цели D. Наконец, узел  A
- цель, узлы a и b - подцели узла A.  Перечисление вида ЦЕЛЬ
- ПОДЦЕЛИ отражает обобщенную структуру алгоритма достижения
целей.
     Введем понятие реконструкция файла-цели. Если файл-цель
существует и "МОЛОЖЕ" всех файлов, от которых зависит, то он
остается без изменений, иначе, если файл-цель  существует  и
"СТАРЕЕ" какого-либо файла, от которого зависит, он реконст-
руируется (изготавливается заново).
     Приведем пример Make-программы, соответствующей  приве-
денному  выше  графу.  Программа  выглядит  как его линейная
запись:
     E : D C              # E зависит от D и C
             cat D C > E  # действие правила 1
     D : A B              # D зависит от A и B
             cat A B > D  # действие правила 2
     A : a b              # A зависит от a, b
             cat a b > A  # действие правила 3
     B : c d              # B зависит от c, d
             cat c d > B  # действие правила 4
     C : e f              # C зависит от e, f
             cat e f > C  # действие правила 5
     clean clear:
             -rm -f A B C D E
Здесь содержится 6 правил. Каждое  правило  включает  строку
зависимостей  файлов  и   командную строку системы.  Правило
описывается просто: сначала указывается имя  файла,  который
                           - 5 -
необходимо создать (цель), затем двоеточие, затем имена фай-
лов, от которых зависит создаваемый  файл  (подцели),  затем
строки  действий.   Первую  строку  правила назовем  строкой
зависимостей.  Следует обратить внимание на шестое  правило:
в нем пустой список подцелей в строке зависимостей.  Синтак-
сис строк действий соответствует синтаксису командных  строк
shell. Первым символом строки действия в Make-программе дол-
жен быть символ табуляции (или 8 пробелов) - это  обязатель-
ное условие.
     Все последовательности символов, начиная от символа # и
до конца строки, являются комментарием. Пустые строки и лиш-
ние пробелы игнорируются.  Если Make-программа  размещена  в
файле  Makefile,  то имя файла с Make-программой в командной
строке можно не указывать.  Допустим, файл с Make-программой
называется  Makefile, в рабочем каталоге имеются файлы a, b,
c и d. Файлы A, B, C и D отсутствуют, тогда  по команде make
мы получим результат - файл Е и файлы A, B, C и D.  Рассмот-
рим порядок выполнения Make-программы, когда эти  файлы  уже
существуют, т.е. при повторном выполнении команды make.
     Первый шаг выполнения Make-программы:
     E : D C
             cat D C > E
Если файлы D и C существуют и не требуется их реконструкция,
а файл E "МОЛОЖЕ", чем файлы D и C, то make прекратит выпол-
нение программы - файл Е готов.  Если требуется  реконструк-
ция файлов D и/или C, то осуществляется переход к выполнению
подцелей D и/или C, затем возврат к этому  правилу.   Иначе,
если  требуется реконструкция файла E, то выполняется дейст-
вие этого правила и make прекращает выполнение  программы  -
готов  реконструированный  файл  E.  Иначе, если отсутствуют
файлы D и/или C, будут выполнены подцели D  и/или  C  в  том
порядке,  в котором они указаны в списке зависимостей, затем
выполняется действие этого правила и make прекратит выполне-
ние программы - готов файл E.
      Второй шаг выполнения Make-программы:
     D : A B
             cat A B > D
Если файлы A, B и D существуют  и не требуется  их  реконст-
рукция,  то  выполняется  переход  к  следующему  шагу Make-
программы (файл D уже готов).  Если требуется  реконструкция
файлов A и/или B, происходит переход к выполнению подцелей A
и/или C, затем возврат к этому правилу.  Иначе, если  требу-
ется  реконструкция файла D, выполняется действие этого пра-
вила и переход к следующему шагу выполнения  Make-программы.
Иначе,  если  отсутствуют  файлы  A и/или B, будут выполнены
подцели A и/или B в том порядке, в  котором  они  указаны  в
                           - 6 -
списке  зависимостей, затем действие этого правила и переход
к выполнению первого правила Make-программы.
     Третий шаг выполнения Make-программы:
     A : a b
             cat a b  > A
Проверяется наличие файлов a и b в  рабочем  каталоге.   При
отсутствии  хотя бы одного из них выполнение программы прек-
ращается.  Затем проверяется наличие файла A, если  его  нет
или  требуется его реконструкция, выполняется действие этого
правила. Иначе осуществляется переход к  выполнению  второго
правила.
     Четвертый шаг выполнения Make-программы:
     B : c d
             cat c d  > B
Действия аналогичны описанным в третьем шаге,  переход  осу-
ществляется к выполнению второго правила.
     Пятый шаг выполнения Make-программ:
     C : e f
             cat e f > C
Проверяется наличие файлов e и f  в рабочем  каталоге.   При
отсутствии  хотя бы одного из них выполнение программы прек-
ращается.  Затем проверяется наличие файла C, если  его  нет
или  требуется его реконструкция, выполняется действие этого
правила. Иначе осуществляется переход к  выполнению  первого
правила.
     При вызове интерпретатора make в командной строке можно
указать  имя цели.  Если, например, необходимо получить файл
D, то командная строка выглядела бы так
       % make  D
или если необходимо получить  файлы  C  и  D,  то  командная
строка выглядела бы
       % make  C D
Таким образом, имя файла-цели в командной строке  определяет
вход в Make-программу. Если задано несколько входов, то make
выполнит в указанном порядке все необходимые  разделы  Make-
программы.  Если  же вход не указан, выполняется первое пра-
вило Make-программы.
                           - 7 -
     В шестом правиле примера цель не является  файлом,  это
важная  особенность  make. Программисту предоставляется воз-
можность записать правило, цель и/или  подцели  которого  не
являются  файлами.   В  таком  случае цель - это имя входа в
Make-программу (или метка правила).  Шестое правило  исполь-
зуется  в  программе  для  удаления файлов. Следует обратить
внимание на то, что в этом правиле два имени цели  (clean  и
clear), поэтому в командной строке можно указывать любое имя
входа, например:
            % make  clean
    или
            % make  clear
В результате выполнения будут удалены  файлы A, B, C, D и E.
     Все строки действий в правилах передаются на выполнение
shell следующим образом:
    sh -c строка_действия
и должны нормально выполняться интерпретатором sh (код возв-
рата  0),  иначе  (по  получении   другого  кода  завершения
командной  строки)  make  прекратит  выполнение   программы.
Существует  способ  обойти это условие. Обратите внимание на
действие в 6-м правиле: строка действий начинается с символа
"-",  что означает не прекращать работу при неудачном выпол-
нении команды rm.
     В Make-программе  можно  использовать  макропеременные.
Механизм  макроопределений  и  подстановок макропеременных в
Make-программе по смыслу аналогичен механизму подстановок  в
shell,  хотя по синтаксису несколько отличается.  Рассмотрим
приведенный выше пример  с  использованием  макропеременных.
Теперь Makefile будет выглядеть так:
                           - 8 -
     SRC1 = a b        # макроопределения
     SRC2 = c d
     SRC3 = e f
     SRC4 = A B C D
     E : D C
             cat D C > E
     D : A B
             cat A B > D
     A : $(SRC1)     # A зависит от SRC1
             cat $(SRC1) > A
     B : $(SRC2)     # B зависит от SRC2
             cat $(SRC2) > B
     C : $(SRC3)     # C зависит от SRC3
             cat $(SRC3) > C
     clean clear:
             -rm -f $(SRC4)
Первые строки Make-программы  - строки с макроопределениями,
где каждой переменной SRC присваиваются значения. В правилах
выполняется операция подстановки  значения  макропеременной,
например  $(SRC1).  Макропеременные позволяют манипулировать
списками имен файлов при  минимальных  изменениях  в  тексте
Make-программы.
     Интерпретатор make реализует механизм обработки умолча-
ний  зависимостей  файлов  со  стандартными суффиксами имен.
Например, файл с суффиксом имени .o можно получить из файлов
с  суффиксами  имен .c (язык программирования Си) и .s (язык
ассемблер).  Рассмотрим пример. Допустим, имеются файлы a.c,
b.c,  c.c  и d.s, образующие программу, которую назовем pro-
gram.  Файлы a.c, b.c и c.c содержат строку
    # include program.h
т.е. зависят от файла program.h. Make-программа для работы с
этими файлами будет содержать 3 строки
    program: a.o b.o c.o d.o
             cc a.o b.o c.o d.o -o program
    a.o b.o c.o: program.h
По команде make  будет  создан  файл  program.   При  первом
выполнении получим на экране дисплея:
                           - 9 -
      cc  -c a.c
      cc  -c b.c
      cc  -c c.c
      as - -o d.o d.s
      cc a.o b.o c.o d.o -o program
Обратите внимание на то, что интерпретатор определил необхо-
димые  действия над исходными файлами с суффиксами имен .c и
.s, хотя имена этих файлов в Make-программе  не  указаны,  и
правильно осуществил сборку программы.  Теперь, допустим, мы
вызываем make на выполнениe второй раз после  редактирования
файла program.h, при этом получим:
      cc  -c a.c
      cc  -c b.c
      cc  -c c.c
      cc a.o b.o c.o d.o -o program
Если выполнить  Make-программу  после  редактирования  файла
b.c, то получим:
      cc  -c b.c
      cc a.o b.o c.o d.o -o program
Наконец, если, допустим, необходимо получить  файл  c.o,  то
можно выполнить команду
    make c.o
Механизм умолчаний и обработки суффиксов  спроектирован  для
автоматизации программирования Make-программ; он существенно
сокращает размеры программ и количество ошибок в них.
3.  Соглашения языка Make
     Ниже в компактной форме приводятся  основные  синтакси-
ческие  конструкции  языка  Make.  В следующих параграфах по
каждой конструкции будут даны описания и примеры.
идентификатор
     последовательность букв, цифр и символа "_", содержащая
     шаблоны имен файлов ([...], ?, *), символы / и  "."
идентификатор = значение
     определение макропеременной.  В правой части могут быть
     макроподстановки,  а  также вся правая часть может быть
     пустой строкой.
     $(идентификатор)
     ${идентификатор}
     $символ
                           - 10 -
     подстановка значения макропеременной.  Специальное зна-
     чение символа $ можно отменить, указывая $$.
комментарий
     текст, следующий за символом # и до конца строки.  Спе-
     циальное  значение символа # отменяется, если он указан
     в кавычках.
обратная наклонная черта
     символ продолжения строки.  Специальное  значение  сим-
     вола \ отменяется, если он указан дважды.
пустая строка, пробелы
     пробелы служат разделителями слов в  командной  строке,
     лишние пробелы и табуляции игнорируются.  Пустая строка
     всюду игнорируется.
список_зависимостей
     правило в общем виде.  Список_действий может быть  пус-
     тым.
 .первый_суффикс.второй_суффикс:
     правило с указанием  зависимостей  суффиксов.   Если  в
     Make-программе  содержится  хотя бы одно правило с суф-
     фиксами, отличными от предопределенных, в  нее  необхо-
     димо включить правило:
          .SUFFIXES: список_суффиксов
список_целей :[:] список_подцелей
     список_зависимостей.  Список_подцелей может  быть  пус-
     тым.   Правила  с  одним  и двумя символами двоеточия в
     списке_зависимостей  отличаются   порядком   выполнения
     списка_подцелей и списка_действий.
имя_цели [имя_цели]
     список_целей.  Имя_цели - идентификатор. Можно  исполь-
     зовать  символ  /  и точку.  Имя_цели может быть именем
     файла или каталога, тогда включается  выполнение  меха-
     низма  реконструкции.   Имя_цели  может  не быть именем
     файла, тогда механизм реконструкции  не  включается,  а
     имя_цели  является  меткой  (именем  правила,  входа  в
     Make-программу).
имя_подцели [имя_подцели] [#комментарий]
     список_подцелей.  Имя_подцели  -  идентификатор.  Можно
                           - 11 -
     использовать  шаблоны имен файлов "*", "?", [...], сим-
     вол / и точку.  Имя_подцели может  быть  именем  файла,
     для  которого  записано или не записано правило в Make-
     программе, в этих случаях включается механизм  реконст-
     рукции.  Имя_подцели может не быть именем файла, в этом
     случае механизм реконструкции не включается, а имя_цели
     является   меткой   (именем   правила,  входа  в  Make-
     программу).
строка_действия [#комментарий]
 ...............
строка_действия [#комментарий]
     список_действий. Любые командные строки ДЕМОС и  управ-
     ляющие конструкции Shell.
'табуляция'командная_строка_shell
      строка_действия.
     Строка_действия может быть указана в строке  зависимос-
     тей через символ ";". Строку_действия можно указывать в
     следующих форматах:
          'табуляция'командная__строка__shell    1
          'табуляция'@командная_строка__shell    2
          'табуляция'-командная_строка__shell    3
          'табуляция'-@командная_строка_shell    4
     В первом формате командная строка выводится на  печать;
     если код возврата после ее  выполнения не 0, make прек-
     ращает выполнение программы по ошибке.  Во втором  фор-
     мате  командная строка не выводится на печать; если код
     возврата после ее  выполнения  не  0,  make  прекращает
     выполнение  программы  по  ошибке.   В  третьем формате
     командная строка выводится на печать; если код возврата
     после  ее   выполнения  не  0, make игнорирует ошибку и
     выполнение программы продолжается.  В четвертом формате
     командная строка не выводится на печать; если код возв-
     рата после ее  выполнения не 0, make игнорирует  ошибку
     и выполнение программы продолжается.  Простая командная
     строка (одна команда ДЕМОС с  аргументами)  выполняется
     без   порождения  оболочки.   Другие  командные  строки
     выполняются    sh    следующим    образом:    sh     -c
     командная_строка_shell
     Для сокращения обозначений предусмотрены  макроперемен-
ные  с  предопределенными  именами.  В правиле без суффиксов
                           - 12 -
строка_действия может  включать  следующие  предопределенные
макропеременные:
@    имя цели;
?    имена  файлов  из  списка  подцелей,   которые   МОЛОЖЕ
     файла_цели.  Эти файлы участвуют в реконструкции цели.
     В правиле с суффиксами строка_действия  может  включать
следующие предопределенные макропеременные:
*    основа_имени_цели;
@    основа_имени_цели.второй_суффикс;
<<    основа_имени_цели.первый_суффикс.
4.  Использование макропеременных
     При выполнении Make-программы значения  макропеременных
устанавливаются в строках макроопределений и/или в командной
строке при запуске make на исполнение.  Кроме того,  сущест-
вуют  макропеременные  с предопределенными именами, значения
которых устанавливаются при выполнении Make-программы. К ним
относятся: макропеременная @, ее значение - имя_цели; макро-
переменная ?, ее значение - имена тех файлов_подцелей, кото-
рые МОЛОЖЕ файла_цели.
     Предопределенные макропеременные "@" и "?" используются
только  в  списке  действий правила и в каждом правиле имеют
свои значения.  Определять значения макропеременных можно  в
любом месте Make-программы, но не внутри правила.  Интерпре-
татор make составляет список имен макропеременных и присваи-
вает им значения в процессе чтения Make-программы. Если зна-
чение макропеременной переопределяется, то ей  присваивается
новое значение; если используются макроопределения с вложен-
ными макроподстановками, то значения устанавливаются с  уче-
том  всех присвоений в Make-программе.  Если значение макро-
переменной задается в командной  строке  при  запуске  Make-
программы  на исполнение, то все определения этой макропере-
менной в Make-программе игнорируются и  используется  значе-
ние,  взятое из командной строки.  Состояние ошибки порожда-
ется, если используется  рекурсия  при  присвоении  значения
макропеременным. Например,
     A = $B
     B = $A
приведет к аварийному завершению выполнения Make-программы.
     В макроопределениях можно использовать метасимволы шаб-
лонов  имен  файлов  shell.  Допустим, рабочий каталог имеет
                           - 13 -
вид:
     -rw-r--r-- 1 user    15658 Авг  6 16:03 1.m
     -rw-r--r-- 1 user     2158 Авг  8 16:38 2.m
     -rw-r--r-- 1 user     5185 Авг  9 17:38 3.m
     -rw-r--r-- 1 user     4068 Июл 28 20:56 6.m
     -rw-r--r-- 1 user      100 Авг  9 14:30 f2
     -rw-r--r-- 1 user       66 Авг  9 17:42 f3
Пример Make-программы в Makefile :
     A = *
     B = f?
     C = [0-9].*
     aaa :
             echo $A
             echo $B
             echo $C
После выполнения команды  make -s получим на экране дисплея:
     1.m 2.m 3.m 6.m f2 f3
     f2 f3
     1.m 2.m 3.m 6.m
В Make-программе часто бывает необходимо манипулировать име-
нами  каталогов  и файлов. Механизм макроподстановок предла-
гает удобные средства для этого. Пример:
     A = *
     B = [pg]*
     C = f?r*
     DIR1 = .
     DIR2 = /etc
     DIR3 = /usr/bin
     aaa :
             echo ${DIR1}/$A
             echo ${DIR2}/$B
             echo ${DIR3}/$C
После выполнения получим:
     ./1.m ./2.m ./3.m ./6.m ./f2 ./f3
     /etc/getty /etc/group /etc/passwd
     /usr/bin/fgrep
     Рассмотрим пример, в котором демонстрируются всевозмож-
ные способы использования макропеременных. Допустим, имеется
Makefile
                           - 14 -
    БИБЛИОТЕКА = ПОЛКА ${ДРУГОЕ}
    ДРУГОЕ = Документы
    Шкаф = ПОЛКА
    папка = справки, копии.
    СПРАВОЧНИКИ =
    ЖУРНАЛЫ =
    Словари = толковые, иностранных языков.
    ТЕХНИЧЕСКАЯ = $(СПРАВОЧНИКИ) $(Словари)
    ХУДОЖЕСТВЕННАЯ = проза, поэзия, драматургия.
    t = Справка с места жительства.
    x = Копия свидетельства о рождении.
    файлы = d e
    библиотека :  ${БИБЛИОТЕКА}
            echo 'Действия правила' $@
            echo '$$? - список подцелей :' $?
            echo '$$@ - имя цели :' $@
            echo 'Техническая : ' $(ТЕХНИЧЕСКАЯ)
            echo 'Худ. : ' $(ХУДОЖЕСТВЕННАЯ)
            echo ' '
    ${Шкаф} : b c
            echo 'Действия правила' $@
            echo '$$? - список подцелей :' $?
            echo '$$@ - имя цели :' $@
            echo ' '
     ${ДРУГОЕ}: ${файлы}
            echo 'Действия правила' $@
            echo '$$? - список подцелей :' $?
            echo '$$@ - имя цели :' $@
            echo 'Папка : ' ${папка}
            echo $t
            echo $x
            echo ' '
     b:
     c:
     d:
     e:
Следует обратить внимание на то, что $буква используется для
подстановки значения макропеременной, имя которой состоит из
одной буквы, а $(идентификатор)  и  ${идентификатор}  равно-
ценны.  Правила  b,  c,  d  и  e  включены исключительно для
демонстрации  значений  макропеременной  "?".  Теперь,  если
выполнить команду make -s, получим на экране дисплея:
                           - 15 -
     Действия правила ПОЛКА
     $? - список подцелей : b c
     $@ - имя цели : ПОЛКА
     Действия правила Документы
     $? - список подцелей : d e
     $@ - имя цели : Документы
     Папка :  справки, копии.
     Справка с места жительства.
     Копия свидетельства о рождении.
     Действия правила библиотека
     $? - список подцелей : ПОЛКА Документы
     $@ - имя цели : библиотека
     Техническая :толковые, иностранных языков.
     Худ. : проза, поэзия, драматургия.
В командной строке можно присвоить значение макропеременной.
После команды
    make -s Словари = английский, немецкий.
получим на экране дисплея:
     Действия правила ПОЛКА
     $? - список подцелей : b c
     $@ - имя цели : ПОЛКА
     Действия правила Документы
     $? - список подцелей : d e
     $@ - имя цели : Документы
     Папка :  справки, копии.
     Справка с места жительства.
     Копия свидетельства о рождении.
     Действия правила библиотека
     $? - список подцелей : ПОЛКА Документы
     $@ - имя цели : библиотека
     Техническая :  английский, немецкий.
     Худ. :  проза, поэзия, драматургия.
5.  Выполнение правил в Make-программе
     Существует несколько разновидностей правил:
     с одним и двумя двоеточиями;
     с одинаковыми именами целей;
     не содержащие списка действий;
                           - 16 -
     не содержащие списка подцелей и/или списка действий;
     правила, предопределенные в интерпретаторе make,  кото-
     рые программист может включать в Make-программу.
     Кроме того, имеются  некоторые  различия  в  выполнении
Make-программы,  когда имя цели не является файлом.  В общем
случае правило может содержать  одно  или  два  двоеточия  в
качестве  разделителей  списков  целей  и  подцелей. Порядок
выполнения в этих правилах одинаков, если в них указаны раз-
личные имена целей.
     Правило выполняется следующим образом: сначала выполня-
ются  правила с именами целей из списка подцелей, затем спи-
сок действий.  Если в списке подцелей указано имя файла, для
которого правило не определено, то время создания (модифика-
ции)  файла  используется  для   определения   необходимости
реконструкции  цели.   Если  имя подцели не является файлом,
оно должно быть меткой правила, иначе порождается  состояние
ошибки.  Допустим, в Make-программе записано правило
    monitor.c : monitor.h
Это означает, что файл monitor.c зависит от файла monitor.h.
Если  monitor.h действительно имеется в рабочем каталоге, то
любые  изменения  в  нем  приведут  к  реконструкции   файла
monitor.o, если файл monitor.h отстутствует, make прекращает
выполнять  программу  по  ошибке.   В  Make-программе  могут
встречаться случаи, когда необходимо для одной цели записать
несколько правил, тогда существенно важно, сколько двоеточий
указано  в  правиле. Ниже приведены схемы, в которых цифрами
показан порядок выполнения этих правил.
     Порядок  выполнения  нескольких  правил  с   одинаковым
именем_цели и одним двоеточием: сначала выполняются правила,
связанные со списками подцелей, затем список_действий одного
из  правил.   Список действий разрешается указывать только в
одном из таких правил:
    имя_цели_А : список_подцелей    <---| 1 |
    имя_цели_А : список_подцелей    <---| 2 |
                 список_действий    <---| 4 |
    имя_цели_А : список_подцелей    <---| 3 |
     Порядок  выполнения  нескольких  правил  с   одинаковым
именем_цели  и двумя двоеточиями. Список действий может быть
в каждом правиле:
                           - 17 -
    имя_цели_А :: список_подцелей   <---| 1 |
                  список_действий   <---| 2 |
    имя_цели_А :: список_подцелей   <---| 3 |
                  список_действий   <---| 4 |
    имя_цели_А :: список_подцелей   <---| 5 |
                  список_действий   <---| 6 |
6.  Режимы выполнения Make-программы
     Интерпретатор make предоставляет ряд  возможностей  для
управления выполнением Make-программы. Для этой цели исполь-
зуются следующие директивы:
     .SILENT     - не печатать строки действий;
     .IGNORE     - игнорировать ошибки действий;
     .DEFAULT    - выполнить альтернативное действие;
     .PRECIOUS   - удалить недостроенный файл.
Директивы .SILENT и .IGNORE  можно  указывать  как  в  Make-
программе, так и в командной строке ключами -s и -i, DEFAULT
и PRECIOUS только в Make-программе.  Допустим имеется следу-
ющая Make-программа:
    aa:     bb
            echo Действия правила $@
    bb:
            echo Действия правила $@
            ppp     # Несуществующая команда
            sort e  # Нет файла e
    После выполнения получим сообщения:
     echo Действия правила bb
     Действия правила bb
     ppp    # Несуществующая команда
     sh: ppp: не найден
     *** Код ошибки 1
     Конец.
Интерпретатор make прекратил работу по первой ошибке.  Кроме
того,  на  экран дисплея выводятся строки действий. Отменить
вывод строк действий можно следующими способами:
     указать ключ -s в командной строке при запуске make;
     указать в Make-программе директиву SILENT;
                           - 18 -
     начинать каждую строку действий символом @.
     В первых двух случаях результат  одинаков  -  не  будут
выводиться  строки действий. В третьем случае не будут выво-
диться строки действий вида
     'табуляция'@строка_действий
Пример использования директивы SILENT.
    aa: bb
           echo Действия правила $@
    bb:
           echo Действия правила $@
           ppp    # Несуществующая команда
           sort e # Нет файла e
     .SILENT:
    После выполнения программы получим:
     Действия правила bb
     sh: ppp: не найден
     *** Код ошибки 1
     Конец.
По любой ошибке make прекратит выполнение программы. Сущест-
вуют  следующие способы игнорирования ошибок (Make-программа
продолжает выполняться):
     указать ключ -i при запуске make на выполнение;
     указать в Make-программе директиву IGNORE;
     после символа табуляция указать символ "-".
     В первом и втором  случаях  будут  проигнорированы  все
ошибки при выполнении строк действий, в третьем игнорируются
ошибки в тех строках  действия,  которые  указаны  следующим
образом:
     'табуляция'-строка_действий
Пример использования  директив SILENT  и  IGNORE.  Обработка
ошибок при выполнении действий в правилах
                           - 19 -
    aa: bb
           echo Действия правила $@
    bb:
           echo Действия правила $@
           ppp    # Несуществующая команда
           sort e # Нет файла e
     .SILENT:
     .IGNORE:
    После выполнения программы получим:
     Действия правила bb
     sh: ppp: не найден
     *** Код ошибки 1 (игнорирован)
     sort: не могу открыть e
     *** Код ошибки 1 (игнорирован)
     Действия правила aa
Правило DEFAULT  используется  для  указания  альтернативных
действий  по  отсутствующему в данный момент файлу.  Правило
DEFAULT позволяет записать список  действий,  которые  будут
выполняться для всех отсутствующих файлов, поэтому требуется
определенная осторожность, например:
                           - 20 -
    aa: bb
           echo Действия правила $@
    bb:  a b c d
           echo Действия правила $@
           ppp    # Несуществующая команда
           sort e # Нет файла e
     .SILENT:
     .IGNORE:
     .DEFAULT:
           echo Действия правила .DEFAULT для $@
    После выполнения программы получим:
     Действия правила .DEFAULT для a
     Действия правила .DEFAULT для b
     Действия правила .DEFAULT для c
     Действия правила .DEFAULT для d
     Действия правила bb
     sh: ppp: не найден
     *** Код ошибки 1 (игнорирован)
     sort: не могу открыть e
     *** Код ошибки 1 (игнорирован)
     Действия правила aa
Часто  бывает   необходимо   прекратить   выполнение   Make-
программы.   Это  не  приведет к фатальным последствиям, так
как сохраняется структура динамических  (зависящих  от  вре-
мени) связей файлов.  Однако, если создание некоторого файла
не завершилось и он тем не менее образовался, его желательно
удалить  перед  повторным запуском интерпретатора. Это можно
сделать автоматически, используя директиву PRECIOUS,  напри-
мер:
    aaa:   file
           sort file > $@
     .PRECIOUS:
Если в момент синхронного исполнения  Make-программы  ввести
сигнал CTRL/C, файл $@ будет удален.
7.  Правила с суффиксами
     В Make-программе можно записать одно правило для  обра-
ботки  различных файлов. В этом случае это одно правило мно-
гократно выполняется для различных файлов,  что  существенно
сокращает размеры Make-программ, упрощает их разработку. Все
полезные  свойства  make  при  этом  сохраняются.   Механизм
                           - 21 -
выполнения  таких  правил строится на суффиксах имен файлов.
Допустим, из файла  с  именем   основа.суффикс_1  необходимо
получить  файл с именем основа.суффикс_2, тогда обычное пра-
вило будет выглядеть так:
    основа.суффикс_2 : основа.суффикс_1
                       список действий
Понятно, что для группы файлов,  основы  имен  которых  раз-
личны, а первый и второй суффиксы имен одинаковы, желательно
было бы записать одно правило обработки. Например, файлы *.c
обычно  преобразуются в файлы *.o одним списком действий, и,
следовательно, эти  правила  желательно  записывать  в  виде
одного  правила.  Интерпретатор  make предлагает эту возмож-
ность в правилах вида
     .суффикс_1.суффикс_2:
             список_действий
     .SUFFIXES: .суффикс_1 .суффикс_2
Если в Make-программе  записаны эти правила,  интерпретатор,
получив   в   качестве   аргумента   имя   файла   с  именем
основа.суффикс_1, выполнит  указанное  выше  правило,  и  мы
получим  результат -  файл с именем основа.суффикс_2.  Поря-
док выполнения правила с суффиксами такой же, как и в  обыч-
ном правиле.  Предопределенное правило SUFFIXES используется
для указания списка суффиксов, который может содержать более
двух  суффиксов.  Порядок суффиксов в списке роли не играет.
Естественно, для  каждой  пары  суффиксов  в  Make-программе
должны  быть записаны соответствующие правила.  В правилах с
суффиксами используются предопределенные макропеременные:
@    - имя_результатa (основа.суффикс_2);
<<    - имя_аргументa (основа.суффикс_1);
*    - основа.
     Рассмотрим пример. Допустим, имеются исходные файлы *.c
и  *.k.   Необходимо  из  них получить файлы *.t, а из них -
файл result.  Допустим также, что файлы *.c зависят от файла
file.h (содержат строку #include file.h).  Граф преобразова-
ний файлов в этом случае выглядит так:
                           - 22 -
                         result
                           |
                          .t
                           |
                        --------
                        |      |
                       .c     .k
Программа, реализующая этот граф, может быть следующей:
                           - 23 -
    result: d.t a.t b.t c.t
           echo 'Действия правила' $@
           echo ' Значение $$@ - ' $@
           echo ' Значение $$? - ' $?
           touch $@
     .k.t :
           echo 'Действия правила .k.t :'
           echo ' Значение $$* - ' $*
           echo ' Значение $$@ - ' $@
           echo ' Значение $$? - ' $?
           echo ' Значение $$< - ' $<
           touch $@
     .c.t :
           echo 'Действия правила .c.t :'
           echo ' Значение $$* - ' $*
           echo ' Значение $$@ - ' $@
           echo ' Значение $$? - ' $?
           echo ' Значение $$< - ' $<
           touch $@
    a.c b.c c.c : file.h
     .SUFFIXES: .t .c .k
    После выполнения команды make -rs получим:
      Действия правила .k.t :
       Значение $* -  d
       Значение $@ -  d.t
       Значение $? -  d.k
       Значение $< -  d.k
      Действия правила .c.t :
       Значение $* -  a
       Значение $@ -  a.t
       Значение $? -  file.h a.c
       Значение $< -  a.c
      Действия правила .c.t :
       Значение $* -  b
       Значение $@ -  b.t
       Значение $? -  file.h b.c
       Значение $< -  b.c
      Действия правила .c.t :
       Значение $* -  c
       Значение $@ -  c.t
       Значение $? -  file.h c.c
       Значение $< -  c.c
      Действия правила result
                           - 24 -
       Значение $@ -  result
       Значение $? -  d.t a.t b.t c.t
Заметим, что правило .k.t выполняется только один раз,  пра-
вило  .c.t - три раза, как и требовалось для списка исходных
файлов, указанных в списке подцелей  правила  result.  Смысл
макропеременной  "?"  в  правиле с суффиксом тот же, что и в
обычном файле - список подцелей. Макропеременная "@" в обыч-
ном правиле - имя цели, а здесь имя результата - файл с име-
нем основа.суффикс_2.
     Интерпретатор make содержит список правил для стандарт-
ных  суффиксов  имен  файлов  и определения самих суффиксов.
Этот список подключается к Make-программе пользователя, если
make  запускается  на  выполнение без ключа -r.  Программист
полностью освобождается от  указания  правил  преобразований
файлов  со  стандартными  суффиксами  имен.   Обрабатываются
файлы, имена которых включают следующие суффиксы:
     .out   -    файл в загрузочном формате;
     .o     -    объектный файл;
     .c     -    файл на языке Си;
     .f     -    файл на языке Фортран;
     .p     -    файл на языке Паскаль;
     .s     -    файл на ассемблере;
     .l     -    файл на lex;
     .y     -    файл на yacc;
Кроме того,  в  Make-программу  включаются  предопределенные
макропеременные:
LOADLIBES =   # имена библиотек
AS = as -     # имя ассемблера
CC = cc       # имя Си-компилятора
CFLAGS =      # ключи Си-компилятора
PC = pc       # имя Паскаль-компилятора
PFLAGS =      # ключи Паскаль-компилятора
FF = f77      # имя f77-компилятора
FFLAGS =      # ключи f77-компилятора
LEX = lex     # имя генератора
LFLAGS =      # ключи lex
YACC = yacc   # имя генератора
YFLAGS =      # ключи yacc
Значения предопределенных  макропеременных  можно  менять  в
Make-программе,  например,  если ввести строку  CFLAGS = -O,
то предопределенная макропеременная CFLAGS будет иметь новое
значение  в Make-программe.  Ниже приводятся граф зависимос-
тей целей и список правил Make-программы, реализующей  обра-
ботку файлов по суффиксам имен.
                           - 25 -
             .c   .f   .p   .s   .l   .y
              |    |    |    |    |    |
              |-------------------------
              |
              |
              --->  .out    <--- .o  ---
                                       |
                                       |
              --------------------------
             |    |    |    |    |    |
            .p   .f   .c   .s   .l   .y
                       |
                      ---
                     |   |
                    .l  .y
                           - 26 -
 .l.out:
       $(LEX) $<
       $(CC) $(CFLAGS) lex.yy.c $(LOADLIBES) -ll -o $@
       rm lex.yy.c
 .y.out:
       $(YACC) $(YFLAGS) $<
       $(CC) $(CFLAGS) y.tab.c  $(LOADLIBES) -ly -o $@
       rm y.tab.c
 .f.out:
       $(FF) $(FFLAGS) $< $(LOADLIBES) -o $@
       -rm $*.o
 .o.out:
       $(CC) $(CFLAGS) $< $(LOADLIBES) -o $@
 .c.out:
       $(CC) $(CFLAGS) $< $(LOADLIBES) -o $@
 .p.out:
       $(PC) $(PFLAGS) $< $(LOADLIBES) -o $@
 .s.out:
       $(CC) $(CFLAGS) $< $(LOADLIBES) -o $@
 .l.c:
       $(LEX) $<
       mv lex.yy.c $@
 .y.c:
       $(YACC) $(YFLAGS) $<
       mv y.tab.c $@
 .l.o:
       $(LEX) $(LFLAGS) $<
       $(CC) $(CFLAGS) -c lex.yy.c
       rm lex.yy.c; mv lex.yy.o $@
 .y.o:
       $(YACC) $(YFLAGS) $<
       $(CC) $(CFLAGS) -c y.tab.c
       rm y.tab.c; mv y.tab.o $@
 .s.o:
       $(AS) -o $@ $<
 .f.o:
       $(FF) $(FFLAGS) -c $<
 .c.o:
       $(CC) $(CFLAGS) -c $<
                           - 27 -
 .p.o:
       $(PC) $(PFLAGS) -c $<
 .SUFFIXES: .out .o .c .f .p .y .l .s
Допустим, имеются файлы f[1-6].c с исходными текстами  прог-
раммы result. Файлы f[1-3].c содержат строки
    # include file1.h
    # include file2.h
    и файлы f[4-6].c содержат строку
    # include file3.h
Следующая программа управляет созданием программы result:
 CFLAGS = -O
 LDFLAGS = -s -n -o
 OBJS = f1.o f2.o f3.o f4.o f5.o f6.o
 result:  ${OBJS}
        ${CC} ${OBJS} ${LDFLAGS} $@
 f1.o f2.o f3.o : file1.h  file2.h
 f4.o f5.o f6.o : file3.h
Так выглядит протокол выполнения:
 cc -O -c f1.c
 cc -O -c f2.c
 cc -O -c f3.c
 cc -O -c f4.c
 cc -O -c f5.c
 cc -O -c f6.c
 cc f1.o f2.o f3.o f4.o f5.o f6.o -s -n -o result
Теперь изменим время модификации файла командой
    touch file3.h
и снова выполним программу, в результате получим:
     cc -O -c f4.c
     cc -O -c f5.c
     cc -O -c f6.c
     cc f1.o f2.o f3.o f4.o f5.o f6.o -s -n -o result
Теперь изменим время модификации файла командой touch f3.c и
снова выполним программу
                           - 28 -
     cc -O -c f3.c
     cc f1.o f2.o f3.o f4.o f5.o f6.o -s -n -o result
Программист может отключить стандартные определения и список
правил  с суффиксами (ключ -r), может их использовать наряду
с теми, что определены в Make-программе. Допустим,  к  имею-
щимся  файлам  предыдущего  примера  добавляются файлы a1.t,
a2.t и a3.t, их необходимо обработать программой sed,  выхо-
дом  которой  будут  файлы  a1.c  a2.c  a3.c.   Теперь Make-
программа выглядит так:
     CFLAGS = -O
     LDFLAGS = -o
     OBJS = a1.o a2.o a3.o f1.o f2.o f3.o f4.o f5.o f6.o
     result:  ${OBJS}
            ${CC} ${OBJS} ${LDFLAGS} $@
     f1.o f2.o f3.o : file1.h  file2.h
     f4.o f5.o f6.o : file3.h
     a1.c: a1.t
     a2.c: a2.t
     a3.c: a3.t
     .t.c:
           sed s/aaa/bbb/ < $< > $*.c
     .SUFFIXES: .t
Протокол выполнения программы:
 sed s/aaa/bbb/ < a1.t > a1.c
 cc -O -c a1.c
 sed s/aaa/bbb/ < a2.t > a2.c
 cc -O -c a2.c
 sed s/aaa/bbb/ < a3.t > a3.c
 cc -O -c a3.c
 cc -O -c f1.c
 cc -O -c f2.c
 cc -O -c f3.c
 cc -O -c f4.c
 cc -O -c f5.c
 cc -O -c f6.c
 cc a1.o a2.o a3.o f1.o f2.o f3.o f4.o f5.o f6.o -o result
Make допускает обработку суффиксов только одного уровня вло-
женности.  В  правиле SUFFIXES указан только суффикс t, суф-
фикс c определен в подключаемом списке суффиксов.
                           - 29 -
8.  Управление архивом в Make-программе
     В ДЕМОС существуют два типа  архивов:  архив  текстовых
файлов  и  архив объектных файлов (имеющий структуру библио-
теки объектных модулей).   Созданный  архив  является  одним
файлом,  а  файлы, из которых он собран, образуют его части.
Управление   архивом   включает   две    задачи:    создание
файла_архива   определенной   структуры  и  его  модификация
(добавление, удаление и замена частей, изменение  структуры,
декомпозиция  архива на файлы, из которых он был образован).
Для решения таких задач Make-программа  должна  обеспечивать
работу  с целями двух типов: файл_архив и файл для включения
в архив.  При этом оценивается время  последней  модификации
включаемого  в  архив  файла (а не время создания архива или
записи файла_части в архив).  Имя файла_архива может  указы-
ваться в списке зависимостей правил как обычная цель:
    имя_архивного_файла(имя_файла_для_включения_в_архив)
Кроме имен файлов, при работе с библиотекой объектных  моду-
лей можно указывать имена функций
    имя_файла_библиотеки((_внешнее_имя_библ_функции))
Рассмотрим фрагмент Make-программы для построения библиотеки
с именем libP.a:
    L      =   libP.a
    CFLAGS =  -O
    $(L)::
           ar r $(L)
    $(L):: $(L)(Ia.o) $(L)(La.o) $(L)(Da.o)
    # Iabs, Labs, Dabs - имена функций
    $(L)(Iabs.o):     lib/Ia.c
           $(CC) $(CFLAGS) lib/Ia.c
           ar r $(L) Ia.o
           -rm -f Ia.o
    $(L)(Labs.o):     lib/La.c
           $(CC) $(CFLAGS) lib/La.c
           ar r $(L) La.o
           -rm -f La.o
    $(L)(Dabs.o):     lib/Da.c
           $(CC) $(CFLAGS) lib/Da.c
           ar r $(L) Da.o
           -rm -f Da.o
У такого способа работы  с  библиотекой  есть  недостаток  -
                           - 30 -
Make-программа должна содержать структуру библиотеки. Список
имен библиотечных модулей во втором правиле  "$(L)::"  соот-
ветствует порядку их размещения в библиотеке.  Еще одно неу-
добство заключается в том, что если бы библиотека  содержала
большое  количество  модулей, например 100, то потребовалась
бы запись 100 правил в программе.  Для  построения  однопро-
ходных  библиотек  такой  способ  указания  структуры  имеет
существенный недостаток, так как изменение исходного  текста
объектного  модуля  может  привести к необходимости изменить
структуру библиотеки, а это,  в  свою  очередь,  приведет  к
необходимости  реконструировать Make-программу.  Кроме того,
при построении библиотеки необходимо,  чтобы  все  объектные
файлы были в рабочем каталоге.
     Рассмотрим Make-программу,  в  которой  эти  недостатки
устранены.   Возьмем  в  качестве  примера файлы с исходными
текстами модулей объектной библиотеки mylib
    m1.c m2.c m3.c m4.s m5.c m6.c m7.s
Допустим, структура однопроходной библиотеки с учетом  вызо-
вов модулей должна быть такой:
    m4.o m2.o m1.o m5.o m6.o m7.o m3.o
Текст Make-программы:
                           - 31 -
    CFLAGS = -O
    SRC    =  m1.c m2.c m3.c m4.s m5.c m6.c m7.s
    LIB    =  mylib
    ${LIB}:  ${SRC}
         echo $? | sed s/\\.[cs]/\\.o/g > list
         make `cat list`
         ar cr $@ `cat list`
         lorder $@ | tsort > list
        -@if cmp list topology ; \
         then \
             rm -f `cat list` list;\
         else \
             ar x ${LIB}; rm $@;\
             mv list topology;\
             ar cr $@ `cat topology`;\
             rm -f `cat topology`;\
             echo Структура $@ изменилась.;\
         fi
         ranlib ${LIB}
         echo Библиотека $@ готова.
    m1.c : x.h
            touch m1.c
    m2.c : x.h y.h
            touch m2.c
    m3.c : x.h
            touch m3.c
    m5.c : y.h
            touch m5.c
    m6.c : x.h
            touch m6.c
Рассмотрим простые  случаи  реконструкции  уже  существующей
библиотеки.   Допустим,  изменился  исходный текст одного из
модулей. В этом случае достаточно на его место в  библиотеке
включить  новую версию объeктного файла этого модуля.  Чтобы
не компилировать другие файлы библиотеки, можно использовать
предопределенную  макропеременную "?" - список файлов, кото-
рые стали МОЛОЖЕ файла mylib. Как  уже  говорилось  выше,  в
каталоге   нет  объектных  файлов.  Следовательно,  в  Make-
программу  необходимо  включить  предопределенные   средства
обработки  суффиксов, а в качестве аргумента формировать имя
цели с суффиксом o,  тогда make автоматически построит новый
объектный  файл  этого модуля. В примере файл list использу-
ется в качестве временного рабочего файла. Строка вида
    echo $? | sed s/\\.[cs]/\\.o/g > list
                           - 32 -
записывает  в файл list список целей с суффиксом o. Редактор
sed  используется в этой командной строке для замены суффик-
сов c и s на o.  Таким образом,  файл  list  содержит  имена
файлов-целей,  которые  необходимо  создать и на этой основе
реконструировать библиотеку. Это  можно  сделать,  например,
так:
    make `cat list`
В  результате  выполнения  будут  созданы  нужные  объектные
файлы.   После этого можно включить объектные модули на свои
места в библиотеке mylib
    ar rc mylib `cat list`
Если же библиотека mylib отсутствует,то она создается,  если
модуль  в библиотеке исходно отсутствовал, он записывается в
конец библиотеки (так работает команда ar с ключами cr).
     По команде "make `cat list`" выполняться будет Makefile
(здесь  срабатывает  механизм  умолчания имени файла с Make-
программой). Таким образом, имеет  место  рекурсивный  вызов
Make-программы.   При  рекурсивном  вызове  в  качестве имен
целей  передаются имена объектных файлов, которые необходимо
получить и включить в библиотеку.  В интерпретаторе make нет
средств, позволяющих менять список целей в процессе выполне-
ния Make-программы.  Когда это необходимо, список целей соз-
дается в Make-программе и передается на выполнение как  спи-
сок  аргументов при вызове подпрограммы.  Рекурсия в примере
понадобилась для того, чтобы  не  записывать  полный  список
правил  для  всех  файлов-целей, а вызвать make с актуальным
списком аргументов.
     Возможна такая реконструкция библиотеки, когда меняется
ее  структура.   Для  этого  в  Make-программе  используются
команды lorder и tsort.  Lorder выводит список вызовов моду-
лей  существующей  библиотеки, а tsort сортирует этот список
таким образом, чтобы структура библиотеки была  непротиворе-
чивой,  т.е.  однопроходный  редактор  связей мог бы за одно
чтение библиотечного файла найти все необходимые модули.   В
Make-программу   включаются  действия,  в  которых  строится
структура библиотеки во временном файле list и  запоминается
в  сохраняемом  файле  topology.  Возможны  следующие случаи
реконструкции библиотеки:
     Реконструкция библиотеки не изменила  ее  структуры,  в
этом случае файл topology не отличается от файла list.
     Реконструкция изменила  структуру  библиотеки.  В  этом
случае  файл  topology отличается от файла list и требуется,
во-первых, получить верную структуру и запомнить ее в  файле
topology,  во-вторых,  извлечь  из  библиотеки все объектные
модули (разобрать библиотеку и удалить файл с  библиотекой),
                           - 33 -
затем  собрать ее в соответствии с новой структурой.  Разоб-
рать библиотеку можно командой
    ar x mylib
В рабочем каталоге будут созданы копии объектных модулей  из
библиотечного файла. Библиотечный файл при этом сохраняется.
Разобранную  библиотеку  можно  собрать  заново,   используя
информацию   о  структуре  библиотеки  в  файле  topology  и
команду:
    ar rc mylib `cat topology`
В shell допускается  записать  if  одной  строкой  следующим
образом:
    if     список_команд;\
    then \
           список_команд;\
    else \
           список_команд;\
    fi
В Makefile эта конструкция записана:
    -@if cmp list topology ; \
      then \
          rm -f `cat list` list;\
      else \
          ar x $(LIB); rm $@;\
          mv list topology;\
          ar cr $@ `cat topology`;\
          rm -f `cat topology`;\
          echo Структура $@ изменилась.;\
      fi
Первая строка в ней выглядит так:
     'табуляция'-@if cmp list topology ; \
Остальные строки имеют более 8 ведущих пробелов.  Символ "-"
указан,  так  как  в отдельных версиях shell оператор if при
нормальном завершении возвращает не 0. Символ  "@"  отменяет
вывод этой строки перед выполнением.
     Приведенная Make-программа позволяет работать с  любыми
объектными  библиотеками.  Для  конкретной  библиотеки  (или
архива) нужно изменить макропеременные LIB, SRC  и  записать
зависимости от файлов включений.  Если необходимо работать с
текстовыми архивами, то достаточно  удалить  строку   ranlib
$@.   Рассмотрим  на примерах работу программы при различных
исходных условиях.
                           - 34 -
     Библиотеки нет, структура неизвестна
     make -s
     cc  -c m1.c
     cc  -c m2.c
     cc  -c m3.c
     as - -o m4.o m4.s
     cc  -c m5.c
     cc  -c m6.c
     as - -o m7.o m7.s
     cmp: не могу открыть topology
     Структура mylib изменилась.
     Библиотека mylib готова.
     Библиотека mylib имеется, структура остается без  изме-
нений, модифицируется файл x.h
     touch x.h
     make -s
     cc  -c m1.c
     cc  -c m2.c
     cc  -c m3.c
     cc  -c m6.c
     Библиотека mylib готова.
     Меняется содержимое библиотечного модуля  m5.c.   Меня-
ется  структура библиотеки: модуль m5 вызывает теперь модуль
m1
     make -s
     cc  -c m5.c
     Структура mylib изменилась.
     Библиотека mylib готова.
В файле topology теперь новая структура библиотеки
    m4.o m2.o m5.o m1.o m6.o m7.o m3.o
     Добавляется новый модуль в библиотеку.  Придется  изме-
нить  строку  SRC в Make-программе.  Имя модуля m8.c , вызы-
вает  он модуль m5.c
     cc  -c m8.c
     list topology различны: char 12, line 3
     Структура mylib изменилась.
     Библиотека mylib готова.
В файле topology теперь новая структура библиотеки
    m4.o m2.o m8.o m5.o m1.o m6.o m7.o m3.o
                           - 35 -
     Изменим модуль m1.c так, чтобы он вызывал модуль  m2.c,
а модуль m2.c вызывал m1.c, т.е. получается зацикленная вза-
имная зависимость библиотечных модулей m1 и m2
     make -s
     cc  -c m1.c
     tsort: зацикленная зависимость
     tsort: m2.o
     tsort: m1.o
     Структура mylib изменилась.
     Библиотека mylib готова.
Команда tsort вывела сообщение об ошибке в структуре библио-
теки.   Библиотека собрана, но пользоваться ею нельзя, необ-
ходимо исправить структуру модуля.  Удалять  файл  mylib  не
нужно,  так  как  он  содержит все объектные модули, которые
понадобятся для новой сборки.
9.  Особенности программирования на языке Make
     Всюду в примерах  Make-программа  размещалась  в  одном
Makefile.  Существует  возможность  разместить  ее в файле с
другим именем и при вызове интерпретатора make  указать  это
имя
    make -f имя_файла
Иногда возникает необходимость использовать несколько  Make-
файлов,  образующих  одну  Make-программу,  тогда при вызове
make можно указать
    make -f имя_файла1 -f имя_файла2 и т.д.
Указанные файлы составят текст одной  Make-программы  в  том
порядке, в котором они указаны в командной строке.
     Внутри одной  Make-программы  можно  вызывать  make  на
выполнение другой Make-программы, например:
    LLL: a b c d
          make -k -f имя_Make-файла $?
В том случае, если эта командная строка не может  быть  нор-
мально выполнена, ключ -k указывает на необходимость продол-
жить выполнение других разделов Make-программы,  которые  не
зависят  от  цели  данного  правила.  Если в этом примере не
указать имя файла, в котором  размещена  Make-программа,  то
автоматически будет выполняться Makefile и будет иметь место
рекурсивный вызов на выполнение одной программы.
     Есть некоторые особенности при использовании макропере-
менных.  Допустим, в Make-программе указана строка
                           - 36 -
    SRC = a1.c a2.c a3.c     # комментарий
Между a3.c и символом # 9 пробелов, они  будут  передаваться
всюду, где будет использовано значение макропеременной SRC.
     Предопределенные макропеременные "<" и "*"  в  правилах
без  суффиксов  не определены, и их использование может при-
вести к непредсказуемым результатам.
     Все, что указано за символом табуляция в строке  дейст-
вий  передается на выполнение shell. Однако идущие за симво-
лом  табуляция  символы  "-"  и  "@"  обрабатываются   make.
Интерпретатор  make  оптимизирует скорость выполнения дейст-
вий.  Если строка действий - простая  команда  системы,  она
выполняется  без порождения процесса shell. По этой причине,
например, такая строка вызовет состояние ошибки
     'табуляция'#cat file
Действительно,  как  бы  выполнялась  строка   "exec(#   cat
file);" в Си-программе?
     Если в списке зависимостей отсутствуют имена  подцелей,
можно  использовать сокращенную форму записи правила с одним
действием. Оно имеет вид:
    имя_цели:[:];  одна_строка_действия [# комментарий]
символ ";" обязателен.
     Особую осторожность необходимо соблюдать при указании в
Make-программе  имени  цели,  которая  не является файлом. В
этом случае программист должен учитывать, что он сознательно
исключает  возможность  использования этой цели при реконст-
рукциях, так как она не связана соотношениями времен  созда-
ния  (модификации)  с другими объектами Make-программы.  Это
препятствие можно обойти, создавая ЛОЖНЫЙ файл-цель,  напри-
мер:
    print: f1 f2 f3
           print $?
           touch print
     .DEFAULT:
           touch print
В рабочем каталоге создан пустой файл с именем print. Теперь
выводиться  на  печать будут только те файлы, которые требу-
ется распечатать как изменившиеся. Правило DEFAULT  записано
на тот случай, когда файл print отсутствует.
     Команду touch можно использовать, когда необходимо раз-
рушить  динамическую  структуру  связей  между файлами. Надо
                           - 37 -
учитывать, что при  этом  make  будет  реконструировать  все
файлы заново.
     Ниже перечислены все ключи  интерпретатора  make  и  их
действие:
-d   отладочный режим, в  котором  выводится  дополнительная
     информация о выполнении Make-программы.
-f   следующий параметр является именем Make-файла. По умол-
     чанию  ищется  Makefile или makefile. Если имеются оба,
     то выполняется Makefile. В командной строке можно  ука-
     зать несколько ключей -f и параметров.
-i   режим игнорирования кодов завершения команд  не  равных
     нулю.  Эквивалентно директиве IGNORE.
-k   если код завершения команды не равен  нулю,  прекратить
     выполнение текущего правила и перейти к выполнению дру-
     гих разделов, не зависящих от файла-цели этого правила.
-n   вывести,  но  не  выполнять   строки   действий   Make-
     программы.
-p   вывести полную информацию о структуре Make-программы.
-q   получить информацию о необходимости реконструкции цели.
     Если реконструкция указанной цели не требуется, возвра-
     щается -1, иначе 0.
-r   отменяет предопределенную обработку  правил  с  суффик-
     сами, предопределенные макропеременные и суффиксы.
-s   отменить вывод выполняемых строк.  Эквивалентно  дирек-
     тиве SILENT.
-S   прервать выполнение Make-программы при ошибочном завер-
     шении какой-либо команды.
-t   уничтожить сложившуюся структуру динамических  (завися-
     щих от времени) связей между файлами.
10.  Автоматизация программирования Make-программ
     Для создания новой  Make-программы  можно  иметь  файл-
шаблон,  добавляя в него необходимые строки мы получим гото-
вую к использованию программу.  Такой принцип  реализован  в
программе mkmf.  Рассмотрим ее работу на примере. Пусть име-
ются исходные файлы f.h, f1.c, f2.c и f3.c, из которых необ-
ходимо получить файл a.out:
                           - 38 -
     /*
     ** файл f.h
     */
     # include       <stdio.h>
     # include       <ctype.h>
     # include       <time.h>
     /*
     ** файл f1.c
     */
     # include       f.h
     main(ac, av)
     int ac;
     char **av;
     {
           f1(); f2(); f3();
     printf("Результат выполнения программы example.\n");
     }
     f1(){
           return;
     }
     /*
     ** файл f2.c
     */
     # include       f.h
     int f2(){
           return( a2());
     }
     # include       <stat.h>
     char *a2(){
          return;
     }
     /*
     ** файл f3.c
     */
     # include       f.h
     int f3(){
            return( nn());
     }
     char *nn(){
            return;
     }
Пусть все эти файлы размещены  в  одном  каталоге.  Выполним
команду  mkmf.   В  результате  ее  выполнения  будет создан
Makefile с программой сборки файла a.out:
                           - 39 -
    DEST          = .
    EXTHDRS       = /usr/include/ctype.h \
                    /usr/include/stat.h  \
                    /usr/include/stdio.h \
                    /usr/include/time.h
    HDRS          = f.h
    LDFLAGS       =
    LIBS          =
    LINKER        = cc
    MAKEFILE      = Makefile
    OBJS          = f1.o f2.o f3.o
    PRINT         = pr
    PROGRAM       = a.out
    SRCS          = f1.c f2.c f3.c
    all:            $(PROGRAM)
    $(PROGRAM): $(OBJS) $(LIBS)
                @echo -n "Сборка $(PROGRAM) ..."
                @$(LINKER) $(LDFLAGS) $(OBJS) \
                           $(LIBS) -o $(PROGRAM)
                @echo "готово."
    clean:;     @rm -f $(OBJS)
    depend:;    @mkmf -f $(MAKEFILE) \
                PROGRAM=$(PROGRAM) DEST=$(DEST)
    index:;     @ctags -wx $(HDRS) $(SRCS)
    install:    $(PROGRAM)
                @echo Установка $(PROGRAM) в $(DEST)
                @install -s $(PROGRAM) $(DEST)
    print:;     @$(PRINT) $(HDRS) $(SRCS)
    program:;   $(PROGRAM)
    tags:       $(HDRS) $(SRCS)
                @ctags $(HDRS) $(SRCS)
    update:     $(DEST)/$(PROGRAM)
                           - 40 -
    $(DEST)/$(PROGRAM): $(SRCS) $(LIBS) \
                        $(HDRS) $(EXTHDRS)
                @make -f $(MAKEFILE) \
                    DEST=$(DEST) install
    ###
    f1.o: f.h /usr/include/stdio.h \
              /usr/include/ctype.h \
              /usr/include/time.h
    f2.o: f.h /usr/include/stdio.h \
              /usr/include/ctype.h \
              /usr/include/time.h  \
              /usr/include/stat.h
    f3.o: f.h /usr/include/stdio.h \
              /usr/include/ctype.h \
              /usr/include/time.h
Программой mkmf в качестве исходного файла-шаблона использо-
ван  стандартный файл /usr/new/lib/p.Makefile, но можно ука-
зать для использования и любой другой.
     Программа  mkmf  работает  следующим  образом:  сначала
выбираются и вносятся в файл-шаблон имена всех исходных фай-
лов рабочего каталога, далее определяется от каких  include-
файлов зависят исходные файлы, формируются правила и записы-
ваются  в  файл-шаблон.  Для  обозначения  исходных   файлов
используются правила с суффиксами.
     Makefile можно редактировать, изменять значения  макро-
переменных.   При этом, если повторить вызов программы mkmf,
в нем появятся только те изменения, которые  необходимы  для
сборки с учетом изменений в исходных текстах.
     В Makefile, полученном из  стандартного  файла-шаблона,
определены следующие макропеременные:
CFLAGS
     ключи Си-компилятора;
DEST
     каталог, в котором будет размещен результат;
EXTHDRS
     перечень полных имен include-файлов;
HDRS
     перечень имен  include-файлов,  размещенных  в  рабочем
     каталоге;
LIBS
     список объектных библиотек для сборки программы;
                           - 41 -
MAKEFILE
     имя файла с Make-программой;
OBJS
     список объектных файлов,  участвующих  в  сборке  прог-
     раммы;
PROGRAM
     имя программы, которую необходимо получить;
SRCS
     список имен всех файлов с исходными текстами;
     Значения  макропеременных  EXTHDRS,  HDRS,  OBJS,  SRCS
устанавливаются  программой  mkmf  и всегда имеют актуальные
значения.  Остальные макропеременные получают  при  создании
Makefile  значения по умолчанию, их можно изменять по своему
усмотрению.
     Рассмотрим  правила  Make-программы,   котороые   можно
использовать как самостоятельные входы:
all
     трансляция, сборка и запуск  на  выполнение  полученной
     программы;
clean
     удаление ненужных файлов;
depend
     изменение структуры Make-программы с учетом  существую-
     щего Makefile;
index
     печать индексов функций собираемой программы;
install
     трансляция, сборка и установка  программы  в  указанный
     каталог;
print
     печать include-файлов и текстов программы;
tags
     создание файла ./tags - ссылок программ, написанных  на
     языках Си, Паскаль и Фортран;
update
     изменение Makefile, перегенерация, сборка  и  установка
     программы  в  указанный каталог.  С учетом произошедших
     изменений в текстах  исходных  файлов  будет  выполнено
     только то, что необходимо в данный момент времени.
                           - 42 -
     Пусть имеются файлы f[123].c, f.h и Makefile, заменим в
нем  значение  макропеременной DEST на /usr/tmp и макропере-
менной PROGRAM - на example.  Выполним  следующую  командную
строку:
    % make program install clean
    получим на экране сообщение
    cc  -c f1.c
    cc  -c f2.c
    cc  -c f3.c
    Сборка example ... готово.
    Результат выполнения программы example
    Установка example в /usr/tmp
Выполним командную строку
     % make index
    получим имена функций и места их определений
     a2       5 f2.c      char *a2(){
     f1      11 f1.c      f1(){
     f2       2 f2.c      int f2(){
     f3       2 f3.c      int f3(){
     main     5 f1.c      main(ac, av)
     nn       6 f3.c      char *nn(){
Программа mkmf позволяет создавать Makefile для сборки  биб-
лиотеки.      Для     этого     используется     файл-шаблон
/usr/new/lib/l.Makefile и дополнительно вводятся  макропере-
менная LIBRARY (имя библиотеки) и  правила extract (извлече-
ние из библиотеки всех  частей  в  виде  отдельных  файлов),
library (трансляция и загрузка библиотеки).
     Программист может  отказаться  от  стандартных  файлов-
шаблонов /usr/new/lib/[lp].Makefile и создать свои шаблоны в
рабочем  каталоге.   Файлы  шаблоны   должны   иметь   имена
l.Makefile и p.Makefile.
                           - 43 -
ЛИТЕРАТУРА
 1. "Банахан М., Раттер Э. Введение в  операционную  систему
    UNIX. - М.: Радио и связь, 1986." AI
 2. "Баурн С. Операционная система UNIX.- М.: Мир, 1986." AN
 3. "Браун П. Введение в операционную систему  UNIX.  -  М.:
    Мир, 1987." AM
 4. "Готье Р. Руководство по операционной  системе  UNIX.  -
    М.: Финансы и статистика, 1985." AE
 5. "Диалоговая единая мобильная операционная система ДЕМОС.
    - Калинин: ЦЕНТРПРОГРАММСИСТЕМ, 1985." AK
 6. "Инструментальная мобильная операционная система  ИНМОС/
    М.И.  Беляков,  А.Ю. Ливеровский, В.П. Семик и др. - М.:
    Финансы и статистика, 1985." AH
 7. "Керниган Б., Ритчи Д., Фьюер А.  Язык  программирования
    Си.  Задачи  по  языку  Си.  - М.: Финансы и статистика,
    1985." AF
 8. "Кристиан К. Введение в операционную систему UNIX. - М.:
    Финансы и статистика, 1985." AJ
 9. "Хенкок Л., Кригер М.  Введение  в  программирование  на
    языке СИ. - М.: Радио и связь, 1986." AG
10. "Aho A. V., Kernighan Brian V. W., Weinberger  Peter  J.
    AWK - a pattern scanning and processing language. Second
    edition. UNIX Programmers manual,  42  BSD,  1980.  Bell
    Laboratries: Murray Hill, New Jersey, 1978." AL
11. "Feldman S. I. Make - a program maitaining computer pro-
    gramms.  Bell  Laboratries:  Murray  Hill,  New  Jersey,
    1978." AB
12. "Joy W. N. An introduction the UNIX C-shell.  UNIX  Pro-
    grammers  manual, 42 BSD, 1980. Bell Laboratries: Murray
    Hill, New Jersey, 1978." AA
13. "Johnson S. C. YACC  -  yet  another  compiler-compiler.
    Comp.  Sci.  tech.  rep.  N 32. Bell Laboratries: Murray
    Hill, New Jersey, 1975." AD
14. "Lesk M. E. Lex - lexical analyzer generator. Comp. Sci.
    tech. rep. N 39. Bell Laboratries: Murray Hill, New Jer-
    sey, 1975." AC
                           - 44 -
                         СОДЕРЖАНИЕ
    АННОТАЦИЯ .........................................    2
1.  Введение ..........................................    3
2.  Принципы выполнения Make-программы ................    4
3.  Соглашения языка Make .............................   10
4.  Использование макропеременных .....................   13
5.  Выполнение правил в Make-программе ................   16
6.  Режимы выполнения Make-программы ..................   18
7.  Правила с суффиксами ..............................   21
8.  Управление архивом в Make-программе ...............   30
9.  Особенности программирования на языке Make ........   36
10. Автоматизация программирования Make-программ ......   38
    ЛИТЕРАТУРА ........................................   44
                           - 45 -
Last-modified: Mon, 29 Jun 1998 14:13:47 GMT