Como adicionar corretamente diretórios de inclusão com o CMake

243

Há cerca de um ano, perguntei sobre dependências de cabeçalho no CMake .

Percebi recentemente que o problema parecia ser que o CMake considerava esses arquivos de cabeçalho externos ao projeto. Pelo menos, ao gerar um projeto Code :: Blocks, os arquivos de cabeçalho não aparecem no projeto (os arquivos de origem). Portanto, parece-me que o CMake considera esses cabeçalhos externos ao projeto e não os rastreia nas dependências.

Uma pesquisa rápida no tutorial do CMake apenas apontou para o include_directoriesque parece não fazer o que eu desejo ...

Qual é a maneira correta de sinalizar ao CMake que um diretório específico contém cabeçalhos a serem incluídos e que esses cabeçalhos devem ser rastreados pelo Makefile gerado?

Matthieu M.
fonte
As edições feitas nesta pergunta a tornam confusa. A pergunta e as respostas originais eram como rastrear arquivos de cabeçalho em um IDE. Isso é bem diferente das dependências de arquivos de cabeçalho ausentes do Makefile gerado e de como resolver esse problema.
Fdk1342 01/01/19
@ Fred: Eu não tenho idéia do que você está falando. Como a revisão de edição mostra claramente, a última frase sempre esteve lá. Somente edições cosméticas foram feitas nessa questão e nenhuma palavra foi introduzida (ou removida).
Matthieu M.
Então esse é o meu mal-entendido. Pareceu-me que um parágrafo inteiro foi adicionado. stackoverflow.com/questions/13703647/… diz que o entendimento comum era como listar o arquivo de cabeçalho no IDE. Isso estaria se referindo ao .cbparquivo do projeto. Agora, se o scanner de dependência cmake falhar na identificação correta de um arquivo de cabeçalho como uma dependência para um Makefile, existem maneiras de corrigir isso, mas em alguns casos ele irá errar porque não inclui um pré-processador completo.
Fdk1342 01/01/19

Respostas:

267

Duas coisas devem ser feitas.

Primeiro adicione o diretório a ser incluído:

target_include_directories(test PRIVATE ${YOUR_DIRECTORY})

Caso você esteja com uma versão CMake muito antiga (2.8.10 ou anterior) sem suporte target_include_directories, também poderá usar o legado include_directories:

include_directories(${YOUR_DIRECTORY})

Em seguida, você também deve adicionar os arquivos de cabeçalho à lista dos arquivos de origem do destino atual, por exemplo:

set(SOURCES file.cpp file2.cpp ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)
add_executable(test ${SOURCES})

Dessa forma, os arquivos de cabeçalho aparecerão como dependências no Makefile e também, por exemplo, no projeto do Visual Studio gerado, se você gerar um.

Como usar esses arquivos de cabeçalho para vários destinos:

set(HEADER_FILES ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)

add_library(mylib libsrc.cpp ${HEADER_FILES})
target_include_directories(mylib PRIVATE ${YOUR_DIRECTORY})
add_executable(myexec execfile.cpp ${HEADER_FILES})
target_include_directories(myexec PRIVATE ${YOUR_DIRECTORY})
SirDarius
fonte
Ah! Eu sabia que deveria ser algo estúpido. Na verdade, eu não listei os cabeçalhos ... Preciso listar os cabeçalhos apenas desta biblioteca ou também todos os cabeçalhos dos quais possa depender (além de declarar a dependência da biblioteca)? É um projeto em crescimento e eu temo bastante a ideia de adicionar um cabeçalho a todas as dependências quando adiciono um na biblioteca raiz.
Matthieu M.
Para permitir um melhor rastreamento de dependência (por exemplo, para garantir que a modificação de um arquivo de cabeçalho acione a compilação para todos os destinos afetados), sim. No entanto, você pode usar as variáveis ​​cmake para listar os arquivos de cabeçalho apenas uma vez e usá-los em vários lugares, veja minha edição.
precisa saber é o seguinte
1
Minha pergunta foi mais no sentido de que tenho várias bibliotecas que dependem uma da outra: libroot, liba depende da libroot, libb depende da libroot. Posso usar a LIBROOT_HEADER_FILESvariável in liba/CMakefilee libb/CMakefilethen?
Matthieu M.
2
Isso está errado, você nunca deve usar de include_directoriesnovo target_include_directories. O primeiro define recursivamente para todos os destinos nesse diretório; enquanto o último o define como um alvo. Fazer isso quebra a noção de um gráfico de destino no CMake e, em vez disso, conta com efeitos colaterais na hierarquia de arquivos.
Andy Andy
1
Editei a resposta para refletir a noção atual de preferência target_include_directoriespelo código CMake moderno. Sinta-se à vontade para me convidar para um bate-papo se não concordar com as alterações.
ComicSansMS
74

