Как создать проекты Makefile для C с подкаталогами SRC, OBJ и BIN?

Несколько месяцев назад я придумал следующий общий Makefile для школьных заданий:

 # ------------------------------------------------ # Generic Makefile # # Author: [email protected] # Date : 2010-11-05 # # Changelog : # 0.01 - first version # ------------------------------------------------ # project name (generate executable with this name) TARGET = projectname CC = gcc -std=c99 -c # compiling flags here CFLAGS = -Wall -I. LINKER = gcc -o # linking flags here LFLAGS = -Wall SOURCES := $(wildcard *.c) INCLUDES := $(wildcard *.h) OBJECTS := $(SOURCES:.c=*.o) rm = rm -f $(TARGET): obj @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS) @echo "Linking complete!" obj: $(SOURCES) $(INCLUDES) @$(CC) $(CFLAGS) $(SOURCES) @echo "Compilation complete!" clean: @$(rm) $(TARGET) $(OBJECTS) @echo "Cleanup complete!" 

Это будет в основном компилировать каждый .c и .h файл для генерации файлов .o и исполняемого имени projectname в одной и той же папке.

Теперь я хотел бы немного подтолкнуть это. Как я могу написать Makefile для компиляции проекта C со следующей структурой каталогов?

  ./ ./Makefile ./src/*.c;*.h ./obj/*.o ./bin/ 

Другими словами, я хотел бы иметь Makefile, который компилирует источники C из ./src/ в ./obj/ а затем связывает все, чтобы создать исполняемый файл в ./bin/ .

Я пытался читать разные Makefile, но я просто не могу заставить их работать над структурой проекта выше; вместо этого проект не может скомпилировать всевозможные ошибки. Конечно, я мог бы использовать полномасштабную IDE (Monodevelop, Anjuta и т. Д.), Но я честно предпочитаю придерживаться gEdit и хорошего терминала.

Есть ли гуру, который может дать мне рабочее решение или получить четкую информацию о том, как это можно сделать? Спасибо!

** ОБНОВЛЕНИЕ (v4) **

Окончательное решение:

 # ------------------------------------------------ # Generic Makefile # # Author: [email protected] # Date : 2011-08-10 # # Changelog : # 2010-11-05 - first version # 2011-08-10 - added structure : sources, objects, binaries # thanks to http://stackoverflow.com/users/128940/beta # 2017-04-24 - changed order of linker params # ------------------------------------------------ # project name (generate executable with this name) TARGET = projectname CC = gcc # compiling flags here CFLAGS = -std=c99 -Wall -I. LINKER = gcc # linking flags here LFLAGS = -Wall -I. -lm # change these to proper directories where each file should be SRCDIR = src OBJDIR = obj BINDIR = bin SOURCES := $(wildcard $(SRCDIR)/*.c) INCLUDES := $(wildcard $(SRCDIR)/*.h) OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) rm = rm -f $(BINDIR)/$(TARGET): $(OBJECTS) @$(LINKER) $(OBJECTS) $(LFLAGS) -o [email protected] @echo "Linking complete!" $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c @$(CC) $(CFLAGS) -c $< -o [email protected] @echo "Compiled "$<" successfully!" .PHONY: clean clean: @$(rm) $(OBJECTS) @echo "Cleanup complete!" .PHONY: remove remove: clean @$(rm) $(BINDIR)/$(TARGET) @echo "Executable removed!" 

Во-первых, ваше правило $(OBJECTS) проблематично, потому что:

  1. это своего рода неparsingчиво, что делает все источники предпосылками каждого объекта,
  2. он часто использует неправильный источник (как вы обнаружили с file1.o и file2.o )
  3. он пытается создавать исполняемые файлы вместо остановки на объектах и
  4. имя цели ( foo.o ) – это не то, что правило будет фактически создавать ( obj/foo.o ).

Я предлагаю следующее:

 OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c $(CC) $(CFLAGS) -c $< -o [email protected] @echo "Compiled "$<" successfully!" 

Правило $(TARGET) имеет ту же проблему, что имя цели фактически не описывает то, что строит правило. По этой причине, если вы набираете make несколько раз, Make будет каждый раз перестраивать цель, даже если нет причин. Небольшое изменение исправляет:

 $(BINDIR)/$(TARGET): $(OBJECTS) $(LINKER) [email protected] $(LFLAGS) $(OBJECTS) @echo "Linking complete!" 

Как только все будет в порядке, вы можете рассмотреть более сложную обработку зависимостей; если вы изменяете один из файлов заголовков, этот make-файл не будет знать, какие объекты / исполняемые файлы необходимо перестроить. Но это может подождать еще один день.

РЕДАКТИРОВАТЬ:
Извините, я пропустил часть правила $(OBJECTS) выше; Я исправил это. (Я хотел бы использовать «забастовку» внутри образца кода.)

Вы можете добавить флаг -I к флагам компилятора (CFLAGS), чтобы указать, где компилятор должен искать исходные файлы, и флаг -o, чтобы указать, где нужно оставить двоичный файл:

 CFLAGS = -Wall -I./src TARGETPATH = ./bin $(TARGET): obj @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS) @echo "Linking complete!" 

Чтобы удалить объектные файлы в каталог obj , используйте команду -o при компиляции. Кроме того, посмотрите на переменные [email protected] и $< .

Например, рассмотрим этот простой Makefile

 CFLAGS= -g -Wall -O3 OBJDIR= ./obj SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o ) all:$(OBJS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/[email protected] 

Обновление>

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

В наши дни я перестала писать make-файлы, если вы намерены учиться, иначе у вас будет хороший генератор makefile, который поставляется с eclipse CDT. Если вы хотите, чтобы в вашем дереве сборки поддерживалась техническая поддержка / поддержка нескольких проектов, посмотрите на следующее:

https://github.com/dmoulding/boilermake Я нашел это довольно хорошим ..!

  • Общий путь каталога файлов GNU
  • Как написать цикл в Makefile?
  • Makefile для размещения объектных файлов из исходных файлов разных каталогов в один отдельный каталог?
  • Что означают символы makefile $ @ и $ <mean?
  • Как сгенерировать Makefile с источником в подкаталогах, используя только один make-файл
  • Makefile ifeq логический или
  • Создание C-программы «из исходного дерева» с помощью GNU make
  • Может ли GNU создавать имена файлов с пробелами?
  • В чем разница между назначениями переменных GNU Makefile =,? =,: = И + =?
  • CMake: Как установить LDFLAGS в CMakeLists.txt?
  • Что противоположно «make install», то есть как вы удаляете библиотеку в Linux?
  • Давайте будем гением компьютера.