Populate include directories from sub_directory to all of parts of the project. How?

Hello all!

My project have this structure:

Application (add_executable)
| – Library 1 (add_subdirectory in application and add_library in library)
| – Library 2 (add_subdirectory in application and add_library in library)
| – Library 3 (add_subdirectory in application and add_library in library)
| – etc

There are few questions:

  1. How to populate include directories correctly from for example Library 2 to Application?
  2. How to populate include directories from Library 1 to Library3 and Application?

For now in Library CMakeLists.txt using this:

target_include_directories(${LIBRARY_1_TARGET}
	INTERFACE
		/dir1
		/dir2
		/dir3
)

And in Application using this:

target_include_directories(${APPLICATION_TARGET}
	PRIVATE
		$<TARGET_PROPERTY:Library_1,INTERFACE_INCLUDE_DIRECTORIES>
)

This approach allow me to populate include directories from library to Application. Is this approach correct from the way of view of CMake best practices. How to populate it from library to another library correctly? Is there way to setup library within automatic population through the all parts of application and application itself.

My global goal - to make library be attached within minimum lines of code in application. Kind of reusing approach.

The modern mechanism is FILE_SET HEADERS

target_sources(Library1
   INTERFACE
     FILE_SET interface_headers
     TYPE HEADERS
     BASE_DIRS
       dir1
       dir2
       dir3
     FILES
       dir1/file/to/be/installed.h
       dir2/file/to/be/installed.h
)

# Elsewhere
target_link_libraries(Application
  PRIVATE
    Library1
    Library2
    Library3
)

This is assuming you intend to install() the libraries at some point. If you don’t ever intend to install the libraries or headers, you don’t need to list any FILES. The file list itself is just to tell CMake which files need to be installed (and some IDEs use the metadata to list files in their explorer).

Interface links are transitive, ie:

target_link_libraries(Library3
  INTERFACE
    Library2
)

target_link_libraries(Application
  PRIVATE
    Library1
    Library3
)
# Application inherits Library2 usage requirements from Library3

Just tried your way. Have no result at all.

Application CMakeLists.txt contain:

target_link_libraries(Application
	PRIVATE
		Library_1
		Library_2
)

Library1 and Library2 CMakeLists.txt contain:

target_sources(Library[1/2]
	INTERFACE
		FILE_SET interface_headers
		TYPE HEADERS
		BASE_DIRS
			/Dir1
			/Dir2
)

But still have this troubles:

fatal error: 'library_1.h' file not found

It’s starting working only for Application (still not available library in library) if adding to Application CMakeLists.txt this:

target_include_directories(Application
	PRIVATE
		$<TARGET_PROPERTY:Library_1,INTERFACE_INCLUDE_DIRECTORIES>
		$<TARGET_PROPERTY:Library_2,INTERFACE_INCLUDE_DIRECTORIES>
)

An it doesn’t matter using by me ‘target_sources’ or ‘target_include_directories’ in Libraries CMakeLists.txt

It’s not /Dir1, it’s Dir1. By putting the / before the directory you’ve made it into an absolute path, literally saying “Dir1 in the root of the entire filesystem”.

You almost certainly want a relative path, no leading slash. Relative paths in most CMake contexts, including target_sources(), are relative to the current source directory

Dir1 or /Dir1 just for example written by me. For this exact situation Libraries using result of this:

${CMAKE_CURRENT_SOURCE_DIR}

For now writing examples within all-in-one directory.

This is piece of code:

set(${A_PREFIX}_DIR_ROOT ${CMAKE_CURRENT_SOURCE_DIR})

and after

target_sources(${A_NAME_TARGET}
	INTERFACE
		FILE_SET interface_headers
		TYPE HEADERS
		BASE_DIRS
			${${A_PREFIX}_DIR_ROOT}
)

or

target_include_directories(${A_NAME_TARGET}
	PUBLIC
		${${A_PREFIX}_DIR_ROOT}
)

Is there any requirements to write something before or after something?

I’ve posted a complete demonstration here: GitHub - nickelpro/discourse-fileset-example

