The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги ]

Каталог документации / Раздел "Документация для Linux" / Оглавление документа
next up previous contents index
Next: Переменные в make-файлах Up: Средство управления проектом make Previous: Файл описания проекта -   Contents   Index

Алгоритм работы make

Типичный make-файл проекта содержит несколько правил. Каждое из правил имеет некоторую цель и некоторые зависимости. Смыслом работы make является достижение цели, которую она выбрала в качестве главной цели (default goal). Если главная цель является именем действия (то есть абстрактной целью), то смысл работы make заключается в выполнении соответствующего действия. Если же главная цель является именем файла, то программа make должна построить самую "свежую" версию указанного файла.

Главная цель может быть прямо указана в командной строке при запуске make. В следующем примере make будет стремиться достичь цели edit (получить новую версию файла edit):

make edit

Если не указывать какой-либо цели в командной строке, то make выбирает в качестве главной первую, встреченную в make-файле цель. Схематично, ``верхний уровень'' алгоритма работы make можно представить так:

make()
    {
        главная_цель = ВыбратьГлавнуюЦель()

        ДостичьЦели( главная_цель )
    }

После того как главная цель выбрана, make запускает ``стандартную'' процедуру достижения цели. Сначала в make-файле ищется правило, которое описывает способ достижения этой цели (функция НайтиПравило). Затем, к найденному правилу применяется обычный алгоритм обработки правил (функция ОбработатьПравило).

ДостичьЦели( Цель )
    {
        правило = НайтиПравило( Цель )

        ОбработатьПравило( правило )
    }

Обработка правила разделяется на два основных этапа. На первом этапе обрабатываются все зависимости, перечисленные в правиле (функция ОбработатьЗависимости). На втором этапе принимается решение - нужно ли выполнять указанные в правиле команды (функция НужноВыполнятьКоманды). При необходимости, перечисленные в правиле команды выполняются (функция ВыполнитьКоманды).

ОбработатьПравило( Правило )
    {
        ОбработатьЗависимости( Правило )

        если НужноВыполнятьКоманды( Правило )
            {
            ВыполнитьКоманды( Правило )
            }
    }

Функция ОбработатьЗависимости поочередно проверяет все перечисленные в правиле зависимости. Некоторые из них могут оказаться целями каких-нибудь правил. Для этих зависимостей выполняется обычная процедура достижения цели (функция ДостичьЦели). Те зависимости, которые не являются целями, считаются именами файлов. Для таких файлов проверяется факт их наличия. При их отсутствии, make аварийно завершает работу с сообщением об ошибке.

ОбработатьЗависимости( Правило )
    {
        цикл от i=1 до Правило.число_зависимостей
            {
            если ЕстьТакаяЦель( Правило.зависимость[ i ] )
                {
                ДостичьЦели( Правило.зависимость[ i ] )
                }
            иначе
                {
                ПроверитьНаличиеФайла( Правило.зависимость[ i ] )
                }
            }
    }

На стадии обработки команд решается вопрос - нужно ли выполнять описанные в правиле команды или нет. Считается, что нужно выполнять команды если:

В противном случае (если ни одно из вышеприведенных условий не выполняется) описанные в правиле команды не выполняются. Алгоритм принятия решения о выполнении команд схематично можно представить так:
НужноВыполнятьКоманды( Правило )
    {
        если Правило.Цель.ЯвляетсяАбстрактной()
            return  true

        //  цель является именем файла

        если ФайлНеСуществует( Правило.Цель )
            return  true

        цикл от i=1 до Правило.Число_зависимостей
            {
            если Правило.Зависимость[ i ].ЯвляетсяАбстрактной()
                return  true
            иначе
                //  зависимость является именем файла
                {
                если ВремяМодефикации( Правило.Зависимость[ i ] )  >
                 ВремяМодефикации( Правило.Цель )
                    return  true
                }
            }

        return  false
    }

В указанном примере целью по умолчанию является edit. Первым шагом по его обновлению является обновление всех фалов объектов (.o), перечисленных как зависимости. Обновление edit.o, в свою очередь, требует обновления edit.cc и defs.h. Предполагается, что edit.cc является исходным файлов, из которого создается edit.o, а defs.h является заголовочным файлом, который включается в edit.cc. Правил, указывающих на эти файлы, нет; поэтому, они, как минимум, должны просто существовать. Теперь edit.o считается готовым, если он изменен позже, чем edit.cc или defs.h (если он страше их, это значит, что один из этих файлов изменился со времени последней компиляции edit.o). Если edit.o старше своих зависимостей, gmake выполняет действие ``gcc -g -c -Wall edit.cc'', создавая новый edit.o. Когда edit.o и все другие файлы .o будут обновлены, они будут собраны вместе действием ``gcc -g -o edit ...'', чтобы создать программу edit, если либо edit еще не существует, либо любой из файлов .o новее, чем существующий файл edit.

Чтобы вызвать gmake для этого примера, используйте команду

gmake -f <makefile-name> <target-names>

где <target-names> - это имена целей, которые Вы хотите обновить, а <makefile-name>, заданное после ключа -f, является именем make-файла. По умолчанию, целью является первое правило в файле. Вы можете (обычно так и делают) опустить -f makefile-name, и в этом случае по умолчанию будет выбрано имя makefile или Makefile, если любой из этих файлов существует.

Обычно каждый каталог содержит исходный код для простой части программы. Принимая правило, что имя программы соответствует первой цели, и что make-файл для каталога называется makefile, Вы добьетесь того, что вызов команды gmake без аргументов в любом каталоге приведет к обновлению части программы в этом каталоге.

Для одной и той же цели может быть несколько правил, но не более чем одно правило для цели должно иметь действия. Поэтому, мы может переписать последнюю часть примера следующим образом.


edit.o : edit.cc

gcc -g -c -Wall edit.cc
kbd.o : kbd.cc
gcc -g -c -Wall kbd.cc
commands.o : command.cc
gcc -g -c -Wall commands.cc
display.o : display.cc
gcc -g -c -Wall display.cc
insert.o : insert.cc
gcc -g -c -Wall insert.cc
search.o : search.cc
gcc -g -c -Wall search.cc
files.o : files.cc
gcc -g -c -Wall files.cc
utils.o : utils.cc
gcc -g -c -Wall utils.cc

edit.o kbd.o commands.o display.o $ \backslash$
insert.o search.o files.o utils.o: defs.h
kbd.o commands.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

Порядок, в котором записаны эти правила, не имеет значения. Скорее он зависит от Вашего вкуса и выбора порядка группировки файлов.



Alex Otwagin 2002-12-16


  Закладки на сайте
  Проследить за страницей
Created 1996-2017 by Maxim Chirkov  
ДобавитьРекламаВебмастеруГИД  
Hosting by Ihor