Как создать 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 и для создания .o файлы и исполняемый projectname все в одной папке.



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



 ./
./Makefile
./src/*.c;*.h
./obj/*.o
./bin/<executable>


другими словами, Я хотел бы 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 $@
@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!"
807   3  

3 ответов:

во-первых, ваш $(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!"

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

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

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

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

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

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

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

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

например, рассмотрим такой простой Makefile

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

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

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

обновление>

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

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

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

Comments

    Ничего не найдено.