Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I let CMake check whether my headers are self-sufficient?

Setup

I have a project that builds and runs fine with CMake. My project setup is like this:

├── CMakeLists.txt
|
├── include/
│   └── standalone/
│       └── x.hpp
|
├── src/
    └── standalone/
        └── main.cpp

The contents of my headers is like this:

// ------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();
}

I use the following 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})

The sequence of commands mkdir build, cd build, cmake .., make, and ./main correctly prints "Hello World!" without warnings.

Problem

The above setup is correct. But suppose <iostream> was not included in x.hpp but in main.cpp instead. Then the program would still have built correctly, but the x.hpp would not be a standalone header. So I would like to test for self-sufficiency of my headers, i.e. for every header I would like to compile a small test program

#include "some_header.hpp"
int main() {}

However, if I add the following section to the 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)

The macro ${header_files} correctly expands to the header x.hpp, but the check_include_files() command does not correctly compile it

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.

Question

Apparantly I am missing some configuration variable that lets CMake search in the right place. Even for correct headers, check_include_files() does not work. What do I need to do to make this work? Only when correct headers are deemed correct, can I go on and test incorrect headers.

NOTE Unless it's absolutey necessary, I am not interested in shell scripts or elaborate CMake for loops that directly call TRY_COMPILE or something like that. AFAICS, that's what the CheckIncludeFiles module is for, and I'd like to know how to correctly configure that, if at all possible.

like image 227
TemplateRex Avatar asked May 07 '13 22:05

TemplateRex


1 Answers

For C++ headers rather than C headers, you need to use CheckIncludeFileCXX. This module also differs from CheckIncludeFiles in that you can only pass a single file at a time to this macro.

It could well also be that you need to set CMAKE_REQUIRED_INCLUDES, or one of the other variables listed in the docs.

For example, if some of your headers refer to eachother (as in #include "h1.hpp"), then you'll need:

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

There's a little more info in the CMake wiki article How To Write Platform Checks.

like image 152
Fraser Avatar answered Oct 07 '22 04:10

Fraser