O que significam os símbolos de makefile $ @ e $ <?

416
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CC) $(CFLAGS) $< -o $@

O que o $@e $<faz exatamente?

Mohit Deshpande
fonte
5
O link acima está quebrado, e aqui está o outro: gnu.org/software/make/manual/html_node/Automatic-Variables.html
asciz
1
Oi, o que significa ".cpp.o:" como destino? (pré última linha?).
Pseudonym_127
3
O ".cpp.o:" significa construir ".o" (arquivos de objetos) de ".cpp" (arquivos de origem)
jaguzu
1
Eu acho que deve ser observado que existe um tutorial de make no link a seguir, no qual acredito que Mohit obteve o makefile em seu post. mrbook.org/blog/tutorials/make
DeepDeadpool 29/10
A Microsoft chama Macros de nome de arquivo (para NMAKE), que é mais clara que as variáveis ​​automáticas (para MAKE). É útil ver os dois lados para fins educacionais.
Ivanzinho

Respostas:

502

$@é o nome do arquivo que está sendo gerado e $<o primeiro pré-requisito (geralmente o arquivo de origem). Você pode encontrar uma lista de todas essas variáveis ​​especiais no manual Make GNU .

Por exemplo, considere a seguinte declaração:

all: library.cpp main.cpp

Nesse caso:

  • $@ avalia como all
  • $< avalia como library.cpp
  • $^ avalia como library.cpp main.cpp
bdonlan
fonte
16
Vale a pena notar que $@não precisa necessariamente ser um arquivo, mas também pode ser o nome de um .PHONYdestino.
Ephemera
Posso adicionar às opções da linha de comando o seguinte: $@sgerar saída de montagem como name.os?
huseyin tugrul buyukisik
4
Cuidado quando a primeira dependência é uma variável que representa uma lista, $ <é avaliado após ser expandido. Portanto, quando LIST = lib1.cpp lib2.cpp e tudo: $ {LIST} main.cpp, $ <é avaliado como apenas lib1.cpp. Alguns anos atrás, eu havia passado algum tempo descobrindo o que aconteceu no resultado causado por esse comportamento.
Chan Kim
Em geral, $ @ refere-se ao nome do destino, localizado no lado esquerdo do:
Deepak Kiran
78

As variáveis$@ e $<são chamadas de variáveis ​​automáticas . A variável $@representa o nome do arquivo criado (ou seja, o destino) e $<representa o primeiro pré-requisito necessário para criar o arquivo de saída.
Por exemplo:

hello.o: hello.c hello.h
         gcc -c $< -o $@

Aqui hello.oestá o arquivo de saída. É para isso que se $@expande. A primeira dependência é hello.c. Isso é o que$< expande.

A -cbandeira gera o .oarquivo; veja man gccpara uma explicação mais detalhada. o-o especifica o arquivo de saída a ser criado.

Para mais detalhes, você pode ler este artigo sobre Makefiles do Linux .

Além disso, você pode verificar os manuais GNU make . Isso tornará mais fácil criar Makefiles e depurá-los.

Se você executar este comando, ele produzirá o banco de dados makefile:

make -p 
hábil
fonte
1
Sua resposta parece que $<será expandida para hello.c hello.h(ambos). Por favor, esclareça.
Dr Beco
Sim, ele vai incluir tanto hello.c e hello.h
destro
19
$<é apenas o primeiro item. Para incluir tudo, use $^.
Dr Beco
1
O Dr. Beco está certo. O autor deve modificar sua resposta.
PT Huynh
67

De Gerenciando projetos com o GNU Make, 3ª edição, p. 16 (está sob GNU Free Documentation License ):

As variáveis ​​automáticas são definidas pormake após a correspondência de uma regra. Eles fornecem acesso aos elementos das listas de destino e pré-requisito, para que você não precise especificar explicitamente nenhum nome de arquivo. Eles são muito úteis para evitar a duplicação de código, mas são críticos ao definir regras de padrão mais gerais.

