So generieren Sie ein Makefile mit der Quelle in Unterverzeichnissen mit nur einem Makefile

StackOverflow https://stackoverflow.com/questions/231229

  •  04-07-2019
  •  | 
  •  

Frage

Ich habe Quelle in einer Reihe von Unterverzeichnissen wie:

src/widgets/apple.cpp
src/widgets/knob.cpp
src/tests/blend.cpp
src/ui/flash.cpp

In der Wurzel des Projekts möchte ich mit einer Regel wie folgt eine einzelne Makefile generieren:

%.o: %.cpp
   $(CC) -c $<

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o
   $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe

Wenn ich dies versuche, findet es keine Regel für Build/Widgets/Apple.o. Kann ich etwas ändern, so dass der %.o: %.CPP verwendet wird, wenn er bau/widgets/Apple.o herstellen muss?

War es hilfreich?

Lösung

Der Grund ist, dass Ihre Regel

%.o: %.cpp
       ...

erwartet, dass die .cpp -Datei im selben Verzeichnis wie das .o -Gebäude liegt. Da test.exe in Ihrem Fall von Build/Widgets/Apple.O (usw.) abhängt, erwartet Make, dass Apple.cpp bau/widgets/Apple.cpp ist.

Sie können VPath verwenden, um dies zu beheben:

VPATH = src/widgets

BUILDDIR = build/widgets

$(BUILDDIR)/%.o: %.cpp
      ...

Wenn Sie versuchen, "Build/Widgets/Apple.o" zu erstellen, suchen Sie nach Apple.cpp in vpath. Beachten Sie, dass die Build -Regel spezielle Variablen verwenden muss, um auf den tatsächlichen Dateinamen zuzugreifen, der Funde machen:

$(BUILDDIR)/%.o: %.cpp
        $(CC) $< -o $@

Wo sich "$ <" auf den Weg ausdehnt, wo sich die erste Abhängigkeit befindet.

Beachten Sie auch, dass dies alle .o -Dateien in Build/Widgets erstellt. Wenn Sie die Binärdateien in verschiedenen Verzeichnissen erstellen möchten, können Sie so etwas wie

build/widgets/%.o: %.cpp
        ....

build/ui/%.o: %.cpp
        ....

build/tests/%.o: %.cpp
        ....

Ich würde empfehlen, dass Sie verwenden "Befehlssequenzen in Dosen"Um zu vermeiden, die tatsächliche Compiler -Build -Regel zu wiederholen:

define cc-command
$(CC) $(CFLAGS) $< -o $@
endef

Sie können dann mehrere Regeln wie diese haben:

build1/foo.o build1/bar.o: %.o: %.cpp
    $(cc-command)

build2/frotz.o build2/fie.o: %.o: %.cpp
    $(cc-command)

Andere Tipps

Dies macht den Trick:

CC        := g++
LD        := g++

MODULES   := widgets test ui
SRC_DIR   := $(addprefix src/,$(MODULES))
BUILD_DIR := $(addprefix build/,$(MODULES))

SRC       := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp))
OBJ       := $(patsubst src/%.cpp,build/%.o,$(SRC))
INCLUDES  := $(addprefix -I,$(SRC_DIR))

vpath %.cpp $(SRC_DIR)

define make-goal
$1/%.o: %.cpp
    $(CC) $(INCLUDES) -c $$< -o $$@
endef

.PHONY: all checkdirs clean

all: checkdirs build/test.exe

build/test.exe: $(OBJ)
    $(LD) $^ -o $@


checkdirs: $(BUILD_DIR)

$(BUILD_DIR):
    @mkdir -p $@

clean:
    @rm -rf $(BUILD_DIR)

$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir))))

Diese Makefile geht davon aus, dass Sie Ihre Dateien in den Quellverzeichnissen einfügen. Außerdem prüft es, ob die Build -Verzeichnisse existieren, und erstellt sie, wenn sie nicht existieren.

Die letzte Zeile ist die wichtigste. Es schafft die impliziten Regeln für jeden Build mit der Funktion make-goal, und es ist nicht notwendig, ihnen nacheinander zu schreiben

Sie können auch die automatische Abhängigkeitserzeugung mithilfe verwenden Tromeys Weg

Die Sache ist $@ Enthält den gesamten (relativen) Pfad in die Quelldatei, die wiederum zum Konstruktion des Objektnamens (und damit seinen relativen Pfad) verwendet wird.

Wir gebrauchen:

