Home > Back-end >  How would I create a Makefile that remotely updates itself?
How would I create a Makefile that remotely updates itself?

Time:10-28

I have a makefile that I've changed up a bit here to look more generalized

.PHONY:
        bash hash.sh

all: .PHONY lab tests.zip

lab: .PHONY lab.cpp
        g   -o lab lab.cpp -g -Wall -std=c  11

tests.zip:
        curl -L -O https://url/tests.zip
        unzip tests.zip

tests: .PHONY lab tests.zip
        bash scripts/test.bash lab.cpp

clean:
        rm -rf scripts tests bitset-tests.zip

I am a TA for an entry level computer science course at my university, and I've created a makefile here for my students to use to compile and test their code seamlessly.

One thing I want to do though is to have the makefile update itself every time the remote repository has a new version of it. I know I could just have them update the file themselves, but my job is to make the students focus less on setting things up and more on just coding for now, since it's entry level. So for the purposes of this, I'm sticking with the idea I have.

Currently, I'm achieving this with a script hash.sh which fetches a hash of the makefile from the repo, and compares it to a hash of the makefile in the student's directory. If the hashes don't match, then the updated makefile is fetched and replaces the old one. This is done in the .PHONY recipe. I should also mention that I don't want to add a recipe that updates it like make update, because again I want the process to be seamless. You'd be surprised how many students wouldn't utilize that feature, so I want to build it into the ones they will use for sure.

Is there a better method for this, or am I doing something wrong with this one?

CodePudding user response:

Thomas has the right idea, but you can't use .PHONY here because it would means the makefile is ALWAYS out of date; make knows this so it doesn't re-exec itself if its included makefile is marked .PHONY.

You need to create a way for make to know if the makefile was changed since the last time it was run locally. I recommend you do it like this:

<normal makefile here>

Makefile: FORCE
        curl https://.../Makefile -o Makefile.tmp
        cmp -s Makefile Makefile.tmp && rm -f Makefile.tmp || mv -f Makefile.tmp Makefile

FORCE:

What does this do? First it uses a FORCE target which is an old-school way to emulate a .PHONY target, which is always out of date, without actually using .PHONY (which as I mentioned above, is handled specially by GNU make in this situation).

Second it retrieves the Makefile but only updates the local makefile if it has changed. If it hasn't changed, it doesn't update the local makefile and so make won't re-exec itself.

CodePudding user response:

The whole stuff with fetching a hash sounds overly complicated. If you're going to do a fetch anyway, why not unconditionally fetch the entire makefile? It saves a network round trip, which is probably the limiting factor; the actual data is probably just a few kB anyway.

If you're using curl, notice the --time-cond option, for example:

curl https://... --time-cond Makefile -o Makefile

This will only fetch and update the Makefile if it's newer than the mtime of the current file. It works by sending an If-Modified-Since HTTP header, so you'll need some cooperation from the server for this to work reliably.

If using GNU make, you can use another neat trick: Remake the makefile itself. If you have a rule whose target is Makefile, it will be executed before anything else happens, and make will re-read the updated Makefile before proceeding:

.PHONY: Makefile
Makefile:
    curl https://... --time-cond Makefile -o Makefile

Note that this will lead to infinite loops if for whatever reason the --time-cond leads to an unconditional update, so it wouldn't hurt to guard against that:

.PHONY: Makefile
Makefile:
    [[ ! -v MAKE_RESTARTS ]] && \
        curl https://... --time-cond Makefile -o Makefile
  • Related