O CMake possui três modificadores de visibilidade: PRIVATE, PUBLIC e INTERFACE.
Estes modificadores são utilizados em conjunto com comandos como target_include_directories e target_link_libraries, e são especificados no contexto de alvos (targets).
Tipos de Alvos
Alvos no CMake referem-se a diferentes tipos de saída:
- Alvos executáveis:
add_executablegera arquivos binários executáveis - Alvos de biblioteca:
add_librarygera arquivos de biblioteca - Alvos personalizados:
add_custom_targetgera arquivos arbitrários através de scripts
Bibliotecas de Interface
As bibliotecas de interface são um caso especial de alvo de biblioteca, definidas da seguinte forma:
add_library(minha_biblioteca_interface INTERFACE)
target_include_directories(minha_biblioteca_interface INTERFACE include/)
Aqui, minha_biblioteca_interface não gera nenhum arquivo imediatamente, mas pode ter alvos dependentes posteriormente. As bibliotecas INTERFACE podem ser consideradas um mecanismo conveniente para gerenciar dependências de build.
Compreendendo os Modificadores de Visibilidade
target_include_directories(teste PRIVADO "${CMAKE_BINARY_DIR}"): O alvotesteusará o diretório de inclusão especificado, mas outros alvos que dependam dele não herdarão essa dependência.target_include_directories(teste PUBLICO "${CMAKE_BINARY_DIR}"): O alvotesteusará o diretório de inclusão especificado, e quaisquer alvos que dependam dele também herdarão essa dependência.target_include_directories(teste INTERFACE "${CMAKE_BINARY_DIR}"): O alvotestenão precisa do diretório de inclusão, mas quaisquer alvos que dependam dele usarão esse diretório.
Gerenciamento de Subdiretórios
O CMake permite especificar módulos independentes com seus próprios fluxos de build personalizados usando subdiretórios. Um arquivo CMake principal pode disparar o build de múltiplos diretórios e, finalmente, linká-los ao programa principal.
Utilização de Bibliotecas
Para localizar arquivos de biblioteca e seus cabeçalhos correspondentes:
- Adicionar bibliotecas com
target_link_libraries() - Incluir caminhos de cabeçalhos de duas maneiras:
INCLUDE_DIRECTORIES- introdução globaltarget_include_directories- permite especificar caminhos de cabeçalhos para um alvo específico
Método Moderno para Inclusão de Bilbiotecas
Por exemplo, se alvos que vinculam à biblioteca Matematica precisam incluir o diretório Matematica como caminho de cabeçalho, podemos adicionar ao CMakeLists.txt da biblioteca:
target_include_directories(
Matematica
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
Com o modificador INTERFACE especificado, o arquivo CMakeLists.txt do principal projeto não precisa usar a variável EXTRA_INCLUDE. Ao vincular à biblioteca Matematica, o diretório da biblioteca será automaticamente incluído nos caminhos de busca de cabeçalhos.
Nota: Quando bibliotecas estáticas são referenciadas por diferentes programas, múltiplas cópias das funções da biblioteca estática podem existir na memória.
Geração de Arquivos Executáveis
Utilize o comando add_executable() para criar executáveis. Para múltiplos arquivos de origem, separe-os por espaços. Você pode usar ${NOME_PROJETO} para referenciar o nome do projeto.
Gerenciamento de Múltiplos Arquivos de Origem
Para definir uma variável que representa múltiplos arquivos de origem:
set(LISTA_FONTE a.cpp b.cpp c.cpp)
Para referenciar variáveis, use a sintaxe ${}.
Adicionando Timestamp de Compilação
Para incluir um timestamp na compilação:
string(TIMESTAMP TEMPO_COMPILACAO %Y%m%d-%H%M%S)
No arquivo Config.h.in, adicione: #define TIMESTAMP @TEMPO_COMPILACAO@
Especificando Padrão C++
Para definir o padrão C++ a ser utilizado:
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED Verdadeiro)
Adicione esses comandos antes do add_executable. A partir do GCC 6.1, o padrão padrão é C++14.
Tornando Bibliotecas Opcionais
Método Clássico
Forneça uma opção para o usuário: option(USAR_MATematica "Descrição" ON) e adicione condições para a construção e vinculação da biblioteca:
if(USAR_MATematica)
add_subdirectory(Matematica)
list(APPEND BIBLIOTECAS_EXTRAS Matematica)
list(APPEND INCLUIR_EXTRAS ${DIRETORIO_PROJETO}/Matematica)
endif()
target_link_libraries(
${NOME_PROJETO}
PUBLICO
${BIBLIOTECAS_EXTRAS}
)
target_include_directories(
${NOME_PROJETO}
PUBLICO
${DIRETORIO_BINARIO_PROJETO}
${INCLUIR_EXTRAS}
)
A variável BIBLIOTECAS_EXTRAS armazena bibliotecas opcionais a serem vinculadas ao executável, enquanto INCLUIR_EXTRAS armazena caminhos de busca de cabeçalhos opcionais.
Método Moderno
Modifique o código-fonte para usar condicionais:
#ifdef USAR_MATematica
#include"Matematica.hpp"
#endif
#ifdef USAR_MATematica
const double valor = raiz_quadrada(valorEntrada);
#else
const double valor = std::sqrt(valorEntrada);
#endif>
Como estamos referenciando uma variável do CMake, precisamos adicionar ao Config.h.in:
#cmakedefine USAR_MATematica
Também é possível especificar o valor da variável durante a build:
cmake -DUSAR_MATematica=OFF ..
cmake --build .
Regras de Instalação
Para instalar alvos:
install(
TARGETS alvo1 [alvo2 ...]
[RUNTIME DESTINATION dir]
[LIBRARY DESTINATION dir]
[ARCHIVE DESTINATION dir]
[INCLUDES DESTINATION [dir ...]]
[PRIVATE_HEADER DESTINATION dir]
[PUBLIC_HEADER DESTINATION dir]
)
Usando Variáveis do CMake em Projetos C++
O CMake fornece o mecanismo config.h.in que permite usar variáveis do CMakeLists.txt em arquivos C++.
Criando o arquivo Config.h.in
#pragma once
#define VERSAO_PROJETO_MAJOR @VERSAO_PROJETO_MAJOR@
#define VERSAO_PROJETO_MINOR @VERSAO_PROJETO_MINOR@
#define NOME_AUTOR "@NOME_AUTOR@"
Configurando no CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
projeto(tutorial)
set(VERSAO_PROJETO_MAJOR 1)
set(VERSAO_PROJETO_MINOR 0)
set(NOME_AUTOR "Joao")
configure_file(Config.h.in Config.h)
add_executable(tutorial tutorial.cpp)
# Inclui o diretório onde o arquivo de cabeçalho gerado está localizado
target_include_directories(tutorial PRIVADO "${CMAKE_BINARY_DIR}")
O arquivo Config.h será ger automaticamente no diretório de build. É necessário adicionar este diretório à lista de caminhos de busca de cabeçalhos. A variável ${CMAKE_BINARY_DIR} representa o caminho binário do projeto atual, onde os arquivos de compilação são gerados (diretório build).
O CMake gera o arquivo de cabeçalho após preencher os espaços reservados para variáveis. Esses arquivso de cabeçalho dinâmicamente gerados precisam ser incluídos no projeto. No exemplo, o arquivo Config.h é colocado no diretório ${CMAKE_BINARY_DIR}, então basta especificar este caminho.