C++

What is the difference between includedirectories and targetincludedirectories in CMake

19 September 2026 · 8 min read

What is the difference between includedirectories and targetincludedirectories in CMake

When diving into CMake for building robust and scalable software projects, understanding the nuances of include paths becomes crucial. Two commands, include_directories and target_include_directories, play vital roles in specifying where the compiler should look for header files. However, their behavior and scope differ significantly. Grasping the difference between include_directories and target_include_directories in CMake is essential for maintaining project integrity, avoiding compilation errors, and ensuring a clean and efficient build process. This article will dissect these commands, providing clear explanations, practical examples, and best practices to help you master include path management in your CMake projects. We’ll explore how each command impacts your build system, what implications they have for your dependencies, and how to choose the right one for different scenarios. Understanding these distinctions will empower you to write more maintainable and portable CMake scripts.

Understanding include_directories: The Global Approach

The include_directories command in CMake functions as a project-wide directive. When you use include_directories, you’re essentially telling CMake to add the specified directories to the compiler’s include path for all targets within the current CMakeLists.txt file and any subdirectories included by add_subdirectory(). This means that every target defined in these scopes will be able to find header files located in the specified directories. This can be useful for projects with a simple structure or when certain header files are globally accessible across the entire project.

However, the global nature of include_directories can also lead to unintended consequences. Because it affects all targets, it can introduce unnecessary dependencies and pollute the include paths of targets that don’t actually need access to those headers. This can increase compilation times and potentially lead to naming conflicts if different libraries define headers with the same name. Furthermore, it can obscure the true dependencies of a target, making it harder to understand which headers are actually required for a particular component to build correctly. Using include_directories excessively is often considered an anti-pattern in modern CMake development due to these potential drawbacks.

Consider a scenario where you have a library with common header files needed across your project. Using include_directories to add the library’s include directory might seem convenient. However, if you later add a new executable target that doesn’t depend on this library, it will still have the library’s include directory in its path, potentially leading to unexpected behavior or increased compilation time. It’s crucial to assess whether a global scope is truly necessary before employing include_directories. According to the CMake documentation, using target-specific methods improves build encapsulation and dependency management. CMake Documentation

Delving into target_include_directories: Target-Specific Control

target_include_directories offers a more refined and controlled approach to managing include paths in CMake. Instead of affecting the entire project, this command allows you to specify include directories for a specific target, such as an executable or a library. This target-specific approach promotes better encapsulation and reduces the risk of unintended side effects. By using target_include_directories, you explicitly define which targets require access to certain header files, making your build system more precise and maintainable.

One of the key advantages of target_include_directories is its ability to specify include directories as PUBLIC, PRIVATE, or INTERFACE. PUBLIC means that the include directory is added to the include path of the target itself and any targets that link against it. PRIVATE means that the include directory is only added to the include path of the target itself. INTERFACE means that the include directory is added to the include path of any targets that link against it, but not the target itself. This level of granularity provides fine-grained control over dependency propagation and ensures that only necessary include paths are exposed to other targets. Using the correct visibility setting is vital for creating modular and reusable components.

For instance, imagine you are building a library that provides a specific API. By using target_include_directories with the PUBLIC keyword, you can ensure that any target linking against your library automatically has access to the necessary header files for using the API. This simplifies the build process for consumers of your library and reduces the chances of configuration errors. This targeted approach is highly recommended for modern CMake projects. Modern CMake emphasizes the use of target_include_directories for better dependency management.

Practical Examples and Use Cases

Let’s illustrate the differences with a practical example. Suppose you have a project with two libraries, libA and libB, and an executable main. libA depends on header files in include/libA, and libB depends on header files in include/libB. The main executable depends on both libA and libB. Here’s how you would manage include directories using both commands:

Using include_directories (less desirable):

cmake include_directories(include/libA include/libB) add_library(libA …) add_library(libB …) add_executable(main …) target_link_libraries(main libA libB) This approach adds both include/libA and include/libB to the include path of all targets, including libA, libB, and main. While it works, it’s not ideal because it pollutes the include paths unnecessarily.

Using target_include_directories (recommended):

