hatch
hatch

Reputation: 327

Makefile rebuilds all files even if only one changes

I am writing a Makefile for a project with a large number of js files in a complex directory structure. When run it needs to perform some compilation on each file and save the results to a different directory with the same tree structure (right now that's simulated by a cp). When I run make js it builds as it should and when I run make js again it says that there is no work to do. However when I modify one of the files and make js it re-builds the entire tree instead of just the modified file.

SHELL := /bin/bash
BUILDDIR := build/gui/

RAWJSFILES := $(shell find app -name '*.js')
BUILT_RAWJSFILES := $(patsubst %, $(BUILDDIR)%,$(RAWJSFILES))

$(BUILDDIR):
    mkdir -p $(BUILDDIR)

$(RAWJSFILES): $(BUILDDIR)

$(BUILT_RAWJSFILES): $(RAWJSFILES)
    mkdir -p $(@D)
    # compile step
    cp $(shell python -c "print '$@'.lstrip('${BUILDDIR}')") $(@D)

.PHONY: js
js: $(BUILT_RAWJSFILES)

Upvotes: 1

Views: 45

Answers (1)

Etan Reisner
Etan Reisner

Reputation: 80921

The line $(BUILT_RAWJSFILES): $(RAWJSFILES) is setting the prerequisites of each file in $(BUILT_RAWJSFILES) to all the files in $(RAWJSFILES).

To get the one-to-one mapping you want you need a pattern rule or a static pattern rule.

Also that embedded python snippet isn't at all doing what you intended for it to do there. It is removing the longest leading prefix from the value of $@ that contains any of the characters in BUILDDIR namely build/gui/ (only not as a string as characters so that's equivalent to lstrip('/iugdlb')). (Oh, also you don't need $(shell) here you are already in a shell context so a normal command substitution would work just as well ($$(python ...).)

That being said if you use a pattern or static pattern rule you'll have easier ways to get the source filename then messing with python like that.

Something like this should work (untested):

$(BUILDDIR)%.js: %.js
        mkdir -p $(@D)
        cp $^ $(@D)

Upvotes: 1

Related Questions