Cpp Notes

4.project_structure

How to structure your project

Organizing your CMake-based project following these conventions will help you easily read and manage other projects that follow similar patterns, avoid conflicts, and simplify your build system.

Example Directory Structure

- project
  - .gitignore
  - README.md
  - LICENCE.md
  - CMakeLists.txt
  - cmake
    - FindSomeLib.cmake
    - something_else.cmake
  - include
    - project
      - lib.hpp
  - src
    - CMakeLists.txt
    - lib.cpp
  - apps
    - CMakeLists.txt
    - app.cpp
  - tests
    - CMakeLists.txt
    - testlib.cpp
  - docs
    - CMakeLists.txt
  - extern
    - googletest
  - scripts
    - helper.py

Key Points

  • Folder Names:

    • The names like test/ vs. tests/ or apps/ may vary, and some projects may not have an apps/ folder (e.g., for library-only projects).
    • There might be additional folders, such as a python/ folder for Python bindings or a cmake/ folder for helper CMake files (like Find<library>.cmake).
  • CMakeLists.txt Structure:

    • CMake files are split across source directories (not in the include/ directories). This keeps the include/ directory clean and avoids conflicts when copying to /usr/include.
    • You should use add_subdirectory() to include CMakeLists.txt files for subdirectories.
  • Helper CMake Modules:

    • Place helper modules in the cmake/ folder, such as Find*.cmake files.
    • To include these in your CMake path:
      set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
  • Extern Folder for Dependencies:

    • The extern/ folder should primarily contain git submodules to manage external dependencies, allowing easy version control and updates.
  • .gitignore:

    • Include /build* in your .gitignore so that users can create build directories inside the source directory for convenience.
    • While some packages discourage in-source builds, allowing this can simplify the build process without requiring a strict out-of-source build.

Enforcing Out-of-Source Builds

To require out-of-source builds, add this code to your CMakeLists.txt:

### Require out-of-source builds
file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" LOC_PATH)
if(EXISTS "${LOC_PATH}")
    message(FATAL_ERROR "You cannot build in a source directory (or any directory with a CMakeLists.txt file). Please make a build subdirectory. Feel free to remove CMakeCache.txt and CMakeFiles.")
endif()

This ensures that users create a separate build directory, avoiding conflicts with source files.

For an extended example project, check this link.