CMake changing a library include directory based off of calling project - c++

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.

Related

CMake with 3rd party libraries that need to be built along with the project

I am confused on the right way to get an external library integrated into my own Cmake project (This external project needs to be built along with my project, it's not installed separately, so we can't use find_library, or so I think)
Let's assume we have a project structure like this (simplified for this post):
my_proj/
--CMakeLists.txt
--src/
+---CMakeLists.txt
+---my_server.cpp
That is, we have a master CMakeLists.txt that basically sits at root and invokes CMakeLists for sub directories. Obviously, in this example, because its simplified, I'm not showing all the other files/directories.
I now want to include another C++ GitHub project in my build, which happens to be this C++ bycrypt implementation: https://github.com/trusch/libbcrypt
My goal:
While building my_server.cpp via its make process, I'd like to include the header files for bcrypt and link with its library.
What I've done so far:
- I added a git module for this external library at my project root:
[submodule "third_party/bcrypt"]
path = third_party/bcrypt
url = https://github.com/trusch/libbcrypt
So now, when I checkout my project and do a submodule update, it pulls down bcrypt to ${PROJ_ROOT}/third_party
Next up, I added this to my ROOT CMakeLists.txt
# Process subdirectories
add_subdirectory(third_party/bcrypt)
add_subdirectory(src/)
Great. I know see when I invoke cmake from root, it builds bcrypt inside third_party. And then it builds my src/ directory. The reason I do this is I assume this is the best way to make sure the bcrypt library is ready before my src directory is built.
Questions:
a) Now how do I correctly get the include header path and the library location of this built library into the CMakeLists.txt file inside src/ ? Should I be hardcoding #include "../third_party/bcrypt/include/bcrypt/bcrypt.h" into my_server.cpp and -L ../third_party/libcrypt.so into src/CMakeLists.txt or is there a better way? This is what I've done today and it works, but it looks odd
I have, in src/CMakeLists.txt
set(BCRYPT_LIB,"../third_party/bcrypt/libbcrypt.so")
target_link_libraries(my app ${MY_OTHERLIBS} ${BCRYPT_LIB})
b) Is my approach of relying on sequence of add_directory correct?
Thank you.
The best approach depends on what the bcrypt CMake files are providing you, but it sounds like you want to use find_package, rather than hard-coding the paths. Check out this answer, but there are a few different configurations for find_package: MODULE and CONFIG mode.
If bcrypt builds, and one of the following files gets created for you:
FindBcrypt.cmake
bcrypt-config.cmake
BcryptConfig.cmake
that might give you an idea for which find_package configuration to use. I suggest you check out the documentation for find_package, and look closely at how the search procedure is set up to determine how CMake is searching for bcrypt.

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.

CMake make add_library depend on ExternalProject_Add

TLDR: I would like to ask CMake to wait for ExternalProject_Add to complete before it attempts to move on to the next subdirectory and build a library that happens to use one of the files of the external project. In other words, I like to declare an external project as a dependency for a shared library.
More Description:
Suppose that my CMake project has two directories: thirdparty and src. My top-level CMakeLists.txt file has:
add_subdirectory(thirdparty)
add_subdirectory(src)
thirdparty/CMakeLists.txt contains multiple ExternalProject_Add commands. My original intention was to pull and build all these external projects and then move on to building my own libraries and executables in src. Unfortunately, this didn't go as I planned:
One of my external projects is called libsvm. And my src/CMakeLists.txt has the following:
set(Libsvm_SOURCE_FILES
${PROJECT_BINARY_DIR}/thirdparty/libsvm/src/libsvm/svm.cpp
)
include_directories(
${Libsvm_INCLUDE_DIR}
)
add_library(
mysvm
SHARED
${Libsvm_SOURCE_FILES}
)
Now the problem I am facing with is that CMake is unable to find ${Libsvm_SOURCE_FILES}, apparently because this step is being executed before the ExternalProject_Add in my thirdparty/CMakeLists.txt file is executed.
I would like to declare this external project as a dependency for this library.
Broader Question:
Is there a clean way to force CMake to finish everything in first subdirectory before moving on to the next? If not, do you recommend that I make any change in the hierarchy and organization of my CMakeLists files?
Thanks!
CMake expects every source file, passed to add_library or add_executable, to be existed unless it is marked as GENERATED. This property is automatically set for files listed as OUTPUT for add_custom_command. In other cases one need to set this property explicitly:
set_source_files_properties(${Libsvm_SOURCE_FILES} PROPERTIES GENERATED TRUE)

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: build target library if any of the executable targets needs it

The layout of my project is as follow :
src/
include/
include1.h
include2.h
include3.h
lib/
lib1/
source1_lib1.c
source2_lib1.c
lib2/
source1_lib2.c
source2_lib2.c
source3_lib2.c
lib3/
source1_lib3.c
lib4/
source1_lib4.c
source2_lib4.c
module_A/ (this module will need lib1 and lib4)
source1_moduleA.c
source2_moduleA.c
module_B/ (this module will need lib2 and lib3)
source1_moduleB.c
source2_moduleB.c
source3_moduleB.c
module_C/ (this module will need lib1, lib2, lib3 and lib4)
source1_moduleC.c
module_D/ (this module will need lib1 and lib3)
source1_moduleD.c
source2_moduleD.c
source3_moduleD.c
source4_moduleD.c
The global solution can be made by any number of module_X (it depends on the customer)
My project CMake file located under "/src" includes a configuration file (it is defined by a customer needs). This configuration file indicates which modules must be built and packaged to the target customer.
Let's say I have a customer X and the modules he selected are module_A and module_D. In this case my build system should only builds lib_1, lib_3 and lib_4.
What I am looking for is a way to define target libraries without being built until I do reference them in one the CMakeLists files under module_X directories.
Oh my bad I missed to say A BIG THANKS FOR YOUR HELP
CMake can not do this out of the box. Any library that is added through an add_library call will be built.
But you can implement the behavior you want inside the CMake. Instead of calling add_library and add_executable directly you would call custom wrapper functions. The add_library wrapper simply stores the information required to call add_library, but does not call it directly. The add_executable wrapper iterates over all dependencies of the executable and makes the call to add_library if required.
You will have to design your own system for maintaining the state whether a particular library has already been added and what the parameters are for constructing the library target. Things get slightly more difficult when respecting transient dependencies as well (module_1 depends on lib_a which in turn depends on lib_b; but no executable depends on lib_b directly). But it is perfectly possible to build such a system with a few hundred lines of CMake code.
cmake ..
make module_A module_D