Qual é a syntax do CMake para definir e usar variables?

Estou perguntando isso como um lembrete para mim mesmo da próxima vez que eu usar o CMake. Isso nunca é garantido, e os resultados do Google não são ótimos.

Qual é a syntax para definir e usar variables ​​no CMake?

Ao escrever scripts do CMake, há muito o que você precisa saber sobre a syntax e como usar variables ​​no CMake.

A syntax

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}")

Listas 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

  • Sintaxe do CMake / Language
  • CMake: variables ​​listas cadeias de caracteres
  • CMake: Variáveis ​​Úteis
  • Comando CMake set()
  • Comando CMake string()
  • Comando CMake list()
  • Cmake: Expressões do Gerador

O escopo ou “Qual o valor da minha variável?”

Primeiro, existem as “Variáveis ​​Normais” e as coisas que você precisa saber sobre seu escopo:

  • As variables ​​normais são visíveis para o CMakeLists.txt que estão definidas e tudo é chamado a partir de lá ( add_subdirectory() , include() , macro() e function() ).
  • Os add_subdirectory() e function() são especiais porque abrem seu próprio escopo.
    • As variables ​​de significado set(...) só são visíveis lá e fazem uma cópia de todas as variables ​​normais do nível de escopo de onde são chamadas (chamado escopo pai).
    • Portanto, se você estiver em um subdiretório ou em uma function, 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 function. Um exemplo seria function(xyz _resultVar) está configurando set(${_resultVar} 1 PARENT_SCOPE)
  • Por outro lado, tudo que você definir nos scripts include() ou macro() modificará as variables ​​diretamente no escopo de onde elas são chamadas.

Em segundo lugar, há o “Cache de Variáveis ​​Globais”. Coisas que você precisa saber sobre o Cache:

  • Se nenhuma variável normal com o nome dado estiver definida no escopo atual, o CMake procurará uma input de Cache correspondente.
  • Os valores de cache são armazenados no arquivo CMakeCache.txt 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 variables ​​normais – possuem um type e uma docstring . Eu normalmente não uso a GUI, então eu uso set(... CACHE INTERNAL "") para definir meus valores globais e persistentes.

    Observe que o tipo de variável de cache INTERNAL implica FORCE

  • Em um script CMake, você só pode alterar as inputs de Cache existentes se usar a syntax set(... CACHE ... FORCE) . Esse comportamento é usado, por exemplo, pelo próprio CMake, porque ele normalmente não força as próprias inputs do Cache e, portanto, você pode pré-defini-lo com outro valor.

  • Você pode usar a linha de comando para definir inputs no Cache com a syntax cmake -D var:type=value , apenas cmake -D var=value ou com cmake -C CMakeInitialCache.cmake .
  • Você pode desmarcar inputs no Cache com unset(... CACHE) .

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

Armadilhas Variáveis ​​e “Como Depurar Mudanças de Variáveis?”

Para evitar armadilhas, você deve saber o seguinte sobre as variables:

  • Variáveis ​​locais ocultam variables ​​em cache se ambas tiverem o mesmo nome
  • Os comandos find_... – se bem-sucedidos – escrevem seus resultados como variables ​​armazenadas em cache “para que nenhuma chamada seja pesquisada novamente”
  • As listas no CMake são apenas cadeias de caracteres com delimitadores de ponto- e- vírgula e, portanto, as aspas são importantes
    • set(MyVar abc) é "a;b;c" e set(MyVar "abc") é "abc"
    • A recomendação é que você sempre use aspas com a única exceção quando quiser dar uma lista como list
    • Geralmente prefere o comando list() para manipular listas
  • Toda a questão do escopo descrita acima. Especialmente é recomendado usar functions() invés de macros() porque você não quer que suas variables ​​locais apareçam no escopo pai.
  • Muitas variables ​​usadas pelo CMake são configuradas com as chamadas project() e enable_language() . Por isso, pode ser importante definir algumas variables ​​antes que esses comandos sejam usados.
  • As variables ​​de ambiente podem diferir de onde o CMake gerou o ambiente de criação e quando os arquivos make são colocados em uso.
    • Uma mudança em uma variável de ambiente não triggers novamente o processo de geração.
    • Especialmente um ambiente IDE gerado pode diferir de sua linha de comando, por isso é recomendado transferir suas variables ​​de ambiente para algo armazenado em cache.

Às vezes, apenas as variables ​​de debugging ajudam. O seguinte pode ajudá-lo:

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

Sintaxe Especial

  • variables ​​ambientais
    • Você pode ler variables ​​de ambiente $ENV{...} e escrever set(ENV{...} ...)
  • Expressões do Gerador
    • As expressões $< ...> do gerador são avaliadas apenas quando o gerador do CMake escreve o ambiente make (comparação com variables ​​normais que são substituídas “in-loco” pelo analisador)
    • Muito útil, por exemplo, em linhas de comando de compilador / linker e em ambientes de multi-configuração
  • Referências
    • Com ${${...}} você pode fornecer nomes de variables ​​em uma variável e referenciar seu conteúdo.
    • Frequentemente usado ao dar um nome de variável como parâmetro de function / macro.
  • Valores constantes (veja o comando if() )
    • Com if(MyVariable) você pode verificar diretamente uma variável para true / false (não é necessário aqui include ${...} )
    • Verdadeiro se a constante for 1 , ON , YES , TRUE , Y ou um número diferente de zero.
    • Falso se a constante for 0 , OFF , NO , FALSE , N , IGNORE , NOTFOUND , a string vazia ou terminar no sufixo -NOTFOUND .
    • Essa syntax costuma ser usada para algo como if (MSVC) , mas pode ser confuso para alguém que não conhece esse atalho de syntax.
  • Substituições Recursivas
    • Você pode construir nomes de variables ​​usando variables. Depois que o CMake tiver substituído as variables, ele verificará novamente se o resultado é uma variável. Este é um recurso muito poderoso usado no próprio CMake, por exemplo, como uma espécie de set(CMAKE_${lang}_COMPILER ...) modelos set(CMAKE_${lang}_COMPILER ...)
    • Mas esteja ciente de que isso pode causar dor de cabeça nos comandos if () . Aqui está um exemplo em que CMAKE_CXX_COMPILER_ID é "MSVC" e MSVC é "1" :
      • if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") é true, porque ele avalia if ("1" STREQUAL "1")
      • if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") é falso, porque ele avalia if ("MSVC" STREQUAL "1")
      • Então a melhor solução aqui seria – veja acima – para 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) para “interpretar apenas if() argumentos como variables ​​ou palavras-chave quando não estiver em aspas.”
  • O comando option()
    • Principalmente apenas cadeias de caracteres em cache que só podem ser ON ou OFF e permitem algum tratamento especial como, por exemplo, dependencies
    • Mas esteja ciente , não confunda a option com o comando set . O valor dado à option é, na verdade, apenas o “valor inicial” (transferido uma vez para o cache durante a primeira etapa de configuração) e, posteriormente, deve ser alterado pelo usuário por meio da GUI do CMake .

Referências

  • Como o CMake é usado?
  • cmake, perdido no conceito de variables ​​globais (e PARENT_SCOPE ou add_subdirectory alternatives)
  • Looping sobre uma lista de strings
  • Como armazenar configurações de compilation do CMake
  • CMake compara a string vazia com STREQUAL com falha
  • cmake: quando citar variables?

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

Uma variável de item

Definir variável:

 SET(INSTALL_ETC_DIR "etc") 

Use variável:

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

Variável de vários itens (ou seja, lista)

Definir variável:

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

Use variável:

 add_executable(program "${PROGRAM_SRCS}") 

CMake docs em variables

$ENV{FOO} para uso, onde o FOO está sendo retirado da variável de ambiente. Caso contrário, use como ${FOO} , onde FOO é outra variável. Para a configuração, SET(FOO "foo") seria usado no cmake.