我正在从事一个数据科学项目,我想在其中使用 GNU Make 设置训练数据文件夹。这涉及对存储在 zip 文件中的大量 (>10k) 音频文件进行预处理。我想编写一个 Makefile,从互联网上获取一个 zip 文件,解压缩所有的 .aiff 文件,使用 ffmpeg 将它们转换为 .wav,然后删除 .aiff 文件。我正在为如何为在执行其他规则并生成它们之前不存在的文件创建目标/依赖关系而苦苦挣扎。这是我的玩具示例:
.PHONY: dataset
DATA_DIR = data
dataset: ${DATA_DIR}/train/%.wav
@echo Dataset built!
${DATA_DIR}/train/%.wav: ${DATA_DIR}/train/%.aiff
ffmpeg -i $*.aiff $*.wav; rm $*.aiff
${DATA_DIR}/train/%.aiff: ${DATA_DIR}/data.zip
unzip $< data/train/*.aiff
${DATA_DIR}/data.zip:
# ... download the zip file and place it in ./data...
下载和解压缩 .aiff 文件的规则工作正常,但是当它使用 ffmpeg 进行转换时失败。
...
inflating: data/train/train9997.aiff
inflating: data/train/train9998.aiff
inflating: data/train/train9999.aiff
ffmpeg -i %.aiff %.wav; rm %.aiff
ffmpeg version 4.2.7-0ubuntu0.1 Copyright (c) 2000-2022 the FFmpeg developers
built with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
configuration: --prefix=/usr --extra-version=0ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-nvenc --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 31.100 / 56. 31.100
libavcodec 58. 54.100 / 58. 54.100
libavformat 58. 29.100 / 58. 29.100
libavdevice 58. 8.100 / 58. 8.100
libavfilter 7. 57.100 / 7. 57.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 5.100 / 5. 5.100
libswresample 3. 5.100 / 3. 5.100
libpostproc 55. 5.100 / 55. 5.100
%.aiff: No such file or directory
rm: cannot remove '%.aiff': No such file or directory
我想我没有正确使用
%
。它似乎在生成文件之前已展开。我不确定我是否应该使用 $(wildcard ...)
或 .SECONDEXPANSION:
或其他一些神秘的魔法来从它们存储的文件夹中的任意数量的 .aiff 依赖项生成任意数量的 .wav 目标相对于 Makefile。我不能在变量中预先声明这些文件,因为它们太多了(为了争论起见,我不知道所有文件的总数或名称,只知道它们的扩展名)。我希望 Make 在文件解压缩后在运行时找出依赖项及其目标。
是否可以在 make 中执行此操作?如果是这样,我该怎么做?
简短的回答是“你不能那样做”。 Make 在两个不同的阶段工作:首先它读取 makefile 并构建目标和先决条件图。然后它遍历图形并构建东西。第一阶段完成后,您将无法返回并向其添加更多目标。只有通过模式规则匹配才能做到这一点。
但是这个:
dataset: ${DATA_DIR}/train/%.wav
@echo Dataset built!
不是模式规则。模式规则必须在
target名称中具有模式匹配字符 (
%
)。如果目标中没有模式字符,它只是一个普通的显式规则,所以这条规则说 dataset
有文字文件名 data/train/%.wav
的先决条件,所以这就是 make 试图构建的内容。
有很多方法可以做你想做的事,但最简单的方法就是使用递归 make。父 make 将提取文件,子 make(具有完全不同的图形)将检查已经可用的内容。像这样:
.PHONY: dataset
DATA_DIR = data
# This target is the parent make
dataset: ${DATA_DIR}/data.zip
$(MAKE) build-wav
@echo Dataset built!
${DATA_DIR}/data.zip:
# ... download the zip file and place it in ./data...
AIFF_FILES := $(wildcard ${DATA_DIR}/train/*.aiff)
# This target is invoked in the sub-make
.PHONY: build-wav
build-wav: $(AIFF_FILES:.aiff=.wav)
${DATA_DIR}/train/%.wav: ${DATA_DIR}/train/%.aiff
ffmpeg -i $< $@