Chyba v GNU make: proměnné cílové specifické nejsou rozšířena do implicitních pravidel?

hlasů
13

Byl jsem pracoval na projektování vícenásobné konfigurace Makefile (jeden, který podporuje zvláštní ‚ladění‘ a ‚uvolnění‘ cíle), a narazili na podivný problém, který se zdá být chyba v GNU make.

Zdá se, že GNU make není rozšiřuje proměnné cílově specifických správně, pokud jsou tyto proměnné odkazováno v implicitního pravidla. Zde je zjednodušený Makefile, který ukazuje tento problém:

all:
    @echo specify configuration 'debug' or 'release'

OBJS := foo.o bar.o

BUILDDIR = .build/$(CONFIG)

TARGET = $(addprefix $(BUILDDIR)/,$(OBJS))

debug: CONFIG := debug
release: CONFIG := release

#CONFIG := debug

debug: $(TARGET)
release: $(TARGET)

clean:
    rm -rf .build

$(BUILDDIR)/%.o: %.c
    @echo [$(BUILDDIR)/$*.o] should be [$@]
    @mkdir -p $(dir $@)
    $(CC) -c $< -o $@

Při zadávání brankovou ‚ladit‘, aby se, CONFIG je nastaven na ‚ladění‘, a BUILDDIR a CÍL jsou rovněž správně rozšířen. Nicméně, v implicitního pravidla stavět zdrojový soubor z objektu, $ @ je rozšířen, jako kdyby neexistuje CONFIG.

Zde je výstup z použití tohoto Makefile:

$ make debug
[.build/debug/foo.o] should be [.build//foo.o]
cc -c foo.c -o .build//foo.o
[.build/debug/bar.o] should be [.build//bar.o]
cc -c bar.c -o .build//bar.o

To ukazuje, že BUILDDIR se rozšiřuje v pořádku, ale výsledný $ @ není. Kdybych pak komentář mimo variabilní specifikaci cílového a ručně nastavit config: = ladění (dále poznamenal řádek výše), chápu to, co by se dalo očekávat:

$ make debug
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c bar.c -o .build/debug/bar.o

Testoval jsem to s oběma make-3.81 na Gentoo a MinGW a make-3,82 na Gentoo. All vykazují stejné chování.

Připadá mi to těžké uvěřit, že bych být první, kdo narazit na tento problém, takže hádám, jsem asi právě dělá něco špatného - ale budu upřímný: Já nechápu, jak jsem mohl být. :)

Existují nějaké dělat guru tam, že by měli být schopni vrhnout světlo na toto téma? Dík!

Položena 01/11/2010 v 14:57
zdroj uživatelem
V jiných jazycích...                            


3 odpovědí

hlasů
9

Zjednodušeně řečeno, I vyjde DAG závislostí a vytvoří seznam pravidel, které musí být spuštěn před spuštěním pravidlo . Přiřazení hodnoty cílové specifické je něco, co dělá Udělat při spuštění pravidlo, které přijde později. To je vážná omezení (což já a jiní si stěžovali dříve), ale já bych to nenazýval chyba, protože je popsáno v dokumentaci. Podle manuálu GNUMake:

6.11 cílově specifických hodnot proměnných : „Jako automatickými proměnnými, tyto hodnoty jsou k dispozici pouze v rámci receptu cílového (a v jiných úkolů cílově specifických).“

A „kontext receptu terčem je“ se rozumí příkazy, nikoliv prereqs:

10.5.3 Automatické proměnné : „[Automatické proměnné] nelze přistupovat přímo prerekvizitního seznamu pravidlo.“

Existuje několik způsobů, jak vyřešit tento problém. Můžete použít sekundární expanzi , je-li vaše verze Make GNUMake má to (3,81 nemá, nevím o 3,82). Nebo si můžete udělat, aniž by veličin zaměřených na určité cílové:

DEBUG_OBJS = $(addprefix $(BUILDDIR)/debug/,$(OBJS))
RELEASE_OBJS = $(addprefix $(BUILDDIR)/release/,$(OBJS))

debug: % : $(DEBUG_OBJS)
release: $(RELEASE_OBJS)

$(DEBUG_OBJS): $(BUILDDIR)/debug/%.o : %.cc
$(RELEASE_OBJS): $(BUILDDIR)/release/%.o : %.cc

$(DEBUG_OBJS) $(RELEASE_OBJS):
    @echo making $@ from $^
    @mkdir -p $(dir $@)                                                        
    $(CC) -c $< -o $@ 
Odpovězeno 01/11/2010 v 16:18
zdroj uživatelem

hlasů
7

Jako Beta poukázal, to opravdu není chyba v make neboť omezení je popsána v dokumentaci (Myslím, že jsem musel vynechal, že zvláštní část - sorry).

V každém případě jsem byl ve skutečnosti schopen tento problém vyřešit tím, že dělá něco, co ještě jednodušší. Vzhledem k tomu, vše, co potřebujete, je přiřadit proměnnou založenou na branku, jsem zjistil, že mohu použít $ (MAKECMDGOALS) proměnná, aby se náležitě rozšířit sestavení adresáře. Eliminovat proměnné $ (config) a přepisování Makefile jako následující dělá přesně to, co potřebuji:

all:
        @echo specify configuration 'debug' or 'release'

OBJS := foo.o bar.o

BUILDDIR := .build/$(MAKECMDGOALS)

TARGET := $(addprefix $(BUILDDIR)/,$(OBJS))

debug: $(TARGET)
release: $(TARGET)

clean:
        rm -rf .build

$(BUILDDIR)/%.o: %.c
        @echo [$(BUILDDIR)/$*.o] should be [$@]
        @mkdir -p $(dir $@)
        $(CC) -c $< -o $@

To pak dává správné výsledky:

$ make debug
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c bar.c -o .build/debug/bar.o
$ make release
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c bar.c -o .build/release/bar.o
$ make debug
make: Nothing to be done for `debug'.
$ make release
make: Nothing to be done for `release'.

Tato vůle přestávky samozřejmě, pokud existuje více cílů stanovených v příkazovém řádku (od $ (MAKECMDGOALS) obsahuje seznam prostor oddělený), ale zabývá se, že není příliš velký problém.

Odpovězeno 02/11/2010 v 07:57
zdroj uživatelem

hlasů
2

Zde je, jak vyřešit problém, aniž by MAKECMDGOALSintrospekci. Tha problém je v podstatě, že pravidla zadáte v Makefilepředstavovat statický graf. Přiřazení cílové specifické jsou použity při provádění všech pravidel orgánů, ale ne během jejich sestavování .

Řešením je chytit kontrolu nad pravidla sestavování: použít makro podobné konstrukty GNU make k vytvoření těchto pravidel. Pak máme plně pod kontrolou: můžeme držet variabilní materiál do cílového, předpoklad nebo recept.

Tady je moje verze vašich Makefile

all:
        @echo specify configuration 'debug' or 'release'

OBJS := foo.o bar.o

# BUILDDIR is a macro
# $(call BUILDDIR,WORD) -> .build/WORD
BUILDDIR = .build/$(1)

# target is a macro
# $(call TARGET,WORD) -> ./build/WORD/foo.o ./build/WORD/bar.o
TARGET = $(addprefix $(call BUILDDIR,$(1))/,$(OBJS))

# BUILDRULE is a macro: it builds a release or debug rule
# or whatever word we pass as argument $(1)
define BUILDRULE
$(call BUILDDIR,$(1))/%.o: %.c
        @echo [$(call BUILDDIR,$(1))/$$*.o] should be [$$@]
        @mkdir -p $$(dir $$@)
        $$(CC) -c -DMODE=$(1) $$< -o $$@
endef

debug: $(call TARGET,debug)
release: $(call TARGET,release)

# generate two build rules from macro
$(eval $(call BUILDRULE,debug))
$(eval $(call BUILDRULE,release))

clean:
        rm -rf .build

Nyní si všimněte výhodu: mohu postavit jak debuga releasecílů najednou, protože jsem instance jak pravidla ze šablony!

$ make clean ; make debug release
rm -rf .build
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c -DMODE=debug foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c -DMODE=debug bar.c -o .build/debug/bar.o
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -DMODE=release bar.c -o .build/release/bar.o

Kromě toho jsem se dovolil přidat makro argumentu do ccpříkazového řádku také tak, že moduly dostávají MODEmakro, které jim řekne, jakým způsobem jsou sestaveny.

Můžeme použít proměnnou indirection nastavit jiný CFLAGSnebo cokoliv jiného. Sledujte, co se stane, když náplast na výše uvedené takto:

--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,9 @@

 OBJS := foo.o bar.o

+CFLAGS_debug = -O0 -g
+CFLAGS_release = -O2
+
 # BUILDDIR is a macro
 # $(call BUILDDIR,WORD) -> .build/WORD
 BUILDDIR = .build/$(1)
@@ -17,7 +20,7 @@ define BUILDRULE
 $(call BUILDDIR,$(1))/%.o: %.c
        @echo [$(call BUILDDIR,$(1))/$$*.o] should be [$$@]
        @mkdir -p $$(dir $$@)
-       $$(CC) -c -DMODE=$(1) $$< -o $$@
+       $$(CC) -c $$(CFLAGS_$(1)) -DMODE=$(1) $$< -o $$@
 endef

 debug: $(call TARGET,debug)

Běh:

$ make clean ; make debug release
rm -rf .build
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c -O0 -g -DMODE=debug foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c -O0 -g -DMODE=debug bar.c -o .build/debug/bar.o
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -O2 -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -O2 -DMODE=release bar.c -o .build/release/bar.o

Nakonec můžeme kombinovat, že se MAKECMDGOALS. Můžeme kontrolovat MAKECMDGOALSa odfiltrovat režimy sestavení, které tam nejsou specifikovány. Pokud make releaseje volána, nemusíme se debugpravidla, která mají být rozšířena. Náplast:

--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,11 @@

 OBJS := foo.o bar.o

+# List of build types, but only those mentioned on command line
+BUILD_TYPES := $(filter $(MAKECMDGOALS),debug release)
+
+$(warning "generating rules for BUILD_TYPES := $(BUILD_TYPES)")
+
 CFLAGS_debug = -O0 -g
 CFLAGS_release = -O2

@@ -17,18 +22,15 @@ TARGET = $(addprefix $(call BUILDDIR,$(1))/,$(OBJS))
 # BUILDRULE is a macro: it builds a release or debug rule
 # or whatever word we pass as argument $(1)
 define BUILDRULE
+$(1): $(call TARGET,$(1))
 $(call BUILDDIR,$(1))/%.o: %.c
        @echo [$(call BUILDDIR,$(1))/$$*.o] should be [$$@]
        @mkdir -p $$(dir $$@)
        $$(CC) -c $$(CFLAGS_$(1)) -DMODE=$(1) $$< -o $$@
 endef

-debug: $(call TARGET,debug)
-release: $(call TARGET,release)
-
-# generate two build rules from macro
-$(eval $(call BUILDRULE,debug))
-$(eval $(call BUILDRULE,release))
+$(foreach type,$(BUILD_TYPES),\
+  $(eval $(call BUILDRULE,$(type))))

 clean:
        rm -rf .build

Všimněte si, že jsem zjednodušené věci válcováním debug:a release:cíle do BUILDRULEmakra.

$ make clean ; make release
Makefile:9: "generating rules for BUILD_TYPES := "
rm -rf .build
Makefile:9: "generating rules for BUILD_TYPES := release"
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -O2 -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -O2 -DMODE=release bar.c -o .build/release/bar.o

$ make clean ; make release debug
Makefile:9: "generating rules for BUILD_TYPES := "
rm -rf .build
Makefile:9: "generating rules for BUILD_TYPES := debug release"
[.build/release/foo.o] should be [.build/release/foo.o]
cc -c -O2 -DMODE=release foo.c -o .build/release/foo.o
[.build/release/bar.o] should be [.build/release/bar.o]
cc -c -O2 -DMODE=release bar.c -o .build/release/bar.o
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c -O0 -g -DMODE=debug foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c -O0 -g -DMODE=debug bar.c -o .build/debug/bar.o
Odpovězeno 05/12/2014 v 02:13
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more