Jak mogę utworzyć plik Makefile dla projektów C z podkatalogami SRC, OBJ i BIN?


95

Kilka miesięcy temu wymyśliłem następujący ogólny opis Makefilezadań szkolnych:

# ------------------------------------------------
# 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!"

To w zasadzie skompiluje wszystkie pliki .ci .h, aby wygenerować .opliki i plik wykonywalny projectnamew tym samym folderze.

Teraz chciałbym to trochę popchnąć. Jak napisać plik Makefile, aby skompilować projekt w języku C o następującej strukturze katalogów?

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

Innymi słowy, chciałbym mieć Makefile że źródła C kompiluje ./src/się ./obj/, a następnie połączyć wszystko, aby utworzyć plik wykonywalny w ./bin/.

Próbowałem czytać różne pliki Makefile, ale po prostu nie mogę zmusić ich do pracy z powyższą strukturą projektu; zamiast tego projekt nie kompiluje się z różnymi rodzajami błędów. Jasne, mógłbym użyć pełnego IDE (Monodevelop, Anjuta itp.), Ale szczerze mówiąc wolę pozostać przy gEdit i starym dobrym terminalu.

Czy jest jakiś guru, który może dać mi działające rozwiązanie lub jasne informacje o tym, jak można to zrobić? Dziękuję Ci!

** UPDATE (v4) **

Ostateczne rozwiązanie :

# ------------------------------------------------
# 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!"

Jakie jest tutaj konkretne pytanie?
— Oliver Charlesworth

Nie jestem pewien, czy rozumiem, co chcesz zrobić.
— Tom,

Zaktualizowano Makefile. Zbliżam się, ale mam problem ze zmiennymi automatycznymi, więc wydaje mi się, że tak czy inaczej
— Yanick Rochon

Właśnie znalazłem rozwiązanie. Jeśli ktoś chce znaleźć coś lepszego, nadal można ulepszyć Makefile.
— Yanick Rochon

2
@YanickRochon Nie chciałem krytykować twoich umiejętności językowych. Ale żeby cele PHONY miały jakikolwiek sens, zdecydowanie nie możesz pisać BANANA;) gnu.org/software/make/manual/html_node/Phony-Targets.html
— joni

Odpowiedzi:


34

Po pierwsze, twoja $(OBJECTS)reguła jest problematyczna, ponieważ:

  1. to trochę bezkrytyczne, stawiając wszystkie źródła jako warunki wstępne każdego obiektu,
  2. często korzysta z niewłaściwego źródła (jak odkryłeś z file1.oi file2.o)
  3. próbuje budować pliki wykonywalne zamiast zatrzymywać się na obiektach, a
  4. nazwa target ( foo.o) nie jest tym, co reguła faktycznie wyprodukuje ( obj/foo.o).

Proponuję co następuje:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

$(TARGET)Reguła ma ten sam problem, że nazwa docelowa faktycznie nie opisać to, co buduje się regułą. Z tego powodu, jeśli wpiszesz makekilka razy, Make za każdym razem odbuduje cel, nawet jeśli nie ma powodu. Mała zmiana naprawia to:

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

Kiedy wszystko jest w porządku, możesz rozważyć bardziej wyrafinowaną obsługę zależności; jeśli zmodyfikujesz jeden z plików nagłówkowych, ten plik makefile nie będzie wiedział, które obiekty / pliki wykonywalne muszą zostać odbudowane. Ale to może poczekać na kolejny dzień.

EDYCJA:
Przepraszam, pominąłem część $(OBJECTS)powyższej reguły; Poprawiłem to. (Chciałbym móc użyć słowa „strike” w próbce kodu).


z twoimi sugerowanymi zmianami, otrzymuję: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

@Yanick Rochon: Czy masz wiele mainfunkcji? Może jeden w file1.ci jeden w main.c? Jeśli tak, to nie będziesz w stanie połączyć tych obiektów; mainplik wykonywalny może zawierać tylko jeden plik.
— Beta

Nie ja nie. Wszystko działa dobrze z ostatnią wersją, którą zamieściłem w pytaniu. Kiedy zmieniam mój plik Makefile na to, co sugerujesz (i rozumiem korzyści z tego, co mówisz), to właśnie otrzymuję. Właśnie wkleiłem, file1.cale daje to ten sam komunikat do wszystkich plików projektu. I main.cjako jedyny ma główną funkcję ... i main.cimportuje file1.hi file2.h(nie ma związku między file1.ci file2.c), ale wątpię, że problem bierze się stąd.
— Yanick Rochon

@Yanick Rochon: Popełniłem błąd, wklejając pierwszą linię mojej $(OBJECTS)reguły; Edytowałem to. Ze złą linią mam błąd, ale nie ten, który masz ...
— Beta

6

Możesz dodać -Iflagę do flag kompilatora (CFLAGS), aby wskazać, gdzie kompilator powinien szukać plików źródłowych, oraz flagę -o, aby wskazać, gdzie powinien pozostać plik binarny:

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

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

Aby upuścić pliki obiektowe do objkatalogu, użyj -oopcji podczas kompilacji. Spójrz także na zmienne automatyczne$@ i .$<

Na przykład rozważmy ten prosty plik Makefile

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

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

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

Aktualizuj>

Patrząc na twój plik makefile, zdaję sobie sprawę, że używasz -oflagi. Dobry. Kontynuuj używanie, ale dodaj zmienną katalogu docelowego, aby wskazać, gdzie powinien zostać zapisany plik wyjściowy.


możesz być bardziej dokładny? Czy masz na myśli dodanie -l ...do CFLAGSi ... jest już -oargument do linkera ( LINKER)
— Yanick Rochon

Tak, CFLAGS i tak, nadal używaj -o, po prostu dodaj zmienną TARGETPATH.
— Tom

Dziękuję, dokonałem modyfikacji, ale wydaje mi się, że wciąż czegoś mi brakuje (zobacz aktualizację pytania)
— Yanick Rochon

właśnie makez miejsca, w którym znajduje się
— plik

Nie możesz odczytać wykonywanej komendy? na przykład gcc -c yadayada. Jestem całkiem pewien, że istnieje zmienna, która nie zawiera tego, czego się spodziewasz
— Tom

-1

W dzisiejszych czasach przestałem pisać makefile, jeśli zamierzasz się uczyć, śmiało, w przeciwnym razie masz dobry generator makefile, który jest dostarczany z eclipse CDT. Jeśli chcesz mieć możliwość utrzymania / obsługę wielu projektów w swoim drzewie kompilacji, spójrz na następujące -

https://github.com/dmoulding/boilermake Znalazłem to całkiem nieźle ...!


3
Oparte na opinii. Nie odpowiada na pytanie OP. Zakłada środowisko Eclipse.
— Nathaniel Johnson
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.