Primeiro, você include_directories()pode dizer ao CMake para adicionar o diretório -Ià linha de comando da compilação. Segundo, você lista os cabeçalhos na sua chamada add_executable()ou add_library().

Por exemplo, se as fontes do seu projeto estiverem dentro srce você precisar de cabeçalhos include, poderá fazer o seguinte:

include_directories(include)

add_executable(MyExec
  src/main.c
  src/other_source.c
  include/header1.h
  include/header2.h
)
Angew não se orgulha mais de SO
fonte
19
Você realmente precisa adicionar cabeçalhos add_executable? Eu pensei que o CMake descobriu as dependências do arquivo de inclusão automaticamente.
Colin D Bennett
57
@ColinDBennett Você não precisa listá-los por motivos de dependência - o CMake calcula as dependências de compilação muito bem, se não o fizer. Mas se você listá-las, elas serão consideradas parte do projeto e serão listadas como tal nos IDEs (que foi o tópico da pergunta).
Angew não está mais orgulhoso de SO
Pelo menos para QtCreator, não é necessário adicionar class.h caso exista um class.cpp. Somente lonely.h precisa ser adicionado à fonte. Veja o tutorial em www.th-thielemann.de/cmake
Th. Thielemann
19

O CMake é mais como uma linguagem de script se comparada com outras maneiras de criar Makefile (por exemplo, make ou qmake). Não é muito legal como o Python, mas ainda assim.

Não existe uma " maneira adequada " se procurar em vários projetos de código aberto como as pessoas incluem diretórios. Mas existem duas maneiras de fazer isso.

  1. Os include_directories brutos anexarão um diretório ao projeto atual e a todos os outros projetos descendentes que você anexará por meio de uma série de comandos add_subdirectory . Às vezes, as pessoas dizem que essa abordagem é legada.

  2. Uma maneira mais elegante é com o target_include_directories . Ele permite anexar um diretório para um projeto / destino específico sem (talvez) herança desnecessária ou conflito de vários diretórios de inclusão. Também permita executar uma configuração sutil e acrescente um dos seguintes marcadores a este comando.

PRIVATE - use apenas para este destino de construção especificado

PUBLIC - use-o para o destino especificado e para destinos vinculados a este projeto

INTERFACE - use-o apenas para destinos vinculados ao projeto atual

PS:

  1. Ambos os comandos permitem marcar um diretório como SYSTEM para dar uma dica de que não é da sua conta que os diretórios especificados contenham avisos.

  2. Uma resposta semelhante é com outros pares de comandos target_compile_definitions / add_definitions , target_compile_options / CMAKE_C_FLAGS

bruziuz
fonte
13

Adicionar include_directories("/your/path/here").

Isso será semelhante a chamar gcccom a -I/your/path/here/opção

Coloque aspas duplas no caminho. Outras pessoas não mencionaram isso e isso me deixou presa por 2 dias. Portanto, esta resposta é para pessoas que são muito novas no CMake e muito confusas.

off99555
fonte
7

Eu tive o mesmo problema.

Meu diretório de projeto era assim:

    --project
    ---Classes
    ----Application
    -----.h and .c files
    ----OtherFolders
    --main.cpp

E o que eu costumava incluir os arquivos em todas essas pastas:

    file(GLOB source_files
            "*.h"
            "*.cpp"
            "Classes/*/*.cpp"
            "Classes/*/*.h"
    )

    add_executable(Server ${source_files})

E funcionou totalmente.

Seyed Hussein Mirzaki
fonte
Lembrar que o cmake é um 'gerador de sistema de compilação' e não um 'sistema de compilação' usando o arquivo glob não é uma boa ideia no cmake moderno (CMake com versões 3.0 e superior) porque os globs de arquivo são avaliados no momento da 'compilação' e não na 'compilação tempo de geração do sistema. Veja o link: gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1
ggulgulia