I'm sorry if this is duplicate. I see a lot of post that are similar, but I am either not "getting" it or it is not exactly the same issue.
I need to build two shared libraries. They are in the same directory structure, and they both get but in to bin. One is dependent on the other.
I've been reading up on add_dependencies, but that seems to deal with project builds outside of cmake. These are both inside
Directory structure
src
CMakeLists.txt
bin
xml
CMakeLists.txt
XML.c
utils.c
XMLSPCL.c
src/CMakelists.txt
set (BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bin")
add_subdirectory(xml/shrxml)
xml/CMakeLists.txt (AFTER the suggestion below)
set(CMAKE_BUILD_TYPE Release)
project(shrxml)
add_library(shrxml SHARED XML.c utils.c)
install(TARGETS shrxml LIBRARY DESTINATION ${BIN_DIR})
project(shrxmlSPCL)
add_dependencies(shrxmlSPCL shrxml)
add_library(shrxmlSPCL SHARED XMLSPCL.c)
target_link_libraries(shrxmlSPCL LINK_PUBLIC shrxml)
install(TARGETS shrxmlSPCL LIBRARY DESTINATION ${BIN_DIR})
I can get it to work, but I know there has to be a correct way to do this.
To get it to work, I:
comment out the xmlSPCL project
cmake ..
make shrxml
run make install so I can get shrxml.so in to the bin directory
un-comment out xmlSPCL
make (again)
make install (again)
What I want is to run make once, have the shrxml.so go into the bin dir so that when shrxmlSPCL is linked, it sees the dependent lib. Can that be done with the extra steps?
to make an example project run I had to make 3 things different
1.
target_link_libraries(shrxmlSPCL LINK_PUBLIC shrxml)
-> So neither lib nor .so as prefix/suffix
2.
add_dependencies(shrxmlSPCL shrxml)
-> somewhere below the add_library() having the dependency
3.
install(TARGETS shrxmlSPCL LIBRARY DESTINATION ${BIN_DIR})
-> add LIBRARY into your install command doc
Related
There is a static library called revolta which is being built and then installed into a sysroot:
set( CMAKE_INSTALL_PREFIX <path to sysroot> )
# ReVolta c++ library name
set( TARGET_LIBREVOLTA "revolta" )
add_library( ${TARGET_LIBREVOLTA} STATIC )
target_include_directories( ${TARGET_LIBREVOLTA}
PUBLIC
# Once the librevolta targets are being exported, this include directory in which the lib is installed is used
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
target_sources( ${TARGET_LIBREVOLTA}
PUBLIC
...
)
Later then once the librevolta is built, it is installed into the sys root using:
# Install all the revolta headers into include directory and copy the built library
install( TARGETS ${TARGET_LIBREVOLTA} EXPORT ${TARGET_LIBREVOLTA}
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
and the connected custom command:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
So far so good. This works as intended, once CMake builds the "revolta" target, it is built and installed into the sysroot as installed using the ${CMAKE_INSTALL_PREFIX}.
My problem is once I try to add the target as the linked one in other lib/executable, it includes somehow automatically the librevolta source path into includes and links the library using the relative path in the build directory rather than the one installed into sysroot as performed in the step right after the librevolta build.
Some other lib/executable:
target_link_libraries( ${APP_EXECUTABLE}
PRIVATE
revolta
)
Once being built, the include path -I/home/martin/git/revolta/source/librevolta is added (the source location) even though it is stated as PRIVATE in the snipped above:
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
and only the ${CMAKE_INSTALL_PREFIX}/include is made public...
Additionally, the library is taken from the build tree rather than from the location where it is installed:
../../librevolta/librevolta.a
instead of
/home/martin/git/revolta/sysroot/lib/librevolta.a
Could you please advice me how to correctly set the revolta target the way it correctly uses its sources for building itself but once used elsewhere it provides the sysroot installed headers and built library from the same location (respecting the standard locations)?
HINT: I also tried to remove the revolta target from the app completely, specifying only to use the sys root (gcc option --sysroot=/home/martin/git/revolta/sysroot), it works fine correct headers and lib is used BUT once the librevolta is not built and installed, the target is not run prior to app build as the dependency is not defined then...
TL;DR: You need to do what's done here:
How to create a ProjectConfig.cmake file
I see a few issues with these CMakeLists.txt files but they aren't related to your problem, because if I understand correctly what you are trying to do here, then there is no problem and it is used as intended.
Let me clarify:
You have a library project that has it's own CMakeLists.txt, where you define the target revolta
You have an executable project that has it's own CMakeLists.txt, where you define your executable target and then you add the revolta target via add_subdirectory() and target_link_libraries(my_executable revolta)
If that's the case then this is just bad:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
Forcing your build to automatically install this library is not the way to go, ever (you for example, need elevated privileges to build it in the first place, because of this command and that poses a security risk).
That being said what is happening is perfectly fine, because from the perspective of the my_executable's CMakeLists.txt you are still building i.e. you use the BUILD_INTERFACE. It is however something you do not want to do.
What instead you want to do is:
Create generator files for a revoltaConfig.cmake file. For that I will refer you to this tutorial:
How to create a ProjectConfig.cmake file
After you create such file, i.e. after building and installing revolta. You will (in the process) also create a revoltaConfig.cmake file. Which helps you populate the my_executable project via find_package(revolta).
The above is probably what you are interested in.
The generator expressions that you use to distinguish BUILD_INTERFACE and INSTALL_INTERFACE are mainly for header file locations (or other linked libraries). Because when you build the library the header files can have a different structure then when you install it (as you already know). And as such work perfectly fine in your CMakeLists.txt, because when you think about it:
You don't want to copy changes to your library files (into the install directory) just to test ongoing development (features/bugfixes) in your executable.
And during the build of the executable if your building another target then IT IS NOT INSTALLED but rather BEING BUILT. And you are most likely adding it as a built target.
So to sum up what would most likely happen here (using your old CMakeLists.txt) is that
The moment you start building the executable which adds the target library as a dependency via add_subdirectory you are implicitly using BUILD_INTERFACE because you are building.
If you were to then install both the executable and the library it would again use the correct install paths, i.e. you would then implicitly start using INSTALL_INTERFACE.
You could hack it without the projectConfig file using the same generator expressions by mixing them up, but I don't recommend it, because then the CMakeLists.txt wouldn't work without doing some weird steps beforehand.
I want anyone who cloned the repository can build it immediately, and don't need to install the dependencies.
Therefore, I found several ways:
Use git submodule and add_subdirectory.
Use find_package to find the built libraries and the headers.
The first one takes much time to build, so I think the second might be better. To make people be able to build the project instantly, I put the the files in the project, but it saied it doesn't know the linker language. What's this? And how to solve?
Direstories:
Project Root
lib
SDL2
(generated files when install)
include
(headers)
src
(sources)
CMakeLists.txt
CMakeLists.txt:
# ...
list(APPEND CMAKE_PREFIX_PATH lib)
find_package(SDL2)
# ...
I am new to cmake, I know a lot of similar questions are asked, but I don't seem to figure out how to do this.
I have very large project which can be simplified with this structure:
Parent_dir
--src
--main1.cpp
--main2.cpp.
--include
--lib_dir_1
--f1.cpp
--f1.h
--lib_dir_2
--f2.cpp
--f2.h
--build
--install
--include
--share
--lib
--bin
I do not have access to /usr/include, /usr/lib, /usr/bin, and /usr/share because it is an account on a cluster. My idea was to run cmake in build dir and install library .o and executives in install dir.
My CMakeFile.txt are:
Parent_dir/CMakeFile.txt
cmake_minimum_required(VERSION 3.13)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
project(MyProject VERSION 2.0)
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(CMAKE_CXX_STANDARD 17)
add_subdirectory("include/")
add_subdirectory(src)
Parent_dir/include/CMakeFile.txt
add_subdirectory(lib_dir1)
add_subdirectory(lib_dir2)
Parent_dir/src/CMakeFile.txt
add_executable(M1 Main1.cpp)
target_include_directories(M1 PUBLIC "${CMAKE_SOURCE_DIR}/include/lib_dir1")
find_library(LIB_1 lib1)
target_link_libraries(M1 lib1)
install (TARGETS M1 DESTINATION ${CMAKE_INSTALL_PREFIX})
#add_executable(M2 Main2.cpp)
#target_include_directories(M2 PUBLIC "${CMAKE_SOURCE_DIR}/include/lib_dir2")
#find_library(LIB_2 lib2)
#target_link_libraries(M2 lib2)
#install (TARGETS M2 DESTINATION ${CMAKE_INSTALL_PREFIX})
Parent_dir/include/lib_dir1/CMakeFile.txt
add_library(lib1 SHARED f1.cpp f1.h)
FIND_PACKAGE(Boost COMPONENTS program_options REQUIRED)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(lib1 ${Boost_LIBRARIES})
target_include_directories(lib1 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
Parent_dir/include/lib_dir2/CMakeFile.txt
add_library(lib2 SHARED f2.cpp f2.h)
target_include_directories(lib2 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
find_library(LIB1 lib1)
target_link_libraries(lib2 lib1)
At the moment I am only testing building M1 that depends on lib1, I commented stuff referring to M2 executable. I know that find_library(LIB1 lib1) should find lib1 library and put its path to LIB1 variable, so clearly I am making a mistake when targeting and linking because for example in Parent_dir/src/CMakeFile.txtit should be target_link_libraries(M1 ${LIB1}), but I am getting an error: `The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
LIB_1.
So find_library never found lib1, if I link as in files above, cmake is fine, make is fine, make install is fine, and in install directory I see M1 executable which does not work when used stating that error while loading shared libraries: liblib1.so: cannot open shared object file:...
So clearly I messed something up and I've been running in circles for two days. Biggest issue with online resources is that a lot of tutorials don't cover the issue how to navigate installation to custom dirs. Note that I have built other packages with typical configure --prefix approaches before and my desire to have install/include share lib bin directories is simply due to previous experience, in CMakeFiles above I only tried to navigate executable M1 to install dir, it worked, but I have no idea where lib1.so is built and whether I messed everything in CmakeFiles.
Also I am not sure if the code structure I am making is "good", perhaps I should move lib1_dir and lib2_dir to src? Problem is that in these libraries I have 100s of files so the structure above made most sense.
PS:
I do not have cmake-gui, cluster has 3.15 and 2.18 version and gui only exists for 2.18 version.
UPDATE:
Managed to get installation to work (check answer below), but some issues still remain:
Executables are now in install/bin, library files are in install/lib. I am just not sure if everything is linked properly, both lib1 and lib2 need to be shared libraries as I have about 6 executables that need them. The xml resource is needed by lib1, I don't know how to change the code to look for it other than the current dir. I could set its location to install/bin, but that is a bit dirty as if someone installs the package on other machine, the path to file would be different. I doubt that CMAKE environment variables work within C++?
I would also like to have all .h files from both libraries in install/include dir, how should I go about that?
Can someone explain whether this is correct. I am skeptical with line in src where I link libraries. I did not use find library, because it fails, if I hint it with install/lib location, than it says it cannot find .h files needed by the library. I am suspecting that by doing target_link_libraries(M1 lib1) I am directly recompiling the whole library to attach it to executable, rather than finding existing one and doing simple linking.
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.
I am trying to use the assimp library in a cross platform C++ project. I include the repo as a git submodule, so, effectively, if someone downloads my project they will also download the ASSIMP project.
After I go through the assimp build / CMAKE instructions and (on Linux) type make install and from then on in my project I can use:
target_link_libraries(${PROJECT_NAME} assimp)
However, there is no make install on Windows.
The only other way I have been able to include the library on Linux is to put (in my CmakeLists.txt file):
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/build/assimp/code/libassimp.so)
This is not cross platform as it hardcodes the name and location of the .so file which will not work on Windows.
How can I expose the library so that I can do something like target_link_libraries(${PROJECT_NAME} assimp) on all platforms?
My directory tree looks like:
- src
- include
- assimp
- bin
Where the assimp directory in the include directory is the git submodule
I think you're going about this the wrong way. You don't need to build assimp in a separate step from your project, and you don't need to make install to make it available.
There are a number of ways of handling third party dependencies in Cmake, since you've already chosen to submodule the assimp repository, we'll start there. Assuming assimp is located in the root of your repository in a directory called assimp/ this would be a barebones project including it:
cmake_minimum_required(VERSION 3.0)
project(Project myassimpproj)
# include your directories
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
# set any variables you might need to set for your app and assimp
set(BUILD_ASSIMP_TOOLS ON)
set(ASSIMP_BUILD_STATIC_LIB ON)
# add assimp source dir as a subdirectory, effectively making
# assimp's CMakeLists.txt part of your build
add_subdirectory(/path/to/assimp ${CMAKE_BINARY_DIR}/assimp)
add_executable(assimp_target main.cpp)
# be sure to link in assimp, use platform-agnostic syntax for the linker
target_link_libraries(assimp_target assimp)
There may be a better way of phrasing this using generator expressions syntax, but I haven't looked at assimp's CMakeLists.txt to know if it's supported (and this is a more generic way anyway.)
Not every project uses Cmake, so you may not be able to just add_subdirectory(). In those cases, you can effectively "fake" a user call to build them using their build commands on respective platforms. execute_process() runs a command at configure time add_custom_command() and add_custom_target() run commands at build time. You then create a fake target to make integration and cross your fingers they support Cmake someday.
You can also use the ExternalProject commands added to Cmake to create a custom target to drive download, update/patch, configure, build, install and test steps of an external project, but note that this solution and the next download the dependency rather than using the submodule'd source code.
Finally, I prefer to work with prebuilt dependencies, cuts down on build time, and they can be unit tested on their own outside of the project. Conan is an open source, decentralized and multi-platform package manager with very good support for C++ and almost transparent support for Cmake when used the right way. They have grown very stable in the last year. More information on how to use Conan with Cmake can be found here.