CMake and external dependency - c++

I'd like to add an external dependency to my project. The one I'm trying to add is the Leptonica library as a submodule.
My project has the following directory structure:
|root
CMakeLists.txt
|-bin
|-build
|-buildsystem
|-executable
|-leptonica
|--CMakeLists.txt
|--cmake
|---Configure.cmake
|-production
In my root CMakeLists.txt file I added ADD_SUBDIRECTORY(${ROOT_DIR}/leptonica)
Unfortunately, CMake is not searching for Configure.cmake in the proper directory:
CMake Error at leptonica/CMakeLists.txt:107 (include):
include could not find load file:
Configure
CMake Error: File
<root>/cmake/templates/LeptonicaConfig-version.cmake.in does not exist.
CMake Error at leptonica/CMakeLists.txt:113 (configure_file):
configure_file Problem configuring file
When I build the project by myself, everything goes fine. In my opinion, the problem is with CMAKE_SOURCE_DIR. When using add_subdirectory it has the value of ROOT CMake instead ROOT/leptonica, so it's searching the wrong paths - as you can see in Leptonica CMake, it's used to determinate paths of its files.
What should be the proper way to fix this - should I set CMAKE_SOURCE_DIR to ROOT/leptonica just before calling add_subdirectory and set it back when it's finished, or does some other, more elegant solutions exist?

Not every CMake project is suitable for inclusion via add_subdirectory.
Among those are projects which uses CMAKE_SOURCE_DIR or CMAKE_BINARY_DIR variables.
However, inclusion via ExternalProject_Add (optionally wrapped with execute_process) always works.
Modifying variable CMAKE_SOURCE_DIR (and CMAKE_BINARY_DIR too) is a bad idea: this variable should be changed only by CMake itself. Otherwise you may get weird errors.
Instead, you may replace (automatically, with some script) all references to the variable with another variable, which is not used in the project. This new variable you may safely set before stepping into the subproject.

${CMAKE_SOURCE_DIR} and ${CMAKE_BINARY_DIR} are set relative to the top-level CMakeLists.txt. If you need something relative to your current CMakeLists.txt (leptonica), use ${CMAKE_CURRENT_SOURCE_DIR} and ${CMAKE_CURRENT_BINARY_DIR}.
If you're having trouble finding a cmake file like LeptonicaConfig-version.cmake.in, try appending the appropriate directory to ${CMAKE_MODULE_DIR}.
list(APPEND ${CMAKE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates)
I prefer to use ${CMAKE_CURRENT_SOURCE_DIR} over ${CMAKE_SOURCE_DIR} any day because using the latter will break your build if you try to integrate it into a super-build later. If I need to pass my current top-level directory to subdirectories, then I do the following and use that later down the chain.
set( LEPTONICA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )

Related

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)

CMake not building a library when added as a subdirectory

