Guia completo do CMake. Parte Um: Sintaxe


1. Introdução


O CMake é um conjunto de utilitários aberto e multiplataforma, projetado para automatizar testes, compilação e criação de pacotes de projetos em C / C ++. Ao escrever um pequeno script que todos entendam, você garantirá a mesma compilação do seu projeto em qualquer plataforma em que o CMake esteja disponível.


O idioma do CMake , traduzido para um arquivo de montagem nativo (por exemplo, Makefile ou Ninja), define o processo de todo o gerenciamento de projetos. À sua disposição, no lado funcional, existem apenas equipes que podem ser formadas em estruturas bastante complexas. Vamos começar com eles.


Lançamento do CMake


A seguir, exemplos de uso da linguagem CMake que você deve praticar. Experimente o código-fonte modificando os comandos existentes e adicionando novos. Para executar esses exemplos, instale o CMake no site oficial .


Equipas


Os comandos no CMake são semelhantes às funções em muitas linguagens de programação. Para chamar um comando, você deve escrever seu nome e depois passá-lo argumentos entre parênteses, separados por espaços. No exemplo acima, seis argumentos são passados ​​para o comando message para saída no console:


 #    "CMake is the most powerful buildsystem!" message("CMake " "is " "the " "most " "powerful " "buildsystem!") 

Argumentos


Argumentos enquadrados entre aspas duplas permitem que você escape e substitua variáveis ​​dentro de si. Argumentos sem moldura não permitem a produção de tais coisas e não podem incluir os caracteres ()#"\ e espaços, mas são mais convenientes de usar. Exemplo:


 #  "Hello, my lovely CMake",    "!": message("Hello, my lovely CMake\t!") #  "Hello,_my_lovely_CMake!"  : message(Hello,_my_lovely_CMake!) 

É importante notar que o argumento Walk;around;the;forest será expandido para a lista Walk around the forest , pois qualquer argumento sem moldura se expande automaticamente para uma lista de valores (desde que os valores do argumento original sejam separados por ponto e vírgula), mas emoldurados em duplo entre aspas como argumento, essa transformação não ocorre (caracteres de ponto e vírgula simplesmente desaparecem). Esse recurso foi mencionado nos comentários.


Comentários


Os comentários começam com um sinal de libra e terminam no final da linha em que foram impressos. O texto contido nos comentários é ignorado pelo sistema de construção e não tem efeito em sua operação. Os exemplos acima também demonstram o uso de comentários.


Variáveis


As variáveis ​​podem ser definidas chamando o comando set e excluídas chamando unset . Você pode obter o valor de uma variável construindo ${VARIABLE} . Se a variável ainda não foi definida e em algum lugar foi necessário obter seu valor, essa variável se transformará em uma sequência vazia. Um exemplo:


 #   VARIABLE   "Mr. Thomas": set(VARIABLE "Mr. Thomas") #  "His name is: Mr. Thomas": message("His name is: " ${VARIABLE}) #  "'BINGO' is equal to: []",   "BINGO"  : message("'BINGO' is equal to: [${BINGO}]") #   VARIABLE: unset(VARIABLE) 

Opções


O CMake suporta opções de configuração que estão sujeitas a modificações do usuário. As opções são semelhantes às variáveis ​​e são definidas pelo comando option , que utiliza apenas três argumentos: o nome da variável, a descrição da string da variável e o valor padrão da variável ( ON ou OFF ):


 #   `USE_ANOTHER_LIBRARY`   # "Do you want to use an another library?"   "OFF": option(USE_ANOTHER_LIBRARY "Do you want to use an another library?" OFF) 

Expressões lógicas


Antes de prosseguir com o estudo de operadores condicionais e construções cíclicas, é necessário entender o trabalho das expressões lógicas. Expressões lógicas são usadas ao verificar condições e podem assumir um dos dois valores: verdadeiro ou falso. Por exemplo, a expressão 52 LESS 58 será verdadeira, desde 52 <58. A expressão 88 EQUAL 88 será verdadeira, 63 GREATER 104 será falsa. Você pode comparar não apenas números, mas também cadeias, versões, arquivos, associação à lista e expressões regulares. Uma lista completa de expressões lógicas pode ser encontrada aqui .


