Não está claro o que você quer fazer, mas, na minha experiência make, nunca quis alterar o diretório assim. Talvez você deva tentar outra abordagem para sua solução?
P Enviado
2
É um erro comum para iniciantes acreditar que seu diretório é importante. Para a maioria das coisas, não é; cd dir; cmd filequase sempre pode ser mais útil expresso como cmd dir/file.
tripleee
34
É um erro comum para iniciantes acreditar que seu diretório de trabalho atual é inconseqüente. Muitos programas, especialmente scripts de shell, são escritos com um valor específico .em mente. É verdade que a maioria das ferramentas é projetada de tal maneira que você não precisa alterar seu código de acesso. Mas isso nem sempre é verdade, e eu não acho uma boa idéia chamá-lo de "erro" para acreditar que seu diretório pode ser importante.
Evelyn Kokemoor
Dica: se o seu comando cd diz "Nenhum tal lima ou diretório", mesmo quando o diretório (relativa) faz existir, verifique que a sua variável de ambiente CDPATH está vazio ou inclui "". Make executa comandos com "sh", que só encontrará um caminho relativo via CDPATH se estiver definido. Isso contrasta com o bash, que tentará. antes de consultar o CDPATH.
Denis Howe
Respostas:
621
Na verdade, ele está executando o comando, alterando o diretório para some_directory, no entanto, isso é realizado em um shell de subprocesso e não afeta a marca nem o shell do qual você está trabalhando.
Se você deseja executar mais tarefas some_directory, precisa adicionar um ponto-e-vírgula e anexar os outros comandos também. Observe que você não pode usar novas linhas, pois elas são interpretadas por make como o fim da regra; portanto, qualquer nova linha usada para maior clareza precisa ser escapada por uma barra invertida.
Por exemplo:
all:
cd some_dir; echo "I'm in some_dir"; \
gcc -Wall -o myTest myTest.c
Observe também que o ponto e vírgula é necessário entre todos os comandos, mesmo que você adicione uma barra invertida e uma nova linha. Isso se deve ao fato de que toda a cadeia é analisada como uma única linha pelo shell. Conforme observado nos comentários, você deve usar '&&' para juntar comandos, o que significa que eles só serão executados se o comando anterior tiver êxito.
all:
cd some_dir && echo "I'm in some_dir" && \
gcc -Wall -o myTest myTest.c
Isso é especialmente crucial ao realizar trabalhos destrutivos, como limpeza, pois, caso contrário, você destruirá as coisas erradas, caso cdfalhe por qualquer motivo.
Um uso comum, porém, é chamar make no subdiretório, no qual você pode procurar. Existe uma opção de linha de comando para isso, para que você não precise se chamar cd, assim sua regra se pareceria com isso
all:
$(MAKE) -C some_dir all
que mudará para some_dire executará o Makefilelá com o destino "todos". Como prática recomendada, use em $(MAKE)vez de chamar makediretamente, pois será necessário chamar a instância make correta (se você, por exemplo, usar uma versão make especial para o seu ambiente de construção), além de fornecer um comportamento ligeiramente diferente ao executar usando certas opções, como -t.
Para o registro, faça sempre o eco do comando que ele executa (a menos que seja explicitamente suprimido), mesmo que não tenha saída, que é o que você está vendo.
Bem, nem sempre . Para suprimir o eco, basta colocar @ no início da linha.
Beta
2
@ Beta: bem, sim, e um prefixo de traço ignora o status do erro também. Talvez eu tenha me empolgado um pouco, eu queria ressaltar o fato de que make ecoa o comando, independentemente de que tipo de comando seja. E, neste caso, é um comando sem saída, o que faz o eco parecer ainda mais estranho para alguém não familiarizado com o make.
falstro
46
Duas lêndeas: 1. Os comandos devem realmente ser acompanhados &&, porque, ;se o diretório não existir e cdfalhar, o shell continuará executando o restante dos comandos no diretório atual, o que pode causar coisas como arquivos misteriosos encontrado ”para compilações, loops infinitos ao chamar make ou desastre para regras como clean:: cd dir; rm -rf *. 2. Ao chamar sub-marcas, chame em $(MAKE)vez de makepara que as opções sejam transmitidas corretamente .
andrewdotn
2
@perreal, eu geralmente defino uma regra de padrão assim: %-recursive:com o corpo: @T="$@";$(MAKE) -C some_dir $${T%-*}(eu também tenho um loop for, para repetir uma lista de subdiretórios, $${T%-*}é uma expansão do bash que remove a -recursiveparte do nome do destino) e, em seguida, definir explícita curto-mão (e .PHONY) metas para cada um, como all: all-recursive, check: check-recursive, clean: clean-recursive.
falstro
11
@ChristianStewart, verdade, como foi mencionado no comentário 2 e 3.
falstro
95
A partir do GNU make 3.82 (julho de 2010), você pode usar o .ONESHELLdestino especial para executar todas as linhas de receita em uma única instanciação do shell (ênfase em negrito):
Novo alvo especial: .ONESHELLinstrui make a invocar uma única instância do shell e fornecer toda a receita , independentemente de quantas linhas ele contenha. [...]
.ONESHELL: # Only applies to all target
all:
cd ~/some_dir
pwd # Prints ~/some_dir if cd succeeded
Basta notar que pwdem si funciona, bem como `pwd`(com acentos graves), mas $(shell pwd)e $(PWD)ainda retornará o diretório antes de fazer o cdcomando, então você não pode usá-los diretamente.
anol
3
Sim, porque variável e expansão função são feitas antes de executar os comandos por marca, enquanto que pwde `pwd`é executado pelo próprio shell.
precisa saber é o seguinte
2
Nem todo mundo trabalha em makefiles legados, e mesmo assim essa resposta é sobre saber que essa possibilidade existe.
Chnossos
11
Essa pode ser uma opção irritante / perigosa, porque apenas o último comando de um destino pode causar uma falha (quaisquer falhas de comando anteriores serão ignoradas) e, provavelmente, ninguém trabalhando com você estará esperando isso.
Tobii
11
Defina SHELLFLAGScomo -e -ce o shell será encerrado no primeiro comando que falhar.
Timothy Baldwin
21
Aqui está um truque fofo para lidar com diretórios e criar. Em vez de usar cadeias de linhas múltiplas, ou "cd;" em cada comando, defina uma função chdir simples da seguinte maneira:
CHDIR_SHELL := $(SHELL)
define chdir
$(eval _D=$(firstword $(1) $(@D)))
$(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef
Então tudo o que você precisa fazer é chamá-lo em sua regra da seguinte maneira:
all:
$(call chdir,some_dir)
echo "I'm now always in some_dir"
gcc -Wall -o myTest myTest.c
Você pode até fazer o seguinte:
some_dir/myTest:
$(call chdir)
echo "I'm now always in some_dir"
gcc -Wall -o myTest myTest.c
"Fofa"? Mais como corda suficiente para dar um tiro no próprio pé.
tripleee
11
Esse diretório atual é definido para os comandos dessa regra ou para todas as regras executadas posteriormente? Além disso, alguma variação desse trabalho funcionará no Windows?
User117529 de
8
É claro que isso interrompe a execução paralela ( -jn), que é realmente o objetivo principal do make .
bobbogo
5
Este é um truque ruim. Se você precisar recorrer a algo assim, não estará usando Makefiles para o que eles servem.
gatopeich
4
Não vou discordar que é um truque ruim, com certeza. Mas demonstra algumas das coisas más que você pode fazer.
joes
9
O que você quer fazer quando chegar lá? Cada comando é executado em um subshell, portanto, o subshell altera o diretório, mas o resultado final é que o próximo comando ainda está no diretório atual.
target:
$(shell cd ....); \
# ... commands execution in this directory
# ... no need to go back (using "cd -" or so)
# ... next target will be automatically in prev dir
Não, isso está errado. Especificamente, o $(shell cd ....)é executado quando o Makefile é analisado inicialmente, não quando essa receita específica é executada.
Tripleee
@triplee Não é bem assim - o $(shell)é expandido somente quando make decide construir alvo . Se o make nunca precisar da receita, ela não será expandida.
make
, nunca quis alterar o diretório assim. Talvez você deva tentar outra abordagem para sua solução?cd dir; cmd file
quase sempre pode ser mais útil expresso comocmd dir/file
..
em mente. É verdade que a maioria das ferramentas é projetada de tal maneira que você não precisa alterar seu código de acesso. Mas isso nem sempre é verdade, e eu não acho uma boa idéia chamá-lo de "erro" para acreditar que seu diretório pode ser importante.Respostas:
Na verdade, ele está executando o comando, alterando o diretório para
some_directory
, no entanto, isso é realizado em um shell de subprocesso e não afeta a marca nem o shell do qual você está trabalhando.Se você deseja executar mais tarefas
some_directory
, precisa adicionar um ponto-e-vírgula e anexar os outros comandos também. Observe que você não pode usar novas linhas, pois elas são interpretadas por make como o fim da regra; portanto, qualquer nova linha usada para maior clareza precisa ser escapada por uma barra invertida.Por exemplo:
Observe também que o ponto e vírgula é necessário entre todos os comandos, mesmo que você adicione uma barra invertida e uma nova linha. Isso se deve ao fato de que toda a cadeia é analisada como uma única linha pelo shell. Conforme observado nos comentários, você deve usar '&&' para juntar comandos, o que significa que eles só serão executados se o comando anterior tiver êxito.
Isso é especialmente crucial ao realizar trabalhos destrutivos, como limpeza, pois, caso contrário, você destruirá as coisas erradas, caso
cd
falhe por qualquer motivo.Um uso comum, porém, é chamar make no subdiretório, no qual você pode procurar. Existe uma opção de linha de comando para isso, para que você não precise se chamar
cd
, assim sua regra se pareceria com issoque mudará para
some_dir
e executará oMakefile
lá com o destino "todos". Como prática recomendada, use em$(MAKE)
vez de chamarmake
diretamente, pois será necessário chamar a instância make correta (se você, por exemplo, usar uma versão make especial para o seu ambiente de construção), além de fornecer um comportamento ligeiramente diferente ao executar usando certas opções, como-t
.Para o registro, faça sempre o eco do comando que ele executa (a menos que seja explicitamente suprimido), mesmo que não tenha saída, que é o que você está vendo.
fonte
&&
, porque,;
se o diretório não existir ecd
falhar, o shell continuará executando o restante dos comandos no diretório atual, o que pode causar coisas como arquivos misteriosos encontrado ”para compilações, loops infinitos ao chamar make ou desastre para regras comoclean:: cd dir; rm -rf *
. 2. Ao chamar sub-marcas, chame em$(MAKE)
vez demake
para que as opções sejam transmitidas corretamente .%-recursive:
com o corpo:@T="$@";$(MAKE) -C some_dir $${T%-*}
(eu também tenho um loop for, para repetir uma lista de subdiretórios,$${T%-*}
é uma expansão do bash que remove a-recursive
parte do nome do destino) e, em seguida, definir explícita curto-mão (e .PHONY) metas para cada um, comoall: all-recursive
,check: check-recursive
,clean: clean-recursive
.A partir do GNU make 3.82 (julho de 2010), você pode usar o
.ONESHELL
destino especial para executar todas as linhas de receita em uma única instanciação do shell (ênfase em negrito):fonte
pwd
em si funciona, bem como`pwd`
(com acentos graves), mas$(shell pwd)
e$(PWD)
ainda retornará o diretório antes de fazer ocd
comando, então você não pode usá-los diretamente.pwd
e`pwd`
é executado pelo próprio shell.SHELLFLAGS
como-e -c
e o shell será encerrado no primeiro comando que falhar.Aqui está um truque fofo para lidar com diretórios e criar. Em vez de usar cadeias de linhas múltiplas, ou "cd;" em cada comando, defina uma função chdir simples da seguinte maneira:
Então tudo o que você precisa fazer é chamá-lo em sua regra da seguinte maneira:
Você pode até fazer o seguinte:
fonte
-jn
), que é realmente o objetivo principal do make .O que você quer fazer quando chegar lá? Cada comando é executado em um subshell, portanto, o subshell altera o diretório, mas o resultado final é que o próximo comando ainda está no diretório atual.
Com o GNU make, você pode fazer algo como:
fonte
$(shell ...)
quandocd $(BIN); ls
oucd $(BIN) && ls
(como @andrewdotn apontou) seria suficiente.Para mudar de dir
fonte
Como isso:
Boa sorte!
fonte
$(shell cd ....)
é executado quando o Makefile é analisado inicialmente, não quando essa receita específica é executada.$(shell)
é expandido somente quando make decide construir alvo . Se o make nunca precisar da receita, ela não será expandida.