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

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

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# 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/<executable>

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

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

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

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

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

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# 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 $@
    @echo "Linking complete!"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"
+63
источник поделиться
3 ответа

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

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

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

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

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

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

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

EDIT:
Извините, я пропустил часть правила $(OBJECTS) выше; Я исправил это. (Я бы хотел использовать "забастовку" внутри образца кода.)

+20
источник

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

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

$(TARGET): obj
    @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

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

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

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

%.o: %.c 
   $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

Update >

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

+4
источник
другие ответы

Связанные вопросы


Похожие вопросы

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

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

+1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос