Passando variáveis ​​adicionais da linha de comando para tornar

614

Posso passar variáveis ​​para um GNU Makefile como argumentos de linha de comando? Em outras palavras, quero passar alguns argumentos que eventualmente se tornarão variáveis ​​no Makefile.

Pablo
fonte

Respostas:

754

Você tem várias opções para configurar variáveis ​​de fora do seu makefile:

  • Do ambiente - cada variável de ambiente é transformada em uma variável makefile com o mesmo nome e valor.

    Você também pode ativar a -eopção (aka --environments-override) e suas variáveis ​​de ambiente substituirão as atribuições feitas no makefile (a menos que essas atribuições usem a overridediretiva . No entanto, não é recomendado, e é muito melhor e flexível usar a ?=atribuição (a variável condicional) operador de atribuição, só terá efeito se a variável ainda não estiver definida):

    FOO?=default_value_if_not_set_in_environment
    

    Observe que certas variáveis ​​não são herdadas do ambiente:

    • MAKE é obtido do nome do script
    • SHELLé definido dentro de um makefile ou o padrão é /bin/sh(justificativa: os comandos são especificados dentro do makefile e são específicos do shell).
  • Na linha de comando - makepode aceitar atribuições de variáveis ​​como parte de sua linha de comando, misturadas com os destinos:

    make target FOO=bar
    

    Mas todas as atribuições para FOOvariáveis ​​dentro do makefile serão ignoradas, a menos que você use a overridediretiva na atribuição. (O efeito é o mesmo que com a -eopção para variáveis ​​de ambiente).

  • Exportando do Make pai - se você chamar Make de um Makefile, geralmente não deve escrever explicitamente atribuições de variáveis ​​como esta:

    # Don't do this!
    target:
            $(MAKE) -C target CC=$(CC) CFLAGS=$(CFLAGS)
    

    Em vez disso, a melhor solução seria exportar essas variáveis. A exportação de uma variável faz com que ela entre no ambiente de todas as chamadas de shell, e as chamadas Fazer desses comandos escolhem essas variáveis ​​de ambiente, conforme especificado acima.

    # Do like this
    CFLAGS=-g
    export CFLAGS
    target:
            $(MAKE) -C target
    

    Você também pode exportar todas as variáveis ​​usando exportsem argumentos.

P Shved
fonte
11
passar da linha de comando, algo com espaçosmake A='"as df"'
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4
Parece que você está pedindo problemas se se preocupa com variáveis ​​de ambiente. Por um lado, é um pesadelo de depuração se funcionar no lugar A e não no lugar B, apenas porque eles têm ambientes diferentes.
James Moore
12
Apenas com base na experiência, exportar coisas como CFLAGS é uma receita para pesadelo para grandes projetos. Projetos grandes geralmente têm bibliotecas de terceiros que são compiladas apenas com um determinado conjunto de sinalizadores (que ninguém se incomoda em corrigir). Se você exportar CFLAGS, o CFLAGS do seu projeto acaba substituindo a biblioteca de terceiros e dispara erros de compilação. Uma maneira alternativa pode ser defini-la export PROJECT_MAKE_ARGS = CC=$(CC) CFLAGS=$(CFLAGS)e transmiti-la como make -C folder $(PROJECT_MAKE_FLAGS). Se houver uma maneira de dizer ao makefile da biblioteca para ignorar o ambiente, isso seria ideal (oposto a -e).
RD
24
AVISO: Na seção "Exportando do pai Make" acima, "Não faça isso!" é criticamente enganador . A passagem de variáveis ​​na linha de comando substitui as atribuições no sub-makefile, mas as variáveis ​​exportadas não substituem as atribuições no sub-makefile. Esses dois métodos para passar variáveis ​​para um sub-makefile não são equivalentes e não devem ser confundidos.
Jonathan Ben-Avraham
4
Alguma diferença ? make target FOO=bar make FOO=bar target?
gfan
213

A maneira mais simples é:

make foo=bar target

Em seu makefile, você pode consultar $(foo) . Observe que isso não será propagado para sub-marcas automaticamente.

