Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile Variable Assignment Executes Early

I have a Makefile rule that requires storing the results from shell commands into variables for later use. For some reason, it seems that the $(shell) call executes as soon as my rule is match, as opposed to when it is encountered during execution.

The file is as follows:

TMPDEV=/tmp/disk.img

$(TMPDEV):
        fallocate -l 806354944 $(TMPDEV)
        sudo parted --script $(TMPDEV) unit s mklabel msdos \
          mkpart primary fat16 2048 526335 \
          mkpart primary fat32 526336 1050623 \
          mkpart primary NTFS 1050624 1574911 \
          quit
        $(eval TMPDISK := $(shell sudo partx --verbose -a $(TMPDEV) | tail -1 | cut -d':' -f1))
        echo $(TMPDISK)
        sudo mkfs.fat -F 16 -n FAT16 $(TMPDISK)p1

It is impossible to know what the value of TMPDISK will be until at least after the fallocate call; that is why the $(eval) statement is delayed until after the disk image is partitioned.

The output I receive is:

$ make
partx: stat failed /tmp/disk.img: No such file or directory
fallocate -l 806354944 /tmp/disk.img || dd if=/dev/zero of=/tmp/disk.img bs=1b count=1574912
sudo parted --script /tmp/disk.img unit s mklabel msdos \
  mkpart primary fat16 2048 526335 \
  mkpart primary fat32 526336 1050623 \
  mkpart primary NTFS 1050624 1574911 \
  quit
echo

The fact that partx errors out (and thus TMPDISK is set to empty) before any of the other commands execute makes me think that $(shell) is called earlier than intended. Is there anyway to delay the shell call and the assignment to TMPDISK until the appropriate line?

like image 926
Tanaki Avatar asked Apr 10 '14 21:04

Tanaki


People also ask

What is := in makefile?

= defines a recursively-expanded variable. := defines a simply-expanded variable.

How do I override a makefile variable?

There is one way that the makefile can change a variable that you have overridden. This is to use the override directive, which is a line that looks like this: ' override variable = value ' (see The override Directive).

What is eval in makefile?

The eval function is very special: it allows you to define new makefile constructs that are not constant; which are the result of evaluating other variables and functions. The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax.


1 Answers

It is not possible to delay expansion like this. Make always expands all the variables in the entire recipe first, before it sends any part of the recipe to the shell. There is no way to "defer" that until later.

Generally, it's not typical to use $(shell ...) in a recipe, since the recipe is already running in a shell. And setting make variables inside a recipe via $(eval ...) is also highly unusual.

I would recommend that you rewrite this recipe to use shell variables, not make variables; it will be much more understandable:

TMPDEV=/tmp/disk.img

$(TMPDEV):
        fallocate -l 806354944 $(TMPDEV)
        sudo parted --script $(TMPDEV) unit s mklabel msdos \
          mkpart primary fat16 2048 526335 \
          mkpart primary fat32 526336 1050623 \
          mkpart primary NTFS 1050624 1574911 \
          quit
        TMPDISK=$$(sudo partx --verbose -a $(TMPDEV) | tail -1 | cut -d':' -f1); \
          echo $$TMPDISK; \
          sudo mkfs.fat -F 16 -n FAT16 $${TMPDISK}p1
like image 192
MadScientist Avatar answered Dec 08 '22 01:12

MadScientist