Могу ли я скомпилировать все файлы .cpp в src / в .o в obj /, а затем связать их с двоичным файлом в ./?

116

Каталог моего проекта выглядит так:

/project
    Makefile
    main
    /src
        main.cpp
        foo.cpp
        foo.h
        bar.cpp
        bar.h
    /obj
        main.o
        foo.o
        bar.o

Я бы хотел, чтобы мой make-файл скомпилировал все .cppфайлы в /srcпапке в .oфайлы в /objпапке, а затем связал все .oфайлы в /objвыходной двоичный файл в папке верхнего уровня /project.

У меня почти нет опыта работы с Makefiles, и я не совсем уверен, что искать для этого.

Кроме того, это «хороший» способ сделать это или есть более стандартный подход к тому, что я пытаюсь сделать?

Остин Хайд
источник
6
@aaa: Я предполагаю, что OP хочет решение, которое не требует явного перечисления каждого исходного файла.
Cascabel
7
Я не хочу указывать каждый исходный файл, который у меня есть, и я пытался прочитать это руководство раньше, но считаю его неорганизованным и трудным для понимания. Я гораздо лучше учусь на реальном примере, который делает то, что я ожидаю, и хорошо объяснен, а не на сухих технических руководствах.
Остин Хайд
Ладно. Но документация make превосходна с хорошими примерами (это не пробное техническое руководство). Вы ищете шаблонных правил: gnu.org/software/make/manual/make.html#Pattern-Rules
Anycorn
11
Это немного больше похоже на то, что я хочу. Хотя, ИМХО, руководство по make немного суховато, так как кажется более ориентированным на разработчиков, которые находятся на среднем уровне с make, а кроме того, оно очень большое и подробное. Возможно, даже слишком.
Остин Хайд

Ответы:

180

Makefile - часть вопроса

Это довольно просто, если вам не нужно обобщать, попробуйте что-то вроде приведенного ниже кода (но замените отступы табуляцией рядом с g ++)

SRC_DIR := .../src
OBJ_DIR := .../obj
SRC_FILES := $(wildcard $(SRC_DIR)/*.cpp)
OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
LDFLAGS := ...
CPPFLAGS := ...
CXXFLAGS := ...

main.exe: $(OBJ_FILES)
   g++ $(LDFLAGS) -o $@ $^

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
   g++ $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<

Автоматическая генерация графа зависимостей

Обязательная функция для большинства систем make. С GCC это можно сделать за один проход в качестве побочного эффекта компиляции, добавив -MMDфлаг в CXXFLAGSи -include $(OBJ_FILES:.o=.d) в конец тела make-файла:

CXXFLAGS += -MMD
-include $(OBJ_FILES:.o=.d)

И, как уже упоминалось, всегда имейте под рукой GNU Make Manual , это очень полезно.

BoBaH
источник
11
Ах, ты победил меня на секунды. Но я предлагаю OBJ_FILES = $(patsubst src/%.cpp,obj/%.o,$(CPP_FILES)).
Бета
1
Мне пришлось изменить это, чтобы он заработал: $<должно быть $^для правила main.exe, и я думаю, что есть опечатка с obj/%.o: src/%cpp.
1
@bobah Вам не хватает "." в правиле ваших объектов для cpp
regomodo
1
специальные переменные, вероятно, заслуживают объяснения, поскольку они специфичны для make-файла и их сложно искать: gnu.org/software/make/manual/html_node/Automatic-Variables.html
Блейк
2
Я знаю, что это старый вопрос, но я немного изменил его в соответствии с моим проектом, но он не работает. Вот мой make-файл: pastebin.com/4CksG9Wc Я получаю в консоли:make: *** No rule to make target '/main.o', needed by 'bin/main'. Pare.
Mateus Felipe
5

Подстановочный знак работает и у меня, но я хотел бы сделать небольшое замечание для тех, кто использует переменные каталога. Всегда используйте косую черту для дерева папок (не обратную косую черту), иначе это не удастся:

BASEDIR = ../..
SRCDIR = $(BASEDIR)/src
INSTALLDIR = $(BASEDIR)/lib

MODULES = $(wildcard $(SRCDIR)/*.cpp)
OBJS = $(wildcard *.o)
xesf
источник
ты имел ввиду косую черту? Ваш пример показывает, что традиционно считается косой чертой. В частности, те, кто «наклоняется вправо», считаются «вперед», а те, что слева, считаются «назад»
Эван Теран
Да, вы правы, я имею в виду только "слэш". Я обновил пост.
xesf