Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile with multiple inputs and outputs

Tags:

r

makefile

I'm wanting to use a makefile to update figure files generated by R code. The R code is in various files in the directory ../R and all ending in .R. The figure files are in the directory ../figs and all ending in .pdf or .png. If an R file has a later date than any of the figure files, I want to process the R file with the command

R --no-save < file.R

I've looked a various example makefiles but couldn't find anything I could adapt.

My current effort (not working) is as follows:

PLOTDIR= ../figs
RDIR= ../R
RFILES= $(RDIR)/*.R
PLOTS= *.pdf *.png
FIGURES= $(PLOTDIR)/$(PLOTS)
$(FIGURES): $(RFILES)
    R --no-save < $<
like image 886
Rob Hyndman Avatar asked Oct 19 '12 05:10

Rob Hyndman


2 Answers

You can try the following.

The tricks are that you need to deduce output from inputs (.R file)

# Makefile
# Beware of indentation when copying use TABS

PLOTDIR = ../figs
RDIR = ../R

# list R files
RFILES = $(wildcard $(RDIR)/*.R)

# compute output file names
PDF_FIGS = $(RFILES:.R=.pdf)
PNG_FIGS = $(RFILES:.R=.png)

# relocate files in output folder
OUT_FILES = $(subst $(RDIR), $(PLOTDIR), $(PDF_FIGS) $(PNG_FIGS))

# first target is the default: simply do 'make'
all: $(OUT_FILES)

clean:
    rm $(OUT_FILES)

.PHONY: all clean

# need to split PNG from PDF rules
$(PLOTDIR)/%.png: $(RDIR)/%.R
    R --no-save < $<

$(PLOTDIR)/%.pdf $(PLOTDIR)/%.png: $(RDIR)/%.R
    R --no-save < $<

Edit to reflect my comment: Use 1 dependency output file per R script

PLOTDIR= ../figs
RDIR= ../R

# list R files
RFILES := $(wildcard $(RDIR)/*.R)

# relocate files in output folder
OUT_FILES=$(subst $(RDIR), $(PLOTDIR), $(RFILES:.R=.out))
#$(warning $(OUT_FILES))

# first target is the default: simply do 'make'
all: $(OUT_FILES)

clean:
    rm $(OUT_FILES)

.PHONY: all clean

$(PLOTDIR)/%.out: $(RDIR)/%.R
    R --no-save < $< && touch $@
like image 96
levif Avatar answered Oct 13 '22 12:10

levif


An interesting problem. It's logically simple, but goes right against the grain of what Make likes to do.

This seems to work. It relies on an obscure feature of pattern rules: if a pattern rule has more than one target, Make infers that it need be run only once to update all of its targets.

PLOTDIR = ../figs
RDIR = ../R
RFILES = $(wildcard $(RDIR)/*.R)
FIGURES = $(wildcard $(PLOTDIR)/*.pdf $(PLOTDIR)/*.png)

all: $(FIGURES)

$(PLOTDIR)/%.pdf $(PLOTDIR)/%.png: $(RFILES)
        @for x in $?; do R --no-save \< $$x; done
like image 1
Beta Avatar answered Oct 13 '22 13:10

Beta