CMake: How can I make an IMPORTED library show up in VS - cmake

I have a CMake project that mostly uses 3 types of library
Some libraries are compiled from source
Some libraries are header only (INTERFACE)
There is one library that is IMPORTED. Which means that I provide a .lib, a .dll, and the headers.
For 1 and 2, I like that they show up in the Solution Explorer of Visual Studio.
Which I find very handy for browsing the code.
For case 3, I haven't been able to.
I would like to be able to browse the headers of this library in Visual Studio.
This is what I have tried so far:
project(steam)
if(WIN32)
if(CMAKE_SIZE_OF_VOID_P EQUAL 4)
set(STEAM_API_LIB_PATH libs/win32/steam_api.lib)
set(STEAM_API_DLL_PATH libs/win32/steam_api.dll)
else()
set(STEAM_API_LIB_PATH libs/win64/steam_api64.lib)
set(STEAM_API_DLL_PATH libs/win64/steam_api64.dll)
endif()
elseif(APPLE)
message(FATAL_ERROR "Apple not supported yet")
set(STEAM_API_LIB_PATH libs/osx/libsteam_api.dylib)
elseif(UNIX)
if(CMAKE_SIZE_OF_VOID_P EQUAL 4)
set(STEAM_API_LIB_PATH libs/linux32/libsteam_api.so)
set(STEAM_API_DLL_PATH ${STEAM_API_LIB_PATH})
else()
set(STEAM_API_LIB_PATH libs/linux64/libsteam_api.so)
set(STEAM_API_DLL_PATH ${STEAM_API_LIB_PATH})
endif()
endif()
set(SRCS
isteamapplist.h
isteamapps.h
isteamappticket.h
isteamclient.h
isteamcontroller.h
isteamdualsense.h
isteamfriends.h
isteamgamecoordinator.h
isteamgameserver.h
isteamgameserverstats.h
isteamhtmlsurface.h
isteamhttp.h
isteaminput.h
isteaminventory.h
isteammatchmaking.h
isteammusic.h
isteammusicremote.h
isteamnetworking.h
isteamnetworkingmessages.h
isteamnetworkingsockets.h
isteamnetworkingutils.h
isteamparentalsettings.h
isteamps3overlayrenderer.h
isteamremoteplay.h
isteamremotestorage.h
isteamscreenshots.h
isteamugc.h
isteamuser.h
isteamuserstats.h
isteamutils.h
isteamvideo.h
matchmakingtypes.h
steamclientpublic.h
steamencryptedappticket.h
steamhttpenums.h
steamnetworkingfakeip.h
steamnetworkingtypes.h
steamps3params.h
steamtypes.h
steamuniverse.h
steam_api.h
steam_api.json
steam_api_common.h
steam_api_flat.h
steam_api_internal.h
steam_gameserver.h
)
PREPEND(SRCS "${CMAKE_CURRENT_SOURCE_DIR}/headers/steam/" ${SRCS})
add_library(steam SHARED IMPORTED GLOBAL ${SRCS})
target_include_directories(steam INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/headers/steam)
set_target_properties(
steam
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_SOURCE_DIR}/headers"
IMPORTED_IMPLIB
"{STEAM_API_LIB_PATH}"
IMPORTED_LOCATION
"${STEAM_API_DLL_PATH}"
)
source_group("" FILES ${SRCS})

Related

How to do target_link_libraries in CMake without actual linking?

