Usando Marca $ (dir) ou $ (NotDir) em um caminho com espaços
Pergunta
Eu estou usando código semelhante ao seguinte em um Makefile:
empty:=
space:= $(empty) $(empty)
path_escape = $(subst $(space),\$(space),$(1))
TOP=$(call path_escape,$(abspath .))
TARGET=$(TOP)/foo
$(info TOP='$(TOP)')
$(info TARGET='$(TARGET)')
all: $(TARGET)
$(TARGET):
touch '$(notdir $@)'
.PHONY: $(TARGET)
Se eu usar isso em um diretório sem espaços, digamos space-test
, ele funciona bem:
$ make
TOP='/tmp/space-test'
TARGET='/tmp/space-test/foo'
touch 'foo'
No entanto, se eu usá-lo em um diretório com espaços, dizem space test
, então $(notdir)
faz a coisa errada:
TOP='/tmp/space\ test'
TARGET='/tmp/space\ test/foo'
touch 'space foo'
O que está acontecendo aqui é que interpreta $(notdir)
/tmp/space test/foo
como dois caminhos e retorna a parte "arquivo" de ambos (ou seja, space
e foo
). A parte estranha disto é que TARGET
está devidamente escapou; de alguma forma, dentro da regra ou $(notdir)
dentro, os escapes barra invertida estão sendo ignoradas.
O que estou fazendo de errado aqui?
Solução
A função $(notdir)
no GNU make leva uma lista de argumentos, separados por espaços. Algumas funções apoiar escapando espaços com \\
, mas $(notdir)
não é um deles.
Isso deve funcionar:
s? = $(subst $(empty) ,?,$1)
?s = $(subst ?, ,$1)
notdirx = $(call ?s,$(notdir $(call s?,$1)))
$(TARGET):
touch '$(call notdirx,$@)'
Isto define uma versão "espaço seguro" de notdir
chamado notdirx
. É muito simples: s?
transforma primeiro todos os espaços para pontos de interrogação (na esperança de que eles não podem estar presentes em nomes de arquivos), e convertidos ?s
costas. Entre nós pode seguramente chamar a função notdir
originais.
Para um excelente resumo sobre o GNU make e espaços em nomes de arquivos, consulte GNU make reúne nomes de arquivos com espaços neles .
Outras dicas
Supondo que você tenha um shell Unix, você poderia desembolsar:
notdirx = $(shell basename '$1')
dirx = $(shell dirname '$1')
Uma estratégia similar poderia ser aplicado a outras funções. Basta lembrar as aspas ao redor a variável que contém o texto infectados com espaços em branco que pretende tratar como um único argumento. Isso também irá protegê-lo de outros caracteres especiais (exceto aspas!). Aqui está um exemplo de duplo barra invertida escapar quaisquer espaços no resultado de um glob curinga:
$(shell ls -1 '*.foo' | sed 's/ /\\\\ /g')
Windows é colocar parênteses em nomes de diretório agora, também: C:\Program Files (x86)\
Ainda não está claro para mim o que são as implicações desta ao usar make
Este é apenas um tiro no escuro:
TOP='"/home/chris/src/tests/make/space test"'