#####################
# rules to build the object files
$(OBJDIR_1)/%.o: %.c
    @$(ECHO) "$< -> $@"
    @test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1)
    @test -d $(@D) || mkdir -pm 775 $(@D)
    @-$(RM) $@
    $(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o $@

Dies erstellt ein Objektverzeichnis mit dem in angegebenen Namen in $(OBJDIR_1)und Unterverzeichnisse gemäß den Unterverzeichnissen in der Quelle.

Zum Beispiel (annehmen OBJs als Toplevel -Objektverzeichnis) in Makefile:

widget/apple.cpp
tests/blend.cpp

führt zu dem folgenden Objektverzeichnis:

objs/widget/apple.o
objs/tests/blend.o

Dies wird es ohne schmerzhafte Manipulation oder mehrere Befehlssequenzen tun:

build/%.o: src/%.cpp
src/%.o: src/%.cpp
%.o:
    $(CC) -c $< -o $@

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o
    $(LD) $^ -o $@

Jaspere hat erklärt, warum " %.o: %.CPP" nicht funktionieren; Diese Version hat eine Musterregel (%.o :) mit Befehlen und ohne Voraussetzungen sowie zwei Musterregeln (Build/%. O: und Src/%. O :) mit Vorbereitungen und ohne Befehle. (Beachten Sie, dass ich die SRC/% -Regel einfügte. Lass es aus.)

bauen/test.exe benötigt Build/Widgets/Apple.O,
bauen/widgets/Apple.O sieht aus wie Build/%. O, daher benötigt es SRC/%.
bauen/widgets/Apple.O sieht auch wie %.o aus, sodass er den CC -Befehl ausführt und die gerade gefundenen Vorbereitungen verwendet (nämlich SRC/Widgets/Apple.CPP), um das Ziel zu erstellen (Build/Widgets/Apple.O).

Dies ist ein weiterer Trick.

Definieren Sie im Haupt -Makefile SRCDIR für jedes Quell -Dir und enthalten "makef.mk" für jeden Wert von srcdir. In jeder Quell -DIR -Datei "Dateien.MK" mit der Liste der Quelldateien und kompilieren Sie Optionen für einige davon. In der Haupt -Makefile kann man Kompilierungsoptionen definieren und Dateien für jeden Wert von SRCDIR ausschließen.

Makefile:

PRG             := prog-name

OPTIMIZE        := -O2 -fomit-frame-pointer

CFLAGS += -finline-functions-called-once
LDFLAGS += -Wl,--gc-section,--reduce-memory-overheads,--relax


.DEFAULT_GOAL   := hex

OBJDIR          := obj

MK_DIRS         := $(OBJDIR)


SRCDIR          := .
include         makef.mk

SRCDIR := crc
CFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE
ASFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE
include makef.mk

################################################################

CC              := avr-gcc -mmcu=$(MCU_TARGET) -I.
OBJCOPY         := avr-objcopy
OBJDUMP         := avr-objdump

C_FLAGS         := $(CFLAGS) $(REGS) $(OPTIMIZE)
CPP_FLAGS       := $(CPPFLAGS) $(REGS) $(OPTIMIZE)
AS_FLAGS        := $(ASFLAGS)
LD_FLAGS        := $(LDFLAGS) -Wl,-Map,$(OBJDIR)/$(PRG).map


C_OBJS          := $(C_SRC:%.c=$(OBJDIR)/%.o)
CPP_OBJS        := $(CPP_SRC:%.cpp=$(OBJDIR)/%.o)
AS_OBJS         := $(AS_SRC:%.S=$(OBJDIR)/%.o)

C_DEPS          := $(C_OBJS:%=%.d)
CPP_DEPS        := $(CPP_OBJS:%=%.d)
AS_DEPS         := $(AS_OBJS:%=%.d)

OBJS            := $(C_OBJS) $(CPP_OBJS) $(AS_OBJS)
DEPS            := $(C_DEPS) $(CPP_DEPS) $(AS_DEPS)


hex:  $(PRG).hex
lst:  $(PRG).lst


$(OBJDIR)/$(PRG).elf : $(OBJS)
    $(CC) $(C_FLAGS) $(LD_FLAGS) $^ -o $@

%.lst: $(OBJDIR)/%.elf
    -@rm $@ 2> /dev/nul
    $(OBJDUMP) -h -s -S $< > $@

%.hex: $(OBJDIR)/%.elf
    -@rm $@ 2> /dev/nul
    $(OBJCOPY) -j .text -j .data -O ihex $< $@


$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile
    $(CC) -MMD -MF $@.p.d -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@
    @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.p.d >> $@.d
    -@rm -f $@.p.d

$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile
    $(CC) -MMD -MF $@.p.d -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o $@
    @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.p.d >> $@.d
    -@rm -f $@.p.d

$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile
    $(CC) -MMD -MF $@.p.d -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o $@
    @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.p.d >> $@.d
    -@rm -f $@.p.d


clean:
    -@rm -rf $(OBJDIR)/$(PRG).elf
    -@rm -rf $(PRG).lst $(OBJDIR)/$(PRG).map
    -@rm -rf $(PRG).hex $(PRG).bin $(PRG).srec
    -@rm -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec
    -@rm -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d)
    -@rm -f tags cscope.out

#   -rm -rf $(OBJDIR)/*
#   -rm -rf $(OBJDIR)
#   -rm $(PRG)


tag: tags
tags: $(SRC_FILES)
    if [ -e tags ] ; then ctags -u $? ; else ctags $^ ; fi
    cscope -U -b $^


# include dep. files
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEPS)
endif


# Create directory
$(shell mkdir $(MK_DIRS) 2>/dev/null)

makef.mk

SAVE_C_SRC := $(C_SRC)
SAVE_CPP_SRC := $(CPP_SRC)
SAVE_AS_SRC := $(AS_SRC)

C_SRC :=
CPP_SRC :=
AS_SRC :=


include $(SRCDIR)/files.mk
MK_DIRS += $(OBJDIR)/$(SRCDIR)


clear_name = $(subst /,_,$(1))


define rename_var
$(2)_$(call clear_name,$(SRCDIR))_$(call clear_name,$(1)) := \
    $($(subst _,,$(2))_$(call clear_name,$(SRCDIR))) $($(call clear_name,$(1)))
$(call clear_name,$(1)) :=
endef


define proc_lang

ORIGIN_SRC_FILES := $($(1)_SRC)

ifneq ($(strip $($(1)_ONLY_FILES)),)
$(1)_SRC := $(filter $($(1)_ONLY_FILES),$($(1)_SRC))
else

ifneq ($(strip $(ONLY_FILES)),)
$(1)_SRC := $(filter $(ONLY_FILES),$($(1)_SRC))
else
$(1)_SRC := $(filter-out $(EXCLUDE_FILES),$($(1)_SRC))
endif

endif

$(1)_ONLY_FILES :=
$(foreach name,$($(1)_SRC),$(eval $(call rename_var,$(name),$(1)_FLAGS)))
$(foreach name,$(ORIGIN_SRC_FILES),$(eval $(call clear_name,$(name)) :=))

endef


$(foreach lang,C CPP AS, $(eval $(call proc_lang,$(lang))))


EXCLUDE_FILES :=
ONLY_FILES :=


SAVE_C_SRC += $(C_SRC:%=$(SRCDIR)/%)
SAVE_CPP_SRC += $(CPP_SRC:%=$(SRCDIR)/%)
SAVE_AS_SRC += $(AS_SRC:%=$(SRCDIR)/%)

C_SRC := $(SAVE_C_SRC)
CPP_SRC := $(SAVE_CPP_SRC)
AS_SRC := $(SAVE_AS_SRC)

./files.mk

C_SRC   := main.c
CPP_SRC :=
AS_SRC  := timer.S

main.c += -DDEBUG

./crc/files.mk

C_SRC    := byte-modbus-crc.c byte-crc8.c
AS_SRC   := modbus-crc.S crc8.S modbus-crc-table.S crc8-table.S

byte-modbus-crc.c += --std=gnu99
byte-crc8.c       += --std=gnu99

Hier ist meine Lösung, inspiriert von der Beta -Antwort. Es ist einfacher als die anderen vorgeschlagenen Lösungen

Ich habe ein Projekt mit mehreren C -Dateien, die in vielen Unterverzeichnissen gespeichert sind. Zum Beispiel:

src/lib.c
src/aa/a1.c
src/aa/a2.c
src/bb/b1.c
src/cc/c1.c

Hier ist mein Makefile (in der src/ Verzeichnis):

# make       -> compile the shared library "libfoo.so"
# make clean -> remove the library file and all object files (.o)
# make all   -> clean and compile
SONAME  = libfoo.so
SRC     = lib.c   \
          aa/a1.c \
          aa/a2.c \
          bb/b1.c \
          cc/c1.c
# compilation options
CFLAGS  = -O2 -g -W -Wall -Wno-unused-parameter -Wbad-function-cast -fPIC
# linking options
LDFLAGS = -shared -Wl,-soname,$(SONAME)

# how to compile individual object files
OBJS    = $(SRC:.c=.o)
.c.o:
    $(CC) $(CFLAGS) -c $< -o $@

.PHONY: all clean

# library compilation
$(SONAME): $(OBJS) $(SRC)
    $(CC) $(OBJS) $(LDFLAGS) -o $(SONAME)

# cleaning rule
clean:
    rm -f $(OBJS) $(SONAME) *~

# additional rule
all: clean lib

Dieses Beispiel funktioniert einwandfrei für eine gemeinsame Bibliothek und sollte für jeden Kompilierungsprozess sehr einfach sein.

Normalerweise erstellen Sie in jedem Unterverzeichnis ein Makefile und schreiben in der obersten Ebene Makefile, um Make in den Unterverzeichnissen anzurufen.

Diese Seite kann helfen: http://www.gnu.org/software/make/

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top