Determining the cmake target name of a Conan package - conan

I feel like there's a gap in the Conan docs: I can add a package to my conanfile.py, and it can find it, but when I want to actually use it, it's not at all clear what the corresponding name is for cmake's find_package and then for target_link_libraries. For example, while zlib does say find_package(ZLIB) and target_link_libraries(${PROJECT_NAME} ZLIB::ZLIB) https://conan.io/center/zlib?tab=useit&os= that seems to be in docs, and so, e.g., fmt doesn't say anything: ihttps://conan.io/center/fmt?tab=useit
Now, if I look at the "recipe" tab, and squint at the code there's
def package_info(self):
target = "fmt-header-only" if self.options.header_only else "fmt"
self.cpp_info.set_property("cmake_file_name", "fmt")
self.cpp_info.set_property("cmake_target_name", f"fmt::{target}")
self.cpp_info.set_property("pkg_config_name", "fmt")
which makes me think cmake_file_name is fmt and so it's find_package(fmt) and (assuming not self.options.header_only) target == "fmt" and so cmake_target_name is fmt::fmt implying fmt::fmt is what target_link_libraries needs?
zlib has
def package_info(self):
self.cpp_info.set_property("cmake_find_mode", "both")
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
self.cpp_info.set_property("pkg_config_name", "zlib")
...
so I think that's right?
If so, is there a reason conan.io doesn't directly and automatically list this stuff? As a user of a package, what I feel like I need is:
The Conan package name (e.g., fmt/8.1.1)
The cmake file name (e.g., fmt)
The cmake target name (e.g., fmt::fmt)
The header dir and/or header(s) (e.g. "fmt/" or fmt/core.h, fmt/format.h, etc.
Doesn't Conan know all this stuff? Is there really no place it presents it to me?

Related

How to include target include directories transitive through multiple linked libraries

we are working on an embedded project in C/C++ and currently some special needs appeared. Background is there are two compiled libraries which define the same symbols. The compiler allows to create relocatable output modules (with partial linking) and to hide symbols for other compilation units when linking. This also means the output module does not need to have all the symbols defined, this will be done in the final linking. Compiler used is TI LTS1.3.0. I will link directly to the relocatable-section of the manual: https://software-dl.ti.com/codegen/docs/tiarmclang/rel1_3_0_LTS/compiler_manual/linker_description/04_linker_options/linker-output-options.html#stdz0756429
The other part of the project is hardly built on CMake with static libraries which are linked against each other via target_link_libraries.
To get this working I created an "add_executable"-target for each of those both output modules with the same symbols. To those I pass the static-libraries by CMake and get the linked with target_link_libraries.
But now I have a problem. All contents of the static libraries are compiled in each of those output modules. This is unwanted behaviour since as said the final linking does the job of linking the missing stuff - so the static-libraries - to it. This should be done with another add_executable command via CMake as well.
using the target include directories property is not suitable since it only adds the include directories of the given target itself but not of the target the target will include and link against.
So e.g. if you have (pseudo code):
#library A
function( create_libA )
add_library( libA src/A.c )
target_include_directories( libA PUBLIC /inc ) #contains A.h
endfunction()
#library B. different location
function( create_libB LIBA )
add_library( libB src/B.c )
target_link_libraries( libB PUBLIC ${LIBA} )
target_include_directories( libB PUBLIC /inc ) #contains B.h
endfunction()
#target output module with partial linking. Only should link and compile LIBTOBELINKEDIN, not libB. different location.
function( build_part_module LIBB LIBTOBELINKEDIN )
add_executable( outputModuleA src/func.c ) #func.c does include A.h
#following would cause libA and libB also to be compiled and linked in the output due to transitive stuff as I understood, which is unwanted.
target_link_libraries( outputModuleA PUBLIC ${LIBB} ${LIBTOBELINKEDIN} )
#trying this
get_target_property(libBInc ${LIBB} INTERFACE_INCLUDE_DIRECTORIES)
#will only include B.h but not A.h. compilation will fail.
target_include_directories(outputModuleA /inc ${libBInc})
I did not find any solution in Cmake itself to solve this problem. It's confusing me since all the include-directories must be known when the libraries are passed transitive, which is stated in the documentation. But I understand that getting the target include directories of just the passed lib does not include the other ones.
Since target_link_libraries does also not work this way I can only think of a maybe recursive solution? But for that my knowledge is just non-existent.
target_link_libraries with something like HEADERS_ONLY would be helpfull for this job.
Also one can say: if the output module contains all the definitions it won't be a problem, since the linker then knows them and will do its magic.
But this is also unwanted, since we use the generated static-libraries to place them into sections in different regions of the RAM directly. This would then mean to create another linker-script for partial linking which defines sections which then can be again moved. But the more we go this direction, the less we need CMake for it.
Instead of get_target_property use $<TARGET_PROPERTY> generator expression: the property's value, extracted by that expression, already includes transitive propagation:
target_include_directories(outputModuleA PRIVATE
$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>
)
Note, that generator expressions has limited usage: not all functions expects them. Documentation for target_include_directories clearly states that the command supports generator expressions.

How can I understand when something is a variable or a value?

Not sure how to perfectly word this from the title, but I am new to CMake and slowly progressing through the online tutorial.
I am up to Step 4 and sometimes find it confusing when mixing passed values that in my eyes are strings, and thus in all programming languages I expect them to have quotation marks or some sort around them. However sometimes I create new targets with the same names. I will elaborate with an example. I reworded some things from the tutorial to make it a bit more clear for me to see what they actually do.
In the root CMakeLists.txt I have this file,
cmake_minimum_required(VERSION 3.10)
project(My_Project VERSION 1.0)
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
option(USE_MYMATH "Use tutorial provided math implementation" TRUE)
configure_file(src/sqrt.h.in src/sqrt.h)
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
add_executable(compute_square_root src/sqrt.cxx)
target_link_libraries(compute_square_root PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
target_include_directories(compute_square_root PUBLIC "${PROJECT_BINARY_DIR}/src")
Inside of MathFunctions I have
add_library(MathFunctions mysqrt.cxx)
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
Here is where the confusion can come from. Notice that in
add_subdirectory(MathFunctions)
MathFunctions is kind of treated as a string in my eyes, because it is now looking for the directory current_location/MathFunctions. However inside of of the MathFunctions CMakeLists.txt it now creates a target with the exact same spelling from the line "add_library(MathFunctions mysqrt.cxx)", this is then immediately referenced afterwards from the "target_include_directories(MathFunctions, ...".
Here, target_include_directories is referring to the target MathFunctions we just created. Now, when we leave that CMakeLists.txt we now have another line "list(APPEND EXTRA_LIBS MathFunctions)". Now I some confusion, like, is this MathFunctions referring to the target we just made? Is it a string called "MathFunctions"? In the documentation for target_link_libraries it says that it has to be a target created by add_library so I assume it is referring to the previous add_library(MathFunctions ...) call. I find the scoping weird here too, since we are referring to something that was made from a child, inside a different call.
Do we have certain rules in CMake for this kind of behaviour? THanks
All command parameters are treated as strings in cmake. Parameters are separated by whitespace unless quoted. The exact effect of a parameter depends on the command.
The following commands have the same effect:
add_subdirectory(MathFunctions)
add_subdirectory("MathFunctions")
In the case of add_library the first parameter is treated as the target name. CMake internally keeps track of targets and stores several pieces of information for them. The target name MathFunctions is entirely unrelated to the name of the subdirectory added via add_subdirectory; you could rename the directory to FooBar and use add_subdirectory(FooBar) and nothing would change.
There are several commands you pass the target name to to modify the properties of the cmake target as well as commands that treat the name of cmake targets specially e.g.:
target_include_directories: the target to modify the [INTERFACE_]INCLUDE_DIRECTORIES property for is passed
target_link_directories: the target to modify the [INTERFACE_]LINK_DIRECTORIES property for is passed
set_target_properties: One or more targets to set properties for are passed
target_link_libraries: The cmake target linked to is passed. Furthermore cmake library targets may be specified as libraries to be linked to the target.
add_test: If you use the name of a cmake target in the COMMAND part, the test logic uses the path to the target instead.
...
As for scope:
Variable values you write are actually set for the current scope only, but reading variables cmake looks into ancestor scopes, unless a variable is found in the current scope. CMake targets are visible in the whole cmake project though from the point of the parsing of the command creating the target(add_library, add_executable, add_custom_target): cmake targets are "global objects". (Exceptions exist for imported libs and alias targets, but that's probably nothing relevant to you right now.)

change cpack generated name for component RPMs

I am trying to create an RPM installer using cpack (via cmake) that creates multiple RPM files. I've almost got it working but the last remaining stumbling block is controlling the package names.
Specifically I want to control where the component name appears in the RPM file name.
Here is my experimental CMakeLists.txt file which installs foo.txt and bar.txt to two different packages "myproject-foo" & "myproject-bar".
I want the RPMs to follow my own naming conventions (actually I want conventions that are more FSH friendly) but for some reason cmake insists on adding the component name at the end. So I get:
myproject-comp-1.0.i686-foo.rpm
myproject-comp-1.0.i686-bar.rpm
rather than:
myproject-compfoo-1.0.i686.rpm
myproject-compbar-1.0.i686.rpm
I notice it names the source RPMs "more correctly":
E.g.
>rpm -qip <package> | grep source
Source RPM : myproject-foo-1.0-1.src.rpm
Though I do not actually want a source RPM at all (is there a way to blank this field?)
cmake_minimum_required(VERSION 2.8)
message("CMAKE_VERSION=${CMAKE_VERSION}")
SET(DOCDIR "doc")
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "core")
install(FILES foo.txt COMPONENT foo DESTINATION "${RPMBUILDROOT}${DOCDIR}")
install(FILES bar.txt COMPONENT bar DESTINATION "${RPMBUILDROOT}${DOCDIR}")
project(myproject CXX)
set(CPACK_PACKAGE_NAME "myproject")
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_PACKAGE_VERSION 1.0)
set(CPACK_PACKAGE_VENDOR "some org")
set(CPACK_COMPONENT_foo_DESCRIPTION "the foo component")
set(CPACK_COMPONENT_bar_DESCRIPTION "the bar component")
# mentioned in cpack docs but doesn't seem to do anything here
set(CPACK_COMPONENT_foo_DISPLAY_NAME "foo display name?")
set(CPACK_COMPONENT_bar_DISPLAY_NAME "foo display name?")
set(CPACK_COMPONENT_bar_DEPENDS foo)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "describe the complete suite of stuff")
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README.txt)
# added in cmake 3.6?
set(CPACK_RPM_foo_PACKAGE_SUMMARY "describe the foo package")
set(CPACK_RPM_bar_PACKAGE_SUMMARY "describe the bar package")
set(CPACK_RPM_foo_PACKAGE_NAME "${CPACK_PACKAGE_NAME}-foo")
set(CPACK_RPM_bar_PACKAGE_NAME "${CPACK_PACKAGE_NAME}-bar")
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README.txt)
#set(CPACK_RPM_foo_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-foo-${CPACK_PACKAGE_VERSION}.i686")
#set(CPACK_RPM_bar_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-bar-${CPACK_PACKAGE_VERSION}.i686")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-comp${CPACK_COMPONENT_DISPLAY_NAME}-${CPACK_PACKAGE_VERSION}.i686")
set(CPACK_RPM_PACKAGE_ARCHITECTURE "i686")
set(CPACK_RPM_PACKAGE_RELOCATABLE TRUE)
include(CPack)
cpack_add_component(foo
DISPLAY_NAME foo comp
DESCRIPTION this is the foocomp)
cpack_add_component(bar
DISPLAY_NAME bar comp
DESCRIPTION this is the bar comp
DEPENDS foo)
It seems like CPACK_PACKAGE_<component>_FILE_NAME or CPACK_PACKAGE_FILE_NAME is not quite working.
I've tried with both cmake 3.4.3 and the latest (3.9.0 at the time of writing) as there is some suggestion of fixes to component based installs in 3.6.0
There is no such variable as the CPACK_COMPONENT_DISPLAY_NAME I've used in CPACK_PACKAGE_FILE_NAME. Also something in cmake adds the component name to the end automatically.
There is a similar question for .deb packages from long ago here but I would rather avoid such a complex solution if possible.
It would be easier to rename the packages after generation (but still feel hacky).
I also note that the package dependency set(CPACK_COMPONENT_bar_DEPENDS foo) is not recorded in the bar package.
Update: 01-Aug-2017
I've added a bug report for cmake here as to my mind there is a bug in the implementation of CPACK_RPM_<component>_PACKAGE_FILE_NAME. At the very least the documentation is insufficiently helpful.
First of there is a large note at the top of the documentation:
Note
part of variables is preferred to be in upper case (for e.g. if component is named foo then use CPACK_RPM_FOO_XXXX variable name format) as is with other CPACK__XXXX variables. For the purposes of back compatibility (CMake/CPack version 3.5 and lower) support for same cased component (e.g. fOo would be used as CPACK_RPM_fOo_XXXX) is still supported for variables defined in older versions of CMake/CPack but is not guaranteed for variables that will be added in the future. For the sake of back compatibility same cased component variables also override upper cased versions where both are present.
So if you have a component named foo, FOO, fOo... the variables are CPACK_RPM_FOO_... otherwise the variables will be ignored - Note that this is the case since the beginning of CPack's existance for the common component variables e.g. CPACK_COMPONENT_foo_DESCRIPTION <- foo part must be in upper case.
Next to the file names - before CPack 3.6 it was not possible to change file names without getting an error and having to copy the generated package manually. Since that version you can set the file name as you like or request the platform specific package name (defined by rpm installation) by setting CPACK_RPM_FILE_NAME or CPACK_RPM_<component>_FILE_NAME to RPM-DEFAULT. See https://cmake.org/cmake/help/v3.6/module/CPackRPM.html?highlight=cpack_rpm_file_name#variable:CPACK_RPM_FILE_NAME
Also set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-comp${CPACK_COMPONENT_DISPLAY_NAME}-${CPACK_PACKAGE_VERSION}.i686") won't work as ${CPACK_COMPONENT_DISPLAY_NAME} is not an anchor and is automatically replaced on that location - not set by you so replacement text is an empty string... So if you really want to set the name manually you should set the RPM packager equivalent of that variable CPACK_RPM_<component>_FILE_NAME for each and every component (so you'll need a for loop or some more copy pasting...) - note that as described in the documentation the file name must end with .rpm and the default is (copied from the documentation) <CPACK_PACKAGE_FILE_NAME>[-<component>].rpm so this is the explanation why component part is always placed at the end.
There are quite a few tests for different CPack packagers here: https://github.com/Kitware/CMake/tree/master/Tests/RunCMake/CPack/tests
So you can explore those snippets as well.
P.S. contributions to the documentation are always welcome :)

