j3d
j3d

Reputation: 9724

Makefile: How to use a loop value inside a shell block

Given the following directory structure...

my-project
    + frames
         + frame-1.jpg
         + frame-2.jpg
         ...
         + frame-200.jpg
    + locales
         + default
         |   + music.mp3
         + en
             + music.mp3

For each locale in directory locales (default and en) I need to:

Here below is an extract of my Makefile:

.DEFAULT_GOAL := build

FRAMES_DIR := ${CURDIR}/frames
LOCALES_DIR := ${CURDIR}/locales
OUTDIR:= ${CURDIR}/build

.PHONY: build

build:
    @for dir in $(shell find $(LOCALES_DIR) -type d -depth 1 -exec base name {} \;); do
        $(eval DURATION := $(shell ffprobe -v error -show_entries format=duration -of \
            default=noprint_wrappers=1:nokey=1 $(LOCALES_DIR)/$$dir/*.mp3)); \
        $(eval FRAMES := $(shell ls $(FRAMES_DIR) | wc -l))
        $(eval FRAMERATE := $(shell awk "BEGIN {print 1 / ($(DURATION) / $(FRAMES))}"))
        ffmpeg -y -framerate $(FRAMERATE) -start_number 1 -i $(FRAMES_DIR)/frame_%d.jpeg -i $(LOCALES_DIR)/$$dir/*.mp3 \
           -c:v libx264 -pix_fmt yuv420p -c:a aac -strict experimental \
           -shortest $(OUTDIR)/$video-$$dir.mp4; \
    done;

And here's the expected result:

my-project
    + build
         + video-default.mp4
         + video-en.mp4

The problem is that the ffprobe command fails as the input path is wrong, i.e. $$dir does not contain the expected subdirectory name (default or en). Long story short, it seems $$dir is not visible inside $(shell ...).

Am I missing something? Thanks.

Upvotes: 1

Views: 138

Answers (1)

Shane Bishop
Shane Bishop

Reputation: 4750

Try using a multiline shell script, as below:

build:
    find "${LOCALES_DIR}" -type d -depth 1 -exec base name {} \; | while read -r dir; \
    do \
        DURATION="$$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${LOCALES_DIR}/$$dir"/*.mp3)"; \
        FRAMES="$$(ls -1 "$$FRAMES_DIR" | wc -l)"; \
        FRAMERATE="$$(awk "BEGIN {print 1 / ($$DURATION / $$FRAMES)}")"; \
        ffmpeg -y -framerate "$$FRAMERATE" -start_number 1 -i "$$FRAMES_DIR"/frame_%d.jpeg -i "${LOCALES_DIR}/$$dir"/*.mp3 \
               -c:v libx264 -pix_fmt yuv420p -c:a aac -strict experimental \
               -shortest "${OUTDIR}"/$$video-"$$dir".mp4 < /dev/null; \
    done;

I made some changes:

  • I used a while read loop to avoid problems with paths with spaces
  • I added -1 to ls so that each item is printed on its own line, which should ensure wc -l gives an accurate count
  • I redirect /dev/null as stdin to ffmpeg so that ffmpeg can't interfere with the while read loop

Upvotes: 4

Related Questions