Search code examples
makefilegnuautotoolsautoconfautomake

How to execute shell commands in Automake/Makefile.am?


After I compile my executable using Automake, I want the executable to output the date compiled, compiler used, and git repo version/date. However, I can't seem to get shell commands to run from Makefile.am. When I try, I get these warnings:

sample/C++/Makefile.am:1: warning: date +%D: non-POSIX variable name

sample/C++/Makefile.am:1: (probably a GNU make extension)

sample/C++/Makefile.am:2: warning: git --no-pager show --date=short --format="%ad" --name-only | { read first _ ; echo $first ; }: non-POSIX variable name

sample/C++/Makefile.am:2: (probably a GNU make extension)

sample/C++/Makefile.am:3: warning: git --no-pager describe --tags --always --dirty: non-POSIX variable name

sample/C++/Makefile.am:3: (probably a GNU make extension)

If there is a better way to do this besides running the commands in configure.ac (because that defeats the purpose of having a compile time) please let me know.


Solution

  • POSIX make and many specific make implementations provide no mechanism for running shell commands outside of recipes. GNU make and perhaps others do provide that as an extension, but one of the objectives of the Autotools is to support the widest variety of build environments possible, so relying on extensions provided by specific implementations is contrary to Autotools idiom.

    One way to approach the problem without relying on extensions would be to make your sources #include a header that is generated dynamically at build time. For example, here's how the Makefile.am part might look:

    bin_PROGRAMS = hello
    
    hello_SOURCES = \
      hello.cpp \
      build_details.h
    
    BUILT_SOURCES = \
      build_details.h
    
    CLEANFILES = \
      build_details.h
    
    build_details.h:
        echo "#define BUILDDATE \"`date +%D`\"" >$@
        echo "#define COMPILER \"$(CXX)\"" >>$@
    

    Because it is listed among the BUILT_SOURCES, target build_details.h will be built early. Because the rule for building it has no prerequisites, it is always considered out of date, so it will be rebuilt, thus getting the fresh details. Of course, this also means that all sources that depend it will be rebuilt every time you run make, but maybe that's actually what you want if you're tracking this kind of build info.

    On the other hand, if you're willing to depend on GNU make extensions, then they provide a way to set variables to the output of shell commands. In that case you might do something more like this:

    bin_PROGRAMS = hello
    
    builddate := $(shell date +%D)
    CXXFLAGS = -DBUILDDATE='"$(builddate)"' -DCOMPILER='"$(CXX)"'
    
    hello_SOURCES = \
      hello.cpp
    

    Note the double quotation: the inner double quotes need to be part of the replacement text of the macros being defined, so they need to be protected from the shell as they appear on the compiler command line.

    This has the advantages that it is shorter, and that it will not cause hello to be rebuilt if hello.cpp has not changed. But it will only work with a make that supports the GNU extensions used. Also, this does not cause any objects to be out of date, so it may leave open the possibility of stale build info being recorded in the binary as a result of the object that holds it not being among those rebuilt on some given make execution.

    Of course, although I've addressed the question as posed, it is worthwhile to note that some of this is duplicative of features that exist directly in the C and C++ languages. Specifically, the __DATE__ and __TIME__ macros are automatically defined to expand to the date and time of compilation. They don't give you control over the format, but that might be a worthwhile tradeoff.