Instruções condicionais


Operadores condicionais no CMake funcionam exatamente como em outras linguagens de programação. Neste exemplo, apenas o primeiro operador condicional funcionará, o que verifica se 5> 1. A segunda e a terceira condições são falsas, pois 5 não pode ser menor ou igual a um. Os blocos de comando elseif e else são opcionais, e endif necessário e sinaliza a conclusão das verificações anteriores.


 #  "Of course, 5 > 1!": if(5 GREATER 1) message("Of course, 5 > 1!") elseif(5 LESS 1) message("Oh no, 5 < 1!") else() message("Oh my god, 5 == 1!") endif() 

Ciclos


Os loops no CMake são semelhantes aos loops em outras linguagens de programação. No exemplo acima, o valor da variável VARIABLE é definido como Airport e, em seguida, quatro comandos aninhados são executados seqüencialmente até que o valor da variável VARIABLE seja igual a Airport . O último quarto comando de set(VARIABLE "Police station") define o valor da variável verificada na Police station , para que o loop pare imediatamente antes de atingir a segunda iteração. O comando endwhile sinaliza a conclusão da lista de comandos aninhados no loop.


 #      "VARIABLE is still 'Airport'": set(VARIABLE Airport) while(${VARIABLE} STREQUAL Airport) message("VARIABLE is still '${VARIABLE}'") message("VARIABLE is still '${VARIABLE}'") message("VARIABLE is still '${VARIABLE}'") set(VARIABLE "Police station") endwhile() 

Este exemplo de foreach funciona da seguinte maneira: a cada iteração desse loop, a variável VARIABLE recebe o seguinte valor da lista Give me the sugar please! e, em seguida, o comando da message(${VARIABLE}) é executado, o que exibe o valor atual da variável VARIABLE . Quando não há valores na lista, o loop termina sua execução. O comando endforeach sinaliza a conclusão da lista de comandos aninhados no loop.


 #  "Give me the sugar please!"   : foreach(VARIABLE Give me the sugar please!) message(${VARIABLE}) endforeach() 

Existem mais 3 formas de escrever um foreach . O primeiro ciclo neste exemplo gera números inteiros de 0 a 10 no lugar da lista, o segundo ciclo gera no intervalo de 3 a 15 e o terceiro ciclo trabalha no segmento de 50 a 90, mas com uma etapa de 10.


 #  "0 1 2 3 4 5 6 7 8 9 10"   : foreach(VARIABLE RANGE 10) message(${VARIABLE}) endforeach() #  "3 4 5 6 7 8 9 10 11 12 13 14 15"   : foreach(VARIABLE RANGE 3 15) message(${VARIABLE}) endforeach() #  "50 60 70 80 90"   : foreach(VARIABLE RANGE 50 90 10) message(${VARIABLE}) endforeach() 

Funções e macros


A sintaxe do CMake permite definir seus próprios comandos que podem ser chamados exatamente como integrados. O exemplo a seguir demonstra o uso de funções e macros: primeiro, uma função e uma macro são definidas com seus próprios comandos e, quando chamadas, seus comandos são executados seqüencialmente.


 #   "print_numbers": function(print_numbers NUM1 NUM2 NUM3) message(${NUM1} " " ${NUM2} " " ${NUM3}) endfunction() #   "print_words": macro(print_words WORD1 WORD2 WORD3) message(${WORD1} " " ${WORD2} " " ${WORD3}) endmacro() #   "print_numbers",   "12 89 225": print_numbers(12 89 225) #   "print_words",   "Hey Hello Goodbye": print_words(Hey Hello Goodbye) 

O comando function usa o nome da função futura como o primeiro argumento, e o restante dos argumentos são os nomes dos parâmetros que podem ser usados ​​como variáveis ​​normais. Os parâmetros são visíveis apenas para a função que está sendo definida, o que significa que não podemos obter acesso a seus parâmetros fora da função. Além disso, todas as outras variáveis ​​definidas e redefinidas na função são visíveis apenas para si.


