Qual é a sintaxe do CMake para definir e usar variáveis?

168

Estou perguntando isso como um lembrete para mim mesma na próxima vez que usar o CMake. Ele nunca fica, e os resultados do Google não são ótimos.

Qual é a sintaxe para definir e usar variáveis ​​no CMake?

CivFan
fonte

Respostas:

281

Ao escrever scripts do CMake, você precisa saber muito sobre a sintaxe e como usar variáveis ​​no CMake.

A sintaxe

Strings usando set():

  • set(MyString "Some Text")
  • set(MyStringWithVar "Some other Text: ${MyString}")
  • set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")

Ou com string():

  • string(APPEND MyStringWithContent " ${MyString}")

Lista usando set():

  • set(MyList "a" "b" "c")
  • set(MyList ${MyList} "d")

Ou melhor com list():

  • list(APPEND MyList "a" "b" "c")
  • list(APPEND MyList "d")

Listas de nomes de arquivos:

  • set(MySourcesList "File.name" "File with Space.name")
  • list(APPEND MySourcesList "File.name" "File with Space.name")
  • add_excutable(MyExeTarget ${MySourcesList})

A documentação

O escopo ou "Qual é o valor da minha variável?"

Primeiro, existem as "Variáveis ​​normais" e o que você precisa saber sobre seu escopo:

  • Variáveis normais são visíveis a CMakeLists.txteles são definidos em e tudo chamado de lá ( add_subdirectory(), include(), macro()e function()).
  • Os comandos add_subdirectory()e function()são especiais, porque abrem seu próprio escopo.
    • As variáveis ​​de significado set(...)lá são visíveis apenas e elas fazem uma cópia de todas as variáveis ​​normais do nível de escopo de onde são chamadas (chamadas de escopo pai).
    • Portanto, se você estiver em um subdiretório ou em uma função, poderá modificar uma variável já existente no escopo pai com set(... PARENT_SCOPE)
    • Você pode fazer uso disso, por exemplo, em funções, passando o nome da variável como um parâmetro de função. Um exemplo seria a function(xyz _resultVar)configuraçãoset(${_resultVar} 1 PARENT_SCOPE)
  • Por outro lado, tudo o que você definir include()ou macro()scripts modificará variáveis ​​diretamente no escopo de onde elas são chamadas.

Segundo, existe o "Cache de Variáveis ​​Globais". O que você precisa saber sobre o cache:

  • Se nenhuma variável normal com o nome fornecido for definida no escopo atual, o CMake procurará uma entrada de cache correspondente.
  • Os valores do cache são armazenados no CMakeCache.txtarquivo no diretório de saída binária.
  • Os valores no cache podem ser modificados no aplicativo GUI do CMake antes de serem gerados. Portanto, eles - em comparação com variáveis ​​normais - têm a typee a docstring. Normalmente, não uso a GUI, portanto, set(... CACHE INTERNAL "")defino meus valores globais e persistentes.

    Observe que o INTERNALtipo de variável de cache implicaFORCE

  • Em um script do CMake, você só pode alterar as entradas de cache existentes se usar a set(... CACHE ... FORCE)sintaxe. Esse comportamento é utilizado, por exemplo, pelo próprio CMake, porque normalmente não força as entradas de cache e, portanto, você pode predefini-lo com outro valor.

  • Você pode usar a linha de comando para definir entradas no cache com a sintaxe cmake -D var:type=value, just cmake -D var=valueou with cmake -C CMakeInitialCache.cmake.
  • Você pode desmarcar entradas no cache com unset(... CACHE).

O cache é global e você pode configurá-los praticamente em qualquer lugar nos scripts do CMake. Mas eu recomendo que você pense duas vezes sobre onde usar as variáveis ​​de cache (são globais e persistentes). Normalmente, prefiro a sintaxe set_property(GLOBAL PROPERTY ...)e set_property(GLOBAL APPEND PROPERTY ...)para definir minhas próprias variáveis ​​globais não persistentes.

Armadilhas variáveis ​​e "Como depurar alterações variáveis?"

Para evitar armadilhas, você deve saber o seguinte sobre variáveis:

  • Variáveis ​​locais ocultam variáveis ​​em cache se ambas tiverem o mesmo nome
  • Os find_...comandos - se bem-sucedidos - gravam seus resultados como variáveis ​​em cache "para que nenhuma chamada procure novamente"
  • As listas no CMake são apenas strings com delimitadores de ponto e vírgula e, portanto, as aspas são importantes
    • set(MyVar a b c)é "a;b;c"e set(MyVar "a b c")é"a b c"
    • A recomendação é que você sempre use aspas com a única exceção quando desejar fornecer uma lista como lista
    • Geralmente prefiro o list()comando para lidar com listas
  • Toda a questão do escopo descrita acima. Especialmente, é recomendável usar em functions()vez de, macros()porque você não deseja que suas variáveis ​​locais apareçam no escopo pai.
  • Muitas variáveis ​​usadas pelo CMake são definidas com as chamadas project()e enable_language(). Portanto, pode ser importante definir algumas variáveis ​​antes que esses comandos sejam usados.
  • As variáveis ​​de ambiente podem diferir de onde o CMake gerou o ambiente make e quando os arquivos make são colocados em uso.
    • Uma mudança em uma variável de ambiente não reativa o processo de geração.
    • Especialmente, um ambiente IDE gerado pode diferir da sua linha de comando; portanto, é recomendável transferir suas variáveis ​​de ambiente para algo armazenado em cache.

Às vezes, apenas variáveis ​​de depuração ajudam. O seguinte pode ajudá-lo:

  • Basta usar o printfestilo antigo de depuração usando o message()comando Existem também alguns módulos prontos para uso fornecidos com o próprio CMake: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Examine o CMakeCache.txtarquivo no diretório de saída binário. Esse arquivo é gerado mesmo se a geração real do seu ambiente de criação falhar.
  • Use variable_watch () para ver onde suas variáveis ​​são lidas / gravadas / removidas.
  • Examine as propriedades do diretório CACHE_VARIABLES e VARIABLES
  • Ligue cmake --trace ...para ver o processo completo de análise do CMake. Essa é a última reserva, porque gera muita produção.

Sintaxe Especial

  • variáveis ​​ambientais
    • Você pode ler $ENV{...}e escrever set(ENV{...} ...)variáveis ​​de ambiente
  • Expressões do gerador
    • As expressões do gerador $<...>são avaliadas apenas quando o gerador do CMake grava o ambiente make (é comparado com variáveis ​​normais que são substituídas "no local" pelo analisador)
    • Muito útil, por exemplo, em linhas de comando do compilador / vinculador e em ambientes com várias configurações
  • Referências
    • Com ${${...}}você pode dar nomes de variáveis ​​em uma variável e referenciar seu conteúdo.
    • Geralmente usado ao atribuir um nome de variável como parâmetro de função / macro.
  • Valores constantes (consulte o if()comando)
    • Com if(MyVariable)você pode verificar diretamente uma variável para true / false (não é necessário aqui para o anexo ${...})
    • Verdadeiro se a constante é 1, ON, YES, TRUE, Y, ou um número diferente de zero.
    • False se a constante é 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, a cadeia vazia, ou termina no sufixo -NOTFOUND.
    • Essa sintaxe geralmente é usada para algo como if(MSVC), mas pode ser confusa para alguém que não conhece esse atalho de sintaxe.
  • Substituições recursivas
    • Você pode construir nomes de variáveis ​​usando variáveis. Após o CMake ter substituído as variáveis, ele verificará novamente se o resultado é uma variável em si. Este é um recurso muito poderoso usado no próprio CMake, por exemplo, como uma espécie de modeloset(CMAKE_${lang}_COMPILER ...)
    • Mas esteja ciente de que isso pode causar dor de cabeça nos if()comandos. Aqui está um exemplo onde CMAKE_CXX_COMPILER_IDé "MSVC"e MSVCé "1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") é verdade, porque avalia como if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") é falso, porque avalia como if("MSVC" STREQUAL "1")
      • Portanto, a melhor solução aqui seria - veja acima - verificar diretamente if(MSVC)
    • A boa notícia é que isso foi corrigido no CMake 3.1 com a introdução da política CMP0054 . Eu recomendaria sempre definir cmake_policy(SET CMP0054 NEW)como "apenas interpretar if()argumentos como variáveis ​​ou palavras-chave quando não estiver entre aspas".
  • O option()comando
    • Principalmente apenas strings em cache que só podem ser ONou OFFpermitem uma manipulação especial, como por exemplo, dependências
    • Mas esteja ciente , não confunda optiono setcomando. O valor atribuído optioné realmente apenas o "valor inicial" (transferido uma vez para o cache durante a primeira etapa da configuração) e posteriormente deve ser alterado pelo usuário através da interface gráfica do usuário do CMake .

Referências

Florian
fonte
Quando eu uso if ("${MyString}" ...)Eu sou avisos vendo: Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. Consulte, por exemplo, Build 367 . Alguma ideia?
JWW
A citação ${MyString}leva a um monte de erros para "se houver argumentos ...", como o erro CMake perto de se: "se houver argumentos" seguidos de parênteses, "NÃO", "IGUAIS" e similares .
JWW
@jww O aviso significa que MyStringcontém um nome de variável que será novamente referenciado. Acredito que ninguém realmente queira esse OLDcomportamento. Então, do meu ponto de vista o seu totalmente seguro e compatível com a política apenas definir CMP0054a NEW(veja a discussão aqui ).
Florian
@jww E a maneira mais segura de evitar esses problemas / avisos é simplesmente fazê- if (MyString ...)lo (se for o seu código dando o aviso).
Florian
Obrigado. Removemos todas as ocorrências ${MyString}e substituímos por MyString(ou acredito que removemos todas). Ainda sem alegria: Build 372 . A porcaria nem vem do nosso código. Parece vir do CMake. A linha 283 é if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
jww 27/08/17
18

Aqui estão alguns exemplos básicos para começar rápido e sujo.

Variável de um item

Definir variável:

SET(INSTALL_ETC_DIR "etc")

Usar variável:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

Variável de vários itens (por exemplo, lista)

Definir variável:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

Usar variável:

add_executable(program "${PROGRAM_SRCS}")

Documentos do CMake sobre variáveis

CivFan
fonte
1

$ENV{FOO}para uso, onde FOOestá sendo selecionado na variável de ambiente. caso contrário, use como ${FOO}, onde FOOestá outra variável. Para configuração, SET(FOO "foo")seria usado no CMake.

parasita
fonte