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).
- lib_name/: Pasta específica da biblioteca.
- 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()