Tenho uma maneira muito confortável de compilar meu projeto por meio de algumas linhas de comandos bash. Mas agora preciso compilá-lo via makefile. Considerando que cada comando é executado em seu próprio shell, minha dúvida é qual a melhor maneira de executar um comando bash multi-linha, dependente um do outro, no makefile? Por exemplo, assim:
for i in `find`
do
all="$all $i"
done
gcc $all
Além disso, alguém pode explicar por que mesmo o comando de linha única bash -c 'a=3; echo $a > file'
funciona corretamente no terminal, mas cria um arquivo vazio no caso de makefile?
Respostas:
Você pode usar barra invertida para a continuação da linha. No entanto, observe que o shell recebe todo o comando concatenado em uma única linha, portanto, você também precisa terminar algumas das linhas com um ponto e vírgula:
Mas se você deseja apenas pegar toda a lista retornada pela
find
invocação e passá-la paragcc
, você não precisa necessariamente de um comando multilinha:Ou, usando uma
$(command)
abordagem mais convencional de shell (observe o$
escape):fonte
Conforme indicado na pergunta, cada subcomando é executado em seu próprio shell . Isso torna a escrita de scripts de shell não triviais um pouco confusa - mas é possível! A solução é consolidar seu script no que o make considerará um único subcomando (uma única linha).
Dicas para escrever scripts de shell em makefiles:
$
substituindo por$$
;
entre os comandos\
set -e
para corresponder à provisão do make para abortar em caso de falha de subcomando()
ou{}
para enfatizar a coesão de uma sequência de múltiplas linhas - que esta não é uma sequência de comando makefile típicaAqui está um exemplo inspirado no OP:
fonte
SHELL := /bin/bash
em seu makefile habilitar recursos específicos do BASH, como substituição de processo .{
é crucial para evitar a interpretação de{set
um comando desconhecido.{}
e()
faz uma grande diferença se você às vezes deseja copiar o script e executá-lo diretamente de um prompt de shell. Você pode causar estragos em sua instância do shell declarando variáveis e, especialmente, modificando o estado comset
, dentro de{}
.()
evita que o script modifique seu ambiente, o que provavelmente é o preferido. Exemplo ( isso vai acabar a sua sessão de shell ):{ set -e ; } ; false
.command ; ## my comment \` (the comment is between
:; `e` \ `). Isso parece funcionar bem, exceto que se você executar o comando manualmente (copiando e colando), o histórico do comando incluirá o comentário de uma forma que quebra o comando (se você tentar reutilizá-lo). [Observação: o realce da sintaxe foi quebrado para este comentário devido ao uso de barra invertida dentro da crase.]O que há de errado em apenas invocar os comandos?
E para sua segunda pergunta, você precisa escapar
$
de usando$$
, em vez disso, iebash -c '... echo $$a ...'
.EDITAR: Seu exemplo poderia ser reescrito em um script de linha única como este:
fonte
Obviamente, a maneira correta de escrever um Makefile é documentar quais destinos dependem de quais fontes. No caso trivial, a solução proposta fará
foo
depender de si mesma, masmake
é claro, é inteligente o suficiente para descartar uma dependência circular. Mas se você adicionar um arquivo temporário ao seu diretório, ele se tornará "magicamente" parte da cadeia de dependências. Melhor criar uma lista explícita de dependências de uma vez por todas, talvez por meio de um script.GNU make sabe como executar
gcc
para produzir um executável a partir de um conjunto de arquivos.c
e.h
, então talvez tudo o que você realmente precisa sejafonte
A diretiva ONESHELL permite escrever várias receitas de linha para serem executadas na mesma invocação do shell.
Porém, há uma desvantagem: caracteres de prefixo especiais ('@', '-' e '+') são interpretados de forma diferente.
https://www.gnu.org/software/make/manual/html_node/One-Shell.html
fonte