CMake: collecting libraries

I am using CMake to build a simple C++ project, hereafter named P. The structure of P is quite simple:
P/src/
P/src/package1
P/src/packege2
P/src/...
P/src/main-app
I would like to collect the libraries in package1, package2, ... in a variable called P_LIBS.
In a first attempt, I tried to collect the libraries available in package1, package2, ... in the variable called P_LIBS initially set in the src/CMakeLists.txt file. However, the updates to P_LIBS made in the CMakeLists.txt of the subfolders are not propagated to the parent folder.
I would rather not write a list of libraries in the main CMakeLists.txt file. I would rather modify such variable while moving in the directory tree.
After a search on the internet I could not find any valid suggestion. If I look at the various Find files, I only see long listings of libraries in their main CMakeLists.txt file.
Is there a way to do what (I hope) I explained above?
Thanks to sakra's link I was able to 'propagate' names up to the parent folder. However, the names I add to the P_LIBS variable are later interpreted as 'library' names, not as reference to CMake targets. In other words, if
P_LIBS = {a, b}
the 'a' and 'b' are interpreted as the library names, i.e. CMake generates:
gcc [...] -l a -o exe
instead of
gcc [...] /path/to/a.o -o exe
(.o or other extensions)
You are propably constructing the targets list as a string, try to make them a list instead. For example:
# in package1/CMakeLists.txt
set(P_LIBS ${P_LIBS} a b PARENT_SCOPE)

