Search code examples
c++cmakeincludeheader-filesself-contained

Check if C++ headers are self contained


I want to create a solution to control if header inclusions are done properly. Imagine a following scenario:

myProject
    | _____ foo.cpp
    | _____ foo_public.h
    | _____ bar.cpp
    | _____ bar_public.h
    | _____ baz.cpp
    | _____ baz.h

inside foo_public.h stands following #include "bar_public.h"

When someone edits the bar_public.h accidentally with #include "baz.h" that solution should catch this since the file does not end with "*_public.h" and give and error in build. Such inclusion error can occur in every header file that ends with _public. For the rest this rule is not important.

Some guarantees in the project:

  • Each header file is a C++ header file, cmake is used in the project
  • All inclusions are done with #include directives inside files.
  • All files needs to be checked complies a certain file name pattern ("*_public.h")
  • All files are inside myProject folder. (The depth could change but that depth is also reflected in inclusion statement eg. myProject/newFile/new_intface_public.h -> included as #include "myProject/newFile/new_intface_public.h"

I don't know which direction should I go, I thought I can use CMake functions for this purpose but I am new to CMake, I don't know where to start. Some alternatives in my mind:

  1. Get all the include statements from the C++ headers matching the pattern and parse them: I read parsing C++ header was not a straightforward action from this thread.
  2. In CMake create a new target/C++ file that includes all the files matching the pattern (included files are copied in a separate redundant place), compile that target and there is no error: Mainly I am interested in this solution but I don't know where I should start.

Any ideas, comments would be appreciated.


Solution

  • The following CMake code should do the job.

    # Get a list of '*_public.h' files
    file(
      GLOB_RECURSE _public_files
      LIST_DIRECTORIES false
      ${CMAKE_SOURCE_DIR}/*_public.h)
    
    # Generate a project in '/public_if_test/' in the build folder and
    # for every header file:
    #  * generate a source file which includes only the header file
    #  * copy the header file
    set(_gen_source_files "")
    foreach(header_file ${_public_files})
      get_filename_component(_name ${header_file} NAME_WLE)
      file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/public_if_test/${_name}.cpp"
           "#include \"${header_file}\"")
      file(COPY "${header_file}"
           DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/public_if_test/")
      list(APPEND _gen_source_files
           "${CMAKE_CURRENT_BINARY_DIR}/public_if_test/${_name}.cpp")
    endforeach()
    
    # Create a build target to check if the '*_public.h' files are self contained
    add_library(test_public_interface MODULE ${_gen_source_files})
    

    An build error is generated, if a header file is not fully self contained.