Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting part of a match in a makefile rule

Tags:

makefile

I have a makefile which generates a bunch of versions of an image in different places:

website/img/logo_256.png
website/img/logo_152.png
/tmp/logo_64.png

and so on (the /tmp/ generation is so I can later use those files to later generate a multiresolution .ico, the details of that aren't important).

I'd like a rule of the form

logo_%.png: ${SRC}
        convert $^ -thumbnail $*x$* $@

but, $* brings in the matched directory too, so I get a command of the form:

convert logo_1024.png -thumbnail /tmp/64x/tmp/64 /tmp/logo_64.png

which is incorrect (I need 48x48, not /tmp/48x/tmp/48).

Or I can write

/tmp/logo_%.png: ${SRC}
        convert $^ -thumbnail $*x$* $@
website/img/logo_%.png: ${SRC}
        convert $^ -thumbnail $*x$* $@

which seems ugly.

I'm sure there are ways to break down and pattern match $@ to get what I want, but I'm not a makefile guru, so this would take some research.

What's the easiest way to do this?

like image 794
Ian Avatar asked Sep 29 '22 19:09

Ian


2 Answers

See the second half of the Automatic Variables in the GNU Make Manual:

Of the variables listed above, four have values that are single file names, and three have values that are lists of file names. These seven have variants that get just the file's directory name or just the file name within the directory. The variant variables' names are formed by appending ‘D’ or ‘F’, respectively. These variants are semi-obsolete in GNU make since the functions dir and notdir can be used to get a similar effect (see Functions for File Names). Note, however, that the ‘D’ variants all omit the trailing slash which always appears in the output of the dir function. Here is a table of the variants:

‘$(@D)’
    The directory part of the file name of the target, with the trailing slash removed. If the value of ‘$@’ is dir/foo.o then ‘$(@D)’ is dir. This value is . if ‘$@’ does not contain a slash.


‘$(@F)’
    The file-within-directory part of the file name of the target. If the value of ‘$@’ is dir/foo.o then ‘$(@F)’ is foo.o. ‘$(@F)’ is equivalent to ‘$(notdir $@)’.


‘$(*D)’
‘$(*F)’
    The directory part and the file-within-directory part of the stem; dir and foo in this example.


‘$(%D)’
‘$(%F)’
    The directory part and the file-within-directory part of the target archive member name. This makes sense only for archive member targets of the form archive(member) and is useful only when member may contain a directory name. (See Archive Members as Targets.)


‘$(<D)’
‘$(<F)’
    The directory part and the file-within-directory part of the first prerequisite.


‘$(^D)’
‘$(^F)’
    Lists of the directory parts and the file-within-directory parts of all prerequisites.


‘$(+D)’
‘$(+F)’
    Lists of the directory parts and the file-within-directory parts of all prerequisites, including multiple instances of duplicated prerequisites.


‘$(?D)’
‘$(?F)’
    Lists of the directory parts and the file-within-directory parts of all prerequisites that are newer than the target.

Edit:

As prompted by @Ian's comment I looked again and realized that this was not a complete solution. A complete solution follows.

The above F modifiers (and the $(notdir) function) will strip the path from the target filename. That's part of what is necessary.

Additional manipulation is required to extract only the numerical component from target like /some/path/logo_64.png.

The $(basename) function will strip off suffixes (as will $(patsubst %.png,%,$@) or $(@:%.png=) in a more specific fashion).

Combining those we get from /some/path/logo_64.png to logo_64. Handling things at this point depends heavily on what the data is going to look like and what assertions about it can be made. If logo_ is a static prefix then a simple $(patsubst logo_%,%,...) will work (as will the matching substitution reference like before).

If that is not guaranteed but the guarantee can be made that the dimension will be the last underscore separated component then $(lastword $(subst _, ,...)) can be used.

like image 191
Etan Reisner Avatar answered Oct 06 '22 19:10

Etan Reisner


The rule needed is:

logo_%.png: ${SRC}
    convert $^ -thumbnail $(*F)x$(*F) $@

The $(*F), is documented very briefly in the Make manual, as quoted in Etan's answer.

$(*F)’ The file-within-directory part of the stem; foo in this example.

The 'stem' ($*) is anything not explicit in the pattern. That includes the wildcard, and any implicit directories. So hence in the question it had the value /tmp/48, /tmp/ from the implicit directory, and 48 from the wildcard in the pattern. So of this combined stem, I need to select just the filename part, $(*F).


Alternatively, noting that the manual states:

These variants are semi-obsolete in GNU make since the functions dir and notdir can be used to get a similar effect

we can instead do:

logo_%.png: ${SRC}
    convert $^ -thumbnail $(notdir $*)x$(notdir $*) $@

In a comment, Etan also linked to the How Patterns Match section of the manual, to help understand how the stem is constructed. I found this useful and wanted to bubble it up into an answer.

like image 43
Ian Avatar answered Oct 06 '22 18:10

Ian