У меня есть проект, который прекрасно работает и работает с CMake. Мой проект настроен так:
├── CMakeLists.txt
|
├── include/
│ └── standalone/
│ └── x.hpp
|
├── src/
└── standalone/
└── main.cpp
Содержимое моих заголовков выглядит так:
// ------x.hpp--------
#pragma once
#include <iostream>
class X
{
public:
void hello()
{
std::cout << "Hello World!\n"; // needs <iostream>
}
};
// -------main.cpp-------
#include <standalone/x.hpp>
int main()
{
X x;
x.hello();
}
Я использую следующее CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(standalone)
###############################################################
# Compiler settings
###############################################################
# use C++11 features
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# set warning level
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -pedantic -pedantic-errors -Wall -Wextra")
###############################################################
# header dependencies
###############################################################
# compile against project headers
include_directories(${PROJECT_SOURCE_DIR}/include)
# the header files
file(GLOB_RECURSE header_files FOLLOW_SYMLINKS "include/*.hpp")
# the source files
file(GLOB_RECURSE source_files FOLLOW_SYMLINKS "src/*.cpp")
###############################################################
# build target
###############################################################
# the program depends on the header files and the source files
add_executable(main ${header_files} ${source_files})
Последовательность команд mkdir build
, cd build
, cmake ..
, make
, а также ./main
правильно печатает «Hello World!» без предупреждений.
Вышеуказанная настройка верна. Но предположим <iostream>
не был включен в x.hpp
но в main.cpp
вместо. Тогда программа все равно была бы построена правильно, но x.hpp
не будет автономным заголовком. Поэтому я хотел бы проверить самодостаточность моих заголовков, т.е. для каждого заголовка я хотел бы собрать небольшую тестовую программу
#include "some_header.hpp"int main() {}
Однако, если я добавлю следующий раздел в CMakeList.txt
###############################################################
# header self-sufficiency
###############################################################
set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
message("REQUIRED_FLAGS=${CMAKE_REQUIRED_FLAGS}")
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_SOURCE_DIR}/include)
message("REQUIRED_INCLUDES=${CMAKE_REQUIRED_INCLUDES}")
include(CheckIncludeFiles)
check_include_files("${header_files}" IS_STANDALONE)
Макрос ${header_files}
правильно расширяется до заголовка x.hpp
, но check_include_files()
команда не правильно его компилирует
REQUIRED_FLAGS= -std=c++11 -Werror -pedantic -pedantic-errors -Wall -Wextra
REQUIRED_INCLUDES=/home/rein/projects/standalone/include
-- Looking for include file /home/rein/projects/standalone/include/standalone/x.hpp
-- Looking for include file /home/rein/projects/standalone/include/standalone/x.hpp - not found.
Очевидно, мне не хватает какой-то переменной конфигурации, которая позволяет CMake искать в нужном месте. Даже для правильных заголовков, check_include_files()
не работает. Что мне нужно сделать, чтобы сделать эту работу? Только когда правильные заголовки считаются правильными, я могу продолжить и проверить неправильные заголовки.
НОТА Если это не
абсолютно необходимо, меня не интересуют сценарии оболочки или разработка CMake для циклов, которые напрямую вызывают TRY_COMPILE
или что-то типа того. AFAICS, вот что CheckIncludeFiles
Модуль для, и я хотел бы знать, как правильно настроить это, если это вообще возможно.
Для заголовков C ++, а не заголовков C, вам нужно использовать CheckIncludeFileCXX
. Этот модуль также отличается от CheckIncludeFiles
в этом вы можете передать только один файл за один раз в этот макрос.
Также вполне может быть, что вам нужно установить CMAKE_REQUIRED_INCLUDES
или одна из других переменных, перечисленных в документации.
Например, если некоторые из ваших заголовков ссылаются на друг друга (как в #include "h1.hpp"
), тогда вам понадобится:
set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_SOURCE_DIR}/include)
include(CheckIncludeFileCXX)
foreach(project_header ${project_headers})
get_filename_component(header_name ${project_header} NAME_WE)
check_include_file_cxx("${project_header}" ${header_name}_IS_STANDALONE)
endforeach()
Немного больше информации в вики-статье CMake Как писать проверки платформ.
После просмотра CMakeFiles/CMakeError.log
вывод, кажется, есть два фундаментальных ограничения check_include_files()
это не может быть преодолено с помощью разрешенных параметров конфигурации (кажется, некоторое подтверждение из этих пунктов):
-std=C++11
не является допустимым параметром командной строки для передачи./usr/include
так далее.) игнорируются даже при добавлении вручную с set(CMAKE_REQUIRED_INCLUDES ${CMAKE_SOURCE_DIR}/include "/usr/include")
, #include <iostream>
внутри x.hpp
поэтому не правильно найден.Журнал ошибок имеет этот фрагмент:
/usr/bin/gcc -I/home/rein/projects/standalone/include -o CMakeFiles/cmTryCompileExec4052324189.dir/CheckIncludeFiles.c.o -c /home/rein/projects/standalone/build/CMakeFiles/CMakeTmp/CheckIncludeFiles.c
In file included from /home/rein/projects/standalone/build/CMakeFiles/CMakeTmp/CheckIncludeFiles.c:2:0:
/home/rein/projects/standalone/include/standalone/x.hpp:2:20: fatal error: iostream: No such file or directory
compilation terminated.
Это существенно ограничивает идею проверки самодостаточности заголовками системы C. Похоже, мне придется искать альтернативы сценария оболочки, например тот на этом ТАК вопрос.
ОБНОВИТЬ: благодаря обновленному ответу @Fraser мой вопрос теперь решается с помощью check_include_file_cxx
с флагами компилятора и предварительно заданными путями.