Dealing with os/platform specific code in cmake

In the multi-platform/os project I'm working on, I'm striving to simplify the platform specific code into subdirectories, with a common directory holding a common implementation. I have a prototype implementation in autotools, but want to move to a cmake implementation if possible. My current sticking point is how to support multiple OSes. My current filesystem structure looks like the following:
/* A common header file for all platforms that presents an interface */
include/my_socket_utils.h
include/my_time_utils.h
/* Platform specific source files */
src/common/my_socket_utils.cpp
src/linux/my_socket_utils.cpp
src/vxworks/my_socket_utils.cpp
src/qnx/my_socket_utils.cpp
src/common/my_time_utils.cpp
src/vxworks/my_time_utils.cpp
The idea is that there is a common interface and a "common" implementation. The implementation is either a stub or a common implementation written to a posix standard that allows it to work for most platforms. Those platforms that require a custom implementation MAY override the common one, but it's optional.
With autotools, I am able to achieve this using VPATH to set a source tree hierarchy, so I set:
VPATH=#srcdir#/src/#target_platform#;#srcdir#/src/common
This makes autotools look for the source file in src/#target_platform# first, then, if it wasn't found, grab it from src/common.
What is the cmake way to do this?
Update:
To help all those lost souls in need, this is what I ended up doing for the time being. I'm not sure it's the best solution, but it works well enough.
FILE(GLOB common_files "src/common/.c")
FILE(GLOB platform_files "src/${os}/.c)
Then, do the dirty n^2 algorithm to override. Not sure how to do any better in cmake "script", but the number of files is low, so it's plenty fast. The diag messages are, of course, optional.
#
# For each common file, check to see if a platform file exists to override it.
#
foreach(fqfn ${common_files})
set(platform_override FALSE)
get_filename_component(filename ${fqfn} NAME)
#
# If filename exists in platform, override it with the platform,
# otherwise fall back to the common implementation. Oh for a real
# language.
#
foreach(platform_fqfn ${platform_files})
get_filename_component(platform_filename ${platform_fqfn} NAME)
message("pf=${platform_filename} cf=${filename}")
if(filename STREQUAL platform_filename)
message("filename == platform_filename")
list(APPEND proj_files ${platform_fqfn})
set(platform_override TRUE)
endif(filename STREQUAL platform_filename)
endforeach(platform_fqfn ${platform_files})
if(NOT ${platform_override})
list(APPEND proj_files ${fqfn})
message("Appended ${fqfn}")
endif(NOT ${platform_override})
endforeach(fqfn ${common_files})
message("proj_files=${proj_files}")
add_executable (cc_dfi_main ${proj_files})
One possible way is to define variable TARGET_BUILD_PLATFORM and set it to exact platform you want to build (linux/qnx/vxworks).
set(PROJECT_NAME some_name_for_project)
project(${PROJECT_NAME} CXX)
file(GLOB COMMON_SRC ${PROJECT_SOURCE_DIR}/common/*.cpp)
file(GLOB PLATFORM_SRC ${PROJECT_SOURCE_DIR}/${TARGET_BUILD_PLATFORM}/*.cpp)
set(SRC_FILES ${COMMON_SRC} ${PLATFORM_SRC})
add_executable(${PROJECT_NAME} ${SRC_FILES})