Now, I know how the question sounds... however, target_link_libraries does more than just linking. And I want this "more" but not the linking. How to do it?
My current setup looks (in a simplified view) like this:
The "3rd Party" target is a PkgConfig created target. The rest is my own.
However, this setup creates a problem in tests. In "Application UTs" I don't want to use the "3rd Party" library. I want to use its stub. To put it in a picture, I would like to have the following instead:
The dotted lines are "something" - it could be targets dependency (to avoid repeating the same information, like include paths), however, I consider it an implementation detail as long as it works.
If I would be in control of the "3rd Party" targets I would just do them like that. But I'm not. I'm receiving .pc files and I have the target created by PkgConfig. While the created target is this single target having it all.
For now, I have solved it by creating a custom function target_nonlink_libraries which propagates the following target properties:
INTERFACE_COMPILE_DEFINITIONS,
INTERFACE_COMPILE_FEATURES,
INTERFACE_COMPILE_OPTIONS,
INTERFACE_INCLUDE_DIRECTORIES,
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES.
(Note, that this does not include all INTERFACE_... properties.)
The code looks like this:
function(target_nonlink_libraries TARGET_NAME SCOPE SOURCE_TARGET)
foreach(source_target IN ITEMS ${SOURCE_TARGET} ${ARGN})
get_target_property(interface_compile_definitions ${source_target} INTERFACE_COMPILE_DEFINITIONS)
if(interface_compile_definitions)
target_compile_definitions(${TARGET_NAME}
${SCOPE}
${interface_compile_definitions}
)
endif()
get_target_property(interface_compile_features ${source_target} INTERFACE_COMPILE_FEATURES)
if(interface_compile_features)
target_compile_definitions(${TARGET_NAME}
${SCOPE}
${interface_compile_features}
)
endif()
get_target_property(interface_compile_options ${source_target} INTERFACE_COMPILE_OPTIONS)
if(interface_compile_options)
target_compile_options(${TARGET_NAME}
${SCOPE}
${interface_compile_options}
)
endif()
get_target_property(interface_include_directories ${source_target} INTERFACE_INCLUDE_DIRECTORIES)
if(interface_include_directories)
target_include_directories(${TARGET_NAME}
${SCOPE}
${interface_include_directories}
)
endif()
get_target_property(interface_system_include_directories ${source_target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
if(interface_system_include_directories)
target_include_directories(${TARGET_NAME} SYSTEM
${SCOPE}
${interface_system_include_directories}
)
endif()
endforeach()
endfunction()
and is later used like this:
target_nonlink_libraries(ApplicationCommon
INTERFACE
PkgConfig::Library
)
target_link_libraries(Application
PRIVATE
PkgConfig::Library
)
The problem here, however, is that I'm not sure that I'm copying all properties (which I care about) and doing it in the correct way.
How to solve this in CMake? Is there any build-in to link targets explicitly skipping actual linking?

External library dependency hierarchies

I am building a project that depends on a third party library that is composed of a fairly large numbers of libraries. The DAG of dependencies between these libraries is clearly defined so for example it might be the following where letters indicate libraries and arrows are dependencies
x -> a, b
y -> a, c
z -> x, b // note I don't need to specify a here as it is implied by x
So what I really want is to be able to express this DAG in CMake and be able to expand dependencies without repetitions. So
Expand( y, z ) -> y, z, x, a, b, c // Note only one a
I will spare you my attempts to do the expand function as I couldn't come up with anything elegant, I am not that good at CMake.
An extra feature would be to detect redundancy in the top level dependencies so
Expand( z, x ) // x not needed as z depends on it
Note CMake already does something like this for internal project library dependencies and uses it in thing like target_link_libraries but these are external so CMake does not know the external dependency tree.
As I understand from CMake documentation, target_link_libraries doesn't work with imported library as first argument. But you can easily develop function, which reads properties from dependent (imported) libraries, and fill corresponded properties for dependee (imported) library. So, resulted imported library can be used with target_link_libraries at the right side.
# _combine_targets_property(VAR PROP target1 target2 ...)
# Helper function: Collects #PROP properties (as lists) from #target1, #target2 ..,
# combines these lists into one and store into variable #VAR.
function(_combine_targets_property VAR PROP)
set(values) # Resulted list
foreach(t ${ARGN})
get_property(v TARGET ${t} PROPERTY ${PROP})
list(APPEND values ${v})
endforeach()
set(${VAR} ${values} PARENT_SCOPE)
endfunction()
# imported_link_libraries(t_dest target1 target2 ...)
# Make imported library target #t_dest effectively linked with #target1, #target2 ...
function(imported_link_libraries t_dest)
# IMPORTED_LOCATION's and INTERFACE_LINK_LIBRARIES's from dependencies
# should be appended to target's INTERFACE_LINK_LIBRARIES.
get_property(v1 TARGET ${t_dest} PROPERTY INTERFACE_LINK_LIBRARIES)
_combine_targets_property(v2 IMPORTED_LOCATION ${ARGN})
_combine_targets_property(v3 INTERFACE_LINK_LIBRARIES ${ARGN})
set(v ${v1} ${v2} ${v3})
list(REMOVE_DUPLICATES v)
set_property(TARGET ${t_dest} PROPERTY INTERFACE_LINK_LIBRARIES ${v})
# INTERFACE_INCLUDE_DIRECTORIES' from dependencies
# should be appended to corresponded target's property.
get_property(v1 TARGET ${t_dest} PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
_combine_targets_property(v2 INTERFACE_INCLUDE_DIRECTORIES ${ARGN})
set(v ${v1} ${v2})
list(REMOVE_DUPLICATES v)
set_property(TARGET ${t_dest} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${v})
# Process other interface properties if needed. E.g., INTERFACE_COMPILE_DEFINITIONS.
...
endfunction()
Usage example:
add_library(a IMPORTED)
set_target_properties(a IMPORTED_LOCATION <a_lib> INCLUDE_DIRECTORIES <a_include_dirs>)
add_library(b IMPORTED)
set_target_properties(b IMPORTED_LOCATION <b_lib> INCLUDE_DIRECTORIES <b_include_dirs>)
add_library(x IMPORTED)
set_target_properties(x IMPORTED_LOCATION <x_lib> INCLUDE_DIRECTORIES <x_include_dirs>)
imported_link_libraries(x a b)
add_executable(my_exec ...)
target_link_libraries(my_exec x)
Note, that imported target names are not tracked in the dependencies, this is similar to target_link_libraries() behaviour. Also redudancy is not detected (but duplicates are removed automatically). Both these features are not natural for CMake, but you can implement them if you want.

How to control the order that CMAKE builds subdirectories when involving options?

I know that CMake automatically somehow figures out the dependency tree. It will build things in the right order normally.
I decided to use options and that has caused it to guess poorly.
I have a variant of this SO question following:
/CMakeLists.txt
/src/CMakeLists.txt
/testpackage1/CMakeLists.txt
/testpackage2/CMakeLists.txt
In my case, mylib has two kinds -- a static and a dynamic.
OPTION(MYLIB_BUILD_STATIC "Build static library" TRUE)
IF(MYLIB_BUILD_STATIC)
ADD_LIBRARY(mylib_static STATIC ${MyLib_SRCS})
...
SET_TARGET_PROPERTIES(mylib_static PROPERTIES OUTPUT_NAME "mylib")
SET(MYLIB_LIB_TARGET mylib_static)
ENDIF(MYLIB_BUILD_STATIC)
OPTION(MYLIB_BUILD_SHARED "Build shared library" TRUE)
IF(MYLIB_BUILD_SHARED)
ADD_LIBRARY(mylib SHARED ${MyLib_SRCS})
...
SET(MYLIB_LIB_TARGET mylib)
ENDIF(MYLIB_BUILD_SHARED)
At this point ${MYLIB_LIB_TARGET} has either mylib or mylib_static in it
In the unit test packages....
OPTION(MYLIB_BUILD_TEST "Build test program." TRUE)
IF(MYLIB_BUILD_TEST)
INCLUDE_DIRECTORIES(${MYLIB_SOURCE_DIR})
ADD_EXECUTABLE(test_mylib main.c)
IF(NOT MYLIB_LIB_TARGET)
MESSAGE(FATAL_ERROR "MYLIB_LIB_TARGET is not specified")
ENDIF()
TARGET_LINK_LIBRARIES(test_mylib ${MYLIB_LIB_TARGET})
ENDIF(MYLIB_BUILD_TEST)
In the parent:
ADD_SUBDIRECTORY("src")
ADD_SUBDIRECTORY("testpackage1")
ADD_SUBDIRECTORY("testpackage2")
What I get is: MYLIB_LIB_TARGET is not specified from the unit test project
I think it's because the options are messing with the automatic dependency tree logic in CMake.

cmake native binary during cross-compile

I'm trying to build a native binary for using during a cross-compile, but CMAKE_CROSSCOMPILING always seems to be set so the ${EXPORT_FILE} never gets the native binary and the ADD_CUSTOM_COMMAND complains that native-uberplac is not found.
Does anyone know how this is supposed to work?
set(EXPORT_FILE ${CMAKE_BINARY_DIR}/ImportExecutables.cmake) file(WRITE ${EXPORT_FILE})
if(CMAKE_CROSSCOMPILING) MESSAGE(INFO "***CMAKE_CROSSCOMPILING SET***") SET(IMPORT_EXECUTABLES ${EXPORT_FILE}) include(${IMPORT_EXECUTABLES}) endif(CMAKE_CROSSCOMPILING)
add_executable(uberplac src/host/uberplac.c) SET_TARGET_PROPERTIES(uberplac PROPERTIES COMPILE_DEFINITIONS "${TARGET_ARCH}") CHECK_LIBRARY_EXISTS(m sin "" uberplac_USE_LIBM) if(uberplac_USE_LIBM) TARGET_LINK_LIBRARIES(uberplac m) endif()
IF(CMAKE_CROSSCOMPILING) SET(uberplac_EXE native-uberplac) ELSE(CMAKE_CROSSCOMPILING) SET(uberplac_EXE uberplac) ENDIF(CMAKE_CROSSCOMPILING)
IF(NOT CMAKE_CROSSCOMPILING) MESSAGE(INFO "***CMAKE_CROSSCOMPILING NOT SET***") EXPORT(TARGETS uberplac FILE ${EXPORT_FILE} NAMESPACE native- ) ENDIF(NOT CMAKE_CROSSCOMPILING)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/van_arch.h COMMAND ${uberplac_EXE} ${CMAKE_CURRENT_SOURCE_DIR}/torpid/torpid.py ${ASM_FLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/van_arch.h ${CMAKE_CURRENT_SOURCE_DIR}/src/vm_${ASM_ARCH}.asc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/torpid/torpid.py uberplac )

External library specific COMPILE_DEFINITIONS in cmake

I've written a cmake module for finding QCustomPlot. However, to use the shared library, one needs to #define QCUSTOMPLOT_USE_LIBRARY. I'd like to provide this define through cmake, automatically adding the definition to any project that uses QCustomPlot.
Here is a snippet of my cmake module and my current attempted solution:
SET(QCP_FOUND "NO")
IF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
SET(QCP_FOUND "YES")
SET_PROPERTY(
GLOBAL
APPEND
PROPERTY COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
ENDIF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
However, no linkers append the -DQCUSTOMPLOT_USE_LIBRARY flag in my compilations. What's the right way to approach this problem?
There is no global property COMPILE_DEFINITIONS. But there are such properties for directory, target and source file (see documentation). So probably the closest commands for you are:
set(QCP_FOUND "NO")
if(QCP_LIBRARY AND QCP_INCLUDE_DIR)
set(QCP_FOUND "YES")
set_directory_properties(
PROPERTIES COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
endif()
But I think that this kind of job is for imported targets:
add_library(QCP::qcp UNKNOWN IMPORTED)
set_target_properties(
QCP::qcp
PROPERTIES
IMPORTED_LOCATION "${QCP_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${QCP_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
and usage:
add_executable(foo ...)
target_link_libraries(foo PUBLIC QCP::qcp)