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:

|250

De que forma poderíamos informar o cmake a localização de arquivos fontes dentro dos subdiretórios ?

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.

add_subdirectory(<diretório>)

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:

|250

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

add_library(<target> <tipo de biblioteca> src1 src2..)

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.

set_target_properties( <target>   PROPERTIES OUTPUT_NAME novo_nome_para_lib

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.

set_target_properties( <target> DEBUG_POSTFIX d)

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

target_link_libraries(<target1> <escopo> <target2>)

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).

Warning

É 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.