Existem sete variáveis ​​automáticas "principais":

  • $@: O nome do arquivo que representa o destino.

  • $%: O elemento filename de uma especificação de membro do archive.

  • $<: O nome do arquivo do primeiro pré-requisito.

  • $?: Os nomes de todos os pré-requisitos mais recentes que o destino, separados por espaços.

  • $^: Os nomes de arquivos de todos os pré-requisitos, separados por espaços. Essa lista possui nomes de arquivos duplicados removidos, pois para a maioria dos usos, como compilação, cópia etc., não são desejadas duplicatas.

  • $+: Semelhante a $^, este é o nome de todos os pré-requisitos separados por espaços, exceto que $+inclui duplicatas. Essa variável foi criada para situações específicas, como argumentos para vinculadores nos quais valores duplicados têm significado.

  • $*: A raiz do nome do arquivo de destino. Um tronco normalmente é um nome de arquivo sem seu sufixo. Seu uso fora das regras padrão é desencorajado.

Além disso, cada uma das variáveis ​​acima tem duas variantes para compatibilidade com outras marcas. Uma variante retorna apenas a parte do diretório do valor. Isto é indicado anexando um “D” para o símbolo, $(@D), $(<D), etc. As outras variantes de retorno apenas a parte do valor arquivo. Isto é indicado anexando um “F” para o símbolo, $(@F), $(<F), etc. Note-se que estes nomes variantes são mais do que um caractere e assim devem ser colocados entre parênteses. O GNU make fornece uma alternativa mais legível com as funções dir e notdir.

alex
fonte
37

O $@e$< macros são especiais.

Onde:

$@ é o nome do arquivo do destino.

$< é o nome da primeira dependência.

Eric
fonte
19

O Makefile constrói o helloexecutável se qualquer um dos main.cpp, hello.cpp, factorial.cppmudou. O menor Makefile possível para atingir essa especificação poderia ter sido:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • pro: muito fácil de ler
  • con: pesadelo de manutenção, duplicação de dependências do C ++
  • problema de eficiência: recompilamos todo o C ++, mesmo que apenas um tenha sido alterado

Para melhorar o exposto, compilamos apenas os arquivos C ++ que foram editados. Em seguida, apenas vinculamos os arquivos de objeto resultantes.

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

main.o: main.cpp
    g++ -c main.cpp

hello.o: hello.cpp
    g++ -c hello.cpp

factorial.o: factorial.cpp
    g++ -c factorial.cpp
  • pro: corrige o problema de eficiência
  • con: novo pesadelo de manutenção, regras de erro de digitação em arquivos de objeto

Para melhorar isso, podemos substituir todas as regras de arquivo de objeto por uma única .cpp.oregra:

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

.cpp.o:
    g++ -c $< -o $@
  • pro: voltando a ter um makefile curto, um pouco fácil de ler

Aqui a .cpp.oregra define como construir a anyfile.opartir anyfile.cpp.

  • $< corresponde à primeira dependência, neste caso, anyfile.cpp
  • $@corresponde ao alvo, neste caso anyfile.o,.

As outras alterações presentes no Makefile são:

  • Facilitando a alteração de compiladores de g ++ para qualquer compilador C ++.
  • Facilitando a alteração das opções do compilador.
  • Facilitando a alteração das opções do vinculador.
  • Facilitando a alteração dos arquivos de origem e saída do C ++.
  • Foi adicionada uma regra padrão 'all', que atua como uma verificação rápida para garantir que todos os seus arquivos de origem estejam presentes antes que seja feita uma tentativa de compilar seu aplicativo.
Stephen Quan
fonte
1

por exemplo, se você deseja compilar fontes, mas possui objetos em um diretório diferente:

Você precisa fazer :

gcc -c -o <obj/1.o> <srcs/1.c> <obj/2.o> <srcs/2.c> ...

mas com a maioria das macros, o resultado será todos os objetos seguidos por todas as fontes, como:

gcc -c -o <all OBJ path> <all SRC path>

portanto, isso não compilará nada ^^ e você não poderá colocar seus arquivos de objetos em um diretório diferente :(

a solução é usar essas macros especiais

$@ $<

isso gerará um arquivo .o (obj / file.o) para cada arquivo .c no SRC (src / file.c)

$(OBJ):$(SRC)
   gcc -c -o $@ $< $(HEADERS) $(FLAGS)

Isso significa :

    $@ = $(OBJ)
    $< = $(SRC)

mas linhas por linhas, em vez de todas as linhas de OBJ, seguidas por todas as linhas de SRC

Aominé
fonte
Eu me pergunto, o que você faz com toda essa quantidade de tempo que economiza digitando "u" em vez de "você"?
Ivanzinho