Como escrevo o comando 'cd' em um makefile?


354

Por exemplo, eu tenho algo parecido com isto no meu makefile:

all:
     cd some_directory

Mas quando eu digitei make, vi apenas 'cd some_directory', como no echocomando.


5
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.


8
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

6
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

41
"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.

Com o GNU make, você pode fazer algo como:

BIN=/bin
foo:
    $(shell cd $(BIN); ls)

5
Por que o $(shell ...)quando cd $(BIN); lsou cd $(BIN) && ls(como @andrewdotn apontou) seria suficiente.
vapace

1

Para mudar de dir

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir

-6

Como isso:

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

Boa sorte!


11
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.
bobbogo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.