We have a big project with many modules (shared libs). They have no static dependencies and work more like plugins. We want the build system to be able to use find_package(modA) when building a specific executable (exeA). We also don't want the project files generated to include every module (which will be hard to handle in VisualStudio etc), so it should just include (add_library) the modules needed for the executable built by a specific CMakeLists.txt (exeA).
One way to do this would be to use a helper function to find all Config.cmake files from the project root folder and either add them to CMAKE_PREFIX_PATH or generate _ROOT variables. Then the executable CMakeLists.txt could just do find_package(modX) on any dependencies.
Q: Is there any way to locate all *Config.cmake files in a folder recursively and add them to CMAKE_PREFIX_PATH or _ROOT? Or any helper (except file(GLOB_RECURSE)) that could help in locating these paths that could be used in a more specific helper macro.
Related
I have this project structure:
and I'm trying to set CMakeList.txt files properly. My aim is simply linking the library to main.cpp. So I have these CMakeList.txt files:
library/CMakeList.txt:
source/CMakeList.txt:
root:
When I try to build the project, I get this error:
ld: library not found for -lMyLib
Where is the mistake, and how should I properly organize structure for such projects?
You used find_library yet you did not use ${MyLib} variable. Consult find_library documentation.
You used link_directories, yet it's local to the directory it was used in. Upper directories are unaffected.
Use an IMPORTED library or an INTERFACE library to link with .a file. See https://stackoverflow.com/a/10550334/9072753 and https://stackoverflow.com/a/41909627/9072753 answers. If it's one library only, I think prefer IMPORTED. Move target_include_directories(Source ...) to library/CMakeLists.txt and instead of Source add include directories to your library, it's conceptually the library include directory.
I recommend, subjective, depens: move target_include_directories(Source.. inside source/CMakeList.txt. I.e. keep all Source related stuff inside source/CMakeLists.txt. And maybe add EXCLUDE_FROM_ALL to add_subdirectory(library).
Use set_target_properties instead of set(CMAKE_CXX_STANDARD. Overall, prefer to solely use *target* specific stuff instead of set(...).
I am trying to build an example project that requires dozens of packages contained in different subdirectories, for example
/home/Olumide/src/project/Release/modules/module1/CMakeFiles/Export/lib/cmake/module1
/home/Olumide/src/project/Release/modules/module2/CMakeFiles/Export/lib/cmake/module2
/home/Olumide/src/project/Release/modules/module3/CMakeFiles/Export/lib/cmake/module3
...
Given that the application that I'm trying to build is in the location /home/Olumide/src/project/applications/tutorial, is it possible to build the application without explicitly specifying the paths to all each package that the project requires, or modifying the CMakeLists.txt file, for example by specify a common root path to be searched for all the packages, for example:
cmake -DCMAKE_MODULE_PATH=/home/Olumide/src/project/Release/modules .
Unfortunately this does not work and cmake complains that it cannot find package configuration files with the names Modul1eConfig.cmake or module1-config.cmake etc.
If all of the modules adhere tho this exact pattern you should be able to provide the "root path" via cache variable and pass a the info where to look for the libs to find_package via PATHS parameter:
# maybe a more reasonable default here?
set(MYPROJECT_MODULE_PATH "/home/Olumide/src/project/Release/modules" CACHE PATH "the path to the modules we're looking for")
set(MYPROJECT_MODULE_NAMES
module1
module2
...
)
foreach(_MODULE IN LISTS MYPROJECT_MODULE_NAMES)
find_package(${_MODULE} REQUIRED CONFIG
PATHS "${MYPROJECT_MODULE_PATH}/${_MODULE}/CMakeFiles/Export"
)
endforeach()
In general if these packages logically belong to the same software package, you should provide a way of finding those modules by passing them as components for find_package but since this seems to be code not under your own control you may not have that luxury. (E.g. boost provides this functionality: find_package(boost COMPONENTS test mpl ...).) However it may be worth some investigation if there is a configuration script somewhere that's supposed to automatically include the modules, if listed as components, e.g. somewhere in Release or Release/modules.
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.
My colleague wrote a CMakeLists.txt, which contains things as below:
find_package(OpenCV 3 REQUIRED
COMPONENTS
opencv_core
opencv_imgproc
opencv_imgcodecs
CONFIG
)
As the project needs these components of Opencv3, my colleague downloaded the whole Opencv3, of course, it works.
But the whole Opencv3 is too big, so I get only the necessary lib files: libopencv_core.so, libopencv_imgproc.so and libopencv_imgcodecs.so and try to replace the whole Opencv3. The three so files are put here: /opt/opencv3/.
I don't know how to tell the CMakeLists.txt to look for the components of Opencv3 at the specific path instead of the path by default.
I'm totally a newbie on writing CMakeLists.txt...
CMake find_package() finds and configure project dependencies in CMake using two modes of operation, Module and Config, as described in its documentation.
In Module mode (not your case), it looks for a Find<package>.cmake file inside CMAKE_MODULE_PATH. This file searches for header files and libraries and set the necessary CMake variables in case of success.
In your case it is using Config, as requested by the CONFIG keyword. In Config mode CMake looks for for a <name>Config.cmake file or <lowercase-name>-config.cmake.
These config files describe the version of the dependency and the location of header files and library modules.
So you should look for OpenCVConfig.cmake or opencv-config.cmake in CMAKE_PREFIX_PATH or in OpenCV_DIR.
Please note that you have to set(OpenCV_DIR ...) before calling find_package().
I have a problem building multiple Fortran-90+ executable targets using CMake, when I use Fortran modules that are shared by all executables, and build in parallel using make -j. The issue seems to be that the compiled object files are placed in different subdirectories for each target, CMakeFiles/targetName.dir/src/file.f90.o, while the module files are placed in the same directory for every target (I can change this directory by setting Fortran_MODULE_DIRECTORY, but it will still be the same dir for all module files). The problem is then that all targets start writing these module files in parallel, and I get a
Fatal Error: Can't rename module file 'module.mod0' to 'module.mod': No such file or directory
when using gfortran (which apparently creates a .mod0 file and then renames it to .mod). The problem does not arise when I issue make without the -j option (serial build).
I can see two solutions, but I don't know how to implement them. Firstly, put the object files for all targets in the same directory rather than target-specific directories. This may be the preferred option, since I won't have to compile the shared source files N times for N targets. The make process will then recognise that the object files exist, and not compile the corresponding source files again, hence not touching the .mod(0) files again (I may need to make all following targets depend on the first).
The second solution would be to put the .mod(0) files in the target-specific directories, so that they are not overwritten or removed by the other targets. This would solve my problem, even though it would still involve more compiling than necessary. I have no idea how to accomplish either option, so any hint there or alternative solution is welcome.
The answer I was looking for was provided in the comments to the question by #RaulLaasner:
I would create an additional target in the form of a core library of the relevant source files, which can then be linked to all executables. This should work in parallel. The mod files can still be in put into a single directory.
I used add_library() and target_link_libraries() to achieve this.
Note that e.g. Gentoo ebuild scripts add --as-needed to the linker, which may cause undefined references in your core library when you link it and external libraries to form the executable. To prevent this, make sure you link the external libraries to your core library first. To this end, my CMakeListst.txt contains:
add_library( "Core" STATIC src/functions.f90 src/routines.f90 ) # creates libCore.a
target_link_libraries( Core ${EXTERNAL_LIBRARIES} ) # link external libraries to libCore.a
...
add_executable( myProgram1 src/myProgram1.f90 ) # creates the first executable
target_link_libraries( myProgram1 Core ) # links libCore.a to myProgram1
The last two lines can be repeated to build the other executables (e.g. using foreach()).