Estou tentando fazer o seguinte. Existe um programa, chame-o foo-bin
, que recebe um único arquivo de entrada e gera dois arquivos de saída. Uma regra Makefile burra para isso seria:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
No entanto, isso não indica de make
forma alguma que ambos os alvos serão gerados simultaneamente. Isso é bom quando executado make
em série, mas provavelmente causará problemas se alguém tentar make -j16
ou algo igualmente maluco.
A questão é se existe uma maneira de escrever uma regra Makefile adequada para tal caso. Claramente, isso geraria um DAG, mas de alguma forma o manual do GNU make não especifica como este caso poderia ser tratado.
Executar o mesmo código duas vezes e gerar apenas um resultado está fora de questão, porque o cálculo leva tempo (pense: horas). A saída de apenas um arquivo também seria bastante difícil, porque freqüentemente é usado como uma entrada para o GNUPLOT, que não sabe como lidar com apenas uma fração de um arquivo de dados.
Respostas:
Eu iria resolver da seguinte forma:
Neste caso, o make paralelo 'serializará' criando aeb, mas como criar b não faz nada, não leva tempo.
fonte
file-b.out
foi criado como declarado e, em seguida, misteriosamente excluído,make
não será possível recriá-lo porquefile-a.out
ainda estará presente. Alguma ideia?file-a.out
, a solução acima funcionará bem. Isso sugere um hack: quando apenas um de a ou b existe, as dependências devem ser ordenadas de forma que o arquivo ausente apareça como saída deinput.in
. Alguns$(widcard...)
s,$(filter...)
s,$(filter-out...)
s etc. devem resolver o problema. Ugh.foo-bin
obviamente criará um dos arquivos primeiro (usando resolução de microssegundos), portanto, você deve garantir que tenha a ordem de dependência correta quando os dois arquivos existirem.touch file-a.out
como um segundo comando após afoo-bin
invocação.touch file-b.out
como um segundo comando na primeira regra e duplique os comandos na segunda regra. Se um dos arquivos estiver faltando ou for mais antigo queinput.in
, o comando será executado apenas uma vez e irá regenerar os dois arquivos comfile-b.out
mais recente quefile-a.out
.O truque é usar uma regra de padrão com vários alvos. Nesse caso, o make assumirá que ambos os destinos são criados por uma única invocação do comando.
Essa diferença de interpretação entre as regras de padrão e as regras normais não faz exatamente sentido, mas é útil para casos como esse e está documentada no manual.
Este truque pode ser usado para qualquer número de arquivos de saída, desde que seus nomes tenham alguma substring comum para a
%
correspondência. (Neste caso, a substring comum é ".")fonte
make
o mesmo comando será executado duas vezes simultaneamente.foo%in bar%src: input.in
:-)$(subst .,%,$(varA) $(varB)): input.in
(testado com GNU make 4.1).O make não tem uma maneira intuitiva de fazer isso, mas existem duas soluções alternativas decentes.
Primeiro, se os alvos envolvidos têm um radical comum, você pode usar uma regra de prefixo (com GNU make). Ou seja, se você quiser corrigir a seguinte regra:
Você poderia escrever desta forma:
(Usando a variável de regra de padrão $ *, que representa a parte correspondente do padrão)
Se você deseja ser portátil para implementações não GNU Make ou se seus arquivos não podem ser nomeados para corresponder a uma regra de padrão, há outra maneira:
Isso diz ao make que input.in.intermediate não existirá antes de make ser executado, então sua ausência (ou seu registro de data e hora) não fará com que foo-bin seja executado espúrio. E se o file-a.out ou file-b.out ou ambos estiverem desatualizados (em relação ao input.in), o foo-bin será executado apenas uma vez. Você pode usar .SECONDARY em vez de .INTERMEDIATE, que instruirá o make NOT a deletar um nome de arquivo hipotético input.in.intermediate. Este método também é seguro para builds paralelos.
O ponto-e-vírgula na primeira linha é importante. Ele cria uma receita vazia para essa regra, para que Make saiba que realmente atualizaremos file-a.out e file-b.out (obrigado @siulkiulki e outros que apontaram isso)
fonte
-j1
compilações). Além disso, se o makefile for interrompido e deixar oinput.in.intermediate
arquivo ao redor, ele ficará muito confuso.file-a.out file-b.out: input.in.intermediate
parafile-a.out file-b.out: input.in.intermediate ;
Ver stackoverflow.com/questions/37873522/… para mais detalhes.Isso se baseia na segunda resposta de @deemer, que não depende de regras de padrão e corrige um problema que estava ocorrendo com o uso aninhado da solução alternativa.
Eu teria adicionado isso como um comentário à resposta de @deemer, mas não posso porque acabei de criar esta conta e não tenho nenhuma reputação.
Explicação: A receita vazia é necessária para permitir que Make faça a contabilidade apropriada para marcar
file-a.out
efile-b.out
como tendo sido reconstruída. Se você tiver outro destino intermediário do qual dependefile-a.out
, o Make escolherá não construir o intermediário externo, alegando:fonte
Após o GNU Make 4.3 (19 de janeiro de 2020), você pode usar “alvos explícitos agrupados”. Substitua
:
por&:
.O arquivo NEWS do GNU Make diz:
fonte
É assim que eu faço. Em primeiro lugar, sempre separo os pré-requisitos das receitas. Então, neste caso, um novo alvo para fazer a receita.
A primeira vez:
1. Tentamos construir file-a.out, mas dummy-a-and-b.out precisa primeiro, então make executa a receita dummy-a-and-b.out.
2. Tentamos construir file-b.out, dummy-a-and-b.out está atualizado.
A segunda e subsequente vez:
1. Tentamos construir file-a.out: olhamos os pré-requisitos, os pré-requisitos normais estão atualizados, os pré-requisitos secundários estão faltando, então são ignorados.
2. Tentamos construir file-b.out: olhamos os pré-requisitos, os pré-requisitos normais estão atualizados, os pré-requisitos secundários estão faltando, então são ignorados.
fonte
file-b.out
make não terá como recriá-lo.Como uma extensão da resposta de @deemer, generalizei em uma função.
Demolir:
Remova qualquer espaço em branco à esquerda / à direita do primeiro argumento
Crie um destino variável para um valor exclusivo para usar como o destino intermediário. Nesse caso, o valor será .inter.file-a.out_file-b.out.
Crie uma receita vazia para as saídas com o destino exclusivo como uma dependência.
Declare o destino único como intermediário.
Termine com uma referência ao destino exclusivo para que esta função possa ser usada diretamente em uma receita.
Observe também que o uso de eval aqui é porque eval se expande para nada, então a expansão completa da função é apenas o destino exclusivo.
Deve-se dar crédito às Regras Atômicas no GNU Make, das quais esta função foi inspirada.
fonte
Para evitar a execução múltipla paralela de uma regra com várias saídas com
make -jN
, use.NOTPARALLEL: outputs
. No seu caso :fonte