Страницы

Поиск по вопросам

пятница, 13 декабря 2019 г.

Создание makefile

#cpp #компиляция #makefile


Пытаюсь разобраться в написании грамотного make файла, это ад.

Есть такие файлы: SubClass.h/.cpp, SuperClass.h/.cpp, Main.cpp.

Сконструировал такой makefile:

Programm: Main.o SuperClass.o SubClass.o
    g++ -o Programm Main.o SuperClass.o SubClass.o
Main.o: Main.cpp SuperClass.o SubClass.o
    g++ -c -o Main.o Main.cpp
SuperClass.o: SuperClass.h SuperClass.cpp
    g++ -c -o SuperClass.o SuperClass.cpp
SubClass.o: SubClass.cpp SubClass.h SuperClass.o
    g++ -c -o SubClass.o SubClass.cpp


Классно бы это автоматизировать и разделить по директориям /h /cpp /o файлы.

Нашел хороший способ получать зависимости (через include bash добавил в makefile)

g++ -MM SubClass.cpp
SubClass.o: SubClass.cpp SubClass.h SuperClass.h


А нужно что бы выхлоп был SubClass.o: SubClass.cpp SubClass.h SuperClass.o, не понимаю,
как мне этого добиться с помощью make? Я явно делаю что то не так.

Через wildecard научился получать список файлов в папке, а вот в подпапках как (нахождение
самих подпапок и тд)?

Это ведь такая простая и популярная модель организации проекта, неужели нет ничего
готового? Возможно я пишу велосипед? :D 

PS: Я не прошу что то писать за меня, просто наведите в нужное направление.
    


Ответы

Ответ 1



мне кажется, ваш файл несколько многословен. для всех описанных действий, кажется, достаточно таких строк: Programm: Main.o SuperClass.o SubClass.o g++ -o $@ $^ запуск make с параметром -n (dry-run) показывает такую последовательность команд (при наличии в текущем каталоге только cpp- и h-файлов): g++ -c -o Main.o Main.cpp g++ -c -o SuperClass.o SuperClass.cpp g++ -c -o SubClass.o SubClass.cpp g++ -o Programm Main.o SuperClass.o SubClass.o немного объяснений по поводу «применённой магии» «магия» называется implicit rules. все актуальные implicit rules (и ещё многое другое) можно посмотреть командой make -p | less. к примеру, о том, что из file.cpp можно собарть file.o (и, главное, как это сделать), make «знает» благодаря вот такому правилу: %.o: %.cpp # recipe to execute (built-in): $(COMPILE.cpp) $(OUTPUT_OPTION) $<

Ответ 2



А нужно что бы выхлоп был SubClass.o: SubClass.cpp SubClass.h SuperClass.o А зачем? Зачем при перекомпиляции SuperClass.cpp перекомпилировать SubClass.cpp? Почему бы не сделать так: Gpp = g++ srcs = SubClass.cpp SuperClass.cpp main.cpp objs = $(srcs:.cpp=.o) deps = $(srcs:.cpp=.d) program: $(objs) $(Gpp) $^ -o $@ %.o: %.cpp $(Gpp) -MMD -MP -c $< -o $@ .PHONY: clean # $(RM) is rm -f by default clean: $(RM) $(objs) $(deps) program -include $(deps) Поясняю: g++ сам сгенерит файлы с зависимостями (.d). %.o: %.cpp $(Gpp) -MMD -MP -c $< -o $@ Это правило сборки каждого .o файла, которое выполняет g++ с параметром -MMD, которая генерирует файл с зависимости (без системных файлов). Файлы с зависимостями перегенерируются только при изменении исходников. objs=,deps= генерируем список файлов с помощью встроенной возможности замены в make $@ и $< так называемые "автоматические переменные". $< - имя первой зависимости (first prerequisite). $@ - имя цели для правила (выходного файла) (target file name). Пример: hello.o: hello.c hello.h gcc -c $< -o $@ $@ = hello.o - имя выходного файла. $< = hello.c hello.h - входные файлы В последней строчке мейкфайла включаем файлы с зависимостями (- в начале строки означает игнорировать ошибки.

Ответ 3



Обычно мы сравнительно редко пишем makefile, а вся вроде бы освоенная "магия" так и норовит за полгода улетучиться из головы. Поэтому я стараюсь писать простые Makefile, вроде такого (для вашего случая). # Эти обозначения я вечно забываю: # $@ target # $^ all right part # $? only new in right part # $< first name from right part # #CC = gcc #CFLAGS = -g -std=gnu99 CC = g++ CXXFLAGS = -O3 -pthread SRCS = SubClass.cpp SuperClass.cpp main.cpp PROGS = programm all: $(PROGS) programm: main.o SubClass.o SuperClass.o $(CC) -o $@ $^ -pthread .PHONY: clean clean: rm -rf $(PROGS) *.o *~ *.bak a.out depend: @makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null # DO NOT DELETE SubClass.o: f.h data.h main.o: f.h Строки после depend с зависимостями .o от .h добавляет команда makedepend (символ @ перед ней подавляет ее вывод в stdout), которая исполняется, когда мы запускаем > make depend после изменения состава файлов проекта или добавления наших (не системных) .h файлов в какой-нибудь .cpp Когда мы вызываем make без аргументов, то Makefile начинает выполняться с первой метки (остальные, если не зависят от нее, уже не будут рассматриваться). Здесь all: вроде бы излишня (можно было бы сразу начать файл с programm:), но иногда хочется одним вызовом make собрать сразу несколько модулей и тогда они просто перечисляются в переменной PROGS. Здесь я в main.cpp включаю f.h, в SubClass.cpp включаю f.h и data.h, а в SuperClass.cpp ничего не включаю. Make по умолчанию строит зависимости .o от .cpp (или .c) и вызывает g++ с подстановкой значения из переменной CXXFLAGS (а для gcc использует CFLAGS). Ключи для линкера (вызов $(CC) -o ...) я предпочитаю прописывать явно либо в команде, либо в переменных makefile, которые туда подставляю. Это может сберечь кучу времени при отладке через год-другой (особенно если при вызове make образуется длиннющая "простыня"). Если для компиляции какого-либо файла требуются нестандартные действия, то это прописываем вручную. Например, мы хотим (может быть иногда) использовать при компиляции файла SubClass.cpp переменную препроцессора TEST. Тогда напишем SubClass.o: SubClass.cpp Makefile $(CC) $(CXXFLAGS) -c -DTEST $< где нибудь между all: и depend:. В таком случае мы должны включать в аргументы выполняемой команды переменную CXXFLAGS явно. Я написал здесь еще и зависимость от самого Makefile, чтобы при редактировании -DTEST проходила перекомпиляция SubClass.o и дальше пересборка programm. Вот результат avp@avp-ubu1:tmake$ make clean rm -rf programm *.o *~ *.bak a.out avp@avp-ubu1:tmake$ make g++ -O3 -pthread -c -o main.o main.cpp g++ -O3 -pthread -c -DTEST SubClass.cpp g++ -O3 -pthread -c -o SuperClass.o SuperClass.cpp g++ -o programm main.o SubClass.o SuperClass.o -pthread avp@avp-ubu1:tmake$ ./programm f1() f2(): xaxa Теперь отредактируем Makefile, уберем -DTEST при компиляции SubClass.cpp avp@avp-ubu1:tmake$ make g++ -O3 -pthread -c SubClass.cpp g++ -o programm main.o SubClass.o SuperClass.o -pthread avp@avp-ubu1:tmake$ ./programm f2(): xaxa avp@avp-ubu1:tmake$

Комментариев нет:

Отправить комментарий