GNU make con molte directory di destinazione
-
06-09-2019 - |
Domanda
devo integrare la generazione di molti file HTML in un Makefile
esistente.
Il problema è che i file HTML devono risiedere in molte directory differenti.
La mia idea è quella di scrivere una regola implicita che converte il file sorgente (* .st) per il file HTML corrispondente
%.html: %.st
$(HPC) -o $@ $<
e una regola che dipende da tutti i file HTML
all: $(html)
Se il file HTML non è nella make
builddir non trova la regola implicita: *** No rule to make target
.
Se cambio la regola implicita in questo modo
$(rootdir)/build/doc/2009/06/01/%.html: %.st
$(HPC) -o $@ $<
è trovato, ma poi devo avere una regola implicita per quasi tutti i file nel progetto.
Secondo implicita regola algoritmo di ricerca nel manuale make
GNU, regola di ricerca funziona in questo modo:
- Spalato t in una parte directory, chiamato d, e il resto, chiamato n. Per esempio, se t è
src/foo.o', then d is
src / 'e n è `foo.o'.- Fare un elenco di tutto il modello di regole di cui uno degli obiettivi partite t o n. Se il modello di destinazione contiene un slash viene confrontato t; in caso contrario, contro n.
Perché la regola implicita non trovato, e quale sarebbe la soluzione più elegante, assumendo GNU make
viene utilizzato?
Ecco una versione ridotta del mio Makefile
:
rootdir = /home/user/project/doc
HPC = /usr/local/bin/hpc
html = $(rootdir)/build/doc/2009/06/01/some.html
%.html: %.st
$(HPC) -o $@ $<
#This works, but requires a rule for every output dir
#$(rootdir)/build/doc/2009/06/01/%.html: %.st
# $(HPC) -o $@ $<
.PHONY: all
all: $(html)
Soluzione
Come Maria Shalnova mi piace ricorsiva make (anche se non sono d'accordo con "Make ricorsiva considerati nocivi"), e in generale è meglio fare qualcosa qui da una fonte lì, non il contrario. Ma se è necessario, mi permetto di suggerire un leggero miglioramento: avere generateHtml generare solo la regola, non l'COMANDI
.Altri suggerimenti
La soluzione migliore che ho trovato finora è quello di generare una regola implicita per directory di destinazione tramite foreach-eval-CALL , come spiegato nel GNU make
manuale . Non ho idea di come questo di scalare fino a qualche migliaio di directory di destinazione, ma vedremo ...
Se si dispone di una soluzione migliore, si prega di postare!
Ecco il codice:
rootdir = /home/user/project/doc
HPC = /usr/local/bin/hpc
html = $(rootdir)/build/doc/2009/06/01/some.html \
$(rootdir)/build/doc/2009/06/02/some.html
targetdirs = $(rootdir)/build/doc/2009/06/01 \
$(rootdir)/build/doc/2009/06/02
define generateHtml
$(1)/%.html: %.st
-mkdir -p $(1)
$(HPC) -o $$@ $$<
endef
$(foreach targetdir, $(targetdirs), $(eval $(call generateHtml, $(targetdir))))
.PHONY: all
all: $(html)
La regola implicita attiva rende $(rootdir)/build/doc/2009/06/01/some.html
dipendono $(rootdir)/build/doc/2009/06/01/some.st
. Se $(rootdir)/build/doc/2009/06/01/some.st
non esiste, allora non sarà utilizzato la regola / trovato.
La regola commentata fa $(rootdir)/build/doc/2009/06/01/some.html
dipendono some.st
.
Una soluzione è quella di fare sei layout di origine corrisponde la tua destinazione / layout di conseguenza.
Un'altra opzione è quella di creare le regole, come richiesto con eval
. Ma che sarà piuttosto complicato:
define HTML_template
$(1) : $(basename $(1))
cp $< $@
endef
$(foreach htmlfile,$(html),$(eval $(call HTML_template,$(htmlfile))))
Un altra possibilità è quella di avere il comando make
chiamarsi in modo ricorsivo con l'-C argomento con ogni directory di output.
make
ricorsiva è un po 'il modo standard di trattare con le sottodirectory, ma attenzione le implicazioni di cui l'articolo "Make ricorsivo considerati nocivi"