I added the xgboost library as a git submodule of my project and I'm trying to add it to cmake as a subdirectory. Unfortunately it's not working. A simple hello world project with the following CMakeLists.txt replicates the error that I'm getting.
cmake_minimum_required(VERSION 3.2)
project(foo)
add_subdirectory(xgboost)
add_executable(${PROJECT_NAME} foo.cpp)
target_link_libraries(${PROJECT_NAME} xgboost)
After building the library there is nothing in the xgboost/lib directory so I get the following error.
clang: error: no such file or directory:
'/Users/.../myproject/xgboost/lib/libxgboost.dylib'
I think that the problem is generated in their CMakeLists file since they have two different targets. Maybe cmake is choosing the wrong target but I'm not familiar enough with cmake to figure it out. The following code is from xgboost's CMakeLists.
# Executable
add_executable(runxgboost $<TARGET_OBJECTS:objxgboost> src/cli_main.cc)
set_target_properties(runxgboost PROPERTIES
OUTPUT_NAME xgboost
)
set_output_directory(runxgboost ${PROJECT_SOURCE_DIR})
target_link_libraries(runxgboost ${LINK_LIBRARIES})
# Shared library
add_library(xgboost SHARED $<TARGET_OBJECTS:objxgboost>)
target_link_libraries(xgboost ${LINK_LIBRARIES})
set_output_directory(xgboost ${PROJECT_SOURCE_DIR}/lib)
#Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
add_dependencies(xgboost runxgboost)
My questions in order of importance are:
Is there any way to fix it without modifying xgboost's CMakeLists.txt file?
Is it reasonable to try to add xgboost to my project as a git submodule?
Is there any reason cmake is not instructing to build the library?
Note: There were several edits to this question since I tried to narrow down the problem and to provide more information.
(I would love to ask for few things beforehand in the comment section, but I have too low reputation to do so, so I will just give it a shot ;))
I have few suspects, and one of them is ${CMAKE_SOURCE_DIR} of the submodule's root CMakeLists.txt. Although the paths are set properly when you run that CMakeLists.txt alone, cmake gets confused the moment you add it as your subdirectory. Have you looked into another directories for your output binaries?
First I would suggest testing this hypothesis, and then I would suggest writing similar, but separate CMakeLists.txt file for xgboost library, and then substitute it in the project temporarily. Unfortunately the CMakeLists.txt filename is hardcoded and there is no possibility to have two files of that kind in one directory; so it seems that the answer to 1) is, that you rather have to change the file.
For the 2): as long as it does not require huge additional logic in your CMakeLists.txt, it makes sense. Other viable option is to create an install target, which you can use to install your xgboost library locally (using CMAKE_INSTALL_PREFIX(doc) variable), and then add the installation path to your CMAKE_LIBRARY_PATH(doc).

CMake install (TARGETS in subdirectories)

Consider the following CMakeLists.txt file:
add_subdirectory(execA)
add_subdirectory(libB)
install(TARGETS execA libB
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
I get the following error:
install TARGETS given target "execA" which does not exist in this
directory
execA and libB have their own CMakeList.txt files and are located under project directory, as well as the build directory I'm running cmake (cmake ..):
project
|------ CMakeList.txt (the one with the code)
|----execA
| \- .cpp, .hpp and CMakelist.txt
|----libB
| \- .cpp, .hpp and CMakelist.txt
|---- lib
|---- bin
\---- build (where I´m commanding: $ cmake ..
How do I fix this error?
According to this bugreport, install(TARGETS) command flow accepts only targets created within the same directory.
So you need either move the add_library() call into the top-level directory, or split install(TARGETS) call into per-target ones, and move each of them into the corresponding subdirectory.
Since CMake 3.13 install(TARGETS) can work even with targets created in other directories.
install(TARGETS) can install targets that were created in other directories. When using such cross-directory install rules, running make install (or similar) from a subdirectory will not guarantee that targets from other directories are up-to-date.
Even though it would help seeing the CMakeLists.txt files contained in the subdirectories, I guess they contain add_executable and/or add_library statements to create your stuff.
Also, because of your example, I guess you are using the same name of your directories for your targets.
That said, you should know that symbols defined in a CMakeLists.txt file in a subdirectory are not visible by default within the context of the CMakeLists.txt file in the parent directory. Because of that, you should rather move your install statements within the CMakeLists.txt files within your subdirectories.
This should solve the problem, if my thoughts were right. Otherwise, I strongly suggest you to post in your question also the content of the other files above mentioned.
Anyway, the error is quite clear.
The file that contains the install statement for the target named X does not contain a target creation statement (add_executable and the others) that gives birth to that target, so it goes on saying that that target does not exist in that directory.
This still seems to be a pain point in CMake 3.11.
In our codebase, we have many targets defined in subdirectories and need to create an assortment of installers with different configurations and (potentially overlapping) combinations of targets.
Here's my solution:
Before calling add_subdirectory in your root CMakeLists.txt file, create a GLOBAL property with the names of the target(s) you want to include in your installer.
Wrap target creation functions (add_executable, etc.) in your own custom functions. Within those functions check if the target is present in the global property, and invoke install accordingly.
That approach allows you to centralize installer configuration.
Also: To support creation of multiple installers, we populate our global list along with other installer properties in separate .cmake files. When we invoke cmake, we pass the name of the installer configuration CMake file as a command-line argument. Our root CMakeLists.txt file simply calls include with that file.

Keeping file hierarchy across subdirectories in CMake

Till date I still do not really understand what the 'best practice' is for doing this for a CMake project with many subdirectories.
Say I have a project hierarchy as such and each subdirectory has source files in it...
--CMake Project Source dir
|-- SubD1
|-- SubSubD1
|-- SubD2
What I would usually do is to do add_subdirectory(SubD1) and respectively for D2 in the CMakeLists.txt of the root directory and recursively for the subdirectory in the CMakeLists.txt of the SubD1 directory, while declaring variables in each subdirectory and making them visible in the root directory with PARENT_SCOPE.
That means if a file Source2.cpp exists in `SubSubD1', I'd simply do
set(SUBSUBD1_SOURCES Source2.cpp PARENT_SCOPE)
and expect to be able to use SUBSUBD1_SOURCE in my SubD1 directory.
Subsequently, say Source.cpp exists in SubD1, I would do
set(SUBD1_SOURCES ${SUBSUBD1_SOURCES} Source.cpp PARENT_SCOPE)
so that all sources would be visible in root dir.
The problem is of course that the file paths aren't kept when the variables arrive at the root directory. What I'm currently doing is for all source files that I set, I include a ${CMAKE_CURRENT_LIST_DIR}, making it
set(SUBSUBD1_SOURCES ${CMAKE_CURRENT_LIST_DIR}/Source2.cpp PARENT_SCOPE)
and
set(SUBD1_SOURCES ${SUBSUBD1_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/Source.cpp PARENT_SCOPE)
In this case, I could then say, do add_executable(myProg SUBSUBD1_SOURCES) in the root directory of my CMake project.
Are there any better ways of doing this then having to always include a CMake variable in front of all source files?
There is a fourth way if you're using newer versions of CMake.
Take a look at target_sources() command of CMake.
It seems like you are declaring your target in your CMakeLists.txt
add_executable(my_target "subd1/CMakeLists.txt" "subd2/CMakeLists.txt")
add_subdirectory(subd1)
add_subdirectory(subd2)
Instead of propagating your Source files up to the root you can depend on the target you have defined in the root CMakeLists.txt. That means subd1/CMakeLists.txt may look like:
target_sources(my_target PRIVATE "subd1/Source.cpp" "subd1/Source2.cpp")
[EDIT]
As stated in the comments you must give the relative path of the source-files to target_sources(). I use target_sources() because I do not want the explicit source file listing to pollute the targets CMakeLists.txt. Another use case is that target_sources() can be invoked with the PUBLIC or INTERFACE keyword to propagate source files to depending targets. Well I never used target_sources() that way.
[/EDIT]
If you're using IDEs like Visual Studio that support folders you make want to also declare a source_group() in the CMakeLists.txt that contains your target. So the root CMakeLists.txt may look like:
add_executable(my_target "subd1/CMakeLists.txt" "subd2/CMakeLists.txt")
add_subdirectory(subd1)
add_subdirectory(subd2)
...
source_group(subd1 REGULAR_EXPRESSION "subd1/*")
source_group(subd2 REGULAR_EXPRESSION "subd2/*")
I'm using this approach because it leads to much cleaner CMakeLists.txt files, its lesser work and I think the introduction of not needed variables only raises the complexity of your CMakeLists.txt files.
CMakeLists.txt as target sources
I currently use the CMakeLists.txt of the sub folders as source files of the target because otherwise CMake will complain that the add_executable command has no source files given.
There are 3 ways I have used before. I normally prefer the 1st way, but have already used all 3 depending on the use case:
1. You directly name the sources in your root CMakeLists.txt file
set(
SUBD1_SOURCES
"SubD1/SubSubD1/Source2.cpp"
"SubD1/Source.cpp"
)
set(
SUBD2_SOURCES
"SubD2/Source3.cpp"
)
add_executable(myProg ${SUBD1_SOURCES} ${SUBD2_SOURCES})
2. You use OBJECT intermediate libraries to collect/group your sources
SubD1/SubSubD1/CMakeLists.txt:
add_library(SubSubD1Objs OBJECT Source2.cpp)
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
add_library(SubD1Objs OBJECT Source.cpp)
CMakeLists.txt:
add_executable(myProg $<TARGET_OBJECTS:SubSubD1Objs> $<TARGET_OBJECTS:SubD1Objs>)
3. You write your own function() to collect the data (and do the prefixing)
CMakeLists.txt:
function(my_collect_sources)
foreach(_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
set(source_abs "${_source}")
else()
get_filename_component(_source_abs "${_source}" ABSOLUTE)
endif()
set_property(GLOBAL APPEND PROPERTY GlobalSourceList "${_source_abs}")
endforeach()
endfunction(my_collect_sources)
add_subdirectory(SubD1)
#add_subdirectory(SubD2)
get_property(MY_SOURCES GLOBAL PROPERTY GlobalSourceList)
add_executable(myProg ${MY_SOURCES})
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
my_collect_sources(Source.cpp)
SubD1/SubSubD1/CMakeLists.txt:
my_collect_sources(Source2.cpp)
In your case there's no need to use add_subdirectory since you have only one target which is created in the root CMakeLists.txt. You can simply write this:
add_executable(myProg
SubD1/Source.cpp
SubD1/SubSubD1/Source2.cpp)
Use add_subdirectory for subdirectories creating their own targets so there's no information to pass upwards.

adding non standard library to cmake using FindLibrary.cmake

I want to use a tree parsing library (NHParser) in my code. I have make-installed the library in /home/ikram/parser (with include files in /home/ikram/parser/include/NHparser/ and lib files in /home/ikram/parser/lib/ directory).
I am wondering if, in the first place, I can just add the library and include file in my main CMakeLists.txt file without resorting to FindNHPARSER.cmake script? If yes, how I may achieve that?
I tried the above but couldn't succeed so I wrote FindNHPARSER.cmake like the following.
find_path(NHPARSER_INCLUDE_DIR include/NHparser/NHparser.h
HINTS PATH_SUFFIXES NHparser)
find_library(NHPARSER_LIBRARY NAMES NHparser)
set(NHparser_LIBRARIES ${NHparser_LIBRARY})
set(NHparser_INCLUDE_DIRS ${NHparser_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
mark_as_advanced(NHparser_INCLUDE_DIR NHparser_LIBRARY )
Is it correctly written? How I can include this in my main CMakeLists.txt script and do I need to set CMAKE_MODULE_PATH while running cmake?
Thanks for your time.
For such a small (fairly standard) find process, I'd just add the code directly to the main CMakeLists.txt.
You can move it to a separate .cmake file if you prefer - then you'd just include that file at the appropriate point in your main CMakeLists.txt.
As for the code, I'd keep it pretty simple; essentially just a find_path and find_library call along with checks that the relevant things were actually found:
find_path(NHPARSER_INCLUDE_DIR NAMES NHparser.h PATH_SUFFIXES NHparser)
if(NOT NHPARSER_INCLUDE_DIR)
message(FATAL_ERROR "Didn't find NHparser.h")
endif()
find_library(NHPARSER_LIBRARY NAMES NHparser)
if(NOT NHPARSER_LIBRARY)
message(FATAL_ERROR "Didn't find NHparser library")
endif()
The you can use the variables like:
include_directories(${NHPARSER_INCLUDE_DIR})
...
target_link_libraries(MyExe ${NHPARSER_LIBRARY})
The variables are unlikely to be found unaided though, since the install prefix for this library on your machine seems to be "/home/ikram/parser". Since this location is probably specific to your own machine, I wouldn't hard-code that into your CMakeLists.txt.
Instead, to have CMake add that path to the list of default search paths, just set CMAKE_PREFIX_PATH when you invoke CMake:
cmake . -DCMAKE_PREFIX_PATH=/home/ikram/parser