Kilka miesięcy temu wymyśliłem następujący ogólny opis Makefile
zadań szkolnych:
# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# 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 .c
i .h
, aby wygenerować .o
pliki i plik wykonywalny projectname
w 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: [email protected]
# 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
. Zbliżam się, ale mam problem ze zmiennymi automatycznymi, więc wydaje mi się, że tak czy inaczejOdpowiedzi:
Po pierwsze, twoja
$(OBJECTS)
reguła jest problematyczna, ponieważ:file1.o
ifile2.o
)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 wpiszeszmake
kilka 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).źródło
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
funkcji? Może jeden wfile1.c
i jeden wmain.c
? Jeśli tak, to nie będziesz w stanie połączyć tych obiektów;main
plik wykonywalny może zawierać tylko jeden plik.file1.c
ale daje to ten sam komunikat do wszystkich plików projektu. Imain.c
jako jedyny ma główną funkcję ... imain.c
importujefile1.h
ifile2.h
(nie ma związku międzyfile1.c
ifile2.c
), ale wątpię, że problem bierze się stąd.$(OBJECTS)
reguły; Edytowałem to. Ze złą linią mam błąd, ale nie ten, który masz ...Możesz dodać
-I
flagę 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
obj
katalogu, użyj-o
opcji podczas kompilacji. Spójrz także na zmienne automatyczne$@
i .$<
Na przykład rozważmy ten prosty plik Makefile
Aktualizuj>
Patrząc na twój plik makefile, zdaję sobie sprawę, że używasz
-o
flagi. Dobry. Kontynuuj używanie, ale dodaj zmienną katalogu docelowego, aby wskazać, gdzie powinien zostać zapisany plik wyjściowy.źródło
-l ...
doCFLAGS
i ... jest już-o
argument do linkera (LINKER
)make
z miejsca, w którym znajduje się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 ...!
źródło