Se você estiver usando subprodutos, consulte este artigo: Comunicando variáveis ​​a um subproduto

Mark Byers
fonte
2
por sub-faz você significar para os makefiles includedno makefile principal?
Pablo
2
@ Michael: Significa chamar make de novo de dentro do makefile. Atualizei minha resposta, pois você parece estar interessado nesse detalhe.
Mark Byers
2
"Observe que isso não será propagado para sub-marcas automaticamente." Falso! "Por padrão, somente variáveis ​​que vieram do ambiente ou da linha de comando são passadas para invocações recursivas. Você pode usar a diretiva de exportação para passar outras variáveis." gnu.org/software/make/manual/html_node/…
ThomasMcLeod
82

Digamos que você tenha um makefile como este:

action:
    echo argument is $(argument)

Você chamaria então make action argument=something

nc3b
fonte
5
para que o alvo e os argumentos possam ser trocados em termos de posição?
Pablo
4
@ Michael: Sim (veja a resposta de Mark Byers)
nc3b
25

Do manual :

Variáveis ​​no make podem vir do ambiente em que o make é executado. Toda variável de ambiente que make vê quando é inicializada é transformada em uma variável make com o mesmo nome e valor. No entanto, uma atribuição explícita no makefile, ou com um argumento de comando, substitui o ambiente.

Então você pode fazer (do bash):

FOOBAR=1 make

resultando em uma variável FOOBARno seu Makefile.

Thomas
fonte
2
A outra maneira é realmente melhor em quase todos os casos. Vou deixar isso aqui por completo.
1316 Thomas
Esta é a única resposta que mostra a atribuição de variáveis antes do comando make na mesma linha - vale a pena saber que este não é um erro de sintaxe.
M_M 01/04
7

Há outra opção não citada aqui, incluída no livro GNU Make de Stallman e McGrath (consulte http://www.chemie.fu-berlin.de/chemnet/use/info/make/make_7.html ). Ele fornece o exemplo:

archive.a: ...
ifneq (,$(findstring t,$(MAKEFLAGS)))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

Envolve verificar se um determinado parâmetro aparece em MAKEFLAGS. Por exemplo .. suponha que você esteja estudando tópicos em c ++ 11 e tenha dividido seu estudo em vários arquivos ( class01, ..., classNM) e deseje: compilar todos e executar individualmente ou compilar um de cada vez cronometre e execute-o se um sinalizador for especificado ( -r, por exemplo). Então, você pode criar o seguinte Makefile:

CXX=clang++-3.5
CXXFLAGS = -Wall -Werror -std=c++11
LDLIBS = -lpthread

SOURCES = class01 class02 class03

%: %.cxx
    $(CXX) $(CXXFLAGS) -o [email protected] $^ $(LDLIBS)
ifneq (,$(findstring r,  $(MAKEFLAGS)))
    ./[email protected]
endif

all: $(SOURCES)

.PHONY: clean

clean:
    find . -name "*.out" -delete

Tendo isso, você:

  • construir e executar um arquivo w / make -r class02;
  • construir tudo w / makeor make all;
  • construir e executar todos os w / make -r(suponha que todos eles contenham algum tipo de afirmação e você só queira testá-los)
Ciro Costa
fonte
6

parece

comando args sobrescrever variável de ambiente

Makefile

send:
    echo $(MESSAGE1) $(MESSAGE2)

Executar exemplo

$ MESSAGE1=YES MESSAGE2=NG  make send MESSAGE2=OK
echo YES OK
YES OK
YumaInaura
fonte
5

Se você criar um arquivo chamado Makefile e adicionar uma variável como $ (unittest), poderá usar essa variável dentro do Makefile, mesmo com caracteres curinga

exemplo:

make unittest=*

Eu uso o BOOST_TEST e, dando um caractere curinga ao parâmetro --run_test = $ (unittest), poderei usar a expressão regular para filtrar o teste que quero que meu Makefile execute

serup
fonte
4
export ROOT_DIR=<path/value>

Em seguida, use a variável $(ROOT_DIR)no Makefile.

parasita
fonte