Search code examples
makefile

Add dynamic dependencies to a target with a placeholder


I have a target

%.hgtForCoordsAndNeighbours (example: +64-016.hgtForCoordsAndNeighbours)

that represents a geo coordinate and depends on build steps that are run on all adjacent 1-degree chunks. These (prerequisite) build steps are called

%.hgtForCoords.

My problem here is that I need a little calculation to expand from that 1 target name into the 8 (+1 for the centre) adjacent prerequisites and also to enforce the filename structure with the leading zeroes (%+03d%+04d in printf).

I have not found a way to express that in Makefile syntax yet so I went for a sub-$(MAKE). Yet that brings its problems with concurrency (-j ...) and I think is generally considered bad style to have a Makefile call itself. I would also like to learn more about the Makefile syntax.

Full example:

SHELL=/bin/bash

%.hgtForCoordsAndNeighbours:
    # this looks too complicated but that's not the point here. 
    # It's an example for dependencies generated from a target name by a script 
    # and not in Makefile syntax.
    coords=$$(basename $@ .hgtForCoordsAndNeighbours) \
        && echo "Prepare elevation data for $$coords and neighbours" \
        && lat=$$(echo $$coords | grep -Eo '^.{3}' | sed -E 's/([+-])0+/\1/') \
        && lon=$$(echo $$coords | grep -Eo '.{4}$$' | sed -E 's/([+-])0+/\1/') \
        && minLat=$$(($$lat -1)) \
        && maxLat=$$(($$lat +1)) \
        && minLon=$$(($$lon -1)) \
        && maxLon=$$(($$lon +1)) \
        && $(MAKE) -f $(lastword $(MAKEFILE_LIST)) \
            $$(printf "%+03d%+04d" "$$minLat" "$$minLon").hgtForCoords \
            $$(printf "%+03d%+04d" "$$lat"    "$$minLon").hgtForCoords \
            $$(printf "%+03d%+04d" "$$maxLat" "$$minLon").hgtForCoords \
            $$(printf "%+03d%+04d" "$$minLat" "$$lon"   ).hgtForCoords \
            $$(printf "%+03d%+04d" "$$lat"    "$$lon"   ).hgtForCoords \
            $$(printf "%+03d%+04d" "$$maxLat" "$$lon"   ).hgtForCoords \
            $$(printf "%+03d%+04d" "$$minLat" "$$maxLon").hgtForCoords \
            $$(printf "%+03d%+04d" "$$lat"    "$$maxLon").hgtForCoords \
            $$(printf "%+03d%+04d" "$$maxLat" "$$maxLon").hgtForCoords \
    ;

Abstract example what I would like to achieve:

%.targetWithAPlaceholder: $(prerequisites generated by a 
                            custom function based on the target name)

It is not a problem if that is not too portable. It is only run on a well-controlled environment (currently a Github actions Linux runner with GNU Make 4.3).

In this case it would be OK if all dependencies were evaluated once (since the coordinate space is finite (360 * 180)).


Solution

  • It's probably easiest to generate the dependencies with a short script. Perhaps something such as:

    #!/bin/sh
    
    for lat in $(seq -90 90)
    do for lon in $(seq -180 180)
       do for dx in -1 0 1
          do for dy in -1 0 1
             do printf "%+03d%+04d.hgtForCoordsAndNeighbours: %+03d%+04d.hgtForCoords\n" \
                       $lat $lon $((lat+dy)) $((lon+dx))
             done
          done
       done
    done
    

    If we call this generate-deps, then the (GNU) Makefile lines to ensure it's called and included are simply this:

    neighbours.mak: generate-deps
        ./$< >$@
    
    include neighbours.mak