使用 GNU Make 处理 space 的文件名

Handling file names with space with GNU Make

我正在使用 GNU Make (v3.80),我必须使用 space 处理文件名。 (对于文件名 本身 ,我无能为力。)我的 makefile 包括 "normal" rules as well as static pattern rules。我如何处理这些文件 而不 改变我的 makefile 太多?

我已经阅读并研究了以前的相关问题 (here, here, here, and there),但是 none 处理静态模式规则。

一个解决方案结合了 "question mark" trick by Mecklenburg 与 shell 命令(find 和 sed)和 not 使用自动变量 $< 而是$*

假设您想将在 data 目录中找到的任何文件复制到目标目录中,这里是规则(包括静态模式规则)。下面是完整的makefile,下面是有趣的部分:

EXEDIR     = bin/vbcc-classic/AmiModRadio/
DATADIR    = $(EXEDIR)data/
DATA       = $(addprefix $(DATADIR),    $(shell find data/   -mindepth 1 -maxdepth 1 -printf "%f\n"         | sed 's/ /?/g'))

在上面的最后一个变量声明中,DATA 包含 data/ 中可用的文件名集,文件名中的任何 space 都替换为 ?.每个文件名都以目标目录 DATADIR 为前缀,并以 spaces.

分隔
space :=
space +=
replaceQuestionBySpace = $(subst ?,$(space),)
replaceSpaceByQuestion = $(subst $(space),?,)

在上面,继 Mecklenburg 之后,定义了两个函数来用问号替换 spaces,反之亦然。

$(DATA) : $(DATADIR)% : $(wildcard data/%)
    cp -f -R "$(call replaceQuestionBySpace,data/$*)" "$(call replaceQuestionBySpace,$@)"

以上是重要的静态模式规则:规则$(wildcard data/%)的先决条件确保Make不会在每次调用时复制data中的文件,但只有当它们发生变化或消失时来自 $(DATADIR)

$(wildcard data/%) 之所以有效,是因为 %$(DATA) : $(DATADIR)% 构建的词干所取代,并且因为 $(DATA) 包含一组没有 space 的文件名(但是问号)并以 $(DATADIR).

为前缀

在配方中,问号被替换为 spaces 以恢复原始文件名。不能使用 $<,因为它会匹配 $(wildcard data/%),这会导致空字符串。但是,因为目标包含 $(DATADIR) 和文件名,所以可以使用 $*(文件名),但要在前面添加 data/


例如完整的makefile

#
#    AmiModRadio
#    All of Aminet modules at your fingertips
#
#
#
#    Copyright 2015, 2016 Tygre <tygre@chingu.asia>
#
#    This file is part of AmiModRadio.
#
#    AmiModRadio is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    AmiModRadio is distributed in the hope fthat it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with AmiModRadio. If not, see <http://www.gnu.org/licenses/>.
#
#
#
#    Entirely developed on an Amiga!
#    (Except for source code versioning...)
#    tygre@chingu.asia
#

# Main paths
EXEDIR     = bin/vbcc-classic/debug/
OBJECTSDIR = o/vbcc-classic/debug/

# Main paths for release ONLY
ifeq "$(strip $(filter dist, $(MAKECMDGOALS)))" "dist"
EXEDIR     = bin/vbcc-classic/AmiModRadio/
OBJECTSDIR = o/vbcc-classic/release/
endif

# Secondary paths
EXE        = $(EXEDIR)AmiModRadio
DATADIR    = $(EXEDIR)data/
ICONSDIR   = $(EXEDIR)icons/
IMAGESDIR  = $(EXEDIR)images/
MODULESDIR = $(EXEDIR)modules/

# (Re)Source files
SOURCES    = $(wildcard *.c)
OBJECTS    = $(addprefix $(OBJECTSDIR), $(SOURCES:.c=.o))
DATA       = $(addprefix $(DATADIR),    $(shell find data/   -mindepth 1 -maxdepth 1 -printf "%f\n"         | sed 's/ /?/g'))
ICONS      = $(addprefix $(ICONSDIR),   $(shell find icons/  -mindepth 1 -maxdepth 1 -printf "%f\n"         | sed 's/ /?/g'))
IMAGES     = $(addprefix $(IMAGESDIR),  $(shell find images/ -mindepth 1 -maxdepth 1 -printf "%f\n" -type d | sed 's/ /?/g'))

# compiler and linker
CC         = vbcc:bin/vc
LD         = vbcc:bin/vc



# Because Make does not support spaces properly...
space :=
space +=
replaceQuestionBySpace = $(subst ?,$(space),)
replaceSpaceByQuestion = $(subst $(space),?,)



# ----------------------------------------

# target 'all' (default target, for debug)
all :  CFLAGS += -DFORTIFY
all :  $(EXEDIR)\
       $(OBJECTSDIR)\
       $(EXE)\
       data\
       images

# target 'dist' for release
dist : mostlyclean\
       $(EXEDIR)\
       $(OBJECTSDIR)\
       $(EXE)\
       data\
       icons\
       images\
       modules\
       $(EXEDIR:/=).lha

# target 'mostlyclean'
mostlyclean :
    -rm $(EXE)
    -rm $(OBJECTSDIR)*

# target 'clean'
clean : mostlyclean
    -rm $(DATADIR)*
    -rm $(ICONSDIR)*
    -rm $(IMAGESDIR)*
    -rm $(MODULESDIR)*

# ----------------------------------------



# Directories
$(EXEDIR) :
    mkdir "$@"

$(OBJECTSDIR) :
    mkdir "$@"



# Objects and executable
$(OBJECTS) : $(OBJECTSDIR)%.o : %.c
    $(CC) $(shell vbccprefs) $(CFLAGS) -c $< -o $@

$(EXE) : $(OBJECTS)
    $(LD) $(shell vbccprefs) -o $(EXE) $(OBJECTS)



# Data
data : $(DATADIR)\
       $(DATA)\

$(DATADIR) :
    mkdir "$@"

$(DATA) : $(DATADIR)% : $(wildcard data/%)
    cp -f -R "$(call replaceQuestionBySpace,data/$*)" "$(call replaceQuestionBySpace,$@)"



# Icons
icons : $(ICONSDIR)\
        $(ICONS)\
        $(EXE).info\
        $(EXEDIR:/=).info

$(ICONSDIR) :
    mkdir "$@"

$(ICONS) : $(ICONSDIR)% : $(wildcard icons/%)
    cp -f -R "$(call replaceQuestionBySpace,icons/$*)" "$(call replaceQuestionBySpace,$@)"

$(EXE).info :
    cp -f -R "$(ICONSDIR)AmiModRadio2.tool.info" "$@"

$(EXEDIR:/=).info :
    cp -f -R "$(ICONSDIR)AmiModRadio3.drawer.info" "$@"



# Images
images : $(IMAGESDIR)\
         $(IMAGES)\

$(IMAGESDIR) :
    mkdir "$@"

$(IMAGES) : $(IMAGESDIR)% : $(wildcard images/%)
    cp -f -R "$(call replaceQuestionBySpace,images/$*)" "$(call replaceQuestionBySpace,$@)"



# Modules
modules : $(MODULESDIR)

$(MODULESDIR) :
    mkdir "$@"



# LHA archive of 'dist'
$(EXEDIR:/=).lha : PARENTDIR = $(dir $(patsubst %/,%,$(EXEDIR)))
$(EXEDIR:/=).lha : EXENAME   = $(notdir $(patsubst %/,%,$(EXEDIR)))
$(EXEDIR:/=).lha :
    rm -f          RAM:$(EXENAME).lha
    /C/LHA -r -e a RAM:$(EXENAME).lha $(PARENTDIR) $(EXENAME).info $(EXENAME)/*