Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I re-use a variable containing a timestamp throughout a Makefile run

Tags:

makefile

I'm writing a simple Makefile to handle my document creation (PDF, HTML, LaTeX, EPUB, DOCX) from a series of Markdown text sources.

Now I want to backup some intermediate files as copies in a separate directory, renaming them in the process by adding a timestamp to their names.

However, I do not get it to work. I define a timestamp variable like this:

.PHONY: timestamp
timestamp: ts := `/bin/date "+%Y-%m-%d---%H-%M-%S"`
timestamp:
        @echo Timestamp is $(ts)

%.tex: %.mmd timestamp
        # My commands to create the LaTeX go here. (Simplified for Stackoverflow)
        pandoc  -t latex  -f markdown  -o $(LATEXDIR)/$@  $<
        $(CP) $(LATEXDIR)/$@ $(BACKUPS)/$(ts)---$@    

(Yes, I'm using tabs in the Makefile -- don't get fooled by the reproduction above, should you copy'n'paste from it...)

When I run make timestamp I see the expected output like Timestamp is 2014-11-01---22-11-10.

But running make bookdraft.tex,...

  1. ...my LaTeX file ends up in $(LATEXDIR),
  2. ...but the backup copy's name is ---bookdraft.tex instead of 2014-11-01---22-11-10---bookdraft.tex

So my ts-variable got lost...

I'm a beginner with Makefiles, and several hours of googling and experimenting didn't get me anywhere.

Note, I could set the timestamp on the fly within the current target. However, this will change the timestamp for every additional target which is backed up during the same make call. But that's not what I want -- I want the timestamp be fixed to the same value for every call of make. So somehow I want to set the timestamp variable at the beginning of the Makefile and then re-use it throughout the current run of make. (The next run of make should set the value anew.)

I came up with a temporary workaround by writing the timestamp into a file named .ts and later reading it again from there:

timestamp:
        @echo `/bin/date "+%Y-%m-%d---%H-%M-%S"` > .ts

and then later:

%.tex: %.mmd timestamp
        # My commands to create the LaTeX go here. (Simplified for Stackoverflow)
        pandoc  -t latex  -f markdown  -o $(LATEXDIR)/$@  $<
        $(CP) $(LATEXDIR)/$@ $(BACKUPS)/`/bin/cat .ts`---$@    

However, I do not like this duct-tape method very much -- I'm looking for a more elegant, Makefile-ish solution.


Update:

Jonathan Leffler suggested a solution, but this didn't match my requirement ... or he didn't understand what I want because I didn't explain well enough, or I didn't implement his suggestion correctly. In any case, I can now better illustrate what I do NOT want.

Consider this Makefile.leffler (with <tabs> done correctly in the code):

ts := `/bin/date "+%Y-%m-%d---%H-%M-%S"`

timestamp:
     @echo Timestamp is $(ts)
     @sleep 10
     @echo Timestamp is $(ts)

dummy:
     @sleep 10
     @echo Timestamp is $(ts)
     @sleep 10
     @echo Timestamp is $(ts)

When I run make -f Makefile.leffler timestamp dummy, the output is this:

Timestamp is 2014-01-13---01-58-43
Timestamp is 2014-01-13---01-58-53
Timestamp is 2014-01-13---01-59-03
Timestamp is 2014-01-13---01-59-13

And that's NOT what I want! I want this:

Timestamp is 2014-01-13---01-58-43
Timestamp is 2014-01-13---01-58-43
Timestamp is 2014-01-13---01-58-43
Timestamp is 2014-01-13---01-58-43

I hope my goal is now better clarified.


Solution / Update 2:

After a revision of Jonathan's original answer, this now is the solution for the Makefile example:

ts := $(shell /bin/date "+%Y-%m-%d---%H-%M-%S")

timestamp:
     @echo Timestamp is $(ts)
     @sleep 10
     @echo Timestamp is $(ts)

dummy:
     @sleep 10
     @echo Timestamp is $(ts)
     @sleep 10
     @echo Timestamp is $(ts)

Output of make -f Makefile.leffler timestamp dummy is now exactly as I want it:

Timestamp is 2014-01-13---02-44-11
Timestamp is 2014-01-13---02-44-11
Timestamp is 2014-01-13---02-44-11
Timestamp is 2014-01-13---02-44-11
like image 725
Kurt Pfeifle Avatar asked Jan 12 '14 23:01

Kurt Pfeifle


1 Answers

Don't make ts conditional on the target being timestamp:

ts := $(shell /bin/date "+%Y-%m-%d---%H-%M-%S")

timestamp:
    @echo Timestamp is $(ts)

%.tex: %.mmd
    # My commands to create the LaTeX go here. (Simplified for Stackoverflow)
    pandoc -t latex -f markdown -o $(LATEXDIR)/$@ $<
    $(CP) $(LATEXDIR)/$@ $(BACKUPS)/$(ts)---$@

This differs from the previous proposed solution by using the GNU make $(shell ...) function. This is evaluated when the macro is defined. With this change, the output from the test makefile in the revised question is (for example):

$ make -f ts.mk
Timestamp is 2014-01-12---17-16-35
Timestamp is 2014-01-12---17-16-35
$ make -f ts.mk timestamp dummy
Timestamp is 2014-01-12---17-16-53
Timestamp is 2014-01-12---17-16-53
Timestamp is 2014-01-12---17-16-53
Timestamp is 2014-01-12---17-16-53
$

Or, with timestamps before and after to make it clearer (10 seconds is a horribly long delay, sometimes!), you might get:

$ date; make -f ts.mk timestamp dummy; date
Sun Jan 12 17:19:02 PST 2014
Timestamp is 2014-01-12---17-19-02
Timestamp is 2014-01-12---17-19-02
Timestamp is 2014-01-12---17-19-02
Timestamp is 2014-01-12---17-19-02
Sun Jan 12 17:19:32 PST 2014
$
like image 185
Jonathan Leffler Avatar answered Sep 21 '22 20:09

Jonathan Leffler