Question

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?

Was it helpful?

Solution

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

OTHER TIPS

I had the same problem. The eval command expands even before the target command runs. It was answered here below for my question by Simon Gibbons.

makefile variable assignment under a target

The solution is to add another target for the "fallocate" command and add that as a dependency to $(TMPDEV) target.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top