Компиляция (программирование) | это… Что такое Компиляция (программирование)?

Компиля́тор —

  • Программа или техническое средство, выполняющее компиляцию.[1][2]
  • Машинная программа, используемая для компиляции.[3][2]
  • Транслятор, выполняющий преобразование программы, составленной на исходном языке, в объектный модуль.[2]
  • Программа, переводящая текст программы на языке высокого уровня, в эквивалентную программу на машинном языке.[4]
  • Программа, предназначенная для трансляции высокоуровневого языка в абсолютный код или, иногда, в язык ассемблера. Входной информацией для компилятора (исходный код) является описание алгоритма или программа на проблемно-ориентированном языке, а на выходе компилятора — эквивалентное описание алгоритма на машинно-ориентированном языке (объектный код).[5]

Компиляция

  • Трансляция программы на язык, близкий к машинному.
    [3][2]
  • Трансляция программы, составленной на исходном языке, в объектный модуль. Осуществляется компилятором.[2]

Компилировать — проводить трансляцию машинной программы с проблемно-ориентированного языка на машинно-ориентированный язык.[3]

Содержание

  • 1 Виды компиляторов[2]
  • 2 Виды компиляции[2]
  • 3 Основы
  • 4 Структура компилятора
  • 5 Трансляция и компоновка
  • 6 Интересные факты
  • 7 Примечания
  • 8 См. также
  • 9 Литература

Виды компиляторов

[2]
  • Векторизующий. Транслирует исходный код в машинный код компьютеров, оснащённых векторным процессором.
  • Гибкий. Составлен по модульному принципу, управляется таблицами и запрограммирован на языке высокого уровня или реализован с помощью компилятора компиляторов.
  • Диалоговый. См.: диалоговый транслятор.
  • Инкрементальный. Повторно транслирует фрагменты программы и дополнения к ней без перекомпиляции всей программы.
  • Интерпретирующий (пошаговый). Последовательно выполняет независимую компиляцию каждого отдельного оператора (команды) исходной программы.
  • Компилятор компиляторов. Транслятор, воспринимающий формальное описание языка программирования и генерирующий компилятор для этого языка.
  • Отладочный. Устраняет отдельные виды синтаксических ошибок.
  • Резидентный. Постоянно находится в основной памяти и доступен для повторного использования многими задачами.
  • Самокомпилируемый. Написан на том же языке, с которого осуществляется трансляция.
  • Универсальный. Основан на формальном описании синтаксиса и семантики входного языка. Составными частями такого компилятора являются: ядро, синтаксический и семантический загрузчики.

Виды компиляции

[2]
  • Пакетная
    . Компиляция нескольких исходных модулей в одном пункте задания.
  • Построчная. То же, что и интерпретация.
  • Условная. Компиляция, при которой транслируемый текст зависит от условий, заданных в исходной программе. Так, в зависимости от значения некоторой константы, можно включать или выключать трансляцию части текста программы.

Основы

Большинство компиляторов переводит программу с некоторого высокоуровневого языка программирования в машинный код, который может быть непосредственно выполнен центральным процессором. Как правило, этот код также ориентирован на исполнение в среде конкретной операционной системы, поскольку использует предоставляемые ею возможности (системные вызовы, библиотеки функций). Архитектура (набор программно-аппаратных средств), для которой производится компиляция, называется

целевой машиной.

