'Is there a way to define custom implicit GNU Make rules?

I'm often creating png files out of dot (graphviz format) files. The command to do so is the following:

$ dot my_graph.dot -o my_graph.png -Tpng

However, I would like to be able to have a shorter command format like $ make my_graph.dot to automatically generate my png file.

For the moment, I'm using a Makefile in which I've defined the following rule, but the recipe is only available in the directory containing the Makefile

%.eps: %.dot
    dot $<  -o $@ -Teps

Is it possible to define custom implicit GNU Make recipes ? Which would allow the above recipe to be available system-wide

If not, what solution do you use to solve those kind of problem ?

Setup:

  • Fedora Linux with ZSH/Bash


Solution 1:[1]

You could define shell functions in your shell's startup files, e.g.

dotpng()
{
    echo dot ${1%.dot}.dot -o ${1%.dot}.png -Tpng;
}

This function can be called like

dotpng my_graph.dot

or

dotpng my_graph

The code ${1%.dot}.dot strips .dot from the file name if present and appends it (again) to allow both my_graph.dot and my_graph as function argument.

Solution 2:[2]

Is it possible to define custom implicit GNU Make recipes ?

Not without modifying the source code of GNU Make.

If not, what solution do you use to solve those kind of problem ?

I wouldn't be a fan o modyfying the system globally, but you could do:

  • Create a file /usr/local/lib/make/myimplicitrules.make with the content

      %.eps: %.dot
          dot $<  -o $@ -Teps
    
  • Use include /usr/local/lib/make/myimplicitrules.make in your Makefile.

I would rather use a git submodule or similar to share common configuration between projects, rather than depending on global configuration. Depending on global environment will make your program hard to test and non-portable.

I would rather go with a shell function, something along:

mymake() {
   make -f <(cat <<'EOF'
%.eps: %.dot
    dot $<  -o $@ -Teps
EOF
   ) "$@"
}
mymake my_graph.dot

Solution 3:[3]

GNU Make lets you specify extra makefiles to read using the MAKEFILES environment variable. Quoting from info '(make)MAKEFILES Variable':

the default goal is never taken from one of these makefiles (or any makefile included by them) and it is not an error if the files listed in 'MAKEFILES' are not found
if you are running 'make' without a specific makefile, a makefile in 'MAKEFILES' can do useful things to help the built-in implicit rules work better

As an example, with no makefile in the current directory and the following .mk files in make's include path (e.g. via MAKEFLAGS=--include-dir="$HOME"/.local/lib/make/) you can create subdir gen/ and convert my_graph.dot or dot/my_graph.dot by running:

MAKEFILES=dot.mk make gen/my_graph.png

To further save some typing it's tempting to add MAKEFILES=dot.mk to a session environment but defining MAKEFILES in startup files can make things completely nontransparent. For that reason I prefer seeing MAKEFILES=… on the command line.

File: dot.mk

include common.mk

genDir ?= gen/
dotDir ?= dot/
dotFlags ?= $(if $(DEBUG),-v)
Tvariant ?= :cairo:cairo

vpath %.dot $(dotDir)

$(genDir)%.png $(genDir)%.svg $(genDir)%.eps : %.dot | $(genDir).
    dot $(dotFlags) $< -o $@ -T'$(patsubst .%,%,$(suffix $@))$(Tvariant)'

The included common.mk is where you'd store general definitions to manage directory creation, diagnostics etc., e.g.

.PRECIOUS: %/. ## preempt 'unlink: ...: Is a directory'
%/. : ; $(if $(wildcard $@),,mkdir -p -- $(@D))

References:

  • ?= = := … - info '(make)Reading Makefiles'
  • vpath - info '(make)Selective Search'
  • order-only prerequisites (e.g. | $(genDir).) - info '(make)Prerequisite Types'
  • .PRECIOUS - info '(make)Chained Rules'

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bodo
Solution 2 KamilCuk
Solution 3 urcodebetterznow