Как правильно добавить включаемые каталоги с CMake


243

Около года назад я спросил о зависимостях заголовка в CMake .

Недавно я понял, что проблема заключается в том, что CMake считает эти файлы заголовков внешними по отношению к проекту. По крайней мере, при генерации проекта Code :: Blocks заголовочные файлы не появляются внутри проекта (исходные файлы делают). Поэтому мне кажется, что CMake считает эти заголовки внешними по отношению к проекту и не отслеживает их в зависимости.

Быстрый поиск в учебнике по CMake только указал на то, include_directoriesчто, кажется, не делает то, что я хочу ...

Как правильно сообщить CMake, что определенный каталог содержит заголовки, которые нужно включить, и что эти заголовки должны отслеживаться сгенерированным Makefile?


Изменения, внесенные в этот вопрос, приводят к путанице. Первоначальный вопрос и ответы были о том, как отслеживать файлы заголовков в IDE. Это сильно отличается от сгенерированных Makefile отсутствующих зависимостей файла заголовка и способа решения этой проблемы.
fdk1342

@ Фред: Понятия не имею, о чем ты говоришь. Как ясно показывает редакция редактирования, последнее предложение всегда было там. По этому вопросу были сделаны только косметические правки, и ни одно слово не было введено (или удалено).
Матье М.

Тогда это мое недоразумение. Выглядело так, как мне понравилось, был добавлен целый параграф stackoverflow.com/questions/13703647/… говорит, что общее понимание заключалось в том, как вывести файл заголовка в IDE. Это было бы со ссылкой на .cbpфайл проекта. Теперь, если сканер зависимостей cmake не может правильно определить заголовочный файл как зависимость для Makefile, есть способы исправить это, но в некоторых случаях это может привести к ошибкам, поскольку он не содержит полноценного препроцессора.
fdk1342

Ответы:


267

Две вещи должны быть сделаны.

Сначала добавьте каталог, который будет включен:

target_include_directories(test PRIVATE ${YOUR_DIRECTORY})

В случае, если вы застряли с очень старой версией CMake (2.8.10 или старше) без поддержки target_include_directories, вы также можете использовать устаревшую версию include_directories:

include_directories(${YOUR_DIRECTORY})

Затем вы также должны добавить файлы заголовков в список ваших исходных файлов для текущей цели, например:

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

Таким образом, файлы заголовков будут отображаться как зависимости в Makefile, а также, например, в сгенерированном проекте Visual Studio, если вы его сгенерируете.

Как использовать эти заголовочные файлы для нескольких целей:

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

Ах! Я знал, что это должно быть что-то глупое. Действительно, я не перечислил заголовки ... Нужно ли перечислять заголовки только этой библиотеки или также всех заголовков, от которых она может зависеть (помимо объявления зависимости от библиотеки)? Это растущий проект, и я очень боюсь добавить заголовок ко всем зависимостям, когда добавляю заголовок в корневую библиотеку.
Матье М.

Для лучшего отслеживания зависимостей (например, чтобы убедиться, что изменение файла заголовка запускает компиляцию для всех затронутых целей), да. Однако вы можете использовать переменные cmake, чтобы перечислять заголовочные файлы только один раз и использовать их в нескольких местах, см. Мое редактирование.
SirDarius

1
Мой вопрос был больше в том смысле, что у меня есть несколько библиотек, которые зависят друг от друга: libroot, liba зависит от libroot, libb зависит от libroot. Могу ли я использовать LIBROOT_HEADER_FILESпеременную в liba/CMakefileи libb/CMakefileзатем?
Матье М.

2
Это неправильно, вы никогда не должны использовать include_directoriesболее target_include_directories. Первый устанавливает его рекурсивно для всех целей в этом каталоге; тогда как последний устанавливает его для цели. Выполнение первого из них нарушает представление о целевом графе в CMake и вместо этого опирается на побочные эффекты в вашей файловой иерархии.
Энди

1
Я отредактировал ответ, чтобы отразить текущее понятие предпочтения target_include_directoriesдля современного кода CMake. Не стесняйтесь пригласить меня в чат, если вы не согласны с изменениями.
ComicSansMS

74

Сначала вы используете include_directories()CMake, чтобы добавить каталог как -Iв командную строку компиляции. Во-вторых, вы перечисляете заголовки в вашем add_executable()или add_library()вызове.

Например, если исходные тексты вашего проекта srcи вам нужны заголовки include, вы можете сделать это следующим образом:

include_directories(include)

add_executable(MyExec
  src/main.c
  src/other_source.c
  include/header1.h
  include/header2.h
)

19
Вы действительно должны добавить заголовки add_executable? Я думал, что CMake выяснил зависимости включаемого файла автоматически.
Колин Д Беннетт

57
@ColinDBennett Вам не нужно перечислять их по причинам зависимости - CMake вычисляет зависимости сборки, если вы этого не сделаете. Но если вы перечислите их, они будут считаться частью проекта и будут перечислены как таковые в IDE (что и было темой вопроса).
Angew больше не гордится SO

По крайней мере, для QtCreator нет необходимости добавлять class.h в случае, если существует class.cpp. Только lonely.h необходимо добавить к источнику. См учебник на www.th-thielemann.de/cmake
Th. Тилеманн

19

CMake больше похож на язык сценариев, если сравнивать его с другими способами создания Makefile (например, make или qmake). Это не очень круто, как Python, но все же.

Нет такой вещи, как « правильный путь », если смотреть в различных проектах с открытым исходным кодом, как люди включают каталоги. Но есть два способа сделать это.

  1. Crude include_directories добавит каталог к ​​текущему проекту и всем другим дочерним проектам, которые вы добавите с помощью ряда команд add_subdirectory . Иногда люди говорят, что такой подход является наследием.

  2. Более элегантный способ - с target_include_directories . Это позволяет добавить каталог для конкретного проекта / цели без (может быть) ненужного наследования или столкновения различных включаемых каталогов. Также позвольте выполнить даже тонкую настройку и добавьте один из следующих маркеров для этой команды.

PRIVATE - использовать только для указанной цели сборки

PUBLIC - используйте его для указанной цели и для целей, которые связаны с этим проектом

ИНТЕРФЕЙС - используйте его только для целей, которые связаны с текущим проектом

PS:

  1. Обе команды позволяют пометить каталог как SYSTEM, чтобы дать подсказку, что это не ваша компания, что указанные каталоги будут содержать предупреждения.

  2. Аналогичный ответ с другими парами команд target_compile_definitions / add_definitions , target_compile_options / CMAKE_C_FLAGS


13

Добавить include_directories("/your/path/here").

Это будет похоже на вызов gccс -I/your/path/here/опцией.

Убедитесь, что вы поместили двойные кавычки вокруг пути. Другие люди не упоминали об этом, и это заставило меня застрять на 2 дня. Таким образом, этот ответ для людей, которые очень плохо знакомы с CMake и очень смущены.


7

У меня такая же проблема.

Моя директория проекта была такой:

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

И что я использовал для включения файлов во все эти папки:

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

    add_executable(Server ${source_files})

И это полностью сработало.


Помнить, что cmake является «генератором системы сборки», а не «системой сборки», использующей файловый глобус, не является хорошей идеей в современном cmake (CMake с версиями 3.0 и выше), потому что файловые глобусы оцениваются во время «сборки», а не «сборки» время генерации системы. Смотрите ссылку: gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1
ggulgulia
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.