Некоторые компиляторы (например, низкоуровневом языке. Такой язык — байт-код — также можно считать языком машинных команд, поскольку он подлежит интерпретации виртуальной машиной. Например, для языка Java это JVM (язык виртуальной машины Java), или так называемый байт-код Java (вслед за ним все промежуточные низкоуровневые языки стали называть байт-кодами). Для языков программирования на платформе .NET Framework (C#, Managed C++, Visual Basic .NET и другие) — это MSIL (Microsoft Intermediate Language).

Программа на байт-коде подлежит интерпретации виртуальной машиной, либо ещё одной компиляции уже в машинный код непосредственно перед исполнением. Последнее называется «Just-In-Time компиляция» (MSIL-код компилируется в код целевой машины также JIT-компилятором, а библиотеки .NET Framework компилируются заранее).

Для каждой целевой машины (Apple и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы

, позволяющие на одной машине и в среде одной ОС получать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут быть оптимизированы под разные типы процессоров из одного семейства (путём использования специфичных для этих процессоров инструкций). Например, код, скомпилированный под процессоры семейства MMX, SSE2.

Также существуют компиляторы, переводящие программу с языка высокого уровня на язык ассемблера.

Существуют программы, которые решают обратную задачу — перевод программы с низкоуровневого языка на высокоуровневый. Этот процесс называют декомпиляцией, а программы — декомпиляторами. Но поскольку компиляция — это процесс с потерями, точно восстановить исходный код, скажем, на C++, в общем случае невозможно. Более эффективно декомпилируются программы в байт-кодах — например, существует довольно надёжный декомпилятор для Flash. Сходным процессом является дизассемблирование машинного кода в код на языке ассемблера, который всегда выполняется успешно. Связано это с тем, что между кодами машинных команд и командами ассемблера имеется практически однозначное соответствие.

Структура компилятора

Процесс компиляции состоит из следующих этапов:

  1. Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.
  2. Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в дерево разбора.
  3. Семантический анализ. Дерево разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным деревом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.
  4. Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом.
  5. Генерация кода. Из промежуточного представления порождается код на целевом языке.

В конкретных реализациях компиляторов эти этапы могут быть раздельны или совмещены в том или ином виде.

Трансляция и компоновка

Важной исторической особенностью компилятора, отражённой в его названии (англ. compile — собирать вместе, составлять), являлось то, что он мог производить и

компоновку (то есть содержал две части — транслятор и компоновщик). Это связано с тем, что раздельная компиляция и компоновка как отдельная стадия сборки выделились значительно позже появления компиляторов, и многие популярные компиляторы (например, GCC) до сих пор физически объединены со своими компоновщиками. В связи с этим, вместо термина «компилятор» иногда используют термин «транслятор» как его синоним: либо в старой литературе, либо когда хотят подчеркнуть его способность переводить программу в машинный код (и наоборот, используют термин «компилятор» для подчёркивания способности собирать из многих файлов один).

Интересные факты

  • На заре развития компьютеров первые компиляторы (трансляторы) называли «программирующими программами»[6] (так как в тот момент программой считался только машинный код, а «программирующая программа» была способна из человеческого текста сделать машинный код, то есть запрограммировать ЭВМ).

Примечания

  1. ГОСТ 19781-83 // Вычислительная техника. Терминология: Справочное пособие. Выпуск 1 / Рецензент канд. техн. наук Ю. П. Селиванов. — М.: Издательство стандартов, 1989. — 168 с. — 55 000 экз. — ISBN 5-7050-0155-X
  2. 1 2
    3
    4 5 6 7 Першиков В. И., Савинков В. М. Толковый словарь по информатике / Рецензенты: канд. физ.-мат. наук А. С. Марков и д-р физ.-мат. наук И. В. Поттосин. — М.: Финансы и статистика, 1991. — 543 с. — 50 000 экз. — ISBN 5-279-00367-0
  3. 1 2 3 СТ ИСО 2382/7-77 // Вычислительная техника. Терминология. Указ. соч.
  4. Борковский А. Б. Англо-русский словарь по программированию и информатике (с толкованиями). — М.: Русский язык, 1990. — 335 с. — 50 050 (доп.) экз. — ISBN 5-200-01169-3
  5. Толковый словарь по вычислительным системам = Dictionary of Computing / Под ред. В. Иллингуорта и др.: Пер. с англ. А. К. Белоцкого и др.; Под ред. Е. К. Масловского. — М.: Машиностроение, 1990. — 560 с. — 70 000 (доп.) экз. — ISBN 5-217-00617-X (СССР), ISBN 0-19-853913-4 (Великобритания)
  6. Н. А. Криницкий, Г. А. Миронов, Г. Д. Фролов. Программирование / Под ред. М. Р. Шура-Бура. — М.: Государственное издательство физико-математической литературы, 1963.

См. также

  • Компилятор компиляторов
  • «Книга дракона» — классический учебник о построении компиляторов.
  • Синтаксический анализ
  • Интерпретатор
Реализации компиляторов
  • GCC
  • Free Pascal Compiler
  • Sun Studio — компиляторы C, C++ и Fortran от Sun Microsystems Inc.
  • Open Watcom — свободное продолжение компиляторов Watcom C/C++/Fortran.
  • Intel C++/Fortran compiler
  • ICC AVR

Литература

  • Альфред В. Ахо, Моника С. Лам, Рави Сети, Джеффри Д. Ульман. Компиляторы: принципы, технологии и инструментарий = Compilers: Principles, Techniques, and Tools. — 2-е изд. — М.: Вильямс, 2008. — ISBN 978-5-8459-1349-4
  • Робин Хантер. Основные концепции компиляторов = The Essence of Compilers. — М.: Вильямс, 2002. — С. 256. — ISBN 0-13-727835-7
  • Хантер Р. Проектирование и конструирование компиляторов / Пер. с англ. С. М. Круговой. — М.: Финансы и статистика, 1984. — 232 с.
  • Д. Креншоу. Давайте создадим компилятор!.

Что такое компиляция, линковка, run time?

Компиляция и линковка

Компиляция – это процесс превращения исходного кода (который написал программист) в код, понятный компьютеру. Java, например, компилируется в код, понятный не компьютеру, а Java Virtual Machine. Но это не суть, главное в итоге получается код, который может прочесть машина.

В большинстве случаев при написании кода вы подключаете какие-то библиотеки. Для того чтобы библиотека была доступна вашему коду, вы ее каким-то образом импортируете. В разных языках есть разные конструкции импорта. После подключения, ваш код может скомпилироваться, потому что ваш код соединяется с кодом библиотеки, т.е. компилируется на его основе. В результате вы получаете бинарный код библиотеки и ваш код программы, который лежит отдельно. Если говорим про Джаву, то так все и остается, связывание кодов происходит дальше в джава машине в процессе выполнения программы. Но в некоторых языках связывание кода требуется до запуска программы. Процесс связывания в единый файл уже скомпилированных кусков вашего кода и кода всех остальных библиотеки называется линковкой. Т.е. линковка – это соединение кода в один большой выполнимый файл. В джаве это не требуется, а в С++, например, это достаточно тяжелый процесс, который занимает достаточно много времени.

Compile time

Это важные термины, но большинство программистов о них спотыкаются. Процесс, когда вы продумываете и пишете код называется design time. В этот момент код не валидный, он не скомпилируется и не сработает, потому что вы прямо сейчас работаете над ним. Но вот вы остановились, сохранили его и нажали кнопку компиляции (в Eclipse компиляция происходит автоматически при сохранении). Запускается компилятор, который для каждого file.java создает file.class с бинарным кодом, который будет выполняться джава машиной. Вот этот процесс превращения называется Compile time. 

Во время Compile time машина ищет все используемые библиотеки. Если находит – компилирует их, если не находит или если в вашем коде есть ошибки, появляется сообщение типа «Ошибка компилятора». Ошибки, произошедшие в это время, называются ошибками Compile time или ошибками компиляции. Тут мы видим, что за ошибка: не нашла библиотеку или ошибка в коде, присвоение неправильного типа, забытые структуры языка и пр. Если вы видите ошибку компиляции (не warning, а именно error), то на выходе вы не получаете никаких классов, чаще всего они остаются старыми. Итак, Compile time – это время, когда исходный код превращается в бинарный. Поиском соответствий занимается джава компилятор.

Runtime

Вот вы скомпилировали код, у вас лежит связка файлов, теперь их нужно запустить. Вы запускаете Джава машину (сами или это делает IDE) и в нее загружаете ваши файлы с командой Запустить. Все, что происходит в это время и дальше называется runtime. 

В чем особенность. На моменте Compile time зависимости от ваших библиотек находит компилятор, у него своя система соответствия, по которой происходит компиляция. А вот в runtime зависимости от библиотек ищет уже джава машина и у нее может быть совсем другой набор мест, где нужно искать. Поэтому довольно часто получается, что после запуска программист видит, что класс не найден. Это вызывает удивление, ведь все скомпилировалось, значит все хорошо. Так получается, потому compile time и untime — абсолютно разные процессы и выполняют их разные программы. Компайл тайм делает компилятор, а рантайм делает джава машина. Поэтому программа может компилироваться без ошибок, но потом падать на этапе запуска. Это нормально, потому что компилятор не может проверить за вас правильность программы. Иначе зачем бы тогда были нужны программисты?

Что означает компиляция кода: объясните, как будто я Пятерка

Компьютеры могут понимать только машинный код , который представляет собой низкоуровневый язык программирования* , обычно полностью состоящий из чисел.

В большинстве случаев, когда люди говорят о машинном коде , они называют его двоичным кодом ** (система счисления с основанием 2 — либо 1, либо 0). Таким образом, мы можем сказать, что машинный код состоит из последовательностей единиц и нулей 9. 0004 . Ниже приведен фрагмент двоичного кода:

 01101000 01100101 01101100 01101100 01101111
00100000 01110111 01101111 01110010 01101100
 
Войти в полноэкранный режимВыйти из полноэкранного режима

Нам нужно общаться с компьютерами, поэтому для этого нам нужно говорить на их языке. Но писать машинный код очень непрактично, поэтому пришлось придумывать языки, напоминающие наш родной язык. Мы называем эти языки языками программирования высокого уровня . Некоторые примеры: Python, Ruby, C# или Java.

Теперь проблема в том, что компьютеры не понимают эти языки напрямую, поэтому нам нужен переводчик , который может объяснить наши инструкции компьютеру. Так родились компилятора и интерпретатора*** . Это программы, которые переводят код, который мы пишем, используя языки высокого уровня, в машинный код.

Компиляция означает, что после того, как мы закончим писать наш код, 9Компилятор 0003 (программа) берет наш код и просматривает его, убеждаясь, что он написан в соответствии с правилами используемого нами языка программирования (он проверяет наличие синтаксических ошибок). Если ошибок не обнаружено, код можно эффективно преобразовать в машинный код и создать файл, содержащий скомпилированный код.

Мы слышим, как люди говорят, что код не компилируется . Это означает, что при проверке кода компилятор обнаружил ошибки и перевод в машинный код не удался. Обычно программист увидит некоторые сообщения об ошибках, которые помогут ему отладить код. Только после исправления всех ошибок код скомпилируется.

ВАЖНО
То, что код компилируется, не означает, что программа работает. Это связано с тем, что компилятор не может улавливать логические ошибки. Это может показаться нелогичным, но подумайте об этом, как о написании текста на английском языке без грамматических ошибок или опечаток, когда слова верны сами по себе, но при совместном чтении они не имеют смысла.

(*) Языки программирования низкого уровня — это языки, близкие к набору инструкций компьютера. Набор инструкций — это набор инструкций, понятных процессору.

(**) Машинный код может быть представлен в других числовых форматах, например в шестнадцатеричном. Эти форматы легче читаются людьми, но компьютер их не понимает, поэтому в конце концов они также преобразуются в двоичные.

(***) Интерпретаторы также используются для преобразования исходного кода в машинный код, но они работают иначе, чем компиляторы.

CS107 Компиляция программ с помощью Make

Написано Крисом Греггом и Кевином Монтагом, с изменениями Ника Трокколи

Нажмите здесь, чтобы просмотреть видео-прохождение.

В CS107 мы будем использовать программу make для компиляции наших программ. Make — это программа, созданная в 1976 году, и она используется для сборки проектов с зависимостями таким образом, что она перекомпилирует только те файлы, которые были изменены, и позволяет избежать ввода длинных команд компиляции. Это один файл, содержащий все файлы и настройки для компиляции проекта и связывания его с соответствующими библиотеками.

Для простых проектов с несложными настройками вы можете выполнять сборку без make-файла, напрямую вызывая компилятор, такой как GCC, например. gcc file1.c file2.c file3.c компилирует три файла и связывает их вместе в исполняемый файл с именем a.out . Вы можете добавить такие флаги, как -Wall (для предупреждений) или -std=gnu99 (для использования обновленной спецификации GNU99), или -o [имя] , чтобы установить имя результирующего исполняемого файла. Однако повторный ввод этих команд компиляции вручную быстро становится утомительным, поскольку проекты становятся даже немного сложными, и легко ошибиться или быть непоследовательным. Управление сборкой с помощью make-файла намного удобнее и менее подвержено ошибкам.

Для наших целей вам не нужно слишком много знать о Make, за исключением того, как его использовать. Все проекты CS107 будут распространяться с предварительно написанным файлом Makefile, который вы обычно можете использовать как есть. Что очень важно, однако, так это то, что вам нужно не забыть запустить сделать после любого изменения исходного кода вашей программы -- многие студенты забывают запустить сделать и удивляются, почему они получают неожиданные результаты от своих программ , когда просто они никогда не перекомпилировали свой код после изменений!

Самый простой способ использовать make — ввести make в директорию, содержащую «Makefile» с соответствующим названием Makefile :

 $ make.
gcc -g -Og -std=gnu99 -o привет helloWorld.c helloLanguages.c
$ ./привет
Привет, мир
Привет Вельт
Бонжур монд
 

Вот пример Makefile для вышеуказанной программы со следующими файлами: hello.h , helloLanguages.c , helloWorld.c :

 #
# Очень простой make-файл
#
# Компилятор C по умолчанию
СС = gcc
# Переменная CFLAGS устанавливает флаги компиляции для gcc:
# -g компилировать с отладочной информацией
# -Стена выдает подробные предупреждения компилятора
# -O0 не оптимизировать сгенерированный код
# -std=gnu99 использовать определение стандартного языка GNU99
CFLAGS = -g -Стена -O0 -std=gnu99
привет: helloWorld. c helloLanguages.c привет.h
    $(CC) $(CFLAGS) -o привет helloWorld.c helloLanguages.c
.ФОНИЯ: чистый
чистый:
    рм -ф привет *.о
 

Обратите внимание, что строки, начинающиеся с '#', являются комментариями и игнорируются при обработке make-файла.

В файле Makefile есть правила, которым следует следовать, чтобы решить, когда компилировать программу. В частности, строка hello: в Makefile указывает Make перекомпилировать программу, если какой-либо из трех файлов ( hello.c , helloLanguages.c и hello.h ) изменились. В следующей строке, которая должна начинаться с табуляции, а не пробелов (если вы этого не сделаете, вы получите ошибку «отсутствует разделитель»!), выполняется строка компиляции. В этом Makefile есть две переменные: CC (компилятор) и CFLAGS (флаги, которые мы собираемся отправить компилятору).

Часто включаемое второе правило, clean: , позволяет вам писать очистить и удалить все файлы, связанные с компиляцией этой программы. (Первое правило, указанное в Makefile, выполняется по умолчанию, когда вы вводите make , поэтому вам не нужно каждый раз писать make hello ).

Посмотрите, как компилировать с помощью gcc для получения информации о том, как происходит компиляция.

Хотя вам не нужно много знать о файлах Makefile для компиляции заданий, читайте дальше, если вам интересно узнать больше о деталях написания файла Makefile. Проверка make-файлов некоторых реальных проектов — еще один интересный способ увидеть make в действии.

Ниже приведен более длинный файл Makefile, который можно использовать для сборки более крупного проекта:

 #
# Простой make-файл для управления сборкой проекта, состоящего из исходных файлов C.
#
# Вполне вероятно, что компилятор C по умолчанию уже является gcc, но явно
# установить, просто чтобы быть уверенным
СС = gcc
# Переменная CFLAGS устанавливает флаги компиляции для gcc:
# -g компилировать с отладочной информацией
# -Стена выдает подробные предупреждения компилятора
# -O0 не оптимизировать сгенерированный код
# -std=gnu99 использовать определение стандартного языка GNU99
CFLAGS = -g -Стена -O0 -std=gnu99
# Переменная LDFLAGS устанавливает флаги для компоновщика
# -lm указывает на ссылку в libm (математическая библиотека)
LDFLAGS = -lm
# В этом разделе вы перечисляете файлы, которые являются частью проекта.  $(LDFLAGS)
# Фальшивая означает не "настоящую" цель, она ничего не создает
# Фальшивая цель "clean" используется для удаления всех скомпилированных объектных файлов.
# 'core' - это имя файла, который выводится в некоторых случаях, когда вы получаете
# сбой (SEGFAULT) с "дампом ядра"; он может содержать дополнительную информацию о
# авария.
.ФОНИЯ: чистый
чистый:
    rm -f $(ЦЕЛЬ) $(ОБЪЕКТЫ) ядро
 

Давайте пройдемся по этому make-файлу и посмотрим, что там.

Макросы

Это замены, определенные в верхней части make-файла (строки, которые выглядят как CFLAGS = -g -Wall ). Они аналогичны операторам #define в языке C и должны использоваться для любого выражения, которое может многократно использоваться в make-файле. После того, как макрос был назначен, мы можем ссылаться на него позже, используя $(MACRO_NAME) (например, $(CFLAGS) в приведенном выше примере). Когда мы набираем сделать в терминале, анализатор файлов просто заменит эти ссылки на макросы назначенным содержимым.

Копнув глубже, строка OBJECTS = $(SOURCES:.c=.o) определяет макрос OBJECTS таким же, как макрос SOURCES , за исключением того, что каждый экземпляр '.c' заменяется на '.o' — то есть это присвоение эквивалентно OBJECTS = demo.o vector.o map.o . Есть также два встроенных макроса, используемых make-файлом, $@ 9 ; они оцениваются как demo и demo.o vector.o map.o соответственно, но нам нужно немного узнать о целях, прежде чем мы узнаем, почему.

Для ясности, возможно, стоит посмотреть на содержимое make-файла так, как его «видит» синтаксический анализатор, с удаленными комментариями и полностью развернутыми макросами. В таком виде наш образец make-файла выглядит так:

 demo : demo.o vector.o map.o
    gcc -g -Wall -O0 -std=gnu99 -o demo demo.o vector.o map.o -lm
.ФОНИЯ: чистый
чистый:
    rm -f demo demo.o vector.o map.o core
 

Цели

Следуя определениям макросов в make-файле, мы видим ряд целей. Цели и связанные с ними действия записываются в виде:

 имя-цели: зависимости
    действие
 

Целевое имя обычно представляет собой имя файла, который будет создан при построении этого целевого объекта. Первая цель, указанная в make-файле, является целью по умолчанию, что означает, что это цель, которая создается, когда make вызывается без аргументов; другие цели могут быть построены с использованием введите [имя_цели] в командной строке. Здесь также стоит упомянуть, что утилита Make распознает ряд неявных целей и, в частности, что каждый из наших объектных файлов имеет связанную неявную цель, эквивалентную:

 [имя файла].o : [имя файла].c
    $(CC) $(CFLAGS) -o [имя файла].o [имя файла].c
 

Большая часть возможностей утилиты Make связана с обработкой зависимостей. Зависимости цели — это файлы, которые должны существовать и обновляться, прежде чем можно будет построить саму цель. В приведенном выше примере demo target зависит от трех объектных файлов (каждый из которых может быть создан со своей собственной неявной целью, как указано). Делайте зависимости процессов рекурсивными; если конкретная зависимость имеет связанную цель, утилита Make (пере)построит цель зависимости перед обработкой родительской цели, гарантируя, что все зависимости будут обновлены до обработки родительской цели. Таким образом, для нашего демонстрационного make-файла команда make demo фактически ведет себя как make demo.o ; сделать вектор.o ; сделать карту.o ; сделать демо (рекурсия заканчивается на зависимостях, у которых нет связанной цели; это происходит, если, например, мы зависим от исходного файла, такого как demo.c , как в случае с целью demo.o ). Затем утилита Make проверит временные метки каждого файла, от которого зависит родительский целевой объект, и создаст родительский целевой объект, если какой-либо из этих файлов был изменен позднее, чем родительский файл (или если родительский файл еще не существует). В нашем случае это означает, что если demo исполняемый файл уже существует в нашем каталоге, make demo ничего не сделает, если объектные файлы каталога не должны быть перестроены во время рекурсивной обработки зависимостей, что, в свою очередь, произойдет только в том случае, если какой-либо из наших исходных файлов ( demo. c , vector.c , map.c ) были изменены позже, чем был создан связанный с ними объектный файл. Таким образом, если мы не изменили ни один из наших исходных файлов, повторный вызов make demo приведет только к сборке demo исполняемый один раз. Кроме того, если мы изменим только один из наших исходных файлов, мы перестроим только связанный объектный файл, а не все три объектных файла. В крупномасштабных проектах такого рода оптимизации могут сэкономить часы компиляции при каждой сборке проекта.

Наконец, с каждой целью связана команда, которая будет выполняться в оболочке для создания цели. Как правило, это команда, которая вызывает компилятор, но технически это может быть любая команда, которая создает файл с именем цели. При определении команды для цели у нас также есть доступ к ряду специальных макросов, таких как 9 выше. Теперь мы можем видеть, что эти макросы оцениваются соответственно имени текущей цели и ее списку зависимостей. Существуют и другие макросы, зависящие от цели, и информация о них доступна в документации Make.

Фальшивые цели: Обратите внимание, что цель clean в нашем образце Makefile на самом деле не создает файл с именем «чистый» и, таким образом, не соответствует шаблону, который мы описывали для целей. Скорее цель clean используется в качестве ярлыка для запуска команды, которая очищает файлы сборки проекта (символ «@» в начале команды указывает Make не выводить ее на терминал во время ее выполнения). Мы помечаем такие цели, перечисляя их как «зависимости» от .PHONY , псевдоцель, которую мы никогда не создадим. Когда утилита Make обнаруживает фальшивую цель, она автоматически запускает соответствующую команду, не выполняя никаких проверок зависимостей.

Если вы хотите узнать больше о make , ознакомьтесь со следующими ресурсами:

  • полное руководство make (от GNU)
  • Раздел 2 этого документа Stanford Unix Programming Tools
  • Пресса без крахмала «Gnu Make Book» (требуется аутентификация, доступ к подписке Стэнфорда на Safari Books Online).