Search code examples
makefilebuildgnu

GNU Make: Batching multiple targets in single command invocation


Consider the following setup:

$ touch 1.src 2.src 3.src
$ cat Makefile
%.dst: %.src
    @convert -o "$@" "$<" 

We can compile our .src files into .dst files by running make 1.dst 2.dst 3.dst which calls the convert (just a placeholder) tool three times.

This setup is fine if there is little overhead in calling convert. However, in my case, it has a startup penalty of a few seconds for every single call. Luckily, the tool can convert multiple files in a single call while paying the startup penalty only once, i.e. convert -o '{}.dst' 1.src 2.src 3.src.

Is there a way in GNU make to specify that multiple src files should be batched into a single call to convert?
Edit: To be more precise, what feature I am looking for: Say that 1.dst is already newer than 1.src so it doesn't need to be recompiled. If I run make 1.dst 2.dst 3.dst, I would like GNU make to execute convert -o '{}.dst' 2.src 3.src.

A quick and dirty way would be creating a .PHONY rule that simply converts all src files to dst files but that way I would convert every src file each and every time. Further more, specifying dst files as prerequisites in other rules would also no longer be possible.

Thanks in advance!


Solution

  • If you have GNU make 4.3 or above, you can use grouped targets like this:

    DST_FILES = 1.dst 2.dst 3.dst
    SRC_FILES = $(_DST_FILES:.dst=.src)
    
    all: $(DST_FILES)
    
    $(DST_FILES) &: $(SRC_FILES)
            convert -o '{}.dst' $?
            @touch $(DST_FILES)
    

    If your convert is only updating some of the targets then you need the explicit touch to update the rest.

    Here's a way to do it with passing a goal on the command line that might work; change DST_FILES to:

    DST_FILES := $(or $(filter %.dst,$(MAKECMDGOALS)),1.dst 2.dst 3.dst)