CMake Nested Projects and Targets - c++

i have a problem on building specific Visual Studio Projects from a CMake source tree. Immagine we have multiple targets in various subdirectories. In one sub-directory we initiate a new project (and solution). The projects in this project targets should be part of ALL_BUILD, but not in the parent ALL_BUILD.
In short:
project(Main)
add_executable(MainApplication ...)
target_link_libraries(MainApplication PRIVATE Library_A)
add_subdirectory(Library_A)
add_library(Library_A STATIC ...)
add_subdirectory(Other_Related_Stuff_Using_MainApplication)
project(OtherRelatedStuff)
add_custom_target(OtherTarget ... DEPENDS MainApplication)
Now with a Visual Studio generator we get two Solution Files. That is right and correct. In both solutions we have these three projects (as OtherReleatedStuff depends on MainApplication beeing built and OtherRelatedStuff is part of the Main-Project).
Now: ALL_BUILD builds all projects.
When i set
set_target_properties(OtherTarget PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
...in both solutions OtherTarget is disabled.
What i would like to do is:
Solution 1:
In Main.Sln there should be not OtherTarget. In OtherRelatedStuff.sln there could be MainApplication, but should not build. Maybe there shouldn't be a MainApplication as well.
Solution 2:
In Main.Sln, OtherTarget should never build (excluded from build). In OtherRelatedStuff.sln, OtherTarget should build, but the dependencies don't or should be even visible.
Is there a solution for it?

All targets found in one CMakeLists.txt source tree will generate a Visual Studio project file. And - as you found out already - EXCLUDE_FROM_ALL and EXCLUDE_FROM_DEFAULT_BUILD are set per target/project or directory.
So you could split/link your projects with the use of ExternalProject_Add(). And since Other_Related_Stuff depends on MainApplication you should call ExternalProject_Add() in Other_Related_Stuff's CMake file.
So my recommendation would look like this:
CMakeLists.txt
project(Main)
add_subdirectory(Library_A)
add_executable(MainApplication ...)
target_link_libraries(MainApplication PRIVATE Library_A)
Other_Related_Stuff/CMakeLists.txt
project(OtherRelatedStuff)
ExternalProject_Add(
MainApplication
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..
PREFIX MainApplication
)
add_custom_target(OtherTarget ... DEPENDS MainApplication)
So now your main CMakeLists.txt is Other_Related_Stuff/CMakeLists.txt and it does generate your second solution file in a MainApplication subdirectory.
Reference
CMake share library with multiple executables

Related

How to build only one target for dependency?

I want to build an application under Windows using CMake + Visual Studio with a lot of dependencies, such as zlib. All of them are static libraries.
I've tried ADD_SUBDIRECTORY and this works pretty well but instead of building only depending target (zlibstatic) it builds all of them.
How to remove unused targets (with their solutions) or choose only one?
Mainly I'm searching for feature to define only needed targets.
Part of my CMakeLists.txt:
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib")
TARGET_INCLUDE_DIRECTORIES(MyProject PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib")
TARGET_LINK_LIBRARIES(MyProject zlibstatic)
I finally figured out how to do it.
MyProject
├───build <- here I call cmake
├───deps
│ └───zlib
│ └───CMakeLists.txt
├───inc
├───src
└───CMakeLists.txt
# Include project but remove all tartgets
ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib EXCLUDE_FROM_ALL)
# Use only specific one target
ADD_DEPENDENCIES(MyProject zlibstatic)
# Include dirs from zlib source directory and from output directory becuse it generates some headers
TARGET_INCLUDE_DIRECTORIES(MyProject PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib
${CMAKE_CURRENT_BINARY_DIR}/deps/zlib
)
# Just to create beautiful structure in project tree
SET_PROPERTY(TARGET zlibstatic PROPERTY FOLDER Deps)
# Link after all
TARGET_LINK_LIBRARIES(MyProject zlibstatic)
I suggest you use vcpkg or conan instead to resolve your dependent library issue this is much cleaner and works well except for header only libraries.
You can of cause do that manually but than you loose the nice cmake setup.

cmake: how to reference and build separate cmake project dependency?

I have a cross-compiler cmake project that depends on libraries from a separate project that happens to also use cmake:
/myProject/CMakeLists.txt (uses cross-compiler)
/anotherProject/CMakeLists.txt (platform-agnostic)
anotherProject can be built completely separately on its own. It has no knowledge of myProject at all.
Now, anotherProject has numerous modules that I need, like:
anotherProject/A/CMakeLists.txt (produces static lib A.a)
anotherProject/B/CMakeLists.txt (produces static lib B.a)
etc
When I build myProject, I want to build and link against anotherProject/A and anotherProject/B, to produce shared lib myproject.so. I'd like to leverage the existing cmake-ness of anotherProject if possible, as opposed to manually globbing its various source sets from myProject.
What's the correct way to achieve this with cmake? I feel like I'm missing something obvious.
It would be straightforward if, say, myProject were just a subdirectory under anotherProject, or if there were a top-level CMakeLists.txt that could reference both myProject and anotherProject; but neither is what I'm after. I know I could build anotherProject and export its libraries to a well-known location, and then reference the export directory from myProject - but I would like to avoid that setup as well.
A solution is to use CMake packages.
Basically, in anotherProject, you craft a CMake configuration file where you set variables to be used by myProject (eg. include directory, list of libraries, compilation flags...), or even targets.
Then, in myProject, you use the find_package() mechanism so that CMake finds this configuration file and imports the variables/targets in your current project.
There is a tutorial on the CMake wiki.
The only alternative setup that I can think of based on your requirements is to allow your main (dependent) project to discover the other (dependee) project using find_package.
In your main project CMakeLists.txt you should add something like this:
find_package(anotherProject CONFIG)
if(anotherProject_FOUND)
message(STATUS "Found project dependency: anotherProject")
else
# change WARNING to FATAL_ERROR if the dependency is NOT optional
message(WARNING "package anotherProject was not found")
endif()
On the differences between CONFIG and MODULE modes, check the documentation and this link.
Then assuming that your main project creates an executable, you could hook up the discovered dependency like this:
add_executable(myProject ${SOURCES})
[...]
if(anotherProject_FOUND)
target_link_libraries(myProject PUBLIC anotherProject)
endif()
This should take care of the required include files and definitions as well.
Now in the dependee project CMakeLists.txt you should do something like this:
set(PRJ_NAME "anotherProject")
string(TOLOWER ${PRJ_NAME} PRJ_NAME_LOWER)
set(ANOTHERPROJECT_EXPORT_NAME "${PRJ_NAME}")
install(TARGETS ${PRJ_NAME} EXPORT ${ANOTHERPROJECT_EXPORT_NAME}
RUNTIME DESTINATION .)
install(EXPORT ${ANOTHERPROJECT_EXPORT_NAME} DESTINATION "share/cmake")
This associates an export with a target and then installs the export.
Now, if you check that export file, it expects certain things to be found and included, that could be specific for your project. To make this as supple as possible, you can use the configure feature to generate them from a template and then install from the build directory.
So, in the project under a subdir named share/cmake you could have a file named config.cmake.in with contents:
include(${CMAKE_CURRENT_LIST_DIR}/#PRJ_NAME#.cmake)
In the main project's CMakeLists.txt you need to add the following for generating the file from that template:
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/share/cmake/config.cmake
${CMAKE_CURRENT_BINARY_DIR}/share/cmake/${PRJ_NAME_LOWER}-config.cmake)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/share/
DESTINATION share)
Notice that I used PRJ_NAME, because you could potentially reuse that to name the actual executable at the add_executable command. It mentally helps if the exported target has the same name with produced one.
This is a more versatile version to accommodate multiple subprojects of this tutorial.

