- Просто о make
- Make- основные сведения
- Простейший Makefile
- Компиляция из множества исходников
- Инкрементная компиляция
- Фиктивные цели
- Переменные
- Автоматические переменные
- Заключение
- Не найдена команда make в Linux
- Проверьте, что команда make установлена и путь к ней есть в PATH
- Как установить команду make в различных ОС?
Просто о make
Меня всегда привлекал минимализм. Идея о том, что одна вещь должна выполнять одну функцию, но при этом выполнять ее как можно лучше, вылилась в создание UNIX. И хотя UNIX давно уже нельзя назвать простой системой, да и минимализм в ней узреть не так то просто, ее можно считать наглядным примером количество- качественной трансформации множества простых и понятных вещей в одну весьма непростую и не прозрачную. В своем развитии make прошел примерно такой же путь: простота и ясность, с ростом масштабов, превратилась в жуткого монстра (вспомните свои ощущения, когда впервые открыли мэйкфайл).
Мое упорное игнорирование make в течении долгого времени, было обусловлено удобством используемых IDE, и нежеланием разбираться в этом ‘пережитке прошлого’ (по сути — ленью). Однако, все эти надоедливые кнопочки, менюшки ит.п. атрибуты всевозможных студий, заставили меня искать альтернативу тому методу работы, который я практиковал до сих пор. Нет, я не стал гуру make, но полученных мною знаний вполне достаточно для моих небольших проектов. Данная статья предназначена для тех, кто так же как и я еще совсем недавно, желают вырваться из уютного оконного рабства в аскетичный, но свободный мир шелла.
Make- основные сведения
make — утилита предназначенная для автоматизации преобразования файлов из одной формы в другую. Правила преобразования задаются в скрипте с именем Makefile, который должен находиться в корне рабочей директории проекта. Сам скрипт состоит из набора правил, которые в свою очередь описываются:
1) целями (то, что данное правило делает);
2) реквизитами (то, что необходимо для выполнения правила и получения целей);
3) командами (выполняющими данные преобразования).
В общем виде синтаксис makefile можно представить так:
# Индентация осуществляется исключительно при помощи символов табуляции, # каждой команде должен предшествовать отступ : .
То есть, правило make это ответы на три вопроса:
Несложно заметить что процессы трансляции и компиляции очень красиво ложатся на эту схему:
Простейший Makefile
Предположим, у нас имеется программа, состоящая всего из одного файла:
Для его компиляции достаточно очень простого мэйкфайла:
hello: main.c gcc -o hello main.c
Данный Makefile состоит из одного правила, которое в свою очередь состоит из цели — «hello», реквизита — «main.c», и команды — «gcc -o hello main.c». Теперь, для компиляции достаточно дать команду make в рабочем каталоге. По умолчанию make станет выполнять самое первое правило, если цель выполнения не была явно указана при вызове:
Компиляция из множества исходников
/* * hello.c */ #include void hello()
Makefile, выполняющий компиляцию этой программы может выглядеть так:
hello: main.c hello.c gcc -o hello main.c hello.c
Он вполне работоспособен, однако имеет один значительный недостаток: какой — раскроем далее.
Инкрементная компиляция
Представим, что наша программа состоит из десятка- другого исходных файлов. Мы вносим изменения в один из них, и хотим ее пересобрать. Использование подхода описанного в предыдущем примере приведет к тому, что все без исключения исходные файлы будут снова скомпилированы, что негативно скажется на времени перекомпиляции. Решение — разделить компиляцию на два этапа: этап трансляции и этап линковки.
Теперь, после изменения одного из исходных файлов, достаточно произвести его трансляцию и линковку всех объектных файлов. При этом мы пропускаем этап трансляции не затронутых изменениями реквизитов, что сокращает время компиляции в целом. Такой подход называется инкрементной компиляцией. Для ее поддержки make сопоставляет время изменения целей и их реквизитов (используя данные файловой системы), благодаря чему самостоятельно решает какие правила следует выполнить, а какие можно просто проигнорировать:
main.o: main.c gcc -c -o main.o main.c hello.o: hello.c gcc -c -o hello.o hello.c hello: main.o hello.o gcc -o hello main.o hello.o
Попробуйте собрать этот проект. Для его сборки необходимо явно указать цель, т.е. дать команду make hello.
После- измените любой из исходных файлов и соберите его снова. Обратите внимание на то, что во время второй компиляции, транслироваться будет только измененный файл.
После запуска make попытается сразу получить цель hello, но для ее создания необходимы файлы main.o и hello.o, которых пока еще нет. Поэтому выполнение правила будет отложено и make станет искать правила, описывающие получение недостающих реквизитов. Как только все реквизиты будут получены, make вернется к выполнению отложенной цели. Отсюда следует, что make выполняет правила рекурсивно.
Фиктивные цели
На самом деле, в качестве make целей могут выступать не только реальные файлы. Все, кому приходилось собирать программы из исходных кодов должны быть знакомы с двумя стандартными в мире UNIX командами:
Командой make производят компиляцию программы, командой make install — установку. Такой подход весьма удобен, поскольку все необходимое для сборки и развертывания приложения в целевой системе включено в один файл (забудем на время о скрипте configure). Обратите внимание на то, что в первом случае мы не указываем цель, а во втором целью является вовсе не создание файла install, а процесс установки приложения в систему. Проделывать такие фокусы нам позволяют так называемые фиктивные (phony) цели. Вот краткий список стандартных целей:
- all — является стандартной целью по умолчанию. При вызове make ее можно явно не указывать.
- clean — очистить каталог от всех файлов полученных в результате компиляции.
- install — произвести инсталляцию
- uninstall — и деинсталляцию соответственно.
.PHONY: all clean install uninstall all: hello clean: rm -rf hello *.o main.o: main.c gcc -c -o main.o main.c hello.o: hello.c gcc -c -o hello.o hello.c hello: main.o hello.o gcc -o hello main.o hello.o install: install ./hello /usr/local/bin uninstall: rm -rf /usr/local/bin/hello
Теперь мы можем собрать нашу программу, произвести ее инсталлцию/деинсталляцию, а так же очистить рабочий каталог, используя для этого стандартные make цели.
Обратите внимание на то, что в цели all не указаны команды; все что ей нужно — получить реквизит hello. Зная о рекурсивной природе make, не сложно предположить как будет работать этот скрипт. Так же следует обратить особое внимание на то, что если файл hello уже имеется (остался после предыдущей компиляции) и его реквизиты не были изменены, то команда make ничего не станет пересобирать. Это классические грабли make. Так например, изменив заголовочный файл, случайно не включенный в список реквизитов, можно получить долгие часы головной боли. Поэтому, чтобы гарантированно полностью пересобрать проект, нужно предварительно очистить рабочий каталог:
Для выполнения целей install/uninstall вам потребуются использовать sudo.
Переменные
Все те, кто знакомы с правилом DRY (Don’t repeat yourself), наверняка уже заметили неладное, а именно — наш Makefile содержит большое число повторяющихся фрагментов, что может привести к путанице при последующих попытках его расширить или изменить. В императивных языках для этих целей у нас имеются переменные и константы; make тоже располагает подобными средствами. Переменные в make представляют собой именованные строки и определяются очень просто:
Существует негласное правило, согласно которому следует именовать переменные в верхнем регистре, например:
Так мы определили список исходных файлов. Для использования значения переменной ее следует разименовать при помощи конструкции $(); например так:
Ниже представлен мэйкфайл, использующий две переменные: TARGET — для определения имени целевой программы и PREFIX — для определения пути установки программы в систему.
TARGET = hello PREFIX = /usr/local/bin .PHONY: all clean install uninstall all: $(TARGET) clean: rm -rf $(TARGET) *.o main.o: main.c gcc -c -o main.o main.c hello.o: hello.c gcc -c -o hello.o hello.c $(TARGET): main.o hello.o gcc -o $(TARGET) main.o hello.o install: install $(TARGET) $(PREFIX) uninstall: rm -rf $(PREFIX)/$(TARGET)
Это уже посимпатичней. Думаю, теперь вышеприведенный пример для вас в особых комментариях не нуждается.
Автоматические переменные
Автоматические переменные предназначены для упрощения мейкфайлов, но на мой взгляд негативно сказываются на их читабельности. Как бы то ни было, я приведу здесь несколько наиболее часто используемых переменных, а что с ними делать (и делать ли вообще) решать вам:
Заключение
В этой статье я попытался подробно объяснить основы написания и работы мэйкфайлов. Надеюсь, что она поможет вам приобрести понимание сути make и в кратчайшие сроки освоить этот провереный временем инструмент.
Не найдена команда make в Linux
Утилита make в Linux используется для сборки и компиляции программ из исходных файлов. Инструкции о компиляции утилита берет из файла makefile. В некоторых дистрибутивах Linux утилита make не установлена по умолчанию (например, в Linux CentOS 8), поэтому при запуске команды сборки, вы увидеть ошибку:
bash: make: command not found
bash: /usr/bin/make: No such file or directory
В консоли Ubuntu появляется уведомление:
The program 'make' is currently not installed. You can install it by typing: sudo apt install make
Проверьте, что команда make установлена и путь к ней есть в PATH
В первую очередь проверьте, что команда make установлена в вашем дистрибутиве Linux и путь к ее директории указан в переменной окружения PATH.В большинстве случае файл make должен находится в директории /usr/bin или /usr/local/bin. Проверьте, есть ли исполняемый бинарный файл make в этих каталогах:
Если вы нашли исполняемый файл make в этой директории, проверьте что вы можете запустить его с абсолютным путем:
GNU Make 3.82 Built for x86_64-redhat-linux-gnu Copyright (C) 2010 Free Software Foundation, Inc.
Затем проверьте, что путь к директории с файлом make указан в переменной окружения PATH:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
В нашем примере путь /usr/bin содержится в переменной окружения PATH. Чтобы добавить в PATH новую директорию, используйте команду:
Чтобы сделать это изменение постоянным для текущего пользователя, отредактируйте файл ~/.bashrc:
export PATH=$PATH:/path_to/dir
Как установить команду make в различных ОС?
Если утилита действительно отсутствует на вашем компьютере, ниже мы покажем установить команду make в разных версиях Linux.
В Fedora, Oracle/Rocky Linux, CentOS, Fedora и RHEL утилита make можно установить отдельно с помощью команды:
Или в составе группы пакетов Development tools (потребуется около 500 Мб на диске).
# dnf groupinstall «Development tools» (в RHEL/CentOS 8)
или
# yum groupinstall «Development tools» (в предыдущих версиях RPM дистрибутивов Linux)
Если make уже установлен, но не работает, можете переустановить его:
В deb-based дистрибутивах, таких Ubuntu, Debian, Raspbian, Kali Linux можно установить пакет make с помощью команд:
Также команда make содержится в метапакете build-essential. Для его установки выполните:
# apt-get install build-essential
Для переустановки пакета make, выполните:
# apt-get install —reinstall make
# sudo dpkg-reconfigure make
# apt-get remove make
# apt-get install make
В дистрибутивах на основе Arch (Arch Linux, Manjaro) утилиту make можно установить отдельно:
$ sudo pacman –Syu
$ sudo pacman -S make
Или вместе с пакетом base-devel:
Для автоматической установки команды make и средств разработки в Docker контейнере можно добавить в docker файл инструкцию:
RUN apt-get install -y build-essential
В Windows можно установить команду make с помощью менеджера пакета Chocolatey. Установите choco (если не установлен):
PS> Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString(‘https://community.chocolatey.org/install.ps1’))
Затем можете установить make в Windows:
Для сборки из исходников также используется команда CMake. Если она не установлена, появится ошибка cmake command not found.