Carregando dependências em projetos C++ usando o CMake
Tags: cpp, beginners, productivity
Published: 2021-07-14 13:32:41

Com as linguagens de programação modernas é muito comum possuir-se um meio de obter e gerenciar dependências, seja usando o `pip`, `cargo`, `npm` ou `yarn`, esse processo se torna extremamente simples. Contudo, quando se trata de linguagens como C e C++ isso é um pouco diferente, não existe um único meio padronizado de obter bibliotecas externas. Pensando nisso, pensei em escrever esse post mostrando como automatizo a obtenção de bibliotecas de repositórios Git remotos usando o CMake.

> Existem soluções criadas pela comunidade como o `conan` e o
> `vcpkg` mas elas carregam outra dependência na máquina e
> requerem que a biblioteca que você quer esteja indexada por eles.

## O módulo FetchContent

O CMake é sistema de build que atomatiza a geração de `Makefile`s que definem o processo de compilação de um projeto de código-fonte. Ele fornece vários módulos que ajudam na configuração de várias formas. Um desses módulos é o [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html), que ajuda a obter conteúdo externo em tempo de configuração, ou seja, quando o CMake está gerando o Makefile.

É bastante simples de usá-lo, no exemplo abaixo adicionamos o  GoogleTest ao nosso projeto:

```cmake
include(FetchContent)
FetchContent_Declare(GoogleTest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        master
)
FetchContent_MakeAvailable(GoogleTest)
```

Depois disso, temos o projeto do GoogleTest bem como suas bibliotecas acessíveis em tempo de configuração, então podemos vincular qualquer executável/biblioteca com o GoogleTest, por exemplo:

```cmake
add_executable(mytests "test/main.cpp" "test/awesome_test.cpp")
target_link_libraries(mytests gtest_main)
```

## Criando uma função CMake

Para automatizar esse processo, eu costumo criar uma função simples que concentra o código que usa o FetchContent, isso torna o script CMake mais legível e fácil de adicionar novas dependências.

```cmake
function(GET_DEPENDENCY D_NAME D_URL D_TAG)
    message(CHECK_START "Configuring ${D_NAME}")
    FetchContent_Declare(${D_NAME}
        GIT_REPOSITORY ${D_URL}
        GIT_TAG        ${D_TAG}
    )
    FetchContent_MakeAvailable(${D_NAME})
endfunction()
```

Assim consigo usar a funcção `get_dependecy` passando o nome, a URL e a versão (ou tag) da biblioteca.

```cmake
get_dependency(GoogleTest "https://github.com/google/googletest.git" master)
```

Para adicionar mais dependências...

```cmake
get_dependency(spdlog "https://github.com/gabime/spdlog" v1.8.5)
get_dependency(CLI11 "https://github.com/CLIUtils/CLI11" v1.9.1)
get_dependency(GoogleTest "https://github.com/google/googletest" master)
```

Quando uso essa configuração, se torna muito mais fácil adicionar e remover dependências de bibliotecas externas, principalmente por usar bibliotecas abertas com código-fonte no Github. Isso também ajuda em projeto em equipes permitindo que não seja necessário um conjunto de passos complexos para configurar o ambiente de desenvolvimento nas diferentes máquinas dos desenvolvedores. Também traz um benefício para o uso CI (_Continous Integration_).