Czy mogę skompilować wszystkie pliki .cpp w src / do .o's w obj /, a następnie połączyć się z binarnym w ./?

116

Mój katalog projektu wygląda następująco:

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

Chciałbym, aby mój .cppplik makefile skompilował wszystkie pliki w /srcfolderze do .oplików w /objfolderze, a następnie połączył wszystkie .opliki w /objwyjściowym pliku binarnym w folderze najwyższego poziomu /project.

Nie mam prawie żadnego doświadczenia z Makefiles i nie jestem pewien, czego szukać, aby to osiągnąć.

Poza tym, czy jest to „dobry” sposób na zrobienie tego, czy też istnieje bardziej standardowe podejście do tego, co próbuję zrobić?

Austin Hyde
źródło
6
@aaa: Zgaduję, że OP chce rozwiązania, które nie wymaga jawnego wymieniania każdego pliku źródłowego.
Cascabel
7
Nie chcę określać każdego pliku źródłowego, który mam i próbowałem wcześniej przeczytać ten podręcznik, ale uważam, że jest on chaotyczny i trudny do zrozumienia. Dużo lepiej uczę się z rzeczywistego przykładu, który robi to, czego oczekuję i jest dobrze wyjaśniony, a nie suchych instrukcji technicznych.
Austin Hyde,
w porządku. Ale dokumentacja jest doskonała z dobrymi przykładami (nie jest to próba podręcznika technicznego). szukasz reguł wzorców: gnu.org/software/make/manual/make.html#Pattern-Rules
Anycorn
11
To wygląda trochę bardziej jak to, czego chcę. Chociaż, IMHO, instrukcja tworzenia jest trochę sucha, ponieważ wydaje się być bardziej ukierunkowana na programistów, którzy są na średnim poziomie z marką, a poza tym jest bardzo obszerna i dogłębna. Być może za bardzo.
Austin Hyde,

Odpowiedzi:

180

Makefile część pytania

Jest to dość łatwe, chyba że nie musisz generalizować, spróbuj czegoś takiego jak poniższy kod (ale zamień wcięcie spacji na tabulatory w pobliżu 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 $@ $<

Automatyczne generowanie wykresów zależności

Funkcja „obowiązkowa” dla większości systemów marki. Za pomocą GCC można to zrobić w jednym przebiegu jako efekt uboczny kompilacji, dodając -MMDflagę do CXXFLAGSi -include $(OBJ_FILES:.o=.d) na końcu treści makefile:

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

I jak już wspomnieliśmy, zawsze miej pod ręką Podręcznik tworzenia GNU , jest on bardzo pomocny.

bobah
źródło
11
Ach, pokonałeś mnie o kilka sekund. Ale proponuję OBJ_FILES = $(patsubst src/%.cpp,obj/%.o,$(CPP_FILES)).
Beta
1
Musiałem to zmienić, aby działało: $<powinno być $^dla reguły main.exe i myślę, że jest literówka z obj/%.o: src/%cpp.
1
@bobah Brakuje „.” in your objects rule for the cpp
regomodo
1
specjalne zmienne są prawdopodobnie warte wyjaśnienia, ponieważ są specyficzne dla makefile i trudne do wyszukania: gnu.org/software/make/manual/html_node/Automatic-Variables.html
Blake
2
Wiem, że to stare pytanie, ale trochę zmodyfikowałem zgodnie z moim projektem, ale nie działa. Oto mój plik makefile: pastebin.com/4CksG9Wc Otrzymuję w konsoli:make: *** No rule to make target '/main.o', needed by 'bin/main'. Pare.
Mateus Felipe,
5

Znaki wieloznaczne również działają u mnie, ale chciałbym wspomnieć o tych, którzy używają zmiennych katalogowych. Zawsze używaj ukośnika dla drzewa folderów (nie odwrotnego ukośnika), w przeciwnym razie nie powiedzie się:

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

MODULES = $(wildcard $(SRCDIR)/*.cpp)
OBJS = $(wildcard *.o)
xesf
źródło
miałeś na myśli cięcie do przodu? Twój przykład pokazuje, co tradycyjnie jest uważane za ukośnik. W szczególności, te „pochylony w prawo” są uważane za „do przodu”, a te są uważane za pozostawione za „plecami”
Evan Teran
Tak, masz rację, mam na myśli tylko „slash”. Zaktualizowałem post.
xesf