cmake - 02 - Interagindo com outros projetos e bibliotecas
cmake - Interagindo com outros projetos e bibliotecas
Index Chapter 01 02 03 04
Ainda sem entrar diretamente no assunto de bibliotecas dinâmicas ou estáticas, é muito comum dividir o projeto em subdiretórios de acordo com determinadas funcionalidades ou separá-los em componentes específicos.
Veja este exemplo:

Existem duas formas.
A primeira solução que parece nos vir a cabeça seria passar o caminho relativo de cada arquivo .cpp. como parâmetro do comando add_executable
Entretanto, o diretório ainda precisará de alguma forma ser incluído no path do build, pois, lembre-se que seu arquivo de cabeçalho (*.h) geralmente não possui caminho absoluto. Apenas um include de "algum arquivo.h" e o compilador não tem como adivinhar que o arquivo se localiza, por exemplo, no diretório "src/somewhereovertherainbown/" 😀. Para isso você vai precisará usar o comando include_directories ou target_include_directories já abordado no tutorial anterior na seção como adicionar um fonte no path do projeto.
A segunda solução consistiria em usar o comando add_subdirectory. Existe o overhead de ter que criar mais um arquivo CMakeLists.txt, mas ganhamos com isso coesão e reaproveitamento de código, pois se for necessário criar um novo projeto em qualquer outro lugar bastará inclui-lo no código com add_subdirectory.
O comando add_subdirectory nos permite aninhar hierarquicamente vários projetos CMakeLists.txt. Com isso, podemos ter outros arquivos fontes separados em vários sub-diretórios.
Atenção: Este comando exige que no diretório informado exista um arquivo CMakeLists.txt
Para tornar mais fácil a compreensão do que acabamos de dizer, aplicando a ideia no exemplo acima, o resultado seria que cada subdiretório teria um arquivo CMakeLists.txt:

Cada CMakeLists.txt é responsável por informar os arquivos fontes dentro do seu diretório e se eles estiverem aninhados você deve usar o add_subdirectory para estabelecer uma hierarquia entre eles. É importante que o CMakeLists.txt localizado na raiz do projeto tenha o comando project
Lembre-se de incluir os arquivos fontes com o comando target_sources
Adicionando bibliotecas
Para adicionar bibliotecas estáticas ou dinâmicas utilize o comando add_library
Onde o <tipo de biblioteca> pode ser STATIC, SHARED, OBJECT ou MODULE. Use STATIC para bibliotecas estáticas e SHARED para bibliotecas dinâmicas. O tipo OBJECT é usado para geração de arquivo objeto '*.o', sem passar pela linkagem. Isso pode ser útil, por exemplo, quando você precisa gerar build para bibliotecas estáticas e dinâmicas ao mesmo tempo, fazendo somente uma compilação. Mais a frente veremos um exemplo disso.
Observação: O arquivo fonte não é obrigatório ser informado neste momento. Você pode adicionar futuramente com target_source
Quando criar uma biblioteca o nome deve ser globalmente único no projeto, geralmente acrescido da extensão (nome.dll) no windows ou lib<nome>.so no linux.
Se quiser alterar o nome da biblioteca, altere a propriedade OUTPUT_NAME do target que foi criado para esta lib.
Para evitar problemas, convencione sempre adicionar a versão do build utilizando novamente as propriedades no target.
project(proj_aluno_lib
VERSION 1.2.3
)
set_target_properties(
proj_aluno_lib
PROPERTIES VERSION {PROJECT_VERSION_MAJOR}
)
Se for listar o diretório do build no linux, o resultado seria conforme a imagem
ls -la --color ./buildtree/aluno

Repare que com isso eliminamos aqueles problemas recorrentes de conflitos de bibliotecas , pois existe um link simbólico apontando para a versão correta da lib após a instalação (ou build).
Outra convenção é adicionar um sufixo de acordo com alguma configuração na propriedade <CONFIG>_POSTFIX.
Isso irá resultar numa lib e links simbólicos renomeados com sufixo d no final.
Para ver o resultado desta mudança faça o build deste projeto em modo DEBUG.
cmake -S . -B buildtree -D CMAKE_BUILD_TYPE=Debug
Repare que agora a letra 'd' foi acrescentada no arquivo so

Conectando os targets
É aqui que associaremos os projetos as respectivas bibliotecas (libs) estáticas ou dinâmicas ao executável ou a outra biblioteca. A ligação entre elas será feita com o comando target_link_libraries
Esse comando estabelece as relações de dependência entre as bibliotecas através dos targets. É este comando que vai vincular suas biblioteca ao seu executável (ou a outra biblioteca). Com relação ao escopo, na maioria dos casos, esse campo vai ser marcado como PRIVATE. Você só vai usar PUBLIC quando a dependência fizer parte de outra dependência (dependência transitiva).
É extremamente importante observar que alguns comandos criam targets como add_executable, add_library, add_custom_target, enquanto outros apenas fazem uso desses targets como target_sources. Aqueles que usam precisam ser dispostos depois daqueles que criam, ou seja, existe uma relação de precedência entre os comandos.