cmake - 01 - Instalação e Build
cmake - Instalação e Build
Index Chapter 01 02 03 04
Antes de começar tenha atenção em caixinhas como esta, porque elas sinalizam os comandos que são usados com mais frequência no cmake.
Instalação no Ubuntu
Baixe do site: https://cmake.org/download/.
Para instalar a versão 3.25 que é a versão corrente na da data 18/11/2022.
sudo apt purge cmake
wget https://github.com/Kitware/CMake/releases/download/v3.25.0/cmake-3.25.0.tar.gz
tar xzf cmake-3.25.0
cd cmake-3.25.0
sudo ./bootstrap
sudo make
sudo make install
Verifique a instalação com o comando version
cmake --version

Onde make version <maj.min.rev>.
Se estiver rodando linux e quiser instalar as ferramentas padrão para gerar o build execute:
sudo apt install build-essential
Docker
Caso prefira usar o docker eu disponibilizei um link com um toolchain que oferece a versão 3.2.0 para executar os exemplos deste tutorial.
docker run -it swidzinski/cmake:toolchain
CMakeLists.txt
É o arquivo de configuração do cmake e deve existir um arquivo desse na raíz do seu projeto ou no local onde você indicar com cmake -S na primeira etapa de configuração do build que será visto mais a frente cmake - Instalação e Build > 1. Configuração do Build
Formato mínimo de um arquivo CMakeLists.txt
Se seu cmake for a versão 3.2.2 você poderia usar
cmake_minimum_required(VERSION 3.2.2)
project(
alo-mundo
VERSION 1.0
DESCRIPTION "Projeto de aprendizado"
LANGUAGES CXX
)
Estes são os campos obrigatórios para configuração mínima do arquivo CMakeLists.txt. Uma informação importante é que os comandos no cmake são case insensitive porém os parâmetros são case sensitive. Portanto, o parâmetro logo abaixo VERSION precisará ser escrito em caixa-alta.
Será onde aplicaremos a primeira política de versão ao nosso projeto. Como o cmake tem mais de 20 anos, algumas mudanças de versão não garantem necessariamente retrocompatibilidade, por isso, este comando é obrigatório. No mínimo, aconselho usar 3.1, pois abaixo desta versão target_sources não estará disponível. É obrigatório existir esta definição em todos os arquivos CMakelists.txt.
Com este comando é possível versionar o projeto, atribuir um nome, uma descrição, além de selecionar a linguagem de programação utilizada (LANGUAGES CXX significa que o projeto irá ser compilado para C++). É recomendável que cada projeto use esta definição no arquivo CMakelists.txt.
A configuração linguagem de programação também poderia ser declarada com o comando:
Onde YY será a versão C++. Por exemplo se quiser usar a versão 11 então seria cxx_std_11. A definição de <escopo> pode ser PRIVATE, PUBLIC ou INTERFACE. Leia mais a frente a declaração do comando target_sources para enteder o significado da declaração de escopo.
Outro comando extremamente importante
Este comando é responsável por criar o target executável. O nome do target será também o nome do binário executável. Esse comando cria um target que possibilita a criação de vínculos com bibliotecas dinâmicas ou estáticas como veremos mais a frente.
Uma dúvida comum é o que seria um target? Conceitualmente targets podem ser entendidos como unidades lógicas que se organizam a fim de de gerar um build. Esses targets poderiam, por exemplo, se transformar em executáveis ou bibliotecas.
Target, portanto, estará sempre ligado a essa ideia de artefato(s) que se (unem e se) transforma(m) em outro(s) artefato(s). Por exemplo, se o comando add_executable não fosse adicionado ao arquivo CMakeLists.txt, o target resultante do build seriam arquivos objetos compilados com extensão .o.
Arquivos fontes podem ser adicionados diretamente com o comando add_executable ou separadamente com o target_sources.
Este comando vincula o código-fonte com o target. A definição de <escopo> pode ser PRIVATE, PUBLIC ou INTERFACE. Se for PRIVATE então o fonte será usado para construir este target e mais nenhum outro target dependente. Use PUBLIC quando o target for usado pelo projeto atual e por outro target que se vincule a ele. Em geral este campo será na maioria das vezes PRIVATE. Não confunda o conceito de private, public de classe com dependência de target. Além de PRIVATE, existem PUBLIC e INTERFACE.
Para evitar problemas, quando trabalhar com vários projetos ou projetos complexos, utilize sempre caminhos absolutos através das variáveis CMAKE_CURRENT_SOURCE_DIR ou CMAKE_CURRENT_LIST_DIR prefixado ao arquivo quando for acrescentar no target_sources. A diferença entre eles é que:
CMAKE_CURRENT_SOURCE_DIRusa o diretório onde esta localizado o arquivo CMakeLists.txt.
CMAKE_CURRENT_LIST_DIRutiliza o diretório onde o script esta sendo executado.
Apesar dos dois comandos geralmente produzirem um resultado semelhante, opte pelo primeiro. Exemplo:
target_sources(
lib_escola
PUBLIC
{CMAKE_CURRENT_SOURCE_DIR}/monitor.cpp
)
Outra informação importante é que se a definição de <escopo> campo for omitida, o cmake consulta a variável BUILD_SHARED_LIBDS para determinar o tipo de lib que será gerado
A vantagem é que target_sources permite explicitamente definir o escopo do arquivo fonte (private, interface, etc..)
Ao utilizar o target_sources você terá a garantia de que qualquer um que utilize em seu código #include <nome-do-arquivo.hpp> irá funcionar. Embora com aspas duplas também deva funcionar.
Com este comando é possível atribuir a versão C++ que será usada para compilar o projeto. Onde YY deverá ser substituido pela versão da linguagem C++11, 14, 17, 20. Interessante que você pode ter targets compilados com versões diferentes.
Se quiser uma lista de features disponíveis consulte a documentação oficial.
Para incluir um fonte no path do projeto use o comando target_include_directories ou include_directories.
Estes dois comandos permitem incluir um fonte no path. O comando include_directories, porém, tem escopo global enquanto o target_include_directories permite atribuir um target, além do escopo, portanto, dê preferência a este último. Se o projeto for uma lib, então mantenha o escopo como PUBLIC para que qualquer outro projeto que faça referência tenha a segurança de ter os arquivos no path durante a compilação
Se quiser ler sobre definição de escopo.

Etapas do build
Podemos dividir o processo em 3 etapas principais:
- Configuração do build
- Geração do build
- Compilação do projeto ou build
1. Configuração do Build
Frequentemente, a etapa 1 é consolidada com a etapa 2, caso tenha necessidade de realizar mais alguma configuração você pode configurar variáveis de cache usando o comando cmake -D ou os utilitários ccmake ou cmake-gui, vamos falar mais adiante.
2. Geração do Build
A sintaxe para geração do build é essa:
cmake -S <código-fonte> -B <buildtree>
Evite poluir o seu repositório com código gerado pelo build.
Em contraste com outras ferramentas, cmake encoraja a produção de artefatos (out of the source building) separado do código.
cmake -S ./src -B ./output
Este comando irá gerar o sistema de build em output e e o código fonte será analisado a partir de ./src.
No local onde se localiza o arquivo de código-fonte, ou seja, no local onde você indicou -S deve conter obrigatoriamente um arquivo de configuração CMakeLists.txt, caso contrário, você receberá uma mensagem de erro informado que o arquivo não foi encontrado.
Generators
Generators são particularmente importantes no cmake pois especificam que tipo de build você quer atribuir para sua instalação.
Usuários do linux estão mais inclinados a escolherem Unix Makefiles ou Ninja, enquanto usuários do windows tem a tendência de escolherem build para a sua IDE favorita. Podemos ter uma lista dos generators suportados pelo cmake com executando o comando
cmake --help

Embora não esteja nesta lista o visual studio é suportado nas versões mais recentes do cmake.
Uma consideração importante na hora de gerar o build é que por padrão a compilação gera código debug. Para compilar código de produção, release você precisará alterar a variável CMAKE_BUILD_TYPE. Isso pode ser incluído no próprio build com o parâmetro -D que permite manipular variáveis do cache,
cmake -S . -B build -D CMAKE_BUILD_TYPE=Release
Onde CMAKE_BUILD_TYPE pode assumir os seguintes valores:
Debug(Padrão) - Não otimizado para desempenho e contem todos os símbolos para depuração. Todos os ASSERT estão habilitados. (Mesmo que -o0 -g )ReleaseOtimizado para obter melhor desempenho. (Mesmo que -O3 0DNDEBUG)MinSizeRelmesmo querelease, mas otimizado para um binário pequeno ao invés de rápido (-Os -DNDEBUG)RelWithDebInfootimiza o código e inclui código de depuração, mas desabilita ASSERT ( Mesmo que -O2 -g -DNDEBUG)
Você pode até listá-las depois com o comando:
cmake -L[A][H] <path-do-bluid>
A saída deverá se parece com

Você também pode configurar níveis de log, combinando com a função da linguagem cmake message() para qualquer listfiles (arquivos com extensão cmake), para exibir mensagens para o usuário de acordo com o level definido pelo usuário.
message([<level>] "menssagem de texto" ...)
cmake --log-level=<level>
onde level pode ser:
- ERROR
- WARNING
- NOTICE
- STATUS
- VERBOSE
- DEBUG
- TRACE
Da mesma forma pode ser atribuído de forma permanente na variável de cache
CMAKE_MESSAGE_LOG_LEVEL
3. Compilando do projeto ou build
Você precisa indicar onde o build foi gerado
cmake --build <buildtree> [<options>] [-- <build-tool-options>]
Para paralelizar e acelerar o build você pode utilizar quaisquer uns desses parâmetros
cmake --build <dir> --parallel [<number-of-jobs>]
cmake --build <dir> -j [<number-of-jobs>]
Antes que pergunte, eles são equivalentes. Alternativamente também é possível atribuir a variável CMAKE_BUILD_PARALLEL_LEVEL
Nos próximos tutoriais veremos como adicionar dependências estáticas e dinâmicas e exemplos mais avançados.