cmake add_library(libA …) target_include_directories(libA PUBLIC include/libA) add_library(libB …) target_include_directories(libB PUBLIC include/libB) add_executable(main …) target_link_libraries(main libA libB) This approach adds include/libA only to libA and any targets that link against it (in this case, main). Similarly, it adds include/libB only to libB and main. This provides better encapsulation and avoids unnecessary dependencies.

Featured Snippet Paragraph: The key distinction between include_directories and target_include_directories lies in their scope. include_directories operates globally, affecting all targets within its scope, while target_include_directories allows you to specify include paths for individual targets. Using target_include_directories promotes better encapsulation, reduces unnecessary dependencies, and improves the maintainability of your CMake projects. This targeted approach is generally preferred in modern CMake development.

Best Practices and Considerations

When choosing between include_directories and target_include_directories, several best practices should guide your decision:

  • Prefer target_include_directories: In most cases, target_include_directories is the better choice due to its target-specific nature and improved encapsulation.
  • Use appropriate visibility: Carefully consider whether an include directory should be PUBLIC, PRIVATE, or INTERFACE to control dependency propagation.
  • Avoid global includes: Minimize the use of include_directories to prevent unnecessary dependencies and potential naming conflicts.

Furthermore, consider these points:

  • Dependency Management: Employing target_include_directories significantly aids in dependency management by clearly defining the header file requirements for each target.
  • Project Structure: Organize your header files logically to facilitate easy inclusion and avoid circular dependencies.

By adhering to these best practices, you can create cleaner, more maintainable, and more efficient CMake build systems. Remember to always prioritize clarity and explicit dependency declaration over convenience when managing include paths.

  1. Identify all header file locations required by your project.
  2. Determine which targets need access to each header file location.
  3. Use target_include_directories with appropriate visibility (PUBLIC, PRIVATE, or INTERFACE) to add the necessary include paths to each target.
Infographic here
FAQ ---
When should I use `include_directories`?
Use `include_directories` sparingly, primarily when you genuinely need a global include path for all targets in your project or a specific subdirectory. Consider refactoring if possible to use `target_include_directories` instead.
What happens if I use both `include_directories` and `target_include_directories`?
The include paths specified by both commands will be combined. However, it's generally better to avoid mixing them to maintain clarity and control over your include paths. Target-specific settings will take precedence.
How does `target_include_directories` affect transitive dependencies?
Using the `PUBLIC` or `INTERFACE` keywords with `target_include_directories` will propagate the include paths to targets that link against the target. This ensures that transitive dependencies have access to the necessary header files.
Understanding the subtle yet significant differences between these two commands is paramount for building robust and maintainable CMake projects. By embracing the target-specific approach of `target_include_directories`, you gain greater control over your build system, minimize unnecessary dependencies, and improve the overall clarity of your project's structure. The ability to designate include directories as public, private, or interface further refines your control, allowing for precise management of header file visibility across your project. Neglecting these distinctions can lead to build errors, increased compilation times, and a less maintainable codebase. Take the time to refactor existing projects to adopt `target_include_directories`, and you'll quickly see the benefits in terms of improved build efficiency and reduced complexity. For further learning, explore CMake's official documentation and online resources like Stack Overflow and dedicated CMake forums [Stack Overflow](https://stackoverflow.com/). By implementing these best practices, you'll be well on your way to mastering CMake and building truly scalable and robust software.

Explore more CMake tutorials and best practices to elevate your build engineering skills!Question & Answer :
I have a directory structure for my C++ code which goes like this :

| |->include |->src 

I am writing a CMakeLists.txt file for my code. I want to understand the difference between include_directories and target_include_directories in CMake.

What is the difference between their usage and in order to add my include file path which one should I be using?

include_directories(x/y) affects directory scope. All targets in this CMakeList, as well as those in all subdirectories added after the point of its call, will have the path x/y added to their include path.

target_include_directories(t x/y) has target scope—it adds x/y to the include path for target t.

You want the former one if all of your targets use the include directories in question. You want the latter one if the path is specific to a target, or if you want finer control of the path’s visibility. The latter comes from the fact that target_include_directories() supports the PRIVATE, PUBLIC, and INTERFACE qualifiers.