- Отладка Makefile /часть 2/
- Общие сообщения об ошибках
- Синтаксические ошибки
- missing separator
- commands commence before first target
- unterminated variable reference
- Ошибки в командных сценариях
- No Rule to Make Target
- Overriding Commands for Target
- Makefile для самых маленьких
- Отладка Makefile /часть 1/
- Отладочные возможности make
- Опции командной строки
- —just-print
- —print-data-base
- —warn-undefined-variables
- —debug опция
- Пишем код, удобный для отладки
- Хорошие практики кодирования
- Защитное программирование
Отладка Makefile /часть 2/
В этой части поговорим об общих методах и проблемах отладки. В конечном итоге, отладка — это солянка из всего, что работает в данной конкретной ситуации. Эти методы работают для меня и мне приходится полагаться на них даже в случае проблем с простейшими makefile. Может быть, они помогут и тебе тоже.
Один из очень раздражающих багов в make 3.80 был в сообщении об ошибке в makefile, где make указывал номер строки, и обычно этот номер строки был неверный. Я не удосужился исследовать из-за чего эта проблема возникает: из-за импортируемых файлов, присваиваний многострочных переменных или из-за пользовательских макросов. Обычно, make дает номер строки больше чем должен был бы. В сложных makefile бывает что номер не совпадает на 20 строк.
Часто наиболее простой путь увидеть значение переменной это напечатать его в ходе выполнения цели. И хотя распечатать легко можно с помощью warning , в долгой перспективе поможет сэкономить много времени немного усилий на добавление общей цели debug для вывода переменных. Вот примерный код цели debug :
Для того чтобы использовать её, нужно перечислить имена переменных которые надо распечатать в командной строке и собрать debug цель:
Если уж совсем делать всё волшебно, то можно использовать MAKECMDGOALS переменную, чтобы избежать присвоения переменной V :
Теперь можно выводить переменные просто перечислив их в командной строке. Однако, я не рекомендую этот способ, так как предупреждения make о невозможности обновлении переменных (так как они указаны как цели) могут сбить с толку:
В то время как make выводит команды из сценариев цели до их выполнения, он не выводит команды выполняющиеся в shell функции. Часто эти команды сложные и неуловимые в том плане, что могут выполняться как незамедлительно, так и в отложенной манере, если они были вызваны в значении рекурсивной переменной. Один из способов увидеть эти команды — включить отладку в самой оболочке:
Если это запустить с опцией отладки sh , мы увидим:
Можно заметить, что также выводятся значения всех переменных и выражений.
Часто встречаются сильно вложенные выражения, например, для оперирования с именами файлов:
Ничего хорошего в отладке таких выражений нет. Один из разумных подходов будет их разворот и печать каждого подвыражения:
Весьма нудно, но без настоящего отладчика это лучший путь (иногда единственный) для определения значений различных подвыражений.
Общие сообщения об ошибках
В руководстве make есть замечательный раздел со списком сообщений об ошибках make и их причин. Мы рассмотрим немного из наиболее часто встречаемых. Некоторые описанные проблемы, строго говоря, не являются ошибками make , такие как синтаксические в командных сценариях, но все же обычными проблемами для разработчиков. Полный список смотри в руководстве.
Сообщение make об ошибке имеет стандартный формат:
где makefile строка — это имя файла или импортированного файла в котором произошла ошибка. Следующая часть — номер строки, в которой произошла ошибка, далее следуют три звездочки, и, наконец, само сообщение.
Заметим, что это задача make запускать другие программы и таким образом, если при этом возникают ошибки, скорее всего проблемы в твоём makefile вызвали ошибки в этих других программах. Для примера, ошибки оболочки могут быть из-за плохо сформированных командных сценариев, или ошибок компилятора из-за некорректных аргументов командной строки. Выяснение того, какая программа выдала сообщение об ошибке — первоочередная задача при решении проблемы. К счастью, сообщения make довольно очевидны.
Синтаксические ошибки
Обычно это типографические ошибки: пропущенные скобки, пробелы после запятых в параметрах функции, и так далее.
Одна из наиболее частых ошибок для новых пользователей make это опускание скобок вокруг имен переменных:
Скорее всего, make развернёт переменную $S в ничего, и оболочка выполнит цикл только раз со значением OURCES в f . В зависимости от того, что ты собрался делать с f , можно получить забавные сообщения оболочки:
но можно и не получить сообщения вовсе. Помни — имена переменных обрамляются скобками.
missing separator
или (в GNU make — пер.):
обычно означает make искал разделитель, такой как :, =, или табуляцию и не нашел ни одного. Вместо этого, он нашел что-то что он не понял.
commands commence before first target
Эта ошибка появляется в основном в середине makefile, когда строка вне командного сценария начинается с отступа (пробелы или символ табуляции). make сделает все возможное, чтобы устранить неоднозначность этой ситуации, но если строка не может быть идентифицирована как присваивание значения, условное выражение или многострочное определение макроса, make решит что это неправильно размещенная команда.
unterminated variable reference
Это простая, но распространённая ошибка. Она означает, что ты забыл закрыть имя переменной или вызов функции правильным количеством скобок. С сильно вложенными вызовами функций и именами переменных make файлы становятся похожими на Lisp! Избежать этого поможет хороший редактор, который умеет сопоставлять скобки, такой как Emacs.
Ошибки в командных сценариях
Есть три типа частых ошибок в командных сценариях: пропущенная точка с запятой в многострочных командах, незаконченная или неверная переменная пути, или просто команда, которая просто обнаружила проблему в ходе выполнения.
Мы обсуждали пропущенные точки с запятыми в разделе «лучшие практики», поэтому не будем на этом останавливаться здесь.
выводится, когда оболочка не смогла найти команду foo . Так, оболочка поискала в каждой папке из переменной PATH исполняемый файл и не нашла совпадений. Чтобы исправить такую ошибку, нужно обновить PATH переменную, обычно в .profile (Bourne shell), .bashrc (bash) или .cshrc (C shell). Конечно, можно также установить PATH в самом makefile, и экспортировать PATH из make .
Если же команда завершилась с ошибкой, она выходит с ненулевым статусом выхода. В этом случае, make отчитается об ошибке со следующим сообщением:
Здесь touch команда не сработала, что напечатало своё собственное сообщение объясняющее сбой. Следующая строка — это итоговая ошибка make . Упавшая цель в makefile указана в квадратных скобках, а затем статус выхода упавшей программы. Если программа вышла по сигналу, а не с ненулевым статусом выхода, то make напечатает более подробное сообщение.
Заметим также, что команды под знаком @ также могут упасть. В этом случае сообщение об ошибке может возникнуть как будто оно из ниоткуда.
В обоих случаях ошибка происходит из программ запускаемых make , нежели от самого make .
No Rule to Make Target
Это сообщение имеет две формы:
Это означает, что make решил обновить файл XXX, но make не смог найти ни одного правила для выполнения работы. make ищет во всех явных и неявных правилах в его базе данных прежде чем сдаться и вывести это сообщение.
Есть три причины для этой ошибки:
- В твоем makefile отсутствует необходимое правило для обновления файла. В этом случае тебе необходимо добавить правило с описанием как построить цель.
- В makefile — опечатка. Или make ищет неверный файл или в правиле построения этого файла указан неверный файл. Если в makefile используются переменные, то опечатки становится еще труднее отыскать. Иногда единственный путь быть точно уверенным в значении сложного имени файла это напечатать его или печатая переменную напрямую или исследуя внутреннюю базу данных make .
Файл должен быть, но make не находит его или из-за того, что его нет, или make не знает где его искать. Конечно, иногда make абсолютно прав. Файла нет — похоже мы забыли его скачать из VCS . Еще чаще, make не может найти файл из-за того, что исходник расположен где-то еще. Иногда исходник в другом дереве исходников, или может файл генерируется другой программой и создался в папке артефактов сборки.
Overriding Commands for Target
make позволяет только один командный сценарий для цели (за исключением «::» правил, которые редко используются). Если встретится больше чем один командный сценарий для одной цели, make выведет предупреждение:
Также он может вывести сообщение:
Первое предупреждение показывает строку, на которой был найден второй сценарий команд; тогда как второе предупреждение указывает на позицию исходного переопределённого командного сценария.
В сложных makefile цели часто определены несколько раз, каждый раз добавляя еще собственные требования. Одно из этих определений цели обычно содержит командный сценарий, но в ходе разработки или отладки очень легко добавить еще один и не заметить, что существующий набор команд уже переопределен.
Например, мы могли бы определить общную цель во включаемом файле:
и позволим нескольким отдельным makefile добавить свои собственные требования. Мы могли бы записать в makefile:
Если непреднамеренно добавить командный сценарий в такой makefile, make выдаст предупреждение переопределения.
Источник
Makefile для самых маленьких
Не очень строгий перевод материала mrbook.org/tutorials/make Мне в свое время очень не хватило подобной методички для понимания базовых вещей о make. Думаю, будет хоть кому-нибудь интересно. Хотя эта технология и отмирает, но все равно используется в очень многих проектах. Кармы на хаб «Переводы» не хватило, как только появится возможность — добавлю и туда. Добавил в Переводы. Если есть ошибки в оформлении, то прошу указать на них. Буду исправлять.
Статья будет интересная прежде всего изучающим программирование на C/C++ в UNIX-подобных системах от самых корней, без использования IDE.
Компилировать проект ручками — занятие весьма утомительное, особенно когда исходных файлов становится больше одного, и для каждого из них надо каждый раз набивать команды компиляции и линковки. Но не все так плохо. Сейчас мы будем учиться создавать и использовать Мейкфайлы. Makefile — это набор инструкций для программы make, которая помогает собирать программный проект буквально в одно касание.
Для практики понадобится создать микроскопический проект а-ля Hello World из четырех файлов в одном каталоге:
Все скопом можно скачать отсюда
Автор использовал язык C++, знать который совсем не обязательно, и компилятор g++ из gcc. Любой другой компилятор скорее всего тоже подойдет. Файлы слегка подправлены, чтобы собирались gcc 4.7.1
Программа make
Если запустить
make
то программа попытается найти файл с именем по умолчание Makefile в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом:
make -f MyMakefile
Есть еще множество других параметров, нам пока не нужных. О них можно узнать в ман-странице.
Процесс сборки
Компилятор берет файлы с исходным кодом и получает из них объектные файлы. Затем линковщик берет объектные файлы и получает из них исполняемый файл. Сборка = компиляция + линковка.
Компиляция руками
Самый простой способ собрать программу:
g++ main.cpp hello.cpp factorial.cpp -o hello
Каждый раз набирать такое неудобно, поэтому будем автоматизировать.
Самый простой Мейкфайл
В нем должны быть такие части:
Для нашего примера мейкфайл будет выглядеть так:
Обратите внимание, что строка с командой должна начинаться с табуляции! Сохраните это под именем Makefile-1 в каталоге с проектом и запустите сборку командой make -f Makefile-1
В первом примере цель называется all . Это цель по умолчанию для мейкфайла, которая будет выполняться, если никакая другая цель не указана явно. Также у этой цели в этом примере нет никаких зависимостей, так что make сразу приступает к выполнению нужной команды. А команда в свою очередь запускает компилятор.
Использование зависимостей
Использовать несколько целей в одном мейкфайле полезно для больших проектов. Это связано с тем, что при изменении одного файла не понадобится пересобирать весь проект, а можно будет обойтись пересборкой только измененной части. Пример:
Это надо сохранить под именем Makefile-2 все в том же каталоге
Теперь у цели all есть только зависимость, но нет команды. В этом случае make при вызове последовательно выполнит все указанные в файле зависимости этой цели.
Еще добавилась новая цель clean . Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: make -f Makefile-2 clean
Использование переменных и комментариев
Переменные широко используются в мейкфайлах. Например, это удобный способ учесть возможность того, что проект будут собирать другим компилятором или с другими опциями.
Это Makefile-3
Переменные — очень удобная штука. Для их использования надо просто присвоить им значение до момента их использования. После этого можно подставлять их значение в нужное место вот таким способом: $(VAR)
Что делать дальше
После этого краткого инструктажа уже можно пробовать создавать простые мейкфайлы самостоятельно. Дальше надо читать серьезные учебники и руководства. Как финальный аккорд можно попробовать самостоятельно разобрать и осознать такой универсальный мейкфайл, который можно в два касания адаптировать под практически любой проект:
Источник
Отладка Makefile /часть 1/
Отладка makefile — это что-то из черной магии. К несчастью, не существует такой вещи как makefile отладчик, чтобы изучить ход выполнения конкретного правила или как разворачивается переменная. Большую часть отладки можно выполнить с помощью обычных print’ов и проверкой makefile. Конечно, GNU make немного помогает своими встроенными методами и опциями командной строки. Один из лучших методов отладки makefile это добавить отладочные перехваты (hooks) и использовать техники безопасного программирования, на которые можно будет опереться, когда дела пойдут совсем плохо. Далее представлено несколько основных техник отладки и практик безопасного программирования, которые будут, на мой взгляд, наиболее полезными.
Отладочные возможности make
Очень полезная для отладки не работающего makefile — функция warning . Так как функция warning разворачивается в пустую строку, ее можно использовать везде в makefile: на верхнем уровне, в имени цели, в списке зависимостей и в командных скриптах. Это позволяет печатать значения переменных там, где это наиболее подходит для их проверки. Например:
Заметим, что выполнение функции warning следует нормальному ходу алгоритма make для немедленных и отложенных вычислений. Также, присваивание к BAZ содержит warning и сообщение не печатается, пока BAZ не будет развернут в списке зависимостей.
Возможность вставки warning вызов куда угодно делает его очень полезным инструментом отладки.
Опции командной строки
Есть три очень полезные опции командной строки для отладки: —just-print (-n) , —print-data-base (-p) и —warn-undefined-variables .
—just-print
Первое испытание новой цели в makefile — это вызвать make с опцией —just-print (-n) . Будучи вызванным с этой опцией make прочитает makefile и напечатает каждую команду, которую в обычном режиме он бы выполнил для обновления цели. Для удобства, GNU make также выведет команды помеченные собачкой ( @ ) — заглушающим модификатором.
Опция предназначена для полного подавления выполнения всех команд. Однако, есть исключения. В то время как make не выполняет командные скрипты целей, он выполнит вызовы shell функции, которые будут в немедленном контексте выполнения. Для примера:
Смысл переменной _MKDIRS в инициировании создания нужных директорий. Если выполнить это с —just-print опцией, команда оболочки будет выполнена как обычно в момент чтения makefile. Затем, make выведет (без исполнения) каждую команду компиляции необходимую для обновления списка файлов $(objects) .
—print-data-base
Еще одна опция, которую нужно использовать почаще. С ней, после обычного «прогона» makefile, make выдаст в конце его внутреннюю базу данных. Данные сгруппированы по группам: переменные, директории, неявные правила, переменные шаблонов, файлы (явные правила) и vpath путь поиска. Давайте остановимся на этих секциях подробнее.
Секция Variables выводит список переменных с описательным комментарием:
Авто-переменные не выводятся, но выводятся другие, зависящие от них, полезные переменные, такие как $( . В комментарии пишется вывод функции origin (см. make manual). Если переменная определена в файле, будет выведено имя файла и номер строки объявления. Простые и рекурсивные переменные определяются по оператору присваивания. Вычисленное значение простых переменных также печатается в правой части выражения.
Следующий раздел Directories более полезен разработчикам make , а не пользователям make . Представляет собой список папок просмотренных make, включая SCCS и RCS под-папки, которые могут быть, но обычно отсутствуют. Для каждой папки выводится детали реализации: номер устройства, inode и статистика по совпадениям шаблонов файлов.
Секция Implicit rules содержит все встроенные и пользовательские шаблоны в базе данных make . Опять же, для тех правил, которые определены в файле, выводится имя файла и строка определения:
Изучение этой секции даст быстрое понимание разнообразия и структуры встроенных правил make . Конечно, не все встроенные правила реализованы как шаблонные. Если нужное правило не находится, его еще можно поискать в разделе Files, где выводятся суффиксные правила в старом стиле.
Следующая секция это каталог зависящих от конкретного шаблона переменных определенных в makefile. Описание этих переменных — это их значения, которые будут подставлены во время выполнения при выполнении соответствующих им правил. Например, для переменной определенной как:
Далее следует Files секция, которая выводит все явные и суффикс- правила, которые относятся к конкретным файлам:
Промежуточные файлы и суффикс-правила обозначены как «Not a target»; остальное — цели. Каждый файл включает комментарии, показывающие как make будет обрабатывать это правило. У файлов, которые найдены через обычный vpath поиск, показан найденный путь до них.
Последняя секция называется VPATH Search Paths и перечисляет значение VPATH и все vpath шаблоны.
Для makefile‘ов, которые обильно используют пользовательские функции и eval для создания сложных переменных и правил, исследование этого вывода — единственный путь проверить, что разворачивание макросов дает ожидаемые значения.
—warn-undefined-variables
Эта опция заставляет make выводить предупреждение при вычислении неопределенной переменной. Так как неопределенные переменные вычисляются в пустую строку, зачастую, опечатки остаются необнаруженными долгое время. Проблема с этим ключом в том, что многие встроенные правила используют неопределенные переменные, которые нужны для перехвата пользовательских значений. Поэтому запуск make с этой опцией неизбежно выведет много предупреждений, которые не являются ошибками и не связаны с makefile‘ом пользователя, Например:
Тем не менее, эта опция может быть крайне полезна в поиске ошибок такого типа.
—debug опция
Когда нужно узнать как make анализирует твой граф зависимостей, используй —debug опцию. Она предоставляет самую детальную доступную информацию без запуска отладчика. Есть пять опций вывода отладки и один модификатор: basic , verbose , implicit , jobs , all , и makefile , соответственно.
Если опция указана в форме —debug , используется basic — краткий вывод для отладки. Если опция указана в виде -d , используется all . Для выбора других комбинаций можно использовать список разделенный запятыми: —debug=option1,option2 , где option может быть одно из следующих значений (на самом деле, make смотрит только на первую букву):
basic
Наименьшая детализированность. Когда включена, make выводит каждую цель, которая нуждается в обновлении и статус действия обновления.
verbose
Эта опция включает basic вывод и дополнительную информацию о проанализированных файлах и о зависимостях, которые не нуждаются в обновлении.
implicit
Эта опция включает basic вывод и дополнительную информацию о неявных правилах, просмотренных в поиске нужного для каждой выполняемой цели.
jobs
Эта опция выводит детали о запущенных make ‘ом подпроцессах. Она не включает basic вывод.
all
Включает все выше перечисленное и является значением по умолчанию для -d опции.
makefile
Обычно, отладочная информация включается после того, как все makefileы будут обновлены. Обновление включает в себя и обновление всех импортированных файлов, таких как файлы со списками зависимостей. С этим модификатором make выведет выбранную опциями информацию в ходе обновления makefile‘ов и импортированных файлов. Он включает basic информацию, а также включается при указании all опции.
Пишем код, удобный для отладки
Как мы видим, средств отладки для makefile не так уж и много, вывод внутреннего состояния и несколько функций отчетов. Когда дела обстоят именно так, мы сами вынуждены делать наши makefileы такими, чтобы и минимизировать шанс ошибок и иметь инструментарий для отладки этих ошибок.
Правила в этом разделе, в несколько произвольном виде, основаны на практиках кодирования, защитном программировании и техник отладки. Тогда как определенные правила, такие как проверка статуса выхода команд, можно поместить как в хорошие практики кодирования так и в защитное программирование, эти три категории отражают общую шкалу. Кодируй хорошо свои makefile, но без излишеств в срезании углов. Используй защитное программирование, чтобы защитить makefile от неожиданных событий и изменчивого внешнего окружения. В конце концов, когда баги все-таки будут возникать, используй каждый приём, доступный тебе, чтобы их размозжить.
Хорошие практики кодирования
По моему опыту, большая часть программистов не рассматривает написание makefile’ов как программирование, и, из-за этого, не уделяют такое же внимание, как при разработке на C++ или Java. Но ведь язык make это полный по Тьюрингу декларативный язык! И если надежность и легкость сопровождения твоей системы сборки важна, пиши ее тщательно и используй все доступные тебе лучшие практики.
Один из важнейших аспектов в программировании безотказных makefile это проверка статуса выхода команд. Конечно, make проверяет для простых команд это дело сам, но makefile часто содержит составные команды, которые могут не сработать молча:
При выполнении этот makefile не прервется с ошибкой, тогда как ошибка несомненно произойдет:
И далее, выражение подстановки не найдет никаких файлов .c , и молча вернет выражение подстановки. Ой. Способ по-лучше, это использовать возможности командной оболочки по проверке и предотвращению ошибок:
Теперь ошибка cd правильно передастся make , команда echo никогда не исполнится и make прервётся со статусом ошибки. В дополнение, после установки nullglob опции bash а подстановка вернет пустую строку если не будут найдены такие файлы. (Конечно, в твоем конкретном случае могут быть другие предпочтения.)
Другой хорошей практикой является форматирование своего кода для максимальной читабельности. Большая часть makefile’ов плохо отформатированы, и как следствие, их трудно читать. Что тут кажется легче прочитать?
Если ты как все люди, то тебе будет труднее прочитать первое, точку с запятой сложно найти, и количество операндов сосчитать еще сложнее. И это не шутки. Огромный процент синтаксических ошибок, которые встречаются в командных скриптах будут из-за пропущенных точек с запятой, символов экранирования, других разделителей, таких как разделитель программного конвейера или логические операторы.
Замечательно, что не все пропущенные разделители дадут ошибку. Для справки, ни одна из следующих ошибок не даст синтаксической ошибки оболочки окружения:
С форматированием команд для читабельности такого рода ошибки станет легко ловить. Делай отступы при работе в пользовательских функциях. Если же пробелы станут проблемой при работе макроса, форматирование можно обернуть через вызов strip функции. Форматируй длинные списки значений по одному на строку. Добавь комментарий перед каждой целью, дай краткое пояснение, и задокументируй параметры.
Следующей хорошей практикой является использование переменных для хранения общих значений. Как и в любой программе, неограниченное использование буквальных значений создает дублицирование кода и приводит к проблемам поддержки и багам. Еще одно большое преимущество переменных — make умеет их выводить в ходе работы в целях отладки. Ниже в разделе «способы отладки» будет приведена полезная команда.
Защитное программирование
Защитный код это код, который выполняется когда одно из твоих предположений или ожиданий неверно — if проверяет то, что всегда истинно, assert функция никогда не упадет, и конечно, значимость такого кода в том, что внезапно (обычно когда меньше всего этого ждешь), он выполняется и дает предупреждение или ошибку, или ты включаешь трассировку кода, чтобы увидеть внутреннюю работу make .
Валидация — это отличный пример защитного программирования. Следующий код проверяет что текущая запущенная версия make — 3.80:
Для приложений Java полезно включить проверку файлов в CLASSPATH .
Код валидации также может просто проверять что что-то истинно.
Другой отличный пример защитного программирования это использование assert функций, например таких:
Небольшое количество assert вызовов в makefile будет дешевым и эффективным путём обнаружения пропавших переменных или переменных с опечатками в названии, а также и других ожиданий.
Также можно определить пару функций для трассировки разворачивания пользовательских функций:
Можно добавить вызовы этих макросов в свою функцию и оставить их выключенными до тех пор, пока они не потребуются для отладки. Для включения их нужно установить debug_trace в любое не пустое значение:
И еще одно средство защитного программирования это легкое и простое выключение действия @ командного модификатора, если использовать его через переменную, а не буквально:
Используя эту технику можно увидеть ход выполнения заглушенных команд просто переопределив переменную в командной строке:
Источник