## 
## Определим компилятор.
## 
CC=gcc

## 
## Определим сборщик.
## 
LD=gcc

## 
## Зададим флаги компиляции.
## 
CFLAGS=-c -Wall -pedantic

## 
## Зададим флаги сборки.
## В них мы подключаем библиотеку libm (-lm) и пр.
## 
LDFLAGS=

## 
## Зададим имя для исполняемого файла.
## 
EXECUTABLE=queue

## 
## Директория с исходными файлами кода *.c
## 
LIBSRCDIR=./lib/src


##
## Директория с исходными файлами кода *.c
##
SRCDIR=./src

## 
## Директория с заголовочными файлами кода *.h
## 
LIBINCDIR=./lib/includes

## 
## Директория с объектными файлами *.o
## 
OBJDIR=./obj

## 
## Опишем модули программы
## 
MODULES=queue
MAIN=main

## Строка с заголовочными файлами.
## 	Для patsubst формат команды: $(patsubst pattern, replacement, text)
## 
INCLUDES = $(patsubst %,$(LIBINCDIR)/%.h,$(MODULES))

##
## Строка с исходными кодами.
##
SOURCES  = $(patsubst %,$(LIBSRCDIR)/%.c,$(MODULES)) $(patsubst %,$(SRCDIR)/%.c,$(MAIN))

##
## Строка с объектными файлами.
##
OBJECTS  = $(patsubst %,$(OBJDIR)/%.o,$(MODULES) $(MAIN))


## 
## Опишем суффиксы, которые используются в целях.
## 
.SUFIXES: .h .c .o


##
## Соберем "всё", т.е. соберем исполняемый файл.
## Псевдоним для цели `$(EXECUTABLE)`.
##	
all: $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
	# 
	# 	Собираем исполняемый файл из объектных.
	# 
	$(LD) $(LDFLAGS) $^ -o $@

$(OBJDIR)/%.o: | $(OBJDIR)
	# 
	# 	Собираем объектные файлы из исходных файлов с кодом.
	# 		Подключаем директорию с .h через опцию -I.
	# 		Если компиляция не свершилась, шумно сообщаем об этом.
	# 
	echo $(LIBINCDIR)
	$(CC) $(CFLAGS) -I$(LIBINCDIR) $< -o $@  -O3 \
	|| (	echo -e "\n\nMAY BE PROBLEMS WITH '.depend'."  && 	\
		echo -e "CHECK IT OR FIX IT WITH 'make depend'.\n\n" && \
		exit 1;);

$(OBJDIR):
	# 	Создаем директорию $(OBJDIR) если ее нет.
	mkdir -p $@

## 
## Подкачаем файл описания зависимостей.
## Если его нет, то он просто не подключится. Ошибка выводиться не будет. 
## Ругнется только `gcc`, потому что не будет знать как и что компилировать.
## 
sinclude .depend


clean: clean_obj
	# 
	# 	Удаляем исполняемый файл.
	# 
	rm -f "$(EXECUTABLE)";
	
clean_obj:
	# 
	# 	Удаляем объектные файлы.
	# 
	rm -f  "$(OBJECTS)";
	
	#
	# 	Удаляем директорию с объектными файлами.
	# 
	rm -rf "$(OBJDIR)";

## -------------------------------------------------------------------------
## В реальной жизни проекты состоят из сотен модулей.
## Прописывать зависимости вручную не представляется возможным.
## Часто удобно пользоваться возможностями `gcc`.
## -------------------------------------------------------------------------

##
## Цель для генерации файла зависимостей.
## 
depend: $(SOURCES)
	# 
	# 	Удаляем старый файл зависимостей.
	# 	
	rm -f ./.depend
	
	# 
	# 	Создаем новый файл зависимостей.
	# 	* Ключ -MM заставляет gcc выводить зависимости файлов 
	# 		при сборке. Результат уже в формате Makefile.
	# 	* sed добавляет префикс `$(OBJDIR)` к каждой цели 
	# 		файла зависимостей. Делается это с помощью 
	# 		регулярного выражения замены.
	# 
	
	$(CC) -I"$(LIBINCDIR)" -MM $^ | sed -E 's/(.*)[:]/$$(OBJDIR)\/\1:/' \
		>> ./.depend;

## -------------------------------------------------------------------------
## Makefile можно использовать не только для сборки программ,
## но и для чего угодно. Например, мы хотим наши исходные коды,
## запаковывать в архив одной командой и не перепаковывать (по этой команде), 
## если такой архив уже есть.
## Или, например, сделать снимок проекта (snapshot) в текущий момент времени.
## -------------------------------------------------------------------------

##
## Зададим имя архива с проектом.
##
PACKNAME=pack

##
## Зададим расширение пакета с проектом.
##
PACKEXTN=tar.bz2

##
## Полное имя архива.
##
FULL_PACK_NAME=$(PACKNAME).$(PACKEXTN)

##
## Что паковать.
##
FILES_FOR_PACK=$(INCLUDES) $(SOURCES) $(MAKEFILE_LIST)

##
## Упакуем архив если его еще нет. 
## Псевдоним для цели `$(FULL_PACK_NAME)`.
##	
pack: | $(FULL_PACK_NAME)

##
## Упакуем архив если его еще нет.
##	
$(FULL_PACK_NAME):
	#
	#	Создаем архив из исходных кодов проекта и элементов Makefile.
	#
	tar -cjf $@ $(FILES_FOR_PACK)

clean_pack:
	#
	# 	Удаляет архив.
	#
	rm -f $(FULL_PACK_NAME);

##
## По `make repack` Перекуем архив, даже если он есть.
##
repack: clean_pack pack

##
## Полное имя для снимка проекта в текущий момент времени.
## C помощью -$(shell <Команда>) мы можем использовать команды bash.
##
SNAPSHOTNAME=$(PACKNAME)-$(shell date "+%Y-%m-%d_%H-%M-%S-%N").$(PACKEXTN)

snapshot:
	#
	# 	Создаем архив со снимком проекта.
	#
	tar -cjf $(SNAPSHOTNAME) $(FILES_FOR_PACK)

clean_snapshots:
	#
	#	Удаляем все архивы в папке.
	#
	rm -f *.$(PACKEXTN);

##
## Цели, которые не создают одноименных файлов.
##
.PHONY : all depend 		\
	clean clean_obj 	\
	pack clean_pack repack 	pack \
	snapshot clean_snapshots

