64

How can I compact the folllowing Makefile targets?

$(GRAPHDIR)/Complex.png: $(GRAPHDIR)/Complex.dot
        dot $(GRAPHDIR)/Complex.dot -Tpng -o $(GRAPHDIR)/Complex.png

$(GRAPHDIR)/Simple.png: $(GRAPHDIR)/Simple.dot
        dot $(GRAPHDIR)/Simple.dot -Tpng -o $(GRAPHDIR)/Simple.png

$(GRAPHDIR)/IFileReader.png: $(GRAPHDIR)/IFileReader.dot
        dot $(GRAPHDIR)/IFileReader.dot -Tpng -o $(GRAPHDIR)/IFileReader.png

$(GRAPHDIR)/McCabe-linear.png: $(GRAPHDIR)/McCabe-linear.dot
        dot $(GRAPHDIR)/McCabe-linear.dot -Tpng -o $(GRAPHDIR)/McCabe-linear.png

graphs: $(GRAPHDIR)/Complex.png $(GRAPHDIR)/Simple.png $(GRAPHDIR)/IFileReader.png $(GRAPHDIR)/McCabe-linear.png

--

Using GNU Make 3.81.

P Shved
  • 96,026
  • 17
  • 121
  • 165
Robert Munteanu
  • 67,031
  • 36
  • 206
  • 278

3 Answers3

77

The concept is called pattern rules. You can read about it in GNU make manual.

$(GRAPHDIR)/%.png: $(GRAPHDIR)/%.dot
        dot $< -Tpng -o $@

graphs: $(patsubst %,$(GRAPHDIR)/%.png, Complex Simple IFileReader McCabe)\

or just

%.png: %.dot
        dot $< -Tpng -o $@

graphs: $(patsubst %,$(GRAPHDIR)/%.png, Complex Simple IFileReader McCabe)

You can also remove all repetition by extracting one of the patterns into a separate variable PNG_PATTERN like so:

PNG_pattern=$(GRAPHDIR)/%.png

$(PNG_pattern): $(GRAPHDIR)/%.dot
        dot $< -Tpng -o $@

graphs: $(patsubst %,$(PNG_pattern), Complex Simple IFileReader McCabe)
P Shved
  • 96,026
  • 17
  • 121
  • 165
  • 1
    @ Beta , well, dunno. I usually deal with situations where processing is directory-specific, so I just got used to it. :) – P Shved Oct 28 '09 at 00:18
  • 1
    The "Advanced stuff" was added before the "or just" (there's no repetition in the "or just" part, which I tried really hard to find). – ShreevatsaR Aug 13 '21 at 22:32
  • @ShreevatsaR ok, I've edited the post a bit :-) P.S. Wow I remember you, years ago we were discussing algorithms here. What are you up to these days? =) – P Shved Aug 15 '21 at 04:24
  • Haha yes I remember your name too; yes it was in the early days of Stack Overflow when there was a greater proportion of algorithms questions here, and at a pace one could actually keep up with, and I too was a student or recently graduated, with more time and interest. Now I'm not sure what I'm doing anymore :-) (officially I'm working as a software engineer, but…) Hope you are doing well! – ShreevatsaR Aug 15 '21 at 06:22
28

Just in case you actually want to generate a .PNG for every .DOT within current directory:

%.png : %.dot
    dot -Tpng -o $@ $<

all: $(addsuffix .png, $(basename $(wildcard *.dot)))

I came up with this Makefile after reading the answer from @Pavel.

Metaphox
  • 2,085
  • 2
  • 21
  • 34
  • Wont basename cut out the GRAPHDIR? – monokrome Apr 20 '14 at 20:12
  • 2
    No: from the docs: "$(basename names…) Extracts all but the suffix of each file name in names. If the file name contains a period, the basename is everything starting up to (and not including) the last period. Periods in the directory part are ignored. If there is no period, the basename is the entire file name. For example, $(basename src/foo.c src-1.0/bar hacks) produces the result ‘src/foo src-1.0/bar hacks’." – Miguel Mar 07 '17 at 08:18
9

I think you want some pattern rules. Try this out.

TARGETS = $(GRAPHDIR)/Complex.png \  
          $(GRAPHDIR)/Simple.png \ 
          $(GRAPHDIR)/IFileReader.png \
          $(GRAPHDIR)/McCabe-linear.png

%.png : %.dot
        dot $^ -Tpng -o $@

graphs: $(TARGETS)
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 4
    much more manageable: `NAMES:=Complex Simple IFileReader McCabe-linear` and then `TARGETS:=$(NAMES:%=$(GRAPHDIR)\%.png` – underscore_d Sep 13 '15 at 14:40