0

Обработка зависимостей заголовочных файлов с помощью CMake

4

Проблема с CMake: неправильное отслеживание зависимостей в проекте на C++

Я использую CMake для небольшого проекта на C++, и он работает отлично... за одним исключением :x

Когда я изменяю заголовочный файл, обычно это требует перекомпиляции нескольких исходных файлов (тех, которые включают его, непосредственно или косвенно). Однако, похоже, что CMake обнаруживает только некоторые из этих файлов, которые должны быть перекомпилированы, что ведет к ненадежному состоянию проекта. Я могу обойти это, удалив проект и начав сборку с нуля, но это противоречит цели использования утилиты сборки: перекомпилировать только то, что необходимо.

Я предполагаю, что делаю что-то не так.

Мой проект очень просто организован:

  • верхний каталог, где находятся все ресурсы, и там же находится главный CMakeLists.txt
  • каталог "include", где располагаются все публичные заголовки (в различных подкаталогах)
  • каталог "src", где находятся все подкаталоги с исходными файлами, и там же находится CMakeLists.txt для src
  • CMakeLists.txt для каждого подкаталога в каталоге "src"

В главном каталоге находится следующее:

cmake_minimum_required(VERSION 2.8)

project(FOO)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

# Опции компилятора
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++0x -Wall -Wextra -Werror")

include_directories($(FOO_SOURCE_DIR)/include)

add_subdirectory(src)

В каталоге "src":

add_subdirectory(sub1)
add_subdirectory(sub2)
add_subdirectory(sub3)
add_subdirectory(sub4)

add_executable(foo main.cpp)

target_link_libraries(foo sub1 sub2 sub3 sub4)

Где sub4 зависит от sub3, sub3 зависит от sub2, а sub2 зависит от sub1.

Пример подкаталога (sub3):

set(SUB3_SRCS
    File1.cpp
    File2.cpp
    File3.cpp
    File4.cpp
    File5.cpp
    File6.cpp
)

add_library(sub3 ${SUB3_SRCS})

target_link_libraries(sub3 sub1 sub2)

Буду признателен, если кто-то укажет на мою ошибку, поскольку поиск информации здесь или на CMake не дал результатов. Я думаю, что это довольно просто или должно работать «из коробки»...

(Для справки, я использую CMake версии 2.8.2 на MSYS)

EDIT: Спасибо за предложение от Билла, я проверил файл depend.make, сгенерированный CMake, и он действительно сильно недостаточно информативен. Вот пример:

src/sub3/CMakeFiles/sub3.dir/File1.cpp.obj: ../src/sub3/File1.cpp

Да, это всё, ни одного упоминания о включениях :x

5 ответ(ов)

0

Вам следует обратить внимание на файлы depend.make в вашем бинарном дереве. Они находятся в каталоге CMakeFiles/target.dir/depend.make. Попробуйте найти один из этих файлов, в котором отсутствует .h файл, который, по вашему мнению, должен присутствовать. Затем создайте отчет об ошибке для CMake или напишите на mailing list CMake.

0

Я столкнулся с той же проблемой. После изменения путей в include_directories() с абсолютных на относительные были добавлены соответствующие зависимости.

Судя по всему, CMake пытается определить, какие заголовочные файлы относятся к системе, а какие — к проекту. Я подозреваю, что директории, начинающиеся с /, передаются как -isystem /some/path, и поэтому не отображаются в сгенерированных зависимостях.

Если вы не можете заменить ${FOO_SOURCE_DIR} на относительный путь, попробуйте вычислить относительный путь с помощью соответствующих функций CMake. Например:

file(RELATIVE_PATH FOO_SOURCE_REL_DIR
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${FOO_SOURCE_DIR}/.)
include_directories(${FOO_SOURCE_REL_DIR}/include)

Это поможет CMake правильно учесть зависимости.

0

В ответ на ваш вопрос, важно помнить, что после внесения изменений в исходный код, такие как добавление директив #include в файлы .cpp, необходимо заново запустить CMake. В вашем случае, если вы добавили #include после первоначального вызова CMake, это может привести к ошибкам при компиляции, так как система сборки не распознает новые зависимости. Ребилд и повторный запуск CMake решают эту проблему. Убедитесь, что вы всегда перегенерируете проект после обновлений в коде.

0

Вопрос: Почему CMake удаляет системные пути включения из деревьев зависимостей и как это обойти, если у меня есть специальная сборка GCC, которая игнорирует /usr/include?

Ответ: Действительно, CMake по умолчанию удаляет системные пути включения из деревьев зависимостей, что обычно имеет смысл. Однако в некоторых случаях CMake не знает, какие пути компилятор считает системными, особенно если вы используете нестандартную сборку GCC, которая игнорирует путь /usr/include, но CMake считает его активным.

Чтобы заставить CMake учитывать /usr/include как зависимость, которую не следует игнорировать, можно воспользоваться интересным приемом: добавьте /. перед путем. Например:

find_package(ZLIB REQUIRED)
message(status "found zlib ${ZLIB_LIBRARIES}")
message(status "found zlib includes ${ZLIB_INCLUDE_DIRS}")
target_link_libraries(zlib_target INTERFACE ${ZLIB_LIBRARIES})
target_include_directories(zlib_target INTERFACE /.${ZLIB_INCLUDE_DIRS})  # Изменен путь

Теперь зависимости, которые используют zlib_target, автоматически получат опцию -I/./usr/include во время компиляции. Это подходящая альтернатива для тех библиотек, которые могут не всегда быть установлены по умолчанию в Linux, например, Z-lib для сжатия.

0

Убедитесь, что директива #include для отсутствующего заголовочного файла размещена перед первой инструкцией программы. В моем случае файл depend.make не включал заголовочный файл, потому что он располагался после первой инструкции программы.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь