Iterate the dependency graph in CMake to generate list of link library files used for CGO - c++

I want to iterate over all link libraries of a target target to build a list of all library binaries (static libraries) which target links to, e.g.
add_executable(exe main.cpp)
target_link_libraries(exe fmt::fmt otherExe)
We want a list of all library binary files (by using $<TARGET_LINKER_FILE:tgt>) for dependent targets fmt::fmt and otherExe to generate the apropriate CGO interface.go file with CFLAGS and LDFLAGS.
The thing is, its hard to write a loop in CMake to iterate over link dependencies of target since
properties like LINK_LIBRBARIES and INTERFACE_LINK_LIBRARIES are in general lists but might contain generator expressions, as in the case for interface library target fmt::fmt which comes from Conan where INTERFACE_LINK_LIBRARIES resolves to
$<$<CONFIG:Release>:CONAN_LIB::fmt_fmt_RELEASE;>. which contains a semicolon ";" which is then treated itself as a list with $<$<CONFIG:Release>:CONAN_LIB::fmt_fmt_RELEASE; and >.
This is due to that Conan adds the following
set_property(TARGET fmt::fmt
PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:Release>:${fmt_LIBRARIES_TARGETS_RELEASE}
${fmt_OBJECTS_RELEASE}> APPEND)
Anybody has a solution to this?

We found a solution by using the file-api of Cmake, we setup a "configure" add_custom_target in CMake which is a pyhton script which is reading the reply for the 'codemodel-v2' (file-api): parsing the JSON output file for the target of interest. Then
gathering all linker and compiler flags from this JSON and then configure a Go file with these flags.
Include files have been extracted by passing $<TARGET_PROPERTY:targetName,INCLUDE_DIRECTORIES> to the add_custom_command.

Related

Force CMake target_link_libraries to fail when adding nonexistent target

CMake has an irritating default (I presume, I see nothing magical in my CMake config, but I could be wrong since I know very little about CMake) behavior that he silently ignores when you add a target to your project even if that target does not exist, for example:
project(StackOverflow)
// another CMakeLists.txt
project (Stuff)
target_link_libraries(Stuff
PUBLIC StackOverlow )
Is there a way to force CMake to check that all projects you link in target_link_libraries must exist?
It is possible for CMake to fail if you link ALIAS targets. For example
In first CMakeLists.txt
add_library(StackOverflow STATIC lib.cpp)
add_library(StackOverflow::StackOverflow ALIAS StackOverflow)
In second CMakeLists.txt
target_link_libraries(Stuff PUBLIC StackOverflow::StackOverflow)
CMake will fail with an error if StackOverflow::StackOverflow is not defined.
https://cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html#alias-targets
In CMake, you do not link projects to other projects. Instead, you link targets to other targets.
CMake targets are only created via a few commands (such as add_library, add_executable, and add_custom_target). The project command does not create a CMake target, it merely declares a project.
Furthermore, the target_link_libraries() command accepts the following arguments after the scoping keyword:
A library target name
A full path to a library file
A plain library name
A link flag
A generator expression
A debug, optimized, or general keyword
It does not accept project names, although if you put a project name, it will instead look for a CMake target or library file on your system with that name.
To get to the root of what I believe you're asking: If you provide link-item name to target_link_libraries() that does not match an existing target, the command will simply search for a library file of that name instead.
To check if a target exists before trying to link it, you can do:
if (TARGET StackOverflow)
target_link_libraries(Stuff PUBLIC StackOverflow)
endif()
I suggest reading through the linked target_link_libraries() documentation if you want more details about what this command does.

How to link external C++ library to my project on Mac OSX [duplicate]

I have 2 folders "inc" and "lib" in my project which have headers and static libs respectively. How do I tell cmake to use those 2 directories for include and linking respectively?
The simplest way of doing this would be to add
include_directories(${CMAKE_SOURCE_DIR}/inc)
link_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(foo ${FOO_SRCS})
target_link_libraries(foo bar) # libbar.so is found in ${CMAKE_SOURCE_DIR}/lib
The modern CMake version that doesn't add the -I and -L flags to every compiler invocation would be to use imported libraries:
add_library(bar SHARED IMPORTED) # or STATIC instead of SHARED
set_target_properties(bar PROPERTIES
IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libbar.so"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libbar"
)
set(FOO_SRCS "foo.cpp")
add_executable(foo ${FOO_SRCS})
target_link_libraries(foo bar) # also adds the required include path
If setting the INTERFACE_INCLUDE_DIRECTORIES doesn't add the path, older versions of CMake also allow you to use target_include_directories(bar PUBLIC /path/to/include). However, this no longer works with CMake 3.6 or newer.
You had better use find_library command instead of link_directories. Concretely speaking there are two ways:
designate the path within the command
find_library(NAMES gtest PATHS path1 path2 ... pathN)
set the variable CMAKE_LIBRARY_PATH
set(CMAKE_LIBRARY_PATH path1 path2)
find_library(NAMES gtest)
the reason is as flowings:
Note This command is rarely necessary and should be avoided where there are other choices. Prefer to pass full absolute paths to
libraries where possible, since this ensures the correct library will
always be linked. The find_library() command provides the full path,
which can generally be used directly in calls to
target_link_libraries(). Situations where a library search path may be
needed include: Project generators like Xcode where the user can
switch target architecture at build time, but a full path to a library
cannot be used because it only provides one architecture (i.e. it is
not a universal binary).
Libraries may themselves have other private library dependencies that
expect to be found via RPATH mechanisms, but some linkers are not able
to fully decode those paths (e.g. due to the presence of things like
$ORIGIN).
If a library search path must be provided, prefer to localize the
effect where possible by using the target_link_directories() command
rather than link_directories(). The target-specific command can also
control how the search directories propagate to other dependent
targets.
might fail working with link_directories, then add each static library like following:
target_link_libraries(foo /path_to_static_library/libbar.a)

SWIG and CMake: make use of information provided by `target_include_directories()`

With CMake 2.8+ you can avoid setting include directories redundantly by using target_include_directories().
E.g. by writing
add_libary(mylib SHARED ${SOURCES})
target_include_directories(mylib PUBLIC ./include)
.. you just have to link against mylib to add the needed include folder to your target.
But how can I make use of this information when I have to use CMake modules which don't make use of this capability yet? (in my case SWIG)
When I configure a SWIG project I currently have to hard code a lot of information:
set(SWIG_MODULE_${PYTHON_MODULE_NAME}_EXTRA_DEPS
"../long/relative/path/1/include/some/header1.h"
"../long/relative/path/1/include/some/header2.h"
"../long/relative/path/2/include/some/header1.h"
"../long/relative/path/2/include/some/header2.h")
I also have to use the old fashioned include_directories() to make the swig generator know what it needs to know:
include_directories(
"../long/relative/path/1/include
"../long/relative/path/2/include)
Otherwise the %include statements inside .i files won't work any more.
Of course I could set variables containing the paths but then I would provide the information I wanted to get rid of..
Is there a way to either extract the directory information from a target or (better of course) make the SWIG CMake module correctly use it?
My current solution:
With some (very beautiful) CMake magic you can automate listing all header files from the interface part of a library and set the include directories:
function(swig_add_library_dependencies swig_module library_names)
foreach(library_name ${library_names})
get_property(LIBRARY_INCLUDES
TARGET ${library_name}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
foreach(INCLUDE_PATH ${LIBRARY_INCLUDES})
include_directories(${INCLUDE_PATH})
file(GLOB_RECURSE header_files "${INCLUDE_PATH}/*.h")
list(APPEND SWIG_MODULE_${swig_module}_EXTRA_DEPS ${header_files})
# export variable to parent scope
set(SWIG_MODULE_${swig_module}_EXTRA_DEPS
${SWIG_MODULE_${swig_module}_EXTRA_DEPS} PARENT_SCOPE)
endforeach()
endforeach()
endfunction()
to be used like this:
swig_add_library_dependencies(<swig_module_name> "library1;library2")
or discretely like this:
swig_add_library_dependencies(<swig_module_name> library1)
swig_add_library_dependencies(<swig_module_name> library2)
Disadvantages:
uses GLOB_RECURSE
only works if target_include_directories had been used correctly
creates dependencies to all header files found in include directories
Have a look at the documentation for get_property:
https://cmake.org/cmake/help/v3.0/command/get_property.html?highlight=get_property
you would do something like this:
get_property(MY_INCLUDES TARGET my_target PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
to get the interface include directories from the target my_target and store them in the variable MY_INCLUDES

How does CMake know which prefixes and suffixes to add to shared libraries?

I am trying to use INSTALL in CMake to copy some external binaries to an install directory. My code goes like:
SET(SimTK_SHARED_LIBS
SimTKsimbody
SimTKmath
SimTKcommon
SimTKmolmodel
)
INSTALL(TARGETS ${SimTK_SHARED_LIBS}
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
I get this error:
CMake Error at CMakeLists.txt:216 (INSTALL):
install TARGETS given target "SimTKsimbody" which does not exist in this
directory.
this is in spite of putting files called both libSimTKsimbody.so and (incorrectly) SimTKsimbody in the current directory as well as in the library directory.
Interestingly, this:
SET(SHARED_MMB_TARGET MMBlib)
ADD_LIBRARY(${SHARED_MMB_TARGET} SHARED
${MMB_LIBRARY_SOURCE_FILES}
${MMB_HEADER_FILES})
SET_TARGET_PROPERTIES(${SHARED_MMB_TARGET}
PROPERTIES
COMPILE_FLAGS "-DMMB_BUILDING_SHARED_LIBRARY"
PROJECT_LABEL "MMBlib (dynamic)")
TARGET_LINK_LIBRARIES(${SHARED_MMB_TARGET}
${SimTK_SHARED_LIBS_D}
${SimTK_SHARED_LIBS}
${OpenMM_SHARED_LIBS_D}
${OpenMM_SHARED_LIBS}
${SimTK_GENERAL_LIBS})
INSTALL(TARGETS ${SHARED_MMB_TARGET}
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
)
.. works fine. It installs libMMBlib.so in ${CMAKE_INSTALL_PREFIX}/lib as it should. Does this mean that INSTALL will only work for this if I issue ADD_LIBRARY and/or SET_TARGET_PROPERTIES? The SimTK_SHARED_LIBS are compiled separately, I really do not want to compile them here.
I have thought about using INSTALL FILES, and just writing code to process the library names for each operating system. However I am convinced that CMake has the means to do this for me easily and elegantly.
Many thanks
Sam
Yes, you should use INSTALL(FILES) for install external libraries files.
CMake uses CMAKE_SHARED_LIBRARY_PREFIX and CMAKE_SHARED_LIBRARY_SUFFIX as default prefix and suffix for libraries created with add_library(... SHARED), so you may expect these components from external library:
INSTALL(FILES /path/to/library/${CMAKE_SHARED_LIBRARY_PREFIX}SimTKsimbody${CMAKE_SHARED_LIBRARY_SUFFIX}
...)
Also you may use FIND_LIBRARY for automatic(and nice) check of your expectations about library suffix and prefix:
FIND_LIBRARY(SIMTK_SIMBODY_LIB
${CMAKE_SHARED_LIBRARY_PREFIX}SimTKsimbody${CMAKE_SHARED_LIBRARY_SUFFIX}
PATH /path/to/library)
INSTALL(FILES ${SIMTK_SIMBODY_LIB} ...)

cmake - find_library - custom library location

I'm currently trying to get CMake running for my project (on windows). I want to use a custom location where all libraries are installed. To inform CMake about that path I tried to do that:
set(CMAKE_PREFIX_PATH D:/develop/cmake/libs)
But when I try to find the library with
find_library(CURL_LIBRARY NAMES curl curllib libcurl_imp curllib_static)
CMake can't find it.
When I set my prefix path to
set(CMAKE_PREFIX_PATH D:/develop/cmake/libs/curl)
... the library is located.
So my question is:
How can I configure CMake properly to work with a directory structore at a custom location which looks like that:
D:/develop/cmake/libs/
-> libA
-> include
-> lib
-> libB
-> include
-> lib
-> ...
-> include
-> lib
In "include" lie the public headers and in "lib" are the compiled libraries.
edit:
The current workaround for me is, to do this before i search for libraries:
set(CUSTOM_LIBRARY_PATH D:/develop/cmake/libs)
file(GLOB sub-dir ${CUSTOM_LIBRARY_PATH}/*)
foreach(dir ${sub-dir})
if(IS_DIRECTORY ${dir})
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};${dir})
endif()
endforeach()
But that way the default module for boost wont find it until it because the directory structore of boost is a bit different.
boost -> include -> boost-1_50 -> *.hpp
When I move the content if "boost-1_50" to "include" the library can be found but that way it's not possible to handle multiple versions right?
The simplest solution may be to add HINTS to each find_* request.
For example:
find_library(CURL_LIBRARY
NAMES curl curllib libcurl_imp curllib_static
HINTS "${CMAKE_PREFIX_PATH}/curl/lib"
)
For Boost I would strongly recommend using the FindBoost standard module and setting the BOOST_DIR variable to point to your Boost libraries.
I saw that two people put that question to their favorites so I will try to answer the solution which works for me:
Instead of using find modules I'm writing configuration files for all libraries which are installed. Those files are extremly simple and can also be used to set non-standard variables. CMake will (at least on windows) search for those configuration files in
CMAKE_PREFIX_PATH/<<package_name>>-<<version>>/<<package_name>>-config.cmake
(which can be set through an environment variable).
So for example the boost configuration is in the path
CMAKE_PREFIX_PATH/boost-1_50/boost-config.cmake
In that configuration you can set variables. My config file for boost looks like that:
set(boost_INCLUDE_DIRS ${boost_DIR}/include)
set(boost_LIBRARY_DIR ${boost_DIR}/lib)
foreach(component ${boost_FIND_COMPONENTS})
set(boost_LIBRARIES ${boost_LIBRARIES} debug ${boost_LIBRARY_DIR}/libboost_${component}-vc110-mt-gd-1_50.lib)
set(boost_LIBRARIES ${boost_LIBRARIES} optimized ${boost_LIBRARY_DIR}/libboost_${component}-vc110-mt-1_50.lib)
endforeach()
add_definitions( -D_WIN32_WINNT=0x0501 )
Pretty straight forward + it's possible to shrink the size of the config files even more when you write some helper functions. The only issue I have with this setup is that I havn't found a way to give config files a priority over find modules - so you need to remove the find modules.
Hope this this is helpful for other people.
Use CMAKE_PREFIX_PATH by adding multiple paths (separated by semicolons and no white spaces). You can set it as an environmental variable to avoid having absolute paths in your cmake configuration files
Notice that cmake will look for config file in any of the following folders
where is any of the path in CMAKE_PREFIX_PATH and name is the name of the library you are looking for
<prefix>/ (W)
<prefix>/(cmake|CMake)/ (W)
<prefix>/<name>*/ (W)
<prefix>/<name>*/(cmake|CMake)/ (W)
<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/ (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/ (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/ (U)
In your case you need to add to CMAKE_PREFIX_PATH the following two paths:
D:/develop/cmake/libs/libA;D:/develop/cmake/libB
There is no way to automatically set CMAKE_PREFIX_PATH in a way you want. I see following ways to solve this problem:
Put all libraries files in the same dir. That is, include/ would contain headers for all libs, lib/ - binaries, etc. FYI, this is common layout for most UNIX-like systems.
Set global environment variable CMAKE_PREFIX_PATH to D:/develop/cmake/libs/libA;D:/develop/cmake/libs/libB;.... When you run CMake, it would aautomatically pick up this env var and populate it's own CMAKE_PREFIX_PATH.
Write a wrapper .bat script, which would call cmake command with -D CMAKE_PREFIX_PATH=... argument.
You have one extra level of nesting.
CMAKE will search under $CMAKE_PREFIX_PATH/include for headers and $CMAKE_PREFIX_PATH/libs for libraries.
From CMAKE documentation:
For each path in the CMAKE_PREFIX_PATH list, CMake will check
"PATH/include" and "PATH" when FIND_PATH() is called, "PATH/bin" and
"PATH" when FIND_PROGRAM() is called, and "PATH/lib and "PATH" when
FIND_LIBRARY() is called.
I've encountered a similar scenario. I solved it by adding in this following code just before find_library():
set(CMAKE_PREFIX_PATH /the/custom/path/to/your/lib/)
then it can find the library location.