Как запустить makefile в linux

Обновлено: 05.07.2024

Сначала Вам скорее всего нужно скачать архив с программой, которую Вы хотите установить.

Например, команда для скачивания python3.7:

Затем распаковать архив

tar -xf Python-3.7.0.tgz

И перейти в только что распакованную директорию

В этой директории скорее всего будет находиться скрипт configure

Configure

configure - это не команда linux а скрипт, который обычно лежит в папке к configure


--prefix=PREFIX - папка для установки программы, вместо /, например, может быть /usr/local/, тогда все файлы будут распространены не по основной файловой системе, а в /usr/local;

--bindir=DIR - папка для размещения исполняемых файлов, должна находится в PREFIX;

--libdir=DIR - папка для размещения и поиска библиотек по умолчанию, тоже в PREFIX;

--includedir=DIR - папка для размещения man страниц;

--disable-возможность - отключить указанную возможность;

--enable-возможность - включить возможность;

--with-библиотека - подобно enable активирует указанную библиотеку или заголовочный файл;

--without-библиотека - подобное disable отключает использование библиотеки.

.cpp файлы компилируются в .o файлы

один .h файл может использоваться несколькими .cpp файлами

makefile нужны для того чтобы печатать меньше названий файлов и опций вручную.

С их помощью можно делать build только тех файлов, которые изменились.

Make это инструмент, который вызывает компиллятор. Можно указать тот компиллятор который нужен именно Вам.

С помощью инструкций в makefile можно указать какие именно файлы нужно заново компилировать.

Рассмотрим пример из C++ .

В директории находятся три .cpp файла, два .h файла и три .o файла, оставшиеся от прошлой компиляции.

  • Example.cpp , Example.o
  • Second.cpp , Second.h , Second.o
  • Third.cpp , Third.h , Third.o

Все они нужны для проекта и не могут быть объединены в один файл.

Известно, что Example.cpp включает в себя файл Second.h и других зависимостей не имеет.

Мы хотим, чтобы при изменении самого Example.cpp либо его зависимости Second.h начиналась новая компиляция Example.cpp а затем новый Example.o линковался со старыми Second.o и Third.o

Для этого напишем Makefile aomake

all: simple simple: Example.o Second.o Third.o g++ Example.o Second.o Third.o - Simple Example.o: Example.cpp Second.h g++ -c Example.cpp

Отступы нужно ставить табуляцией

Смотрим на последние две строки:

Если Second.h или Example.cpp (в который включен Second.h) изменились нужно компилировать файл Example.cpp после чего получается object файл Example.o и, как видно из первых двух строк - после обновления Example.o все object файлы линкуются.

Выигрыш в этой ситуации заключается в том, что нам не нужно было перекомпилировать Second.cpp и Third.cpp мы просто перелинковали уже существующие Second.o Third.o с новым Example.o

Чтобы запустить этот файл нужно к команде make добавить опцию f и название файла

У файла Second.cpp две зависимости: Second.h и Third.h

У файла Third.cpp только одна зависимость: Third.h

Учтём эти зависимости в нашем aomake

all: simple simple: Example.o Second.o Third.o g++ Example.o Second.o Third.o - Simple Second.o: Second.cpp Second.h Third.h g++ -c Second.cpp Third.o: Third.cpp Third.h g++ -c Third.cpp Example.o: Example.cpp Second.h g++ -c Example.cpp

Ещё одной полезной опцией является j

-j - jobs Определяет число работ - jobs (commands) которые запускаются одновременно.

Если указано больше чем одна -j опция, то применяться будет последняя.

Если -j опция дана без аргументов make не будет ограничивать количество работ запускаемых одновременно. То есть запуститься сможет неограниченное количество работ одновременно.

Более подробную информацию по Makefile вы можете найти в статье «makefile tutorial»

Install

Make - это не единственный способ устанавливать программы под linux. Более того, за сорок с лишним лет существования утилиты make вышло много её версий.

Прежде чем ставить что-то по-старинке с make install - советую изучить checkinstall

Статья будет интересная прежде всего изучающим программирование на 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)

Что делать дальше

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

Меня всегда привлекал минимализм. Идея о том, что одна вещь должна выполнять одну функцию, но при этом выполнять ее как можно лучше, вылилась в создание UNIX. И хотя UNIX давно уже нельзя назвать простой системой, да и минимализм в ней узреть не так то просто, ее можно считать наглядным примером количество- качественной трансформации множества простых и понятных вещей в одну весьма непростую и не прозрачную. В своем развитии make прошел примерно такой же путь: простота и ясность, с ростом масштабов, превратилась в жуткого монстра (вспомните свои ощущения, когда впервые открыли мэйкфайл).

Мое упорное игнорирование make в течении долгого времени, было обусловлено удобством используемых IDE, и нежеланием разбираться в этом 'пережитке прошлого' (по сути — ленью). Однако, все эти надоедливые кнопочки, менюшки ит.п. атрибуты всевозможных студий, заставили меня искать альтернативу тому методу работы, который я практиковал до сих пор. Нет, я не стал гуру make, но полученных мною знаний вполне достаточно для моих небольших проектов. Данная статья предназначена для тех, кто так же как и я еще совсем недавно, желают вырваться из уютного оконного рабства в аскетичный, но свободный мир шелла.

Make- основные сведения

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

1) целями (то, что данное правило делает);
2) реквизитами (то, что необходимо для выполнения правила и получения целей);
3) командами (выполняющими данные преобразования).

В общем виде синтаксис makefile можно представить так:

То есть, правило make это ответы на три вопроса:


Несложно заметить что процессы трансляции и компиляции очень красиво ложатся на эту схему:

Простейший Makefile

Предположим, у нас имеется программа, состоящая всего из одного файла:


Для его компиляции достаточно очень простого мэйкфайла:


Данный Makefile состоит из одного правила, которое в свою очередь состоит из цели — «hello», реквизита — «main.c», и команды — «gcc -o hello main.c». Теперь, для компиляции достаточно дать команду make в рабочем каталоге. По умолчанию make станет выполнять самое первое правило, если цель выполнения не была явно указана при вызове:

Компиляция из множества исходников

Предположим, что у нас имеется программа, состоящая из 2 файлов:
main.c

Makefile, выполняющий компиляцию этой программы может выглядеть так:


Он вполне работоспособен, однако имеет один значительный недостаток: какой — раскроем далее.

Инкрементная компиляция

Представим, что наша программа состоит из десятка- другого исходных файлов. Мы вносим изменения в один из них, и хотим ее пересобрать. Использование подхода описанного в предыдущем примере приведет к тому, что все без исключения исходные файлы будут снова скомпилированы, что негативно скажется на времени перекомпиляции. Решение — разделить компиляцию на два этапа: этап трансляции и этап линковки.

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


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

После запуска make попытается сразу получить цель hello, но для ее создания необходимы файлы main.o и hello.o, которых пока еще нет. Поэтому выполнение правила будет отложено и make станет искать правила, описывающие получение недостающих реквизитов. Как только все реквизиты будут получены, make вернется к выполнению отложенной цели. Отсюда следует, что make выполняет правила рекурсивно.

Фиктивные цели

На самом деле, в качестве make целей могут выступать не только реальные файлы. Все, кому приходилось собирать программы из исходных кодов должны быть знакомы с двумя стандартными в мире UNIX командами:


Командой make производят компиляцию программы, командой make install — установку. Такой подход весьма удобен, поскольку все необходимое для сборки и развертывания приложения в целевой системе включено в один файл (забудем на время о скрипте configure). Обратите внимание на то, что в первом случае мы не указываем цель, а во втором целью является вовсе не создание файла install, а процесс установки приложения в систему. Проделывать такие фокусы нам позволяют так называемые фиктивные (phony) цели. Вот краткий список стандартных целей:

  • all — является стандартной целью по умолчанию. При вызове make ее можно явно не указывать.
  • clean — очистить каталог от всех файлов полученных в результате компиляции.
  • install — произвести инсталляцию
  • uninstall — и деинсталляцию соответственно.


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

Обратите внимание на то, что в цели all не указаны команды; все что ей нужно — получить реквизит hello. Зная о рекурсивной природе make, не сложно предположить как будет работать этот скрипт. Так же следует обратить особое внимание на то, что если файл hello уже имеется (остался после предыдущей компиляции) и его реквизиты не были изменены, то команда make ничего не станет пересобирать. Это классические грабли make. Так например, изменив заголовочный файл, случайно не включенный в список реквизитов, можно получить долгие часы головной боли. Поэтому, чтобы гарантированно полностью пересобрать проект, нужно предварительно очистить рабочий каталог:


Для выполнения целей install/uninstall вам потребуются использовать sudo.

Переменные

Все те, кто знакомы с правилом DRY (Don't repeat yourself), наверняка уже заметили неладное, а именно — наш Makefile содержит большое число повторяющихся фрагментов, что может привести к путанице при последующих попытках его расширить или изменить. В императивных языках для этих целей у нас имеются переменные и константы; make тоже располагает подобными средствами. Переменные в make представляют собой именованные строки и определяются очень просто:


Существует негласное правило, согласно которому следует именовать переменные в верхнем регистре, например:


Так мы определили список исходных файлов. Для использования значения переменной ее следует разименовать при помощи конструкции $(<VAR_NAME>); например так:


Ниже представлен мэйкфайл, использующий две переменные: TARGET — для определения имени целевой программы и PREFIX — для определения пути установки программы в систему.


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

Автоматические переменные

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

  • $@ Имя цели обрабатываемого правила
  • $< Имя первой зависимости обрабатываемого правила
  • $^ Список всех зависимостей обрабатываемого правила

Заключение

В этой статье я попытался подробно объяснить основы написания и работы мэйкфайлов. Надеюсь, что она поможет вам приобрести понимание сути make и в кратчайшие сроки освоить этот провереный временем инструмент.

sudo apt install make

sudo yum install make

Так как make входит в состав build-essentials можно установить вместе с этим пакетом

sudo apt install build-essentials

Проверить версию make

Для чего используются Makefiles

Make-файлы используются, чтобы помочь решить, какие части большой программы должны быть перекомпилированы.

В подавляющем большинстве случаев компилируются файлы C или C++ .

Другие языки обычно имеют свои собственные инструменты, которые служат той же цели, что и Make.

Его можно использовать и за пределами программ, когда вам нужна серия инструкций для запуска в зависимости от того, какие файлы изменились.

В этой статье вы узнаете про использование компиляции C/C++.

Вот пример графика зависимостей, который вы можете построить с помощью Make.

Если какие-либо зависимости файла изменятся, то файл будет перекомпилирован:

Граф зависимостей для компиляции изображение с сайта www.aredel.com

Формат

Зависимости нужны не всегда и указываются по необходимости. Для простоты на первом этапе можно думать о зависимостях как о файлах, которые нужно проверить: если ни один не изменился - заново компилировать не нужно.

Отступы по умолчанию нужно ставить табуляцией. Если хотите поменять на другой символ - задайте .RECIPEPREFIX

target: prerequisites recipe

На русский обычно переводят так

цель: зависимости команды

Типичное применение: какая-то зависимость изменилась → выполнятеся действие в результате которого создаётся таргет файл.

output: main.o message.o g++ main.o message.o -o output clean: rm *.o output

Как и в статье Configure, make, install в примере выше используются стандартные цели (target)

Про опции -o и -c читайте статью «Компиляция в C++

Если файл вам не нужен, например, вы просто хотите выполнить какие-то команды - можно использовать .PHONY

.PHONY

Рассмотрим следующий Makefile

.PHONY: site site: echo "HeiHei.ru"

Если теперь выполнить

Удалите site из первой строки, а всё остальное не трогайте

Вроде бы ничего не изменилось, но теперь создайте файл site рядом с Makefile

touch site
make site

Так как таргет теперь реальный - make не нашёл изменений и ничего не сделал. Из-за такого простого совпадения имени цели (target) и какого-то файла в директории может перестать работать скрипт.

Для защиты от таких неприятностей и применяют PHONY

Также PHONY удобен тем, что можно перечислить все цели в самом начале файла.

Если не злоупотреблять этой возможностью - можно улучшить читаемость кода, особенно в небольших файлах.

Пример из C++

Functions.cpp Functions.h Main.cpp

Functions.cpp

double add( double x, double y)

Если один из этих файлов изменился - нужно перекомпилировать проект. Для начала будем пользоваться командой

g++ -o output Main.cpp Functions.cpp

Эта команда сначала вызывает компиляцию, затем линковку

Создайте Makefile и откройте его в текстовом редакторе. Например, в Vim

touch Makefile
vi Makefile

Makefile будет выглядеть следующим образом

output: Main.cpp Functions.cpp Functions.h g++ -o output Main.cpp Functions.cpp

Теперь для компиляции достаточно выполнить

В результате появится исполняемый файл output

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

Если вам не понятно что происходит в этом файле - изучите статью «Компиляция в C++

.PHONY: clean output: Main.o Functions.o g++ Main.o Functions.o -o output Main.o: Main.cpp g++ -c Main.cpp Functions.o: Functions.cpp g++ -c Functions.cpp clean: rm *.o output

To запустить скрипт, достаточно выполнить

g++ -c Main.cpp
g++ -c Functions.cpp
g++ -o output Main.o Functions.o

Если нужно скомпилировать Main execute

Появится файл Main.o но не появятся остальные (Functions.o, output)

Functions.cpp Functions.h Main.cpp Main.o Makefile

На примере команды make Main.o можно понять почему в Make-файлах используется термин цели (target)

make Main.o говорит - создай файл Main.o а инструкция в Makefile определяет правило по которому это нужно сделать.

Если теперь выполнить make Main.o не будет перекомпилироваться. Будут выполнены только последние два шага.

g++ -c Functions.cpp
g++ -o output Main.o Functions.o

Выполните make если ещё не выполняли и не делайте после этого clean

Добавим ещё одну функцию в наш проект. Нужно указать её в файлах Functions.*

Вызывать пока не будет, поэтому Main.cpp остаётся без изменений

Functions.cpp

bool test( bool x)

bool test(bool x);

g++ -c Functions.cpp g++ -o output Main.o Functions.o

Обратите внимание: Main.cpp не был перекомпилирован так как в нём нет изменений.

Таже посмотрите на время изменения файла output оно должно измениться.

Не вносите никаких изменений в файлы и execute

Перекомпиляция не нужна и поэтому не выполнена

Переменные

Запустить Docker container из Makefile

.PHONY: docker docker: docker-compose -f docker/dev/docker-compose.yml build

Параметризация Make

?= позволяет переменным быть перезаписанными на существующие переменные окружения

:= перезаписывает значение переменной

BUILD_ID

To добавить переменным уникальности используют BUILD_ID

USER_ID

To получить ID пользователя запустившего GNUmakefile

Какие альтернативы Make существуют

Популярными альтернативными системами сборки C/C++ являются SCons, CMake, Bazel и Ninja. Некоторые редакторы кода, такие как Microsoft Visual Studio , имеют свои собственные встроенные инструменты сборки.

Для Java есть Ant, Maven и Gradle.

Другие языки, такие как Go и Rust, имеют свои собственные инструменты сборки.

Интерпретируемые языки, такие как Python , Ruby и JavaScript , не требуют аналога для создания файлов.

Цель Makefile состоит в том, чтобы скомпилировать любые файлы, которые должны быть скомпилированы, основываясь на том, какие файлы изменились.

Но когда файлы на интерпретируемых языках меняются, ничего не нужно перекомпилировать.

При запуске программы используется самая последняя версия файла.

Что означает cc -c

cc это C compiler

Существует несколько общедоступных компиляторов C

В этой статье использовался gcc

-c это опция, которую разбирали здесь

whoami

В Make файле это может не получиться. Есть два варианта решить проблему

Игнорировать ошибки

Если какая-то команда выполнена с ошибкой выполнение сценария прерывается.

RPM_DIR =/home/$$(whoami)/rpms/ .PHONY: clean-repo clean-repo: @ sudo rm $(RPM_DIR)release/* @ sudo rm $(RPM_DIR)master/*

Вместо этого появится ошибка:

sudo rm /home/$(whoami)/rpms/release/* rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory make: *** [clean-repo] Error 1

Избежать этой проблемы можно поставив - перед командой

RPM_DIR =/home/$$(whoami)/rpms/ .PHONY: clean-repo clean-repo: @ -sudo rm $(RPM_DIR)release/* @ -sudo rm $(RPM_DIR)master/*

rm: cannot remove ‘/home/andrei/Downloads/privx/release_rpms/*’: No such file or directory
make: [clean-repo] Error 1 (ignored)

Читайте также: