Organização de Projetos C++ com CMake: Estruturas e Melhores Práticas

A organização de um projeto em C++ impacta diretamente a manutenibilidade e a escalabilidade do código. Ao utilizar o CMake como sistema de build, é fundamental estabelecer uma hierarquia de diretórios clara, diferenciando projetos focados na criação de bibliotecas daqueles destinados à geração de executáveis.

Estrutura Hierárquica Sugerida

Para a maioria dos cenários de desenvolvimento, a seguinte estrutura de diretórios provê um equilíbrio entre isolamento de dependências e clareza de código:

  • dependencies/: Armazena bibliotecas de terceiros (ex: spdlog, nlohmann_json).
    • lib_name/: Pasta específica da biblioteca.
      • include/: Arquivos de cabeçalho (.h, .hpp).
      • lib/: Binários pré-compilados ou arquivos de objeto (opcional).
  • src/: Contém a lógica principal e o código-fonte do projeto.
    • CMakeLists.txt: Configuração específica do módulo principal.
  • examples/: Demonstrações de uso do projeto (comum em bibliotecas).
    • CMakeLists.txt: Orquestração dos subprojetos de exemplo.
  • .clang-format: Regras de estilização de código.
  • .clangd: Configurações para o servidor de linguagem (LSP).
  • .gitignore: Lista de arquivos e pastas ignorados pelo controle de versão.
  • CMakeLists.txt: Arquivo de entrada principal que integra os subdiretórios.

Gestão de Dependências Externas

No diretório dependencies/, as bibliotecas são organizadas por nome. Algumas são header-only (apenas cabeçalhos), enquanto outras exigem a inclusão de arquivos estáticos ou dinâmicos. Ao configurar o CMake, é necessário apontar para esses diretórios para que o compilador localize as definições e o linker encontre os símbolos.

Configuração do Módulo Principal (src)

A configuração do CMake dentro da pasta src/ varia conforme o objetivo da saída.

Criação de Bibliotecas

cmake_minimum_required(VERSION 3.10)

# Extrai o nome do diretório para definir como nome do projeto
get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${MODULE_NAME})

# Coleta arquivos de código-fonte
aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}" CORE_SOURCES)

# Define a criação da biblioteca
add_library(${PROJECT_NAME} ${CORE_SOURCES})

# Configuração de caminhos para dependências externas
set(LIB_EXT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../dependencies/external_lib")
set(CMAKE_PREFIX_PATH "${LIB_EXT_ROOT}/lib/cmake")

find_package(external_lib REQUIRED)

target_include_directories(${PROJECT_NAME} PRIVATE 
    "${LIB_EXT_ROOT}/include"
)

target_link_directories(${PROJECT_NAME} PRIVATE 
    "${LIB_EXT_ROOT}/lib"
)

# Ajustes específicos para compilador MSVC
if(MSVC)
    target_link_options(${PROJECT_NAME} PRIVATE "/NODEFAULTLIB:MSVCRT")
endif()

target_link_libraries(${PROJECT_NAME} PRIVATE 
    external_lib::module
    "additional_lib"
)

Criação de Executáveis

cmake_minimum_required(VERSION 3.10)

get_filename_component(APP_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${APP_NAME})

aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}" APP_SOURCES)

add_executable(${PROJECT_NAME} ${APP_SOURCES})

target_include_directories(${PROJECT_NAME} PRIVATE 
    "${CMAKE_CURRENT_SOURCE_DIR}/../dependencies/external_lib/include"
)

target_link_libraries(${PROJECT_NAME} PUBLIC 
    external_lib::module
)

Ferramentas de Qualidade e Ambiente

Configurações externas ajudam a manter a consistência entre diferentes ambientes de desenvolvimento.

Estilização com .clang-format

Uma configuração baseada no estilo LLVM com ajustes para recuo e quebra de chaves é recomendada para manter a legibilidade:

BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Never
ColumnLimit: 0
NamespaceIndentation: All
BreakBeforeBraces: Custom
BraceWrapping:
  AfterClass: true
  AfterFunction: true
  AfterNamespace: true
  AfterStruct: true
  BeforeElse: true
  BeforeWhile: true

Configuração do Clangd

Para evitar ruídos durante o desenvolvimento, é possível desativar diagnósticos específicos sobre inclusões não utilizadas no arquivo .clangd:

Diagnostics:
  MissingIncludes: None
  UnusedIncludes: None

Arquivo .gitignore

Deve-se ignorar artefatos de build e caches de ferramentas de análise:

/build/
/.cache/
/.vscode/

O Arquivo CMakeLists.txt Raiz

O arquivo na raiz do projeto serve como o ponto de entrada principal, definindo padrões globais e incluindo os subdiretórios de código e exemplos. A opção CMAKE_EXPORT_COMPILE_COMMANDS é habilitada para gerar o arquivo compile_commands.json, essencial para o funcionamento correto de ferramentas baseadas em Clang em editores como VS Code ou Neovim.

cmake_minimum_required(VERSION 3.10)

# Gera base de dados para ferramentas de análise estática
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

get_filename_component(PROJECT_ROOT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${PROJECT_ROOT_NAME} LANGS CXX)

# Definição do padrão C++
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(src)

# Adiciona exemplos se o diretório existir
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples")
    add_subdirectory(examples)
endif()

Tags: CMake C++ Build Systems Project Structure Clang

Publicado em 7-2 03:07