Как я могу создать Makefile для проектов C с подкаталогами SRC, OBJ и BIN? - PullRequest
77 голосов
/ 10 августа 2011

Несколько месяцев назад я придумал следующий общий 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!"

Ответы [ 3 ]

28 голосов
/ 10 августа 2011

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

  1. это как бы неразборчиво, поэтому все исходные предпосылки каждого объекта
  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 $@
    @echo "Compiled "$<" successfully!"

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

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

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

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

5 голосов
/ 10 августа 2011

Вы можете добавить флаг -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)/$@

Обновление>

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

0 голосов
/ 10 августа 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...