Makefile для самых маленьких
Не очень строгий перевод материала mrbook.org/tutorials/make Мне в свое время очень не хватило подобной методички для понимания базовых вещей о make. Думаю, будет хоть кому-нибудь интересно. Хотя эта технология и отмирает, но все равно используется в очень многих проектах. Кармы на хаб «Переводы» не хватило, как только появится возможность — добавлю и туда. Добавил в Переводы. Если есть ошибки в оформлении, то прошу указать на них. Буду исправлять.
Статья будет интересная прежде всего изучающим программирование на C/C++ в UNIX-подобных системах от самых корней, без использования IDE.
Компилировать проект ручками — занятие весьма утомительное, особенно когда исходных файлов становится больше одного, и для каждого из них надо каждый раз набивать команды компиляции и линковки. Но не все так плохо. Сейчас мы будем учиться создавать и использовать Мейкфайлы. Makefile — это набор инструкций для программы make, которая помогает собирать программный проект буквально в одно касание.
Для практики понадобится создать микроскопический проект а-ля Hello World из четырех файлов в одном каталоге:
#include #include "functions.h" using namespace std; int main()
#include #include "functions.h" using namespace std; void print_hello()
#include "functions.h" int factorial(int n) < if(n!=1)< return(n * factorial(n-1)); >else return 1; >
void print_hello(); int factorial(int n);
Все скопом можно скачать отсюда
Автор использовал язык C++, знать который совсем не обязательно, и компилятор g++ из gcc. Любой другой компилятор скорее всего тоже подойдет. Файлы слегка подправлены, чтобы собирались gcc 4.7.1
Программа make
Если запустить
make
то программа попытается найти файл с именем по умолчание Makefile в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом:
make -f MyMakefile
Есть еще множество других параметров, нам пока не нужных. О них можно узнать в ман-странице.
Процесс сборки
Компилятор берет файлы с исходным кодом и получает из них объектные файлы. Затем линковщик берет объектные файлы и получает из них исполняемый файл. Сборка = компиляция + линковка.
Компиляция руками
Самый простой способ собрать программу:
g++ main.cpp hello.cpp factorial.cpp -o hello
Каждый раз набирать такое неудобно, поэтому будем автоматизировать.
Самый простой Мейкфайл
цель: зависимости [tab] команда
all: g++ main.cpp hello.cpp factorial.cpp -o hello
Обратите внимание, что строка с командой должна начинаться с табуляции! Сохраните это под именем Makefile-1 в каталоге с проектом и запустите сборку командой make -f Makefile-1
В первом примере цель называется all . Это цель по умолчанию для мейкфайла, которая будет выполняться, если никакая другая цель не указана явно. Также у этой цели в этом примере нет никаких зависимостей, так что make сразу приступает к выполнению нужной команды. А команда в свою очередь запускает компилятор.
Использование зависимостей
Использовать несколько целей в одном мейкфайле полезно для больших проектов. Это связано с тем, что при изменении одного файла не понадобится пересобирать весь проект, а можно будет обойтись пересборкой только измененной части. Пример:
all: hello hello: main.o factorial.o hello.o g++ main.o factorial.o hello.o -o hello main.o: main.cpp g++ -c main.cpp factorial.o: factorial.cpp g++ -c factorial.cpp hello.o: hello.cpp g++ -c hello.cpp clean: rm -rf *.o hello
Это надо сохранить под именем Makefile-2 все в том же каталоге
Теперь у цели all есть только зависимость, но нет команды. В этом случае make при вызове последовательно выполнит все указанные в файле зависимости этой цели.
Еще добавилась новая цель clean . Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: make -f Makefile-2 clean
Использование переменных и комментариев
Переменные широко используются в мейкфайлах. Например, это удобный способ учесть возможность того, что проект будут собирать другим компилятором или с другими опциями.
# Это комментарий, который говорит, что переменная CC указывает компилятор, используемый для сборки CC=g++ #Это еще один комментарий. Он поясняет, что в переменной CFLAGS лежат флаги, которые передаются компилятору CFLAGS=-c -Wall all: hello hello: main.o factorial.o hello.o $(CC) main.o factorial.o hello.o -o hello main.o: main.cpp $(CC) $(CFLAGS) main.cpp factorial.o: factorial.cpp $(CC) $(CFLAGS) factorial.cpp hello.o: hello.cpp $(CC) $(CFLAGS) hello.cpp clean: rm -rf *.o hello
Это Makefile-3
Переменные — очень удобная штука. Для их использования надо просто присвоить им значение до момента их использования. После этого можно подставлять их значение в нужное место вот таким способом: $(VAR)
Что делать дальше
После этого краткого инструктажа уже можно пробовать создавать простые мейкфайлы самостоятельно. Дальше надо читать серьезные учебники и руководства. Как финальный аккорд можно попробовать самостоятельно разобрать и осознать такой универсальный мейкфайл, который можно в два касания адаптировать под практически любой проект:
CC=g++ CFLAGS=-c -Wall LDFLAGS= SOURCES=main.cpp hello.cpp factorial.cpp OBJECTS=$(SOURCES:.cpp=.o) EXECUTABLE=hello all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ .cpp.o: $(CC) $(CFLAGS) $< -o $@
Linux: How to Run make
The Linux make utility works by reading and interpreting a makefile. Typically you run make by simply typing the following command at the shell prompt:
When run this way, GNU make looks for a file named GNUmakefile , makefile , or Makefile — in that order. If make finds one of these makefile s, it builds the first target specified in that makefile . However, if make doesn’t find an appropriate makefile , it displays the following error message and exits:
make: *** No targets specified and no makefile found. Stop.
If your makefile happens to have a different name from the default names, you have to use the -f option to specify the makefile . The syntax of the make command with this option is
where filename is the name of the makefile .
Even when you have a makefile with a default name such as Makefile , you may want to build a specific target out of several targets defined in the makefile . In that case, you have to use the following syntax when you run make :
For example, if the makefile contains the target named clean , you can build that target with this command:
Another special syntax overrides the value of a make variable. For example, GNU make uses the CFLAGS variable to hold the flags used when compiling C files. You can override the value of this variable when you invoke make. Here’s an example of how you can define CFLAGS as the option -g -O2 :
In addition to these options, GNU make accepts several other command-line options. This table lists the GNU make options.
Option | Meaning |
---|---|
-b | Ignores the variable given but accepts that variable for compatibility with other versions of make . |
-C DIR | Changes to the specified directory before reading the makefile . |
-d | Prints debugging information. |
-e | Allows environment variables to override definitions of similarly named variables in the makefile . |
-f FILE | Reads FILE as the makefile . |
-h | Displays the list of make options. |
-i | Ignores all errors in commands executed when building a target. |
-I DIR | Searches the specified directory for included makefile s. (The capability to include a file in a makefile is unique to GNU make .) |
-j NUM | Specifies the number of commands that make can run simultaneously. |
-k | Continues to build unrelated targets, even if an error occurs when building one of the targets. |
-l LOAD | Doesn’t start a new job if load average is at least LOAD (a floating-point number). |
-m | Ignores the variable given but accepts that variable for compatibility with other versions of make . |
-n | Prints the commands to execute but does not execute them. |
-o FILE | Does not rebuild the file named FILE, even if it is older than its dependents. |
-p | Displays the make database of variables and implicit rules. |
-q | Does not run anything, but returns 0 (zero) if all targets are up to date, 1 if anything needs updating, or 2 if an error occurs. |
-r | Gets rid of all built-in rules. |
-R | Gets rid of all built-in variables and rules. |
-s | Works silently (without displaying the commands as they execute). |
-t | Changes the timestamp of the files. |
-v | Displays the version number of make and a copyright notice. |
-w | Displays the name of the working directory before and after processing the makefile . |
-W FILE | Assumes that the specified file has been modified (used with -n to see what happens if you modify that file). |
how to run makefile in ubuntu
./hello.exe or ./add.exe works fine. when I type ./all on my friend's pc, it shows proper output.
I've just noticed that you've tagged this as ubuntu but you're compiling .exe programs? My answer still stands but which OS are you using and which OS is your friend using?
2 Answers 2
Your friend's PC is doing something strange and its not clear from your question what they've done. Your PC is acting normally.
Calling make all Will compile everything, but it doesn't actually make a program called all . It only compiles two programs: hello.exe and add.exe .
So calling ./all should fail because that is asking to run a program called all which doesn't exist.
It's quite possible that your friend has written themselves a program or script called "all". You'll need to ask your friend what that script / program does and how it does it.
To see what your friend has done open a terminal on your friends pc (like the one in your screen shot) and type the command
This will list all the files in that directory. Look for one named "all". It might look something like this (with the word "all" in green):
-rwxr----- 1 appy appy 67 Oct 23 15:05 all
Assuming that's there you can look at the contents of it by typing
To get yours to work like your friends, create a similar file with the same contents. To make it runnable you may need to change the file permissions: