Утилита make определяет, какие файлы проекта необходимо перестроить для получения готовой программы. Она позволяет собирать огромные и сложные проекты со множеством зависимостей. Для использования make нужно подготовить специальный Makefile с определённой структурой. Работа с make зависит от содержимого Makefile. Makefile представляет собой обычный текстовый файл, который можно распространять вместе с проектом, редактировать в обычном редакторе.
Использование компилятора без make:
gcc -o имя_программы имя1.с имя2.с ...
или раздельная компиляция каждого модуля:
gcc -c имя1.c
gcc -c имя2.с
gcc -o имя_программы имя1.о имя2.о
Каждый раз нужно отслеживать и перекомпилировать изменившиеся файлы. Если построение программы зависит от библиотек, то необходимо писать длинные команды со множеством ключей. Makefile позволяет упростить процесс сборки, автоматизировать его.
Мakefile состоит из так называемых ''правил'', имеющих вид:
имя-результата: исходные-имена ...
команды
...
...
имя-результата - это обычно имя файла, генерируемого программой, например, исполняемый или объектный файл. ''Результатом'' может быть действие никак не связанное с процессом компиляции, например, clean - очистка.
исходное-имя - это имя файла, используемого на вводе, необходимое, чтобы создать файл с именем-результата.
команда - это действие, выполняемое утилитой make. Правило может включать более одной команды, В начале каждой команды надо вставлять отступ (символ ''Tab''). Команда выполняется, если если один из файлов в списке исходные-имена изменился. Допускается написание правила содержащего команду без указания зависимостей. Например, можно создать правило clean, удаляющее объектные файлы проекта, без указания имен.
К числу стандартных правил относятся:
В качестве примера рассмотрим небольшой проект filestat, состоящий из 2-х исходных файлов:
main.c
fstat.c
main.c содержит описание main
, а также вызов функции StatDir
, определённой в файле fstat.c
Содержимое main.c
#include <stdio.h>
#include <dirent.h>
int StatDir(char *dname);
int main(int argc, char **argv)
{
if(argc != 2) {
printf("Usage: filestat dirname\n");
return 1;
}
printf("Using directory %s\n", argv[1]);
printf("Files: %d\n",StatDir(argv[1]));
return 0;
}
Содержимое fstat.c
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int StatDir(char *dname) {
int count=0;
DIR *dir,*subDir;
struct dirent *entry;
struct dirent *subEntry;
char path[1024];
dir = opendir(dname);
if(dir == 0)
return 0;
while(entry = readdir(dir)) {
if(entry->d_type == DT_DIR && (!strcmp(entry->d_name,".") || !strcmp(entry->d_name,"..")))
continue;
if(entry->d_type == DT_DIR)
{
printf("%s\n", entry->d_name);
strcpy(path, dname);
strcat(path, "/");
strcat(path, entry->d_name);
count+=StatDir(path);
}
else {
printf(" %s\n", entry->d_name);
count++;
}
}
closedir(dir);
return count;
}
Приведём пример make-файла для рассматриваемого проекта:
filestat: main.o fstat.o
gcc $(CFLAGS) -o filestat main.o fstat.o
main.o: main.c
gcc $(CFLAGS) -c main.c
fstat.o: fstat.c
gcc $(CFLAGS) -c fstat.c
clean:
rm -f *.o filestat
По-умолчанию работает первое правило в файле (в рассматриваемом примере filestat). Если дать команду make
, то утилита построит программу в соответствием с make-файлом:
$ make
gcc -c main.c
gcc -c fstat.c
gcc -o filestat main.o fstat.o
Если потом будут внесены исправления в один из исходных файлов, то make определит изменения и перекомпилирует только зависимые файлы.
Через переменную CFLSGS в программу можно передавать дополнительные параметры, например
make CFLAGS=-g3
Для очистки содержимого каталога используем другое правило:
$make clean
rm -f *.o filestat
Макросы являются средством замены одних данных на другие (по принципу препроцессора). Часто можно увидеть такое использование макросов
CC=gcc
Далее в тексте Makefile можно использовать запись команды
$(CC) -c main.c
Аналогично можно задавать названия опций, значения параметров и т.д.
Для вывода на экран значения текущих макросов используется команда
make -p
Приведём пример небольшого Make-файла с макросами
CC = gcc
FILES = in_one.c in_two.c
OUT_EXE = out_executable
build: $(FILES)
$(CC) -o $(OUT_EXE) $(FILES)
Другой пример с большим числом правил:
CC = gcc
FILES = in_one.c in_two.c
OUT_EXE = out_executable
build: $(FILES)
$(CC) -o $(OUT_EXE) $(FILES)
clean:
rm -f *.o core
rebuild: clean build
# Makefile to compare sorting routines
BASE = /home/blufox/base
CC = gcc
CFLAGS = -O –Wall
EFILE = $(BASE)/bin/compare_sorts
INCLS = -I$(LOC)/include
LIBS = $(LOC)/lib/g_lib.a \
$(LOC)/lib/h_lib.a
LOC = /usr/local
OBJS = main.o another_qsort.o chk_order.o \
compare.o quicksort.o
$(EFILE): $(OBJS)
@echo "linking …"
@$(CC) $(CFLAGS) –o $@ $(OBJS) $(LIBS)
$(OBJS): compare_sorts.h
$(CC) $(CFLAGS) $(INCLS) –c $*.c
# Clean intermediate files
clean:
rm *~ $(OBJS)