As macros são semelhantes às funções, exceto pelo fato de não terem escopo próprio: todas as variáveis ​​dentro das macros são consideradas globais. Você pode ler mais sobre as diferenças entre macros e funções aqui .


Conforme observado nos comentários, as macros no CMake são semelhantes às do pré-processador C: se você colocar o comando return no corpo da macro, sairá da função de chamada (ou de todo o script), demonstrado por este exemplo:


 #  ,   : macro(demonstrate_macro) return() endmacro() #  ,   : function(demonstrate_func) demonstrate_macro() message("The function was invoked!") endfunction() #  "Something happened with the function!" demonstrate_func() message("Something happened with the function!") 

No exemplo acima, a função demonstrate_func não terá tempo para imprimir a mensagem The function was invoked! , como antes, o local de chamada da macro demonstrate_macro será substituído e o comando exit será executado.


Analisando argumentos


Conforme observado nos comentários, o poderoso mecanismo cmake_parse_arguments permite analisar argumentos passados ​​para uma função ou macro.


Este comando aceita o prefixo usado na definição de variáveis ​​(consulte o próximo parágrafo), uma lista de opções usadas sem valores subseqüentes, uma lista de palavras-chave seguida por um único valor, uma lista de palavras-chave seguida por conjuntos de valores e uma lista de todos os valores passados ​​para a função ou macro.


O trabalho do mecanismo de análise de argumentos é converter os argumentos recebidos em valores variáveis. Portanto, o comando considerado para cada opção e palavra-chave define sua própria variável no formato <Prefix>_<OptionOrKeyword> , que encapsula um determinado valor. Para opções, esses são valores booleanos (true - a opção é indicada; caso contrário, false) e, para palavras-chave, todos os valores transferidos localizados após eles.


A função custom_function contém uma chamada para cmake_parse_arguments e, em seguida, imprime os valores de determinadas variáveis. Em seguida, a função é chamada com os argumentos LOW NUMBER 30 COLORS red green blue , após o que é impressa na tela:


 function(custom_function) #       : cmake_parse_arguments(CUSTOM_FUNCTION "LOW;HIGH" "NUMBER" "COLORS" ${ARGV}) #  "'LOW' = [TRUE]": message("'LOW' = [${CUSTOM_FUNCTION_LOW}]") # "'HIGH' = [FALSE]": message("'HIGH' = [${CUSTOM_FUNCTION_HIGH}]") #  "'NUMBER' = [30]": message("'NUMBER' = [${CUSTOM_FUNCTION_NUMBER}]") #  "'COLORS' = [red;green;blue]": message("'COLORS' = [${CUSTOM_FUNCTION_COLORS}]") endfunction() #   "custom_function"   : custom_function(LOW NUMBER 30 COLORS red green blue) 

Escopos


Na seção anterior, você aprendeu que algumas construções no CMake podem definir seu próprio escopo. De fato, todas as variáveis ​​são consideradas globais por padrão (o acesso a elas está em toda parte), com exceção daquelas que foram definidas e redefinidas em funções. Também existem variáveis ​​de cache que têm seu próprio escopo, mas não são usadas com tanta frequência.


Conforme mencionado nos comentários, as variáveis ​​podem ser definidas no escopo "pai" usando o comando set(VARIABLE ... PARENT_SCOPE) . Este exemplo demonstra esse recurso:


 # ,   "VARIABLE"   # "In the parent scope..."    : function(demonstrate_variable) set(VARIABLE "In the parent scope..." PARENT_SCOPE) endfunction() #   "VARIABLE"    : demonstrate_variable() #      "VARIABLE" : message("'VARIABLE' is equal to: ${VARIABLE}") 

Se PARENT_SCOPE removido da definição da variável VARIABLE , a variável estará acessível apenas à função demonstrate_variable e, no escopo global, ela assumirá um valor vazio.


Conclusão


Isso conclui a sintaxe do CMake. O próximo artigo será lançado em alguns dias e apresentará o uso do sistema de compilação CMake. Até breve!

Source: https://habr.com/ru/post/pt431428/


All Articles