Несколько месяцев назад я придумал следующий универсальный вариант 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/
.
Я пробовал читать разные Makefile, но просто не могу заставить их работать для структуры проекта выше; вместо этого проект не компилируется со всевозможными ошибками. Конечно, я мог бы использовать полноценную среду 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!"
Makefile
. Я приближаюсь, но у меня проблемы с автоматическими переменными, так что, похоже, все равноОтветы:
Во-первых, ваше
$(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-файл не будет знать, какие объекты / исполняемые файлы необходимо перестроить. Но это может подождать еще один день.
РЕДАКТИРОВАТЬ:
Извините, я пропустил часть
$(OBJECTS)
правила выше; Я поправил. (Хотел бы я использовать "strike" внутри образца кода.)источник
obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
main
функций? Может быть, один заfile1.c
другимmain.c
? В таком случае вы не сможете связать эти объекты;main
в исполняемом файле может быть только один .file1.c
но он дает одно и то же сообщение для всех файлов проекта. Иmain.c
это единственный, у которого есть основная функция ... иmain.c
импортfile1.h
иfile2.h
(нет связи междуfile1.c
иfile2.c
), но я сомневаюсь, что проблема исходит оттуда.$(OBJECTS)
правила; Я его отредактировал. С плохой строкой у меня ошибка, но не та, что у тебя ...Вы можете добавить
-I
флаг к флагам компилятора (CFLAGS), чтобы указать, где компилятор должен искать исходные файлы, и флаг -o, чтобы указать, где следует оставить двоичный файл:CFLAGS = -Wall -I./src TARGETPATH = ./bin $(TARGET): obj @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS) @echo "Linking complete!"
Чтобы поместить объектные файлы в
obj
каталог, используйте-o
опцию компиляции. Кроме того , обратите внимание на$@
и$<
автоматических переменных .Например, рассмотрим этот простой Makefile
Обновить>
Глядя на ваш make-файл, я понимаю, что вы используете
-o
флаг. Хороший. Продолжайте использовать его, но добавьте переменную целевого каталога, чтобы указать, куда должен быть записан выходной файл.источник
-l ...
кCFLAGS
и ... уже есть-o
аргумент для линкера (LINKER
)make
, где находится MakefileВ наши дни я перестал писать make-файлы, если вы собираетесь учиться, продолжайте, иначе у вас есть хороший генератор make-файлов, который поставляется с eclipse CDT. Если вам нужна некоторая ремонтопригодность / поддержка нескольких проектов в вашем дереве сборки, взгляните на следующее:
https://github.com/dmoulding/boilermake Я нашел это очень хорошим ...!
источник