Albus Dumbledore
Albus Dumbledore

Reputation: 12616

Copying a bunch of files with GNU make

Say I have a makefile in which I want to copy a few files from one place to another, e.g.:

This is part of the "install" rule. What comes to my mind is something of the sort:

EXTRA_INSTALL += include/mylib/mylib.h=dist/include/mylib/mylib.h
EXTRA_INSTALL += include/mylib/platform/linux/platform.h=dist/include/mylib/platform.h

# All other dependecies (objects, .so, etc.) go through `$(TARGET)`
install: $(TARGET) $(EXTRA_INSTALL)

$(EXTRA_INSTALL):
    @cp $(firstword $(subst =, ,$@)) $(lastword $(subst =, ,$@))

.PHONY: install $(EXTRA_INSTALL)

I think it's a sort of a hack, but I couldn't think of a proper way to do it. So is there a better way to achieve the same thing? Note that there's no apparent connection between the input filename and the output one, so a rule of the sort dist/include/%.h:include/%.h would not be suitable.

Upvotes: 3

Views: 306

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171263

It's a horrible hack. Doing this kind of thing is exactly what make is for, you don't need to invent your own mini-language inside make.

Define the source=target mapping as a make prerequisite instead, then define the recipe for creating it via cp:

dist/include/mylib/mylib.h: include/mylib/mylib.h 
        cp $^ $@

This says creating the file dist/include/mylib/mylib.h depends on the other file (so it will be recreated if the other file changes) and that it should be created by copying it.

The answer from @user657267 shows how to reduce duplication by separating the definition of the dependencies from the recipe for doing the actual copying, so you don't need to repeat the cp recipe on every file. However, as the recipe is incredibly simple, I see no harm in just repeating it. You could always replace the recipe with a function call if it got more complicated:

CP := cp -f
copy_file := $(CP) $(1) $(2)

dist/include/mylib/mylib.h: include/mylib/mylib.h 
        $(call copy_file,$^,$@)

dist/include/mylib/platform.h: include/mylib/platform/linux/platform.h
        $(call copy_file,$^,$@)

Upvotes: 2

user657267
user657267

Reputation: 20990

Something like the following might be more readable (although it adds a little duplication), it has the benefit of updating the files if they happen to have changed.

EXTRA_INSTALL := dist/include/mylib/mylib.h dist/include/mylib/platform.h

.PHONY: install
install: $(EXTRA_INSTALL)

dist/include/mylib/mylib.h: include/mylib/mylib.h
dist/include/mylib/platform.h: include/mylib/platform/linux/platform.h

.SECONDEXPANSION:
$(EXTRA_INSTALL): $$^
    cp $^ $@

You could also forgo .SECONDEXPANSION entirely at the expense of an extra line per file

EXTRA_INSTALL := dist/include/mylib/mylib.h dist/include/mylib/platform.h

.PHONY: install
install: $(EXTRA_INSTALL)

dist/include/mylib/mylib.h: include/mylib/mylib.h
    cp $^ $@
dist/include/mylib/platform.h: include/mylib/platform/linux/platform.h
    cp $^ $@

Upvotes: 2

Related Questions