If you check the CI you’ll see this does indeed build and run on all platforms. If you’re having trouble with something specific in this example, or you have a usage that diverges from it, please post the complete reproducible example and I’ll demonstrate where the problem is.

Thx a lot.

Found few differences to my implementation. There are top-level CMakeLists.txt not for application. Application CMakeLists.txt is added to the project with ‘add_subdirectory’. In my case Application is top-level CMakeLists.txt.

Will reorganize my project in accordance with your example and try everything you mentioned in your messages.

Will reply soon with result of my tests

It shouldn’t matter in what directories you call these commands in so long as you get the relative paths correct. I chose completely arbitrarily to split things, assuming maybe that was what you were doing.

In this branch I’ve moved the application target to the top level: GitHub - nickelpro/discourse-fileset-example at app-in-top-level

Not directories. Top level project.
Your schema looks like:

Root CMakeLists.txt
|-- Application (add_subdirectory in root and add_executable in Application)
|-- Library (add_subdirectory in root and add_library in Library)

In my situation:

Application CMakeLists.txt
|-- Library (add_subdirectory in Application and add_library in Library)

Yes, that’s what I’ve done in the branch, your situation. There is no add_subdirectory for the application anymore.

Will check it out and reply.
Really don’t know what is going on here.
4 days trying to find the cause of it.
Been reading manuals and examples many times.

First difference

[cmake] -- Configuring incomplete, errors occurred!
[cmake] CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
[cmake]   CMake 3.31 or higher is required.  You are running version 3.30.5

Is it critical to use your version?
Just lowered version to 3.30

Found another one difference. My Library located outside of Application or Root folder. In ‘add_subdirectory’ using absolute path. Could it be the reason of troubles?

That’s not a subdirectory, it’s not even really part of the same project. All source requirements of a given project are required to be in a single source tree underneath a top level.

add_subdirectory() won’t work at all if the targeted folder is not a subdirectory of the top-level, you will get an error akin to:

CMake Error at CMakeLists.txt:12 (add_subdirectory):
  add_subdirectory not given a binary directory but the given source
  directory "/.../discourse-fileset-example/lib1" is not a
  subdirectory of
  "/.../discourse-fileset-example/application".  When
  specifying an out-of-tree source a binary directory must be explicitly
  specified.

I’ve added an example demonstrating this just so you can see the error message for yourself: GitHub - nickelpro/discourse-fileset-example at app-with-different-roots

If the library is not a part of the same source tree, you should build and install the library separately, then consume the library from said install tree in the application project.

Broadly the “source tree” is the thing you track in a single source control repository. The root of that repository is where your top-level CMakeLists.txt should go most of the time.

Spent few days on tests and experiments. Got published own example based on Qt arboreus_examples/qt6/CMake/ProjectArchitecture/ProjectArchitecture_v4 at master · ArboreusSystems/arboreus_examples · GitHub

My global goal is to make correctly working when Libraries outside of Project tree. Have few libraries that is used by different projects. Want to make possible using one source located outside for using in different projects. For me it’s critical. Don’t want to multiply physically one sources to different projects.

Maybe my solution is kind of hack. But it’s working.

The solution:

– creating symlinks on libraries in project tree
– adding libraries to project with ‘add_subdirectory’ (it’s working for both approaches ‘application-in-root’ and ‘root-application’)

Only one not working automatically - adding includes of library to another library. It’s still require using something like this:

target_include_directories(Library1
	PRIVATE
		$<TARGET_PROPERTY:Library_2,INTERFACE_INCLUDE_DIRECTORIES>
		$<TARGET_PROPERTY:Library_3,INTERFACE_INCLUDE_DIRECTORIES>
)

For me it’s Ok if all is working and project is building correctly.

SUPER-MEGA-HUGE Thanks for assistance.

You’re very much in “whatever works, works” territory here.

Consuming libraries outside the source tree via add_subdirectory() is explicitly not supported. Consuming source libraries (ie, not IMPORTED) from outside the source tree at all isn’t an orthodox workflow.

I would encourage you to learn how to use target_sources(FILE_SET), and how to install libraries and consume them via find_package(), as it will significantly improve your projects’ robustness, and allow them to be built and packaged by others.

That said, I’m glad you found a solution which worked for you.