mercredi 6 juillet 2016

gnu make - recipe to keep installed version of file aligned with a master version of file

So here's a Makefile to install foo.conf, based on a master copy called foo.conf.master. It installs it to the currently directory rather than /etc, just for testing purposes:

all: foo.conf.copied

foo.conf.copied: foo.conf.master foo.conf
        cp foo.conf.master foo.conf
        touch $@

#  Recipe to tell make that it is okay for foo.conf not to exist beforehand. 
foo.conf:

So then create foo.conf.master:

$ touch foo.conf.master
$ 

and you're ready to test:

$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$ 

The point is that if I (with my "trusted" sysadmin hat on) modify foo.conf.master then make (possibly called by cron) will roll out the update:

$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$ 

But equally important: if I (with my "rogue" sysadmin hat on) modify the installed version then make will back out the update:

$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$ 

Woohoo.

Okay, so now the problem: obviously foo.conf isn't the only file I want do this for, so I need to change my static rules to pattern rules. Okay, that's easy: substitute foo.conf for % in targets and dependencies, substitute foo.conf for $* in the commands, and make a minor modification to the last recipe (which would otherwise become only '%:') so that it doesn't look like I'm trying to cancel a builtin pattern rule.

So clean up and create this Makefile:

all: foo.conf.copied

%.copied: %.master %
        cp $*.master $*
        touch $@

#  Recipe to tell make that it is okay for foo.conf not to exist beforehand. 
#  Nop tells make that I'm not *cancelling* a pattern rule here
#  (see http://ift.tt/29qn9qr).
%: ;

But this doesn't work:

$ make
make: *** No rule to make target `foo.conf.copied', needed by `all'.  Stop.
$ 

The error message is misleading; it is really foo.conf that it doesn't know how to make, which can be demonstrated by adding the following at the bottom of the Makefile:

foo.conf:
        touch $@

But then that's a static rule again, which I don't want.

There are a couple of requirements I would like to satisfy, which the above example doesn't demonstrate, which are:

  • foo.conf should be installable anywhere in the filesystem (e.g. /etc/foo/server/foo.conf)
  • foo.conf.master should be in a central directory, or subdirectly thereof, for all master versions, preferably without the '.master' extension (e.g. ~/poor-mans-puppet/master-files/etc/foo/foo.conf)
  • foo.conf.copied should be in a central directory, not in the same directory as foo.conf (e.g. ~/poor-mans-puppet/timestamp-files/etc/foo/foo.conf)

After much googling, hair pulling, I'm asking here! Any ideas please? (PS: if copying Makefiles from here, remember to change indentation back to tabs.)

Aucun commentaire:

Enregistrer un commentaire