Як я можу створити файл Makefile для проектів C із підкаталогами SRC, OBJ та BIN?


95

Кілька місяців тому я придумав такий загальний 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/ .

Я намагався читати різні файли Makefi, але я просто не можу змусити їх працювати для наведеної вище структури проекту; натомість проект не вдається скомпілювати зі всілякими помилками. Звичайно, я міг би використовувати повномасштабну IDE (Monodevelop, Anjuta тощо), але я чесно вважаю за краще дотримуватися gEdit і хороший термінал ol '.

Чи є гуру, який може дати мені діюче рішення або чітку інформацію про те, як це можна зробити? Дякую!

** ОНОВЛЕННЯ (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!"

Яке конкретне питання тут?
Oliver Charlesworth

Я не впевнений, що розумію, що ти хочеш зробити.
Том

Оновлено Makefile. Я наближаюся, але у мене проблеми з автоматичними змінними, тому все одно здається
Янік Рошон

Я щойно знайшов рішення. Якщо хтось піклується знайти щось краще, Makefile все ще можна вдосконалити.
Янік Рошон

2
@YanickRochon Я не хотів критикувати ваші знання англійської мови. Але для цілей PHONY зробити якийсь - який сенс ви напевне не можете писати BANANA;) gnu.org/software/make/manual/html_node/Phony-Targets.html
Джоні

Відповіді:


34

По-перше, ваше $(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)правила вище; Я це виправив. (Мені б хотілося використовувати "страйк" всередині зразка коду.)


з запропонованими вами змінами, я отримую: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
Янік Рошон

@Yanick Rochon: У вас багато mainфункцій? Може, один file1.cі один всередині main.c? Якщо так, тоді ви не зможете зв’язати ці об’єкти; mainу виконуваному файлі може бути лише один .
Бета,

Ні, я не. З останньою версією, яку я розмістив у питанні, все працює нормально. Коли я зміню свій Makefile на те, що ви пропонуєте (і я розумію переваги того, що ви говорите), це я отримую. Я щойно вставив, file1.cале це дає одне і те ж повідомлення для кожного файлу проекту. І main.cце тільки один з основною функцією ... і main.cімпорт file1.hі file2.h(немає ніякого зв'язку між file1.cі file2.c), але я сумніваюся , що проблема приходить звідти.
Янік Рошон

@ Янік Рошон: Я помилився, вставивши перший рядок свого $(OBJECTS)правила; Я його відредагував. З поганою лінією я отримав помилку, але не ту, яку ви отримали ...
Бета

6

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

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

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

Для того, щоб скинути об'єктні файли до objкаталогу, використовуйте -oопцію під час компіляції. Крім того , зверніть увагу на $@і $< автоматичних змінних .

Наприклад, розглянемо цей простий файл Make

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

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

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

Оновити>

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


Ви можете бути більш конкретним? Ви маєте на увазі додавання -l ...до CFLAGSі ... там вже є -oаргумент до посилання ( LINKER)
Янік Рошон

Так, CFLAGS, та так, продовжуйте використовувати -o, просто додайте змінну TARGETPATH.
Том

Дякую, я вніс зміни, але, здається, мені все ще чогось не вистачає (див. Оновлення у питанні)
Янік Рошон

просто make, звідки сидить Makefile
Янік Рошон

Ви не можете прочитати виконувану команду? наприклад gcc -c yadayada. Цілком впевнений, що є змінна, яка не містить того, що ви очікуєте
Том

-1

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

https://github.com/dmoulding/boilermake Я знайшов це досить непогано ..!


3
Думка заснована. Не відповідає на запитання ОП. Припускає середовище Eclipse.
Натаніель Джонсон,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.