preferred cmake project structure

I would like to have the following structure A -> B -> C, where:
C is boilerplate code, wrappers for third-party libraries, very
basic code etc.
B is the common classes, functions and data
structures specific to the project's domain.
A is the project itself.
I would like to make it easy to reuse C or B(+C) in future in my other projects. In addition, I have the following requirements:
As all three projects are in-progress, I would like to have an ability to build C, C+B and C+B+A in one shot.
I would prefer the static linkage over dynamic, so that C and C+B would be static libraries, and C+B+A would be the executable
I would like to keep cmake lists and config files simple and clean. Examples which I found in the official wiki and over the internet are pretty big and monstrous.
It would be great if it won't require changing more than a couple of lines if I'd change the locations of A, B or C in the filesystem.
All these three components are using google-test, but I'm not sure if it is important for the project layout.
I am pretty new to cmake and I don't even understand is it better to write XXXConfig.cmake or FindXXX.cmake files. Also, I am not sure, how should I pass relative paths from subcomponent to the parent component using X_INCLUDE_DIRS.
First I have to admit that I agree with #Tsyvarev. Your CMake environment should fit to your processes/workflow and should take project sizes and team structure into account. Or generally speaking the environment CMake will be used in. And this tends to be - in a positive way - very alive.
So this part of your question is difficult to answer and I'll concentrate on the technical part:
CMake has to know the location of the dependencies - relative or absolute - by
having a monolithic source tree (the one you don't want anymore)
CMake share library with multiple executables
CMake: How to setup Source, Library and CMakeLists.txt dependencies?
a common directory location for includes/libraries/binaries
Custom Directory for CMake Library Output
cmake install not installing libraries on windows
getting the paths via config files/variable definitions
How can I get cmake to find my alternative boost installation?
How to add_custom_command() for the CMake build process itself?
using registration in or installation from a database provided on the host
Making cmake library accessible by other cmake packages automatically
cmake wont run build_command in ExternalProject_Add correctly
To keep your CMake files as simple as possible I would recommend to group your CMake code into separate dedicated files:
Prefer toolchain files over if(SomeCompiler) statements
Move common/repeating code parts as function() bodies into a shared CMake include file
Move complex non-target specific code parts into their own (CMake) script files
Example Code
Since you have specifically asked for the find_package() variant, taking Use CMake-enabled libraries in your CMake project and the things listed above:
MyCommonCode.cmake
cmake_policy(SET CMP0022 NEW)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(
TARGETS ${_target}
FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake"
EXPORT_LINK_INTERFACE_LIBRARIES
)
export(PACKAGE ${_target})
endfunction(my_export_target)
C/CMakeLists.txt
include(MyCommonCode.cmake)
...
my_export_target(C "${CMAKE_CURRENT_SOURCE_DIR}/include")
B/CMakeLists.txt
include(MyCommonCode.cmake)
find_package(C REQUIRED)
...
target_link_libraries(B C)
my_export_target(B "${CMAKE_CURRENT_SOURCE_DIR}/include")
A/CMakeLists.txt
include(MyCommonCode.cmake)
find_package(B REQUIRED)
...
target_link_libraries(A B)
This keeps all 3 build environments separate, only sharing the relatively static MyCommonCode.cmake file. So in this approach I have so far not covered your first point, but would recommend the use of a external script to chain/trigger your build steps for A/B/C.

CMake changing a library include directory based off of calling project

Here is my current project structure:
ProjectX
|_ projs
|_ A ( builds an external project POO version 1 )
add_executable(A ${A_HDRS} ${A_SRCS} $<TARGET_OBJECTS:libbar>)
|_ B ( builds an external project POO version 2 )
add_executable(B ${B_HDRS} ${B_SRCS} $<TARGET_OBJECTS:libbar>)
|
|_ libs
|_ libbar (objects only - no linking)
(needs POO either v1 or v2 depending on calling project)
issue lives here -->INCLUDE_DIRECTORIES(${POO_BUILD_DIR}/include)
add_library(bar OBJECT ${LIB_BAR_HDRS} ${LIB_BAR_SRCS})
GOAL:
I would like to be able to build from the top level including all projects and have libbar to be built with the correct dependencies. I would like to be able to have a continuous build process.
DETAILS:
When project A build builds, it needs to compile libbar with POO version1 and when project B builds it needs to compile libbar wtih POO version2. In either case it is still a version of POO. I would like to be able to pass the path to libbar from A or B.
ISSUE:
The POO_BUILD_DIR path is different depending on the project building libbar. When I build from the top level, make tries to build libbar first and doesn't find the correct includes.
QUESTION:
Does anyone know how to achieve this in cmake or how can I achieve finer grained control over the build order?
UPDATE and SOLUTION:
The problem that I was having could have been solved with Chris's solution below. However, my problem (that I should have realized earlier) is that even though it lives in a library directory and is called a library it's not a library. The naming and directory structure I can't change b/c I don't own the code base. However, I can change how I build it! My quote-unquote solution was to just include the files to the compilation like this: add_executable(A ... ${libbar_hdrs} ${libbar_srcs}
Moral of the Story
If someone tells you it's a library and names it a library - it still may not be. Or expressed in a colloquialism "Don't always believe what you're told".
Well there's lots of ways that you could do that, but one way that I have done it in a cmake C++ project is, for each library (each folder inside of /lib in my project root) I have a secondary Cmake script which builds that library, and I use add_subdirectory within my primary cmake script to invoke those secondary cmake scripts.
Here's an (open source) example from my project: https://github.com/cbeck88/cegui-emscripten
For instance, if you look here you'll see how I do "in tree" (what you are talking about) libs using add_subdirectory, vs. "out of tree" (user provided) libs which I find using find_package. https://github.com/cbeck88/cegui-emscripten/blob/17f0d097f989862035e977a6b9e0b1bbb1fcdf21/CMakeLists.txt#L58
if (FREETYPE_IN_TREE)
add_subdirectory(lib/freetype-2.5.5 freetype-2.5.5)
else()
LIST(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib/freetype-2.5.5-old )
find_package(freetype NO_CMAKE_FIND_ROOT_PATH)
MESSAGE ( STATUS "FREETYPE found = " ${FREETYPE_FOUND} )
endif()
This is what my secondary cmake script (in folder freetype-2.5.5) looks like, it will look very different in your case but at least it's an example. https://github.com/cbeck88/cegui-emscripten/blob/d24fcd6a5dc4697b8718564fadb25d76c255bce2/lib/freetype-2.5.5/CMakeLists.txt
Note especially some of these lines at the very end SET(FREETYPE_INCLUDE_DIRS ${INCL} PARENT_SCOPE) which you might need to use if you do it this way.

QtCreator CMake project - how to show all project files

I use QtCreator to open CMake project. Some directories apart from CMakeLists.txt contains only headers files *.h and for those directories QtCreator in the project tree view shows only CMakeLists.txt. How to fix that ? I need to see all project files from QtCreator.
Viewing project as a file system is not a solution at all cause your project editor settings for example would not apply.
And I do not like to add headers to executable target, cause they do not actually belong there. You effectively cripple the project file to work nicely with one particular IDE... not good.
The cleaner option IMHO would be:
FILE(GLOB_RECURSE LibFiles "include/*.hpp")
add_custom_target(headers SOURCES ${LibFiles})
As a bonus you get your includes shown in a separate folder.
(borrowed from https://cmake.org/pipermail/cmake/2012-August/051811.html)
I would suggest you switching your project view to File System. This would display a view where you could view any file you want:
You might want to split your project view into two by clicking the second to right button, if you still desire the Projects mode.
You should add header files to the list of your source files: add_executable(${Executable} ${Sources} ${headers})
You can use GLOB_RECURSE if have many header files:
FILE(GLOB_RECURSE INC_ALL "headers/*.h")
include_directories("headers")
add_executable(main "main.cpp" ${INC_ALL})
Don't forget to run CMake again (Build>Run Cmake).
Based on another thread asking the same question, I found a generic solution to the problem, working for all IDE's (at least tested with QtCreator and Visual Studio).
Can be found here : https://github.com/sauter-hq/cmake-ide-support
# \brief adds for the given target a fake executable targets which allows all
# headers and symbols to be shown in IDEs.
# \param target_name Which target properties should be added to the IDE support target.
function(target_add_ide_support target_name)
if (NOT TARGET ${target_name})
message(FATAL_ERROR "No target defined with name ${target_name}, cannot target_add_ide_support it.")
endif()
set (target_for_ide "${target_name}_ide_support")
if (NOT TARGET ${target_for_ide})
file(GLOB_RECURSE target_for_ide_srcs "*.h" "*.hpp" "*.hxx" "*.c" "*.cpp" "*.cxx")
add_executable(${target_for_ide} ${target_for_ide_srcs})
set_target_properties(${target_for_ide} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
get_target_property(dirs ${target_name} INCLUDE_DIRECTORIES)
target_include_directories(${target_for_ide} PRIVATE ${dirs})
endfunction(target_add_ide_support)
Usage is then for any targets in the CMakeLists, add the following call (can be made in top-most CMakeLists.txt after all add_subdirectory :
include(add_ide_support.cmake)
target_add_ide_support(some-target)
You can try CMakeProjectManager2. Code to display all files already propagated to upstream as a proof of concept. Concept applied but code can't be applied as-is for some reasons. So, simple wait feature in upstream.
There is a closed bug report about this issue: CMake project shows no files.
In that particular case the issue was with the chosen generator, Ninja, which is not well supported by QtCreator.
Please change that to "CodeBlocks - Ninja". Creator needs the CodeBlocks extra generator.
You should see a warning about that when hovering the kit (and the kit should have a warning icon in front of its name).
Using CodeBlocks - Ninja solved it for me too.
Overall, it may help to try up a few generators...