Niels
Niels

Reputation: 597

How not to remake pre-requisites when final target exists with Make

In the following Makefile, I would like make not to remake the ALL md5 files when the final file: checksums.md5 already exists:

SOURCEDIR := .
SOURCES := $(shell find $(SOURCEDIR) -name '*.gz')
STAGES := .md5
CAT := checksums.md5
ALL := $(foreach I, $(STAGES), $(addsuffix $I, $(SOURCES)))

all : ${CAT}

%.md5: %
    md5sum $< > $@

${CAT} : ${ALL}
    cat $^ >> $@
    rm *gz.md5

Upvotes: 0

Views: 57

Answers (1)

Renaud Pacalet
Renaud Pacalet

Reputation: 29050

You can use conditionals. Example:

SOURCEDIR := .
SOURCES   := $(shell find $(SOURCEDIR) -name '*.gz')
STAGES    := .md5
CAT       := checksums.md5
ALL       := $(foreach I,$(STAGES),$(addsuffix $(I),$(SOURCES)))

ifeq ($(wildcard $(CAT)),)

all: $(CAT)

%.md5: %
    md5sum $< > $@

$(CAT): $(ALL)
    cat $^ >> $@
    rm $(ALL)

else

all:
    @echo "Nothing to be done"

endif

Explanation: if checksums.md5 exists, $(wildcard $(CAT)) will expand as checksums.md5 (not the empty string) and the ifeq conditional will instantiate what is between else and endif. If checksums.md5 does not exist, $(wildcard $(CAT)) will expand as the empty string and the ifeq conditional will instantiate what is between ifeq and else. It is a bit like if you had two different Makefiles for the two situations.

Notes:

  • I suppressed useless spaces in your foreach call (avoid useless spaces with make, they may have an impact on its behavior).
  • Do not suppress the space between ifeq and ($(wildcard..., it is needed.
  • I replaced curly braces (obsolete) by parentheses.
  • I added parentheses to references to non-automatic variables ($I => $(I)) because it is strongly advised.
  • I replaced rm *gz.md5 by rm $(ALL) which seems better if you have some of your sources in sub-directories.

General remark: what you do is not inline with the make philosophy. make decides what should be done and what should not, based on the last modification time of files and a set of target-prerequisites dependency specifications. If you do not use this feature of make you should probably not use it at all. Any scripting language (bash, python, perl...) would probably be better.

If you do not want these partial md5 files, why don't you directly put the sums in checksums.md5? Example:

SOURCEDIR := .
SOURCES   := $(shell find $(SOURCEDIR) -name '*.gz')
CAT       := checksums.md5

all: $(CAT)

$(CAT): $(SOURCES)
    md5sum $^ > $@

clean:
    rm -f $(CAT)

If checksums.md5 exists and is up-to-date (more recent than the sources), nothing will be done. Else, all md5sums will be recomputed and stored in checksums.md5.

If your problem is to avoid recomputing md5sums of files that did not change, you need to keep track of the last time you computed them and there is no better way than keeping these intermediate md5sum files. But you can put them all in a separate directory, if you wish:

SOURCEDIR := .
MD5DIR    := .md5sums
SOURCES   := $(shell find $(SOURCEDIR) -name '*.gz')
CAT       := checksums.md5
ALL       := $(addprefix $(MD5DIR)/,$(patsubst %,%.md5,$(SOURCES)))

all: $(CAT)

$(MD5DIR)/%.md5: %
    mkdir -p $(dir $@) && \
    md5sum $< > $@

$(CAT): $(ALL)
    cat $^ > $@

clean:
    rm -rf $(MD5DIR) $(CAT)

Notes:

  • Just for fun I used a different way of computing the md5sum file names ($(ALL)) using addprefix and patsubst.
  • The mkdir -p in the $(MD5DIR)/%.md5: % recipe is needed to create the destination directory before storing the md5sum file in it.
  • These destination sub-directories of $(MD5DIR) are needed if you have your sources also in sub-directories and some of them can have the same base name.

Upvotes: 2

Related Questions