In this kind of situation the templates tend to become complicated stores of knowledge and choices about how to build something for a particular platform and there are usually some complications about maintaining them.
It all depends on what you want out of the system - the ability to build against multiple versions of a specific linux distribution or compatibility across unix or even to non-posix operating systems. So the complexity exits from the generic parts where you say:
PROGRAM:= bob
SOURCE:=fred.c
LIBRARIES=gtk
include build_program_template_$(PLATFORM).mk
and enters the templates for building on a specific platform where you have to decide if that's gtk4 or gtk2 and how to tell the program what it can/cannot do on that specific platform.
If you're the one building the code and deciding where to do it that's cool. For people who want to port your code to some other platform, cmake or autoconf will help discover what needs to be done on some specific plaform, check that the needed features/libraries are on the platform and construct a build with all the features it can support on that platform turned on and the ones it cannot cope with turned off.
They automatically do part of the job you might be doing manually when maintaining templates. This is useful when you're giving the build to someone who just wants it to work and doesn't know the ins and outs like you do. But of course they are complicated and for all the great functionality they are far more